initial snapshot, trunk@5966

Change-Id: I5c9aa88bd82f150da9f5b1152bc2d5aa4baaabb2
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b10e128
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+.DS_Store
+.gclient*
+/gyp/build/
+/out/
+/third_party/externals/
+/xcodebuild/
diff --git a/DEPS b/DEPS
new file mode 100644
index 0000000..016213e
--- /dev/null
+++ b/DEPS
@@ -0,0 +1,25 @@
+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/gyp" : "http://gyp.googlecode.com/svn/trunk@1517",
+  "third_party/externals/libjpeg" : "http://src.chromium.org/svn/trunk/src/third_party/libjpeg@125399",
+  "third_party/externals/jsoncpp" : "http://src.chromium.org/svn/trunk/src/third_party/jsoncpp@125399",
+  "third_party/externals/jsoncpp/source/include" : "http://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/include@248",
+  "third_party/externals/jsoncpp/source/src/lib_json" : "http://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/src/lib_json@248",
+}
+
+#hooks = [
+#  {
+#    # A change to a .gyp, .gypi, or to GYP itself should run the generator.
+#    "pattern": ".",
+#    "action": ["python", "trunk/gyp_skia"],
+#  },
+#]
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..eafa01a
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,50 @@
+# To update the Doxygen output checked into the Skia subversion repo (which is
+# browsable at http://skia-autogen.googlecode.com/svn/docs/html/index.html ), run:
+# tools/update-doxygen.sh
+
+PROJECT_NAME = skia
+PROJECT_BRIEF = 2D Graphics Library
+OUTPUT_DIRECTORY = ../docs
+HTML_FOOTER = ../docs/static_footer.txt
+
+EXTRACT_ALL = NO
+INHERIT_DOCS = YES
+INLINE_INHERITED_MEMB = NO
+JAVADOC_AUTOBRIEF = YES
+TAB_SIZE = 4
+WARN_IF_UNDOCUMENTED = NO
+
+# This file only creates documentation for the most important parts of the
+# external-visible API.
+INPUT = include/core include/effects
+EXTRACT_PRIVATE = YES
+EXTRACT_STATIC = YES
+
+HTML_DYNAMIC_SECTIONS = NO
+GENERATE_TREEVIEW = YES
+
+GENERATE_LATEX = NO
+
+# Good class diagrams require graphviz, but also more parameter tuning and
+# more build time than seems worthwhile.
+CLASS_DIAGRAMS = YES
+# HAVE_DOT = YES
+# CLASS_GRAPH = YES
+# COLLABORATION_GRAPH = YES
+# UML_LOOK = YES
+# GRAPHICAL_HIERARCHY = YES 
+
+# Make SkDEBUGCODE disappear, but not SK_OVERRIDE.
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = YES
+EXPAND_AS_DEFINED = SkDEBUGCODE
+
+# experimental evil only! inflates build time by 10 minutes
+# SEARCH_INCLUDES = YES
+# INCLUDE_GRAPH = YES
+# INCLUDED_BY_GRAPH = YES
+# DIRECTORY_GRAPH = YES
+# INTERACTIVE_SVG = YES
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e74c256
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..20dc224
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,114 @@
+# Makefile that wraps the Gyp and build steps for Unix and Mac (but not Windows)
+# Uses "make" to build on Unix, and "xcodebuild" to build on Mac.
+#
+# Some usage examples (tested on both Linux and Mac):
+#
+#   # Clean everything
+#   make clean
+#
+#   # Build and run tests (in Debug mode)
+#   make tests
+#   out/Debug/tests
+#
+#   # Build and run tests (in Release mode)
+#   make tests BUILDTYPE=Release
+#   out/Release/tests
+#
+#   # Build bench and SampleApp (both in Release mode), and then run them
+#   make SampleApp bench BUILDTYPE=Release
+#   out/Release/bench -repeat 2
+#   out/Release/SampleApp
+#
+#   # Build all targets (in Debug mode)
+#   make
+#
+# If you want more fine-grained control, you can run gyp and then build the
+# gyp-generated projects yourself.
+#
+# See http://code.google.com/p/skia/wiki/DocRoot for complete documentation.
+
+BUILDTYPE ?= Debug
+CWD := $(shell pwd)
+ALL_TARGETS := skia_base_libs \
+               bench \
+               gm \
+               SampleApp \
+               tests \
+               tools
+
+ifneq (,$(findstring skia_os=android, $(GYP_DEFINES)))
+  ALL_TARGETS += SkiaAndroidApp
+endif
+
+# Default target.  This must be listed before all other targets.
+.PHONY: default
+default: $(ALL_TARGETS)
+
+# As noted in http://code.google.com/p/skia/issues/detail?id=330 , building
+# multiple targets in parallel was failing.  The special .NOTPARALLEL target
+# tells gnu make not to run targets within _this_ Makefile in parallel, but the
+# recursively invoked Makefile within out/ _is_ allowed to run in parallel
+# (so you can still get some speedup that way).
+.NOTPARALLEL:
+
+uname := $(shell uname)
+ifneq (,$(findstring CYGWIN, $(uname)))
+  $(error Cannot build using Make on Windows. See http://code.google.com/p/skia/wiki/GettingStartedOnWindows)
+endif
+
+.PHONY: all
+all: $(ALL_TARGETS)
+
+.PHONY: clean
+clean:
+	rm -rf out xcodebuild
+
+# Add the debugger to the target list after the 'all' target is defined so that the
+# debugger is only executed with 'make debugger' and not 'make all' as well. The reason
+# for this is unless the user has Qt installed the debugger target will fail.
+ALL_TARGETS += debugger
+
+# Run gyp no matter what.
+.PHONY: gyp
+gyp:
+	$(CWD)/gyp_skia
+
+# Run gyp if necessary.
+#
+# On Linux, only run gyp if we haven't already generated the platform-specific
+# Makefiles.  If the underlying gyp configuration has changed since these
+# Makefiles were generated, they will rerun gyp on their own.
+#
+# This does not work for Mac, though... so for now, we ALWAYS rerun gyp on Mac.
+# TODO(epoger): Figure out a better solution for Mac... maybe compare the
+# gypfile timestamps to the xcodebuild project timestamps?
+.PHONY: gyp_if_needed
+gyp_if_needed:
+ifneq (,$(findstring Linux, $(uname)))
+	$(MAKE) out/Makefile
+endif
+ifneq (,$(findstring Darwin, $(uname)))
+	$(CWD)/gyp_skia
+endif
+
+out/Makefile:
+	$(CWD)/gyp_skia
+
+# For all specific targets: run gyp if necessary, and then pass control to
+# the gyp-generated buildfiles.
+#
+# For the Mac, we create a convenience symlink to the generated binary.
+.PHONY: $(ALL_TARGETS)
+$(ALL_TARGETS):: gyp_if_needed
+ifneq (,$(findstring skia_os=android, $(GYP_DEFINES)))
+	$(MAKE) -C out $@ BUILDTYPE=$(BUILDTYPE)
+else ifneq (,$(findstring Linux, $(uname)))
+	$(MAKE) -C out $@ BUILDTYPE=$(BUILDTYPE)
+else ifneq (,$(findstring Darwin, $(uname)))
+	rm -f out/$(BUILDTYPE) || if test -d out/$(BUILDTYPE); then echo "run 'make clean' or otherwise delete out/$(BUILDTYPE)"; exit 1; fi
+	xcodebuild -project out/gyp/$@.xcodeproj -configuration $(BUILDTYPE)
+	ln -s $(CWD)/xcodebuild/$(BUILDTYPE) out/$(BUILDTYPE)
+else
+	echo "unknown platform $(uname)"
+	exit 1
+endif
diff --git a/Makefile.old b/Makefile.old
new file mode 100644
index 0000000..c78c101
--- /dev/null
+++ b/Makefile.old
@@ -0,0 +1,329 @@
+# Simple makefile for skia library and test apps
+#
+# This is the handmade Makefile that we *used* to use before changing over
+# to gyp.  Keeping it around for now in case some folks need to use it...
+# but please contact epoger@google.com about anything you're still using in
+# here, so we can provide equivalent functionality in the gyp build.
+
+# setup our defaults
+CC := gcc
+GPP := g++
+C_INCLUDES := -Iinclude/config -Iinclude/core -Iinclude/effects -Iinclude/images -Iinclude/ports
+C_INCLUDES +=  -Iinclude/gpu -Iinclude/utils -Igpu/include
+C_INCLUDES +=  -Ithird_party/glu
+
+CFLAGS := -Wall -fstrict-aliasing
+#CFLAGS += -W -Wextra -Wcast-align -Wchar-subscripts -Wformat -Wformat-security -Wno-format-y2k -Wno-parentheses -Wno-unused-parameter -Wpointer-arith  -Wreturn-type -Wundef -Wwrite-strings
+CFLAGS_SSE2 = $(CFLAGS) -msse2
+LINKER_OPTS := -lpthread -lz
+DEFINES := -DSK_CAN_USE_FLOAT
+HIDE = @
+
+ifeq ($(SKIA_SCALAR),fixed)
+	DEFINES += -DSK_SCALAR_IS_FIXED
+else
+	DEFINES += -DSK_SCALAR_IS_FLOAT
+endif
+
+ifeq ($(SKIA_DEBUG),true)
+ 	DEFINES += -DSK_DEBUG -DSK_SUPPORT_UNIT -DGR_DEBUG=1
+	CFLAGS += -g
+else
+	CFLAGS += -O3
+	DEFINES += -DSK_RELEASE -DGR_DEBUG=0
+endif
+
+DEFINES += -DGR_IMPLEMENTATION=1
+
+ifneq ($(SKIA_PDF_SUPPORT),false)
+	DEFINES += -DSK_SUPPORT_PDF
+	DEFINES += -DSK_ZLIB_INCLUDE="<zlib.h>"
+endif
+
+ifeq ($(SKIA_SHARED),true)
+	CFLAGS += -fPIC
+	LIBSKIA = out/libskia.so
+else
+	LIBSKIA = out/libskia.a
+endif
+
+# start with the core (required)
+include src/core/core_files.mk
+SRC_LIST := $(addprefix src/core/, $(SOURCE))
+
+# add the opts (optimizations)
+include src/opts/opts_sse2_files.mk
+#include src/opts/opts_files.mk
+SRC_LIST += $(addprefix src/opts/, $(SOURCE))
+
+# we usually need ports
+include src/ports/ports_files.mk
+SRC_LIST += $(addprefix src/ports/, $(SOURCE))
+
+# do we want effects?
+include src/effects/effects_files.mk
+SRC_LIST += $(addprefix src/effects/, $(SOURCE))
+
+# core image files
+include src/images/images_files.mk
+SRC_LIST += $(addprefix src/images/, $(SOURCE))
+
+# core util files
+include src/utils/utils_files.mk
+SRC_LIST += $(addprefix src/utils/, $(SOURCE))
+
+# GPU files
+include gpu/src/gr_files.mk
+SRC_LIST += $(addprefix gpu/src/, $(SOURCE))
+
+# GPU support files
+include src/gpu/skgr_files.mk
+SRC_LIST += $(addprefix src/gpu/, $(SOURCE))
+
+# pdf backend files
+ifneq ($(SKIA_PDF_SUPPORT),false)
+	C_INCLUDES += -Iinclude/pdf
+	include src/pdf/pdf_files.mk
+	SRC_LIST += $(addprefix src/pdf/, $(SOURCE))
+endif
+
+# extra files we want to build to prevent bit-rot, but not link
+JUST_COMPILE_LIST := src/ports/SkFontHost_tables.cpp
+
+# conditional files based on our platform
+ifeq ($(SKIA_BUILD_FOR),mac)
+	# make it work with 10.4 for our font port
+#	GPP := g++-4.0
+#	SDK := /Developer/SDKs/MacOSX10.4u.sdk
+#	SDK_OPTS := -isysroot $(SDK) -mmacosx-version-min=10.4
+#	CC := gcc-4.0 $(SDK_OPTS)
+
+	C_INCLUDES += -I/opt/local/include
+	LINKER_OPTS += -L/opt/local/lib -framework Carbon -lpng
+	DEFINES += -DSK_BUILD_FOR_MAC -DSK_ENABLE_LIBPNG
+	ifeq ($(SKIA_MESA),true)
+		C_INCLUDES += -I/usr/X11/include
+		LINKER_OPTS += -L/usr/X11/lib -lOSMesa -lGLU
+		DEFINES += -DSK_MESA
+	else
+		LINKER_OPTS += -framework OpenGL -framework AGL
+	endif
+	C_INCLUDES += -Iinclude/utils/mac
+#	SRC_LIST += src/ports/SkImageDecoder_CG.cpp
+	SRC_LIST += src/utils/mac/SkCreateCGImageRef.cpp
+	ifeq ($(SKIA_MESA),true)
+		SRC_LIST += src/utils/mesa/SkEGLContext_Mesa.cpp
+	else
+		SRC_LIST += src/utils/mac/SkEGLContext_mac.cpp
+	endif
+	SRC_LIST += src/core/SkTypefaceCache.cpp
+	SRC_LIST += src/ports/SkFontHost_mac_coretext.cpp
+
+	# these are our registry-based factories
+	SRC_LIST += src/images/SkImageDecoder_Factory.cpp
+	SRC_LIST += src/images/SkImageEncoder_Factory.cpp
+        SRC_LIST += src/images/SkImageDecoder_libpng.cpp
+	# support files
+	SRC_LIST += src/images/SkScaledBitmapSampler.cpp
+	
+	ifeq ($(SKIA_MESA),true)
+		SRC_LIST += gpu/src/mesa/GrGLDefaultInterface_mesa.cpp
+	else
+		SRC_LIST += gpu/src/mac/GrGLDefaultInterface_mac.cpp
+	endif
+	
+else
+	LINKER_OPTS += -lpng -lfreetype -lrt
+	DEFINES += -DSK_BUILD_FOR_UNIX -DSK_ENABLE_LIBPNG -DGR_LINUX_BUILD=1
+	ifeq ($(SKIA_MESA),true)
+		LINKER_OPTS += -lOSMesa -lGLU
+		DEFINES += -DSK_MESA
+	else
+		LINKER_OPTS += -lGL -lGLU -lX11
+	endif
+
+	#Assume the color order for now.
+	DEFINES += -DSK_SAMPLES_FOR_X
+
+	# needed for freetype support
+	C_INCLUDES += -I/usr/include/freetype2
+	SRC_LIST += src/ports/SkFontHost_linux.cpp
+	SRC_LIST += src/ports/SkFontHost_gamma_none.cpp
+	SRC_LIST += src/ports/SkFontHost_FreeType.cpp
+	SRC_LIST += src/ports/SkFontHost_FreeType_Subpixel.cpp
+	ifeq ($(SKIA_MESA),true)
+		SRC_LIST += src/utils/mesa/SkEGLContext_Mesa.cpp
+	else
+		SRC_LIST += src/utils/unix/SkEGLContext_Unix.cpp
+	endif
+	# these are our registry-based factories
+	SRC_LIST += src/images/SkImageDecoder_Factory.cpp
+	SRC_LIST += src/images/SkImageEncoder_Factory.cpp
+        SRC_LIST += src/images/SkImageDecoder_libpng.cpp
+	# support files
+	SRC_LIST += src/images/SkScaledBitmapSampler.cpp
+	
+	ifeq ($(SKIA_MESA),true)
+		SRC_LIST += gpu/src/mesa/GrGLDefaultInterface_mesa.cpp
+	else
+		SRC_LIST += gpu/src/unix/GrGLDefaultInterface_unix.cpp
+	endif
+endif
+
+# For these files, and these files only, compile with -msse2.
+SSE2_OBJS := out/src/opts/SkBlitRow_opts_SSE2.o \
+             out/src/opts/SkBitmapProcState_opts_SSE2.o \
+             out/src/opts/SkUtils_opts_SSE2.o
+$(SSE2_OBJS) : CFLAGS := $(CFLAGS_SSE2)
+
+out/%.o : %.cpp
+	@mkdir -p $(dir $@)
+	$(HIDE)$(CC) $(C_INCLUDES) $(CFLAGS) $(DEFINES) -c $< -o $@
+	@echo "compiling $@"
+
+%.s : %.cpp
+	@mkdir -p $(dir $@)
+	$(CC) $(C_INCLUDES) $(CFLAGS) $(DEFINES) -S -c $< -o $@
+
+# now build out objects
+OBJ_LIST := $(SRC_LIST:.cpp=.o)
+OBJ_LIST := $(addprefix out/, $(OBJ_LIST))
+
+# we want to compile these, but we don't actually link them
+JUST_COMPILE_OBJS := $(JUST_COMPILE_LIST:.cpp=.o)
+JUST_COMPILE_OBJS := $(addprefix out/, $(JUST_COMPILE_OBJS))
+
+out/libskia.a: Makefile $(OBJ_LIST) $(JUST_COMPILE_OBJS)
+	$(HIDE)$(AR) ru $@ $(OBJ_LIST)
+	$(HIDE)ranlib $@
+
+out/libskia.so: Makefile $(OBJ_LIST) $(JUST_COMPILE_OBJS)
+	$(HIDE)$(GPP) -shared -o $@ $(OBJ_LIST) $(JUST_COMPILE_OBJS) $(LINKER_OPTS)
+
+##############################################################################
+
+BENCH_SRCS := RectBench.cpp SkBenchmark.cpp benchmain.cpp BitmapBench.cpp \
+		RepeatTileBench.cpp DecodeBench.cpp FPSBench.cpp PathBench.cpp \
+		GradientBench.cpp MatrixBench.cpp ScalarBench.cpp \
+		BenchTimer.cpp BenchGpuTimer_gl.cpp
+ 
+ifeq ($(SKIA_BUILD_FOR),mac)
+    BENCH_SRCS += BenchSysTimer_mach.cpp
+else
+    BENCH_SRCS += BenchSysTimer_posix.cpp
+endif
+
+BENCH_SRCS := $(addprefix bench/, $(BENCH_SRCS))
+
+# add any optional codecs for this app
+ifeq ($(SKIA_BUILD_FOR),mac)
+    BENCH_SRCS += bench/TextBench.cpp
+else
+    BENCH_SRCS += src/images/SkImageDecoder_libpng.cpp
+endif
+
+BENCH_OBJS := $(BENCH_SRCS:.cpp=.o)
+BENCH_OBJS := $(addprefix out/, $(BENCH_OBJS))
+
+bench: $(BENCH_OBJS) $(LIBSKIA)
+	@echo "linking bench..."
+	$(HIDE)$(GPP) $(BENCH_OBJS) $(LIBSKIA) -o out/bench/bench $(LINKER_OPTS)
+
+##############################################################################
+
+# we let tests cheat and see private headers, so we can unittest modules
+C_INCLUDES += -Isrc/core
+
+include tests/tests_files.mk
+ifneq ($(SKIA_PDF_SUPPORT),false)
+  SOURCE += PDFPrimitivesTest.cpp
+endif
+TESTS_SRCS := $(addprefix tests/, $(SOURCE))
+
+TESTS_OBJS := $(TESTS_SRCS:.cpp=.o)
+TESTS_OBJS := $(addprefix out/, $(TESTS_OBJS))
+
+tests: $(TESTS_OBJS) $(LIBSKIA)
+	@echo "linking tests..."
+	$(HIDE)$(GPP) $(TESTS_OBJS) $(LIBSKIA) -o out/tests/tests $(LINKER_OPTS)
+
+##############################################################################
+
+SKIMAGE_SRCS := skimage_main.cpp
+
+SKIMAGE_SRCS := $(addprefix tools/, $(SKIMAGE_SRCS))
+
+SKIMAGE_OBJS := $(SKIMAGE_SRCS:.cpp=.o)
+SKIMAGE_OBJS := $(addprefix out/, $(SKIMAGE_OBJS))
+
+skimage: $(SKIMAGE_OBJS) $(LIBSKIA)
+	@echo "linking skimage..."
+	$(HIDE)$(GPP) $(SKIMAGE_OBJS) $(LIBSKIA) -o out/tools/skimage $(LINKER_OPTS)
+
+##############################################################################
+
+SKDIFF_SRCS := skdiff_main.cpp
+SKDIFF_SRCS := $(addprefix tools/, $(SKDIFF_SRCS))
+SKDIFF_OBJS := $(SKDIFF_SRCS:.cpp=.o)
+SKDIFF_OBJS := $(addprefix out/, $(SKDIFF_OBJS))
+skdiff: $(SKDIFF_OBJS) out/libskia.a
+	@echo "linking skdiff..."
+	$(HIDE)$(GPP) $(SKDIFF_OBJS) out/libskia.a -o out/tools/skdiff $(LINKER_OPTS)
+
+##############################################################################
+
+SKHELLO_SRCS := skhello.cpp
+
+SKHELLO_SRCS := $(addprefix tools/, $(SKHELLO_SRCS))
+
+SKHELLO_OBJS := $(SKHELLO_SRCS:.cpp=.o)
+SKHELLO_OBJS := $(addprefix out/, $(SKHELLO_OBJS))
+
+skhello: $(SKHELLO_OBJS) $(LIBSKIA)
+	@echo "linking shkello..."
+	$(HIDE)$(GPP) $(SKHELLO_OBJS) $(LIBSKIA) -o out/tools/skhello $(LINKER_OPTS)
+
+##############################################################################
+
+include gm/gm_files.mk
+GM_SRCS := $(addprefix gm/, $(SOURCE))
+
+ifneq ($(SKIA_BUILD_FOR),mac)
+    GM_SRCS += src/images/SkImageDecoder_libpng.cpp
+endif
+
+GM_OBJS := $(GM_SRCS:.cpp=.o)
+GM_OBJS := $(addprefix out/, $(GM_OBJS))
+
+gm: $(GM_OBJS) $(LIBSKIA)
+	@echo "linking gm..."
+	$(HIDE)$(GPP) $(GM_OBJS) $(LIBSKIA) -o out/gm/gm $(LINKER_OPTS)
+
+##############################################################################
+
+.PHONY: all
+all: $ bench gm skimage tests skhello skdiff
+
+.PHONY: clean
+clean:
+	$(HIDE)rm -rf out
+
+.PHONY: help
+help:
+	@echo "Targets:"
+	@echo "    <default>: out/libskia.a"
+	@echo "    bench: out/bench/bench"
+	@echo "    gm: out/gm/gm"
+	@echo "    skimage: out/tools/skimage"
+	@echo "    skhello: out/tools/skhello"
+	@echo "    tests: out/tests/tests"
+	@echo "    clean: removes entire out/ directory"
+	@echo "    help: this text"
+	@echo "Options: (after make, or in bash shell)"
+	@echo "    SKIA_DEBUG=true for debug build"
+	@echo "    SKIA_SHARED=true for shared-object libskia build"
+	@echo "    SKIA_SCALAR=fixed for fixed-point build"
+	@echo "    SKIA_BUILD_FOR=mac for mac build (e.g. CG for image decoding)"
+	@echo "    SKIA_PDF_SUPPORT=false to disable the pdf generation backend"
+	@echo "    SKIA_MESA=true to build with osmesa instead of native GL.
+	@echo ""
diff --git a/README b/README
new file mode 100644
index 0000000..84e4ecc
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+Skia is a complete 2D graphic library for drawing Text, Geometries, and Images.
+
+See full details, and build instructions, at http://code.google.com/p/skia/wiki/DocRoot
diff --git a/animations/checkbox.xml b/animations/checkbox.xml
new file mode 100644
index 0000000..7750c36
--- /dev/null
+++ b/animations/checkbox.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<screenplay xmlns="urn:screenplay">
+
+	<path id="check">
+		<moveTo x="20" y="50" />
+        <quadTo x1="40" y1="70" x2="40" y2="77" />
+		<quadTo x1="45" y1="55" x2="75" y2="30" />
+	</path>
+
+    <roundRect id="frame"
+               left="3" top="3" right="97" bottom="97"
+               rx="17" ry="17" />
+
+	<event kind="onLoad">
+		<paint antiAlias="true" />
+
+        <!-- draw the background -->
+
+        <paint stroke="true" strokeWidth="4">
+            <color color="0x66000000"/>
+        </paint>
+        <matrix translate="[0,2]"/>
+        <add use="frame" />
+        <paint>
+            <color color="black"/>
+        </paint>
+        <matrix translate="[0,-2]"/>
+        <add use="frame" />
+
+        <paint stroke="false">
+			<linearGradient points="[0,frame.top,0,frame.bottom]" tileMode="clamp"
+                    offsets="[0,0.65,1]">
+				<color color="#F2F2F2" />
+				<color color="#AFAFAF" />
+				<color color="#C7C7C7" />
+			</linearGradient>
+        </paint>
+        <add use="frame" />
+
+        <!-- draw the checkmark background -->
+
+		<paint stroke="true" strokeWidth="9">
+            <shader/>
+            <blur radius="1" blurStyle="normal"/>
+            <color color="0x88777777"/>
+        </paint>
+        <matrix translate="[0,-2]" />
+        <add use="check" />
+
+		<paint>
+            <color color="0x88BBBBBB"/>
+        </paint>
+        <matrix translate="[0,4]" />
+        <add use="check" />
+
+        <!-- draw the checkmark -->
+
+		<paint>
+            <maskFilter/>
+            <color color="#66CC00"/>
+        </paint>
+        <matrix translate="[0,-2]" />
+        <add use="check" />
+
+	</event>
+
+</screenplay>
diff --git "a/animations/chest\0431.jpg" "b/animations/chest\0431.jpg"
new file mode 100644
index 0000000..9ef7194
--- /dev/null
+++ "b/animations/chest\0431.jpg"
Binary files differ
diff --git "a/animations/fire\0431.jpg" "b/animations/fire\0431.jpg"
new file mode 100644
index 0000000..6269ff7
--- /dev/null
+++ "b/animations/fire\0431.jpg"
Binary files differ
diff --git "a/animations/images\0431.xml" "b/animations/images\0431.xml"
new file mode 100644
index 0000000..c6ad7a6
--- /dev/null
+++ "b/animations/images\0431.xml"
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8" ?> 
+<screenplay xmlns="urn:screenplay">
+
+	<event kind="onLoad">
+		<matrix id="initialMatrix" translate="[80,80]" />
+		<paint  id="imagePaint" antiAlias="true">
+			<color color="lightpink" />
+		</paint>
+		<apply  >
+			<paint />
+			<set begin="4" field="linearText" to="false" />
+		</apply>
+		<save>
+			<matrix>
+				<scale x="0.5" y="0.5" />
+			</matrix>
+		<image id="backImage" src="redcross.jpg" />
+		<save>
+			<matrix id="polyMatrix">
+				<polyToPoly >
+					<polygon>
+						<moveTo id="pt1" x="0" y="0" />
+						<lineTo id="pt2" x="256" y="0" />
+						<lineTo x="256" y="256" />
+						<lineTo x="0" y="256" />
+						<close />
+					</polygon>
+					<polygon >
+						<moveTo x="0" y="0" />
+						<lineTo x="256" y="0" />
+						<lineTo x="256" y="256" />
+						<lineTo x="0" y="256" />
+						<close />
+					</polygon>
+				</polyToPoly>
+			</matrix>
+			<paint id="paintFade">
+				<color id="fade" />
+			</paint>
+			<image id="frontImage" src="bulgaria.jpg" />
+		</save>
+		</save>
+		<apply begin="0.5" scope="polyMatrix">
+			<animate id="pt1x" target="pt1" field="x" from="0" to="64" dur="0.5" repeat="3" />
+			<animate target="pt1" field="y" from="0" to="-455" dur="0.5"  repeat="3"/>
+			<animate target="pt2" field="x" from="256" to="192" dur="0.5"  repeat="3" />
+			<animate target="pt2" field="y" from="0" to="-455" dur="0.5" repeat="3" />
+		</apply>
+		<apply begin="1" scope="backImage" >
+			<set field="src" to="jet.jpg" />
+		</apply>
+		<apply begin="1" scope="frontImage" >
+			<set field="src" to="redcross.jpg" />
+		</apply>
+		<apply begin="1.5" scope="backImage" >
+			<set field="src" to="fire.jpg" />
+		</apply>
+		<apply begin="1.5" scope="frontImage" >
+			<set field="src" to="jet.jpg" />
+		</apply>
+		<apply begin="2" scope="backImage" >
+			<set field="src" to="chest.jpg" />
+		</apply>
+		<apply begin="2" scope="frontImage" >
+			<set field="src" to="fire.jpg" />
+		</apply>
+		<apply begin="2" scope="paintFade" >
+			<animate field="alpha" target="fade" from="1" to="0" dur=".5" />
+		</apply>
+		<post target="addCaptionFade" delay="2" />
+		<post target="addLineCaption" delay="2" />
+		<post target="scaleInitial" delay="3" />
+	</event>
+	
+	<event kind="user" id="addCaptionFade" >
+		<apply>
+			<paint  > 
+				<color id="captionFade" alpha="0" />
+			</paint>
+			<animate target="captionFade" field="alpha" from="0" to="1" dur="1" />
+		</apply>
+	</event>
+	
+	<event kind="user" id="addLineCaption" >
+		<paint textSize="24" textAlign="center" > <!--  -->
+			<shader  />
+		</paint>
+		<text text="Images" x="70" y="160" />
+	</event>
+
+	<event kind="user" id="scaleInitial" >
+		<apply scope="imagePaint" >
+			<set field="linearText" to="true" />
+		</apply>
+		<apply scope="initialMatrix" >
+			<animate field="scale" from="1" to=".5" dur="1"/>
+			<animate field="translateX" from="80" to="145" dur="1"/>
+			<animate field="translateY" from="80" to="172" dur="1"/>
+		</apply>
+	</event>
+
+</screenplay>
diff --git "a/animations/jet\0431.jpg" "b/animations/jet\0431.jpg"
new file mode 100644
index 0000000..1a5a3e7
--- /dev/null
+++ "b/animations/jet\0431.jpg"
Binary files differ
diff --git "a/animations/lines\0431.xml" "b/animations/lines\0431.xml"
new file mode 100644
index 0000000..fe120a1
--- /dev/null
+++ "b/animations/lines\0431.xml"
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8" ?> 
+<screenplay xmlns="urn:screenplay">
+	<line id="line" x1="0" y1="0" x2="100" y2="0" />
+	<random id="rRed" min="0" max="255" seed="1900"  />
+	<random id="rBlue" min="0" max="255" seed="50000"  />
+	<random id="rGreen" min="0" max="255" seed="99"  />
+	
+	<event kind="onLoad" >
+		<matrix id="initialMatrix" translate="[120,100]" />
+		<paint id="basePaint" strokeWidth="2" antiAlias="true" textAlign="center" 
+				textSize="24" linearText="false" >
+			<color color="lightblue" />
+		</paint>
+		<apply mode="immediate" steps="17" >
+			<post target="addLine" />
+			<animate field="delay" from="0.1" to="0.95" />
+		</apply>
+		<post id="postAddCaptionFade" target="addCaptionFade" delay="1" />
+		<post id="postAddLineCaption"  target="addLineCaption" delay="1" />
+		<post target="scaleInitial" delay="2" />
+		<apply scope="basePaint"  begin="3">
+			<set begin="0" field="linearText" to="false" />
+		</apply>
+	</event>
+
+	<event kind="user" id="addLine" >
+		<matrix rotate="20" />
+		<apply restore="true" >
+			<paint >
+				<color id="ramp" />
+			</paint>
+			<set target="ramp" field="color" to="rgb(rRed.random,rBlue.random,rGreen.random)" />
+		</apply>
+		<add use="line" />
+	</event>
+	
+	<event kind="user" id="addCaptionFade" >
+		<apply>
+			<paint>
+				<color id="captionFade" alpha="0" />
+			</paint>
+			<animate target="captionFade" field="alpha" from="0" to="1" dur="1" />
+		</apply>
+	</event>
+	
+	<event kind="user" id="addLineCaption" >
+		<text id="linescaption" text="Lines" x="0" y="120" />
+	</event>
+	
+	<event kind="user" id="scaleInitial" >
+		<apply scope="basePaint" >
+			<set field="linearText" to="true" dur="1" reset="true" />
+		</apply>
+		<apply scope="initialMatrix" >
+			<animate field="scale" from="1" to=".5" dur="1"/>
+			<animate field="translateX" from="120" to="60" dur="1"/>
+			<animate field="translateY" from="100" to="60" dur="1"/>
+		</apply>
+	</event>
+		
+<!--
+	<event kind="keyChar" key="d" >
+		<dump />
+	</event>
+-->
+</screenplay>
\ No newline at end of file
diff --git "a/animations/movie\0431.xml" "b/animations/movie\0431.xml"
new file mode 100644
index 0000000..872c7c2
--- /dev/null
+++ "b/animations/movie\0431.xml"
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" ?> 
+<screenplay xmlns="urn:screenplay">
+
+	<event kind="user" id="pathsMovie" >
+		<movie src="paths.xml" />
+	</event>
+
+	<event kind="user" id="textMovie" >
+		<movie src="text.xml" />
+	</event>
+
+	<event kind="user" id="bitmapMovie" >
+		<movie src="images.xml" />
+	</event>
+	
+	<event kind="onLoad">
+		<movie src="lines.xml" />
+		<post delay="5" target="pathsMovie" />
+		<post delay="10" target="textMovie" />
+		<post delay="15" target="bitmapMovie" />
+		<dump />
+	</event>
+</screenplay>
diff --git "a/animations/paths\0431.xml" "b/animations/paths\0431.xml"
new file mode 100644
index 0000000..7e14f92
--- /dev/null
+++ "b/animations/paths\0431.xml"
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<screenplay xmlns="urn:screenplay">
+	<array id="colors" 
+		values="[yellow,blue,green,red,orange,cyan,magenta,lime,navy,olive]" />
+	<path id="path">
+		<moveTo x="20" y="20" />
+		<lineTo x="20" y="80" />
+		<quadTo id="quad" x1="80" y1="80" x2="80" y2="20" />
+		<close />
+	</path>
+
+	<event kind="onLoad">
+		<matrix id="initialMatrix" translate="[120,150]" />
+		<paint  id="pathPaint" antiAlias="true">
+			<color color="lightpink" />
+		</paint>
+		<apply  >
+			<paint />
+			<set begin="3" field="linearText" to="false" />
+		</apply>
+		<apply scope="path" >
+			<animate target="quad" field="x1" from="10" to="120" dur="1"/>
+			<animate target="quad" field="y1" from="10" to="120" dur="1"/>
+		</apply>
+		<apply id="apply" mode="immediate" steps="colors.length-1" >
+			<group id="newPath">
+				<matrix rotate="360/colors.length" />
+				<paint >
+					<color id="color" />
+				</paint>
+				<add use="path" />
+			</group>
+			<set target="color" field="color" to="colors[apply.step]" />
+			<set target="color" field="alpha" to=".5" />
+		</apply>
+		<post target="addCaptionFade" delay="1" />
+		<post target="addLineCaption" delay="1" />
+		<post target="scaleInitial" delay="2" />
+	</event>
+	
+	
+	<event kind="user" id="addCaptionFade" >
+		<apply>
+			<paint  > 
+				<color id="captionFade" alpha="0" />
+			</paint>
+			<animate target="captionFade" field="alpha" from="0" to="1" dur="1" />
+		</apply>
+	</event>
+	
+	<event kind="user" id="addLineCaption" >
+		<paint textSize="24" textAlign="center" > <!--  -->
+			<shader  />
+		</paint>
+		<text text="Paths" x="10" y="145" />
+	</event>
+
+	<event kind="user" id="scaleInitial" >
+		<apply scope="pathPaint" >
+			<set field="linearText" to="true" />
+		</apply>
+		<apply scope="initialMatrix" >
+			<animate field="scale" from="1" to=".5" dur="1"/>
+			<animate field="translateX" from="120" to="60" dur="1"/>
+			<animate field="translateY" from="150" to="180" dur="1"/>
+		</apply>
+	</event>
+	
+</screenplay>
diff --git "a/animations/redcross\0431.jpg" "b/animations/redcross\0431.jpg"
new file mode 100644
index 0000000..0bbae04
--- /dev/null
+++ "b/animations/redcross\0431.jpg"
Binary files differ
diff --git "a/animations/text\0431.xml" "b/animations/text\0431.xml"
new file mode 100644
index 0000000..5a279b6
--- /dev/null
+++ "b/animations/text\0431.xml"
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8" ?> 
+<screenplay xmlns="urn:screenplay">
+	<text id="partial" />
+	<text id="type" text="Typewriter" />
+	<int id="typeLength" value="0" />
+	
+	<event kind="onLoad" >
+		<matrix id="initialMatrix" translate="[65,100]" />
+		<paint id="textPaint" textSize="36"  antiAlias="true" linearText="false" >
+			<color color="lightBlue" />
+		</paint>
+		<apply  >
+			<paint />
+			<set begin="3" field="linearText" to="false" />
+		</apply>
+		<matrix translate="[0,60]" />
+		<rect top="textPaint.ascent" width="textPaint.measureText(type.text)" 
+			height="-textPaint.ascent+textPaint.descent" />
+		<paint>
+			<linearGradient points="[0,25,205,25]"  tileMode="mirror" >
+				<matrix>
+					<scale x="3" y="2" />
+				</matrix>
+				<color color="brown" />
+				<color color="yellow" />
+			</linearGradient>
+		</paint>
+		<add use="partial" />
+		<textOnPath text="partial"  offset="220" >
+			<path >
+				<addOval left="0" right="160" top="-70" bottom="30" direction="cw" />
+			</path>
+		</textOnPath>
+		<textOnPath text="partial"  offset="20" >
+			<path >
+				<addOval left="0" right="160" top="-50" bottom="50" direction="cw" />
+			</path>
+		</textOnPath>
+		<apply mode="immediate" steps="type.length-1" >
+			<post target="nextChar" />
+			<animate field="delay" from="0.1" to="0.95" />
+		</apply>
+		<post target="addCaptionFade" delay="1" />
+		<post target="addLineCaption" delay="1" />
+		<post target="scaleInitial" delay="2" />
+	</event>
+	
+	<event kind="user" id="nextChar" >
+		<apply id="applyTypeLength" scope="typeLength" >
+			<set id="incValue" field="value" to="typeLength.value + 1" />
+		</apply>
+		<apply id="applySlice" scope="partial">
+			<set id="setSlice" field="text" to="#script:type.text.slice(0, typeLength.value)" />
+		</apply>
+	</event>
+	
+	<event kind="user" id="addCaptionFade" >
+		<apply>
+			<paint  > 
+				<color id="captionFade" alpha="0" />
+			</paint>
+			<animate target="captionFade" field="alpha" from="0" to="1" dur="1" />
+		</apply>
+	</event>
+	
+	<event kind="user" id="addLineCaption" >
+		<paint textSize="24" textAlign="center" > <!--  -->
+			<shader  />
+		</paint>
+		<text text="Text" x="textPaint.measureText(type.text)/2" y="40" />
+	</event>
+	
+	<event kind="user" id="scaleInitial" >
+		<apply scope="textPaint" >
+			<set field="linearText" to="true" />
+		</apply>
+		<apply scope="initialMatrix" >
+			<animate field="scale" from="1" to=".5" dur="1"/>
+			<animate field="translateX" from="65" to="130" dur="1"/>
+			<animate field="translateY" from="100" to="30" dur="1"/>
+		</apply>
+	</event>
+	
+</screenplay>
diff --git a/bench/AAClipBench.cpp b/bench/AAClipBench.cpp
new file mode 100644
index 0000000..f85af68
--- /dev/null
+++ b/bench/AAClipBench.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 "SkBenchmark.h"
+#include "SkAAClip.h"
+#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;
+    SkRect   fRect;
+    SkRegion fRegion;
+    bool     fDoPath;
+    bool     fDoAA;
+
+    enum {
+        N = SkBENCHLOOP(200),
+    };
+
+public:
+    AAClipBuilderBench(void* param, bool doPath, bool doAA) : INHERITED(param) {
+        fDoPath = doPath;
+        fDoAA = doAA;
+
+        fName.printf("aaclip_build_%s_%s", doPath ? "path" : "rect",
+                     doAA ? "AA" : "BW");
+
+        fRegion.setRect(0, 0, 640, 480);
+        fRect.set(fRegion.getBounds());
+        fRect.inset(SK_Scalar1/4, SK_Scalar1/4);
+        fPath.addRoundRect(fRect, SkIntToScalar(20), SkIntToScalar(20));
+    }
+
+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) {
+            SkAAClip clip;
+            if (fDoPath) {
+                clip.setPath(fPath, &fRegion, fDoAA);
+            } else {
+                clip.setRect(fRect, fDoAA);
+            }
+        }
+    }
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+class AAClipRegionBench : public SkBenchmark {
+public:
+    AAClipRegionBench(void* param) : INHERITED(param) {
+        SkPath path;
+        // test conversion of a complex clip to a aaclip
+        path.addCircle(0, 0, SkIntToScalar(200));
+        path.addCircle(0, 0, SkIntToScalar(180));
+        // evenodd means we've constructed basically a stroked circle
+        path.setFillType(SkPath::kEvenOdd_FillType);
+
+        SkIRect bounds;
+        path.getBounds().roundOut(&bounds);
+        fRegion.setPath(path, SkRegion(bounds));
+    }
+
+protected:
+    virtual const char* onGetName() { return "aaclip_setregion"; }
+    virtual void onDraw(SkCanvas* canvas) {
+        for (int i = 0; i < N; ++i) {
+            SkAAClip clip;
+            clip.setRegion(fRegion);
+        }
+    }
+
+private:
+    enum {
+        N = SkBENCHLOOP(400),
+    };
+    SkRegion fRegion;
+    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)); }
+static SkBenchmark* Fact2(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, true, false)); }
+static SkBenchmark* Fact3(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, true, true)); }
+
+static BenchRegistry gReg0(Fact0);
+static BenchRegistry gReg1(Fact1);
+static BenchRegistry gReg2(Fact2);
+static BenchRegistry gReg3(Fact3);
+
+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/BenchGpuTimer_gl.cpp b/bench/BenchGpuTimer_gl.cpp
new file mode 100644
index 0000000..699f5e5
--- /dev/null
+++ b/bench/BenchGpuTimer_gl.cpp
@@ -0,0 +1,67 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "BenchGpuTimer_gl.h"
+#include "gl/SkGLContext.h"
+#include "gl/GrGLUtil.h"
+
+BenchGpuTimer::BenchGpuTimer(const SkGLContext* glctx) {
+    fContext = glctx;
+    glctx->ref();
+    glctx->makeCurrent();
+    fStarted = false;
+    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));
+    }
+}
+
+BenchGpuTimer::~BenchGpuTimer() {
+    if (fSupported) {
+        fContext->makeCurrent();
+        SK_GL(*fContext, DeleteQueries(1, &fQuery));
+    }
+    fContext->unref();
+}
+
+void BenchGpuTimer::startGpu() {
+    if (fSupported) {
+        fContext->makeCurrent();
+        fStarted = true;
+        SK_GL(*fContext, BeginQuery(GR_GL_TIME_ELAPSED, fQuery));
+    }
+}
+
+/**
+ * It is important to stop the cpu clocks first,
+ * as this will cpu wait for the gpu to finish.
+ */
+double BenchGpuTimer::endGpu() {
+    if (fSupported) {
+        fStarted = false;
+        fContext->makeCurrent();
+        SK_GL(*fContext, EndQuery(GR_GL_TIME_ELAPSED));
+
+        GrGLint available = 0;
+        while (!available) {
+            SK_GL(*fContext, GetQueryObjectiv(fQuery,
+                                             GR_GL_QUERY_RESULT_AVAILABLE,
+                                             &available));
+        }
+        GrGLuint64 totalGPUTimeElapsed = 0;
+        SK_GL(*fContext, GetQueryObjectui64v(fQuery,
+                                             GR_GL_QUERY_RESULT,
+                                             &totalGPUTimeElapsed));
+
+        return totalGPUTimeElapsed / 1000000.0;
+    } else {
+        return 0;
+    }
+}
diff --git a/bench/BenchGpuTimer_gl.h b/bench/BenchGpuTimer_gl.h
new file mode 100644
index 0000000..7c7b5c2
--- /dev/null
+++ b/bench/BenchGpuTimer_gl.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 SkBenchGpuTimer_DEFINED
+#define SkBenchGpuTimer_DEFINED
+
+class SkGLContext;
+
+class BenchGpuTimer {
+public:
+    BenchGpuTimer(const SkGLContext* glctx);
+    ~BenchGpuTimer();
+    void startGpu();
+    double endGpu();
+private:
+    unsigned fQuery;
+    int fStarted;
+    const SkGLContext* fContext;
+    bool fSupported;
+};
+
+#endif
diff --git a/bench/BenchSysTimer_c.cpp b/bench/BenchSysTimer_c.cpp
new file mode 100644
index 0000000..f4cbd39
--- /dev/null
+++ b/bench/BenchSysTimer_c.cpp
@@ -0,0 +1,27 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "BenchSysTimer_c.h"
+
+//Time
+#include <time.h>
+
+void BenchSysTimer::startWall() {
+    this->fStartWall = time();
+}
+void BenchSysTimer::startCpu() {
+    this->fStartCpu = clock();
+}
+
+double BenchSysTimer::endCpu() {
+    clock_t end_cpu = clock();
+    this->fCpu = (end_cpu - this->fStartCpu) * CLOCKS_PER_SEC / 1000.0;
+}
+double BenchSysTimer::endWall() {
+    time_t end_wall = time();
+    this->fWall = difftime(end_wall, this->fstartWall) / 1000.0;
+}
diff --git a/bench/BenchSysTimer_c.h b/bench/BenchSysTimer_c.h
new file mode 100644
index 0000000..2ddc83d
--- /dev/null
+++ b/bench/BenchSysTimer_c.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 SkBenchSysTimer_DEFINED
+#define SkBenchSysTimer_DEFINED
+
+//Time
+#include <time.h>
+#warning standard clocks
+
+class BenchSysTimer {
+public:
+    void startWall();
+    void startCpu();
+    double endCpu();
+    double endWall();
+private:
+    clock_t start_cpu;
+    time_t fStartWall;
+};
+
+#endif
diff --git a/bench/BenchSysTimer_mach.cpp b/bench/BenchSysTimer_mach.cpp
new file mode 100644
index 0000000..cf3f1c1
--- /dev/null
+++ b/bench/BenchSysTimer_mach.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 "BenchSysTimer_mach.h"
+
+//Time
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+
+static time_value_t macCpuTime() {
+    mach_port_t task = mach_task_self();
+    if (task == MACH_PORT_NULL) {
+        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,
+                 TASK_THREAD_TIMES_INFO,
+                 reinterpret_cast<task_info_t>(&thread_info_data),
+                 &thread_info_count))
+    {
+        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;
+}
+
+static double intervalInMSec(const time_value_t start_clock
+                           , const time_value_t end_clock)
+{
+    double duration_clock;
+    if ((end_clock.microseconds - start_clock.microseconds) < 0) {
+        duration_clock = (end_clock.seconds - start_clock.seconds-1)*1000;
+        duration_clock += (1000000
+                           + end_clock.microseconds
+                           - start_clock.microseconds) / 1000.0;
+    } else {
+        duration_clock = (end_clock.seconds - start_clock.seconds)*1000;
+        duration_clock += (end_clock.microseconds - start_clock.microseconds)
+                           / 1000.0;
+    }
+    return duration_clock;
+}
+
+void BenchSysTimer::startWall() {
+    this->fStartWall = mach_absolute_time();
+}
+void BenchSysTimer::startCpu() {
+    this->fStartCpu = macCpuTime();
+}
+
+double BenchSysTimer::endCpu() {
+    time_value_t end_cpu = macCpuTime();
+    return intervalInMSec(this->fStartCpu, end_cpu);
+}
+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)) {
+        return 0;
+    } else {
+        uint64_t elapsedNano = elapsed * sTimebaseInfo.numer
+                               / sTimebaseInfo.denom;
+        return elapsedNano / 1000000;
+    }
+}
diff --git a/bench/BenchSysTimer_mach.h b/bench/BenchSysTimer_mach.h
new file mode 100644
index 0000000..44d0e5e
--- /dev/null
+++ b/bench/BenchSysTimer_mach.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 SkBenchSysTimer_DEFINED
+#define SkBenchSysTimer_DEFINED
+
+//Time
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+
+class BenchSysTimer {
+public:
+    void startWall();
+    void startCpu();
+    double endCpu();
+    double endWall();
+private:
+    time_value_t fStartCpu;
+    uint64_t fStartWall;
+};
+
+#endif
diff --git a/bench/BenchSysTimer_posix.cpp b/bench/BenchSysTimer_posix.cpp
new file mode 100644
index 0000000..e6767e5
--- /dev/null
+++ b/bench/BenchSysTimer_posix.cpp
@@ -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.
+ */
+#include "BenchSysTimer_posix.h"
+
+//Time
+#include <time.h>
+
+static double intervalInMSec(const timespec start_clock
+                           , const timespec end_clock)
+{
+    double duration_clock;
+    if ((end_clock.tv_nsec - start_clock.tv_nsec) < 0) {
+        duration_clock = (end_clock.tv_sec - start_clock.tv_sec-1)*1000;
+        duration_clock += (1000000000 + end_clock.tv_nsec - start_clock.tv_nsec)
+                           / 1000000.0;
+    } else {
+        duration_clock = (end_clock.tv_sec - start_clock.tv_sec)*1000;
+        duration_clock += (end_clock.tv_nsec - start_clock.tv_nsec) / 1000000.0;
+    }
+    return duration_clock;
+}
+
+void BenchSysTimer::startWall() {
+    if (-1 == clock_gettime(CLOCK_MONOTONIC, &this->fWall)) {
+        timespec none = {0, 0};
+        this->fWall = none;
+    }
+}
+void BenchSysTimer::startCpu() {
+    if (-1 == clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &this->fCpu)) {
+        timespec none = {0, 0};
+        this->fCpu = none;
+    }
+}
+
+double BenchSysTimer::endCpu() {
+    timespec end_cpu;
+    if (-1 == clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_cpu)) {
+        timespec none = {0, 0};
+        end_cpu = none;
+    }
+    return intervalInMSec(this->fCpu, end_cpu);
+}
+
+double BenchSysTimer::endWall() {
+    timespec end_wall;
+    if (-1 == clock_gettime(CLOCK_MONOTONIC, &end_wall)) {
+        timespec none = {0, 0};
+        end_wall = none;
+    }
+    return intervalInMSec(this->fWall, end_wall);
+}
diff --git a/bench/BenchSysTimer_posix.h b/bench/BenchSysTimer_posix.h
new file mode 100644
index 0000000..de793f3
--- /dev/null
+++ b/bench/BenchSysTimer_posix.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 SkBenchSysTimer_DEFINED
+#define SkBenchSysTimer_DEFINED
+
+//Time
+#include <time.h>
+
+class BenchSysTimer {
+public:
+    void startWall();
+    void startCpu();
+    double endCpu();
+    double endWall();
+private:
+    timespec fCpu;
+    timespec fWall;
+};
+
+#endif
+
diff --git a/bench/BenchSysTimer_windows.cpp b/bench/BenchSysTimer_windows.cpp
new file mode 100644
index 0000000..1c4e404
--- /dev/null
+++ b/bench/BenchSysTimer_windows.cpp
@@ -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.
+ */
+#include "BenchSysTimer_windows.h"
+
+//Time
+#define WIN32_LEAN_AND_MEAN 1
+#include <Windows.h>
+
+static ULONGLONG winCpuTime() {
+    FILETIME createTime;
+    FILETIME exitTime;
+    FILETIME usrTime;
+    FILETIME sysTime;
+    if (0 == GetProcessTimes(GetCurrentProcess()
+                           , &createTime, &exitTime
+                           , &sysTime, &usrTime))
+    {
+        return 0;
+    }
+    ULARGE_INTEGER start_cpu_sys;
+    ULARGE_INTEGER start_cpu_usr;
+    start_cpu_sys.LowPart  = sysTime.dwLowDateTime;
+    start_cpu_sys.HighPart = sysTime.dwHighDateTime;
+    start_cpu_usr.LowPart  = usrTime.dwLowDateTime;
+    start_cpu_usr.HighPart = usrTime.dwHighDateTime;
+    return start_cpu_sys.QuadPart + start_cpu_usr.QuadPart;
+}
+
+void BenchSysTimer::startWall() {
+    if (0 == ::QueryPerformanceCounter(&this->fStartWall)) {
+        this->fStartWall.QuadPart = 0;
+    }
+}
+void BenchSysTimer::startCpu() {
+    this->fStartCpu = winCpuTime();
+}
+
+double BenchSysTimer::endCpu() {
+    ULONGLONG end_cpu = winCpuTime();
+    return static_cast<double>((end_cpu - this->fStartCpu)) / 10000.0L;
+}
+double BenchSysTimer::endWall() {
+    LARGE_INTEGER end_wall;
+    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;
+    } else {
+        return static_cast<double>(ticks_elapsed.QuadPart)
+             / static_cast<double>(frequency.QuadPart)
+             * 1000.0L;
+    }
+}
diff --git a/bench/BenchSysTimer_windows.h b/bench/BenchSysTimer_windows.h
new file mode 100644
index 0000000..c3d0c9b
--- /dev/null
+++ b/bench/BenchSysTimer_windows.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 SkBenchSysTimer_DEFINED
+#define SkBenchSysTimer_DEFINED
+
+//Time
+#define WIN32_LEAN_AND_MEAN 1
+#include <Windows.h>
+
+class BenchSysTimer {
+public:
+    void startWall();
+    void startCpu();
+    double endCpu();
+    double endWall();
+private:
+    ULONGLONG fStartCpu;
+    LARGE_INTEGER fStartWall;
+};
+
+#endif
diff --git a/bench/BenchTimer.cpp b/bench/BenchTimer.cpp
new file mode 100644
index 0000000..4e04844
--- /dev/null
+++ b/bench/BenchTimer.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 "BenchTimer.h"
+#if defined(SK_BUILD_FOR_WIN32)
+    #include "BenchSysTimer_windows.h"
+#elif defined(SK_BUILD_FOR_MAC)
+    #include "BenchSysTimer_mach.h"
+#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
+    #include "BenchSysTimer_posix.h"
+#else
+    #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
new file mode 100644
index 0000000..58773d4
--- /dev/null
+++ b/bench/BenchTimer.h
@@ -0,0 +1,49 @@
+
+/*
+ * 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 SkBenchTimer_DEFINED
+#define SkBenchTimer_DEFINED
+
+#include <SkTypes.h>
+
+
+class BenchSysTimer;
+class BenchGpuTimer;
+
+class SkGLContext;
+
+/**
+ * SysTimers and GpuTimers are implemented orthogonally.
+ * 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:
+    BenchTimer(SkGLContext* gl = NULL);
+    ~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/BenchTool/BenchTool.xcodeproj/project.pbxproj b/bench/BenchTool/BenchTool.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..b637494
--- /dev/null
+++ b/bench/BenchTool/BenchTool.xcodeproj/project.pbxproj
@@ -0,0 +1,324 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 45;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		272FB43A0F11A19C00CA935D /* RectBench.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 272FB4390F11A19C00CA935D /* RectBench.cpp */; };
+		272FB4F30F11B40300CA935D /* SkBenchmark.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 272FB4F20F11B40300CA935D /* SkBenchmark.cpp */; };
+		2752A08A0F14CE1300BBDC03 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2752A0890F14CE1300BBDC03 /* main.cpp */; };
+		27739F4D0F11439200F233EA /* libmaccore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27739F240F11404A00F233EA /* libmaccore.a */; };
+		27739F4E0F11439300F233EA /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 27739F1A0F11403B00F233EA /* libcore.a */; };
+		27739F520F1143C000F233EA /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27739F510F1143C000F233EA /* Carbon.framework */; };
+		8DD76F6A0486A84900D96B5E /* BenchTool.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859E8B029090EE04C91782 /* BenchTool.1 */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		27739F190F11403B00F233EA /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 27739F120F11403B00F233EA /* core.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = D2AAC046055464E500DB518D;
+			remoteInfo = core;
+		};
+		27739F230F11404A00F233EA /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 27739F1C0F11404A00F233EA /* maccore.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = D2AAC046055464E500DB518D;
+			remoteInfo = maccore;
+		};
+		27739F3C0F11424800F233EA /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 27739F1C0F11404A00F233EA /* maccore.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = D2AAC045055464E500DB518D;
+			remoteInfo = maccore;
+		};
+		27739F3E0F11424C00F233EA /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 27739F120F11403B00F233EA /* core.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = D2AAC045055464E500DB518D;
+			remoteInfo = core;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		8DD76F690486A84900D96B5E /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 8;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+				8DD76F6A0486A84900D96B5E /* BenchTool.1 in CopyFiles */,
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		272FB4390F11A19C00CA935D /* RectBench.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RectBench.cpp; path = ../RectBench.cpp; sourceTree = SOURCE_ROOT; };
+		272FB4F20F11B40300CA935D /* SkBenchmark.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBenchmark.cpp; path = ../SkBenchmark.cpp; sourceTree = SOURCE_ROOT; };
+		2752A0890F14CE1300BBDC03 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = ../main.cpp; sourceTree = SOURCE_ROOT; };
+		27739F120F11403B00F233EA /* core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = core.xcodeproj; path = ../../xcode/core/core.xcodeproj; sourceTree = SOURCE_ROOT; };
+		27739F1C0F11404A00F233EA /* maccore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = maccore.xcodeproj; path = ../../xcode/maccore/maccore.xcodeproj; sourceTree = SOURCE_ROOT; };
+		27739F510F1143C000F233EA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
+		8DD76F6C0486A84900D96B5E /* BenchTool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = BenchTool; sourceTree = BUILT_PRODUCTS_DIR; };
+		C6859E8B029090EE04C91782 /* BenchTool.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = BenchTool.1; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		8DD76F660486A84900D96B5E /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				27739F4D0F11439200F233EA /* libmaccore.a in Frameworks */,
+				27739F4E0F11439300F233EA /* libcore.a in Frameworks */,
+				27739F520F1143C000F233EA /* Carbon.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		08FB7794FE84155DC02AAC07 /* BenchTool */ = {
+			isa = PBXGroup;
+			children = (
+				27739F510F1143C000F233EA /* Carbon.framework */,
+				27739F1C0F11404A00F233EA /* maccore.xcodeproj */,
+				27739F120F11403B00F233EA /* core.xcodeproj */,
+				08FB7795FE84155DC02AAC07 /* Source */,
+				C6859E8C029090F304C91782 /* Documentation */,
+				1AB674ADFE9D54B511CA2CBB /* Products */,
+			);
+			name = BenchTool;
+			sourceTree = "<group>";
+		};
+		08FB7795FE84155DC02AAC07 /* Source */ = {
+			isa = PBXGroup;
+			children = (
+				2752A0890F14CE1300BBDC03 /* main.cpp */,
+				272FB4F20F11B40300CA935D /* SkBenchmark.cpp */,
+				272FB4390F11A19C00CA935D /* RectBench.cpp */,
+			);
+			name = Source;
+			sourceTree = "<group>";
+		};
+		1AB674ADFE9D54B511CA2CBB /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				8DD76F6C0486A84900D96B5E /* BenchTool */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		27739F130F11403B00F233EA /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				27739F1A0F11403B00F233EA /* libcore.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		27739F1D0F11404A00F233EA /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				27739F240F11404A00F233EA /* libmaccore.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		C6859E8C029090F304C91782 /* Documentation */ = {
+			isa = PBXGroup;
+			children = (
+				C6859E8B029090EE04C91782 /* BenchTool.1 */,
+			);
+			name = Documentation;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		8DD76F620486A84900D96B5E /* BenchTool */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "BenchTool" */;
+			buildPhases = (
+				8DD76F640486A84900D96B5E /* Sources */,
+				8DD76F660486A84900D96B5E /* Frameworks */,
+				8DD76F690486A84900D96B5E /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				27739F3D0F11424800F233EA /* PBXTargetDependency */,
+				27739F3F0F11424C00F233EA /* PBXTargetDependency */,
+			);
+			name = BenchTool;
+			productInstallPath = "$(HOME)/bin";
+			productName = BenchTool;
+			productReference = 8DD76F6C0486A84900D96B5E /* BenchTool */;
+			productType = "com.apple.product-type.tool";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		08FB7793FE84155DC02AAC07 /* Project object */ = {
+			isa = PBXProject;
+			buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "BenchTool" */;
+			compatibilityVersion = "Xcode 3.1";
+			hasScannedForEncodings = 1;
+			mainGroup = 08FB7794FE84155DC02AAC07 /* BenchTool */;
+			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = 27739F130F11403B00F233EA /* Products */;
+					ProjectRef = 27739F120F11403B00F233EA /* core.xcodeproj */;
+				},
+				{
+					ProductGroup = 27739F1D0F11404A00F233EA /* Products */;
+					ProjectRef = 27739F1C0F11404A00F233EA /* maccore.xcodeproj */;
+				},
+			);
+			projectRoot = "";
+			targets = (
+				8DD76F620486A84900D96B5E /* BenchTool */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXReferenceProxy section */
+		27739F1A0F11403B00F233EA /* libcore.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = libcore.a;
+			remoteRef = 27739F190F11403B00F233EA /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		27739F240F11404A00F233EA /* libmaccore.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = libmaccore.a;
+			remoteRef = 27739F230F11404A00F233EA /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
+/* Begin PBXSourcesBuildPhase section */
+		8DD76F640486A84900D96B5E /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				272FB43A0F11A19C00CA935D /* RectBench.cpp in Sources */,
+				272FB4F30F11B40300CA935D /* SkBenchmark.cpp in Sources */,
+				2752A08A0F14CE1300BBDC03 /* main.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		27739F3D0F11424800F233EA /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = maccore;
+			targetProxy = 27739F3C0F11424800F233EA /* PBXContainerItemProxy */;
+		};
+		27739F3F0F11424C00F233EA /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = core;
+			targetProxy = 27739F3E0F11424C00F233EA /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+		1DEB923208733DC60010E9CD /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_ENABLE_FIX_AND_CONTINUE = YES;
+				GCC_MODEL_TUNING = G5;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"_GLIBCXX_DEBUG=1",
+					"_GLIBCXX_DEBUG_PEDANTIC=1",
+				);
+				INSTALL_PATH = /usr/local/bin;
+				PRODUCT_NAME = BenchTool;
+			};
+			name = Debug;
+		};
+		1DEB923308733DC60010E9CD /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_MODEL_TUNING = G5;
+				INSTALL_PATH = /usr/local/bin;
+				PRODUCT_NAME = BenchTool;
+			};
+			name = Release;
+		};
+		1DEB923608733DC60010E9CD /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_ENABLE_CPP_EXCEPTIONS = NO;
+				GCC_ENABLE_CPP_RTTI = NO;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_THREADSAFE_STATICS = NO;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				PREBINDING = NO;
+				SDKROOT = macosx10.5;
+				USER_HEADER_SEARCH_PATHS = ".. ../../include/**";
+			};
+			name = Debug;
+		};
+		1DEB923708733DC60010E9CD /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_ENABLE_CPP_EXCEPTIONS = NO;
+				GCC_ENABLE_CPP_RTTI = NO;
+				GCC_THREADSAFE_STATICS = NO;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				PREBINDING = NO;
+				SDKROOT = macosx10.5;
+				USER_HEADER_SEARCH_PATHS = ".. ../../include/**";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "BenchTool" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1DEB923208733DC60010E9CD /* Debug */,
+				1DEB923308733DC60010E9CD /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "BenchTool" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1DEB923608733DC60010E9CD /* Debug */,
+				1DEB923708733DC60010E9CD /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}
diff --git a/bench/BitmapBench.cpp b/bench/BitmapBench.cpp
new file mode 100644
index 0000000..5f06f88
--- /dev/null
+++ b/bench/BitmapBench.cpp
@@ -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.
+ */
+#include "SkBenchmark.h"
+#include "SkBitmap.h"
+#include "SkPaint.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+static const char* gTileName[] = {
+    "clamp", "repeat", "mirror"
+};
+
+static const char* gConfigName[] = {
+    "ERROR", "a1", "a8", "index8", "565", "4444", "8888"
+};
+
+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);
+}
+
+static int conv6ToByte(int x) {
+    return x * 0xFF / 5;
+}
+
+static int convByteTo6(int x) {
+    return x * 5 / 255;
+}
+
+static uint8_t compute666Index(SkPMColor c) {
+    int r = SkGetPackedR32(c);
+    int g = SkGetPackedG32(c);
+    int b = SkGetPackedB32(c);
+
+    return convByteTo6(r) * 36 + convByteTo6(g) * 6 + convByteTo6(b);
+}
+
+static void convertToIndex666(const SkBitmap& src, SkBitmap* dst) {
+    SkColorTable* ctable = new SkColorTable(216);
+    SkPMColor* colors = ctable->lockColors();
+    // rrr ggg bbb
+    for (int r = 0; r < 6; r++) {
+        int rr = conv6ToByte(r);
+        for (int g = 0; g < 6; g++) {
+            int gg = conv6ToByte(g);
+            for (int b = 0; b < 6; b++) {
+                int bb = conv6ToByte(b);
+                *colors++ = SkPreMultiplyARGB(0xFF, rr, gg, bb);
+            }
+        }
+    }
+    ctable->unlockColors(true);
+    dst->setConfig(SkBitmap::kIndex8_Config, src.width(), src.height());
+    dst->allocPixels(ctable);
+    ctable->unref();
+
+    SkAutoLockPixels alps(src);
+    SkAutoLockPixels alpd(*dst);
+
+    for (int y = 0; y < src.height(); y++) {
+        const SkPMColor* srcP = src.getAddr32(0, y);
+        uint8_t* dstP = dst->getAddr8(0, y);
+        for (int x = src.width() - 1; x >= 0; --x) {
+            *dstP++ = compute666Index(*srcP++);
+        }
+    }
+}
+
+/*  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 {
+    SkBitmap    fBitmap;
+    SkPaint     fPaint;
+    bool        fIsOpaque;
+    bool        fForceUpdate; //bitmap marked as dirty before each draw. forces bitmap to be updated on device cache
+    int         fTileX, fTileY; // -1 means don't use shader
+    SkString    fName;
+    enum { N = SkBENCHLOOP(300) };
+public:
+    BitmapBench(void* param, bool isOpaque, SkBitmap::Config c,
+                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;
+        const int h = 128;
+        SkBitmap bm;
+
+        if (SkBitmap::kIndex8_Config == c) {
+            bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+        } else {
+            bm.setConfig(c, w, h);
+        }
+        bm.allocPixels();
+        bm.eraseColor(isOpaque ? SK_ColorBLACK : 0);
+
+        drawIntoBitmap(bm);
+
+        if (SkBitmap::kIndex8_Config == c) {
+            convertToIndex666(bm, &fBitmap);
+        } else {
+            fBitmap = bm;
+        }
+
+        if (fBitmap.getColorTable()) {
+            fBitmap.getColorTable()->setIsOpaque(isOpaque);
+        }
+        fBitmap.setIsOpaque(isOpaque);
+        fBitmap.setIsVolatile(bitmapVolatile);
+    }
+
+protected:
+    virtual const char* onGetName() {
+        fName.set("bitmap");
+        if (fTileX >= 0) {
+            fName.appendf("_%s", gTileName[fTileX]);
+            if (fTileY != fTileX) {
+                fName.appendf("_%s", gTileName[fTileY]);
+            }
+        }
+        fName.appendf("_%s%s", gConfigName[fBitmap.config()],
+                      fIsOpaque ? "" : "_A");
+        if (fForceUpdate)
+            fName.append("_update");
+        if (fBitmap.isVolatile())
+            fName.append("_volatile");
+
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkIPoint dim = this->getSize();
+        SkRandom rand;
+
+        SkPaint paint(fPaint);
+        this->setupPaint(&paint);
+
+        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;
+
+            if (fForceUpdate)
+                bitmap.notifyPixelsChanged();
+
+            canvas->drawBitmap(bitmap, x, y, &paint);
+        }
+    }
+
+private:
+    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); }
+static SkBenchmark* Fact3(void* p) { return new BitmapBench(p, false, SkBitmap::kARGB_4444_Config); }
+static SkBenchmark* Fact4(void* p) { return new BitmapBench(p, true, SkBitmap::kARGB_4444_Config); }
+static SkBenchmark* Fact5(void* p) { return new BitmapBench(p, false, SkBitmap::kIndex8_Config); }
+static SkBenchmark* Fact6(void* p) { return new BitmapBench(p, true, SkBitmap::kIndex8_Config); }
+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);
+static BenchRegistry gReg3(Fact3);
+static BenchRegistry gReg4(Fact4);
+static BenchRegistry gReg5(Fact5);
+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..b9e71f7
--- /dev/null
+++ b/bench/BitmapRectBench.cpp
@@ -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.
+ */
+#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;
+    uint8_t     fAlpha;
+    SkString    fName;
+    SkRect      fSrcR, fDstR;
+    enum { N = SkBENCHLOOP(300) };
+public:
+    BitmapRectBench(void* param, U8CPU alpha, bool doFilter) : INHERITED(param) {
+        fAlpha = SkToU8(alpha);
+        fDoFilter = doFilter;
+
+        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.set(0, 0, w, h);
+        fDstR.set(0, 0, w, h);
+    }
+
+protected:
+    virtual const char* onGetName() {
+        fName.printf("bitmaprect_%02X_%sfilter", fAlpha, fDoFilter ? "" : "no");
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkIPoint dim = this->getSize();
+        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))
+DEF_BENCH(return new BitmapRectBench(p, 0x80, false))
+DEF_BENCH(return new BitmapRectBench(p, 0xFF, true))
+DEF_BENCH(return new BitmapRectBench(p, 0x80, true))
+
diff --git a/bench/BlurBench.cpp b/bench/BlurBench.cpp
new file mode 100644
index 0000000..371e26d
--- /dev/null
+++ b/bench/BlurBench.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 "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkShader.h"
+#include "SkString.h"
+#include "SkBlurMaskFilter.h"
+
+#define SMALL   SkIntToScalar(2)
+#define REAL    SkFloatToScalar(1.5f)
+#define BIG     SkIntToScalar(10)
+
+static const char* gStyleName[] = {
+    "normal",
+    "solid",
+    "outer",
+    "inner"
+};
+
+class BlurBench : public SkBenchmark {
+    SkScalar    fRadius;
+    SkBlurMaskFilter::BlurStyle fStyle;
+    SkString    fName;
+
+public:
+    BlurBench(void* param, SkScalar rad, SkBlurMaskFilter::BlurStyle bs) : INHERITED(param) {
+        fRadius = rad;
+        fStyle = bs;
+        const char* name = rad > 0 ? gStyleName[bs] : "none";
+        if (SkScalarFraction(rad) != 0) {
+            fName.printf("blur_%.2f_%s", SkScalarToFloat(rad), name);
+        } else {
+            fName.printf("blur_%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(10); i++) {
+            SkRect r = SkRect::MakeWH(rand.nextUScalar1() * 400,
+                                      rand.nextUScalar1() * 400);
+            r.offset(fRadius, fRadius);
+
+            if (fRadius > 0) {
+                SkMaskFilter* mf = SkBlurMaskFilter::Create(fRadius, fStyle, 0);
+                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); }
+
+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); }
+
+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); }
+
+static SkBenchmark* FactNone(void* p) { return new BlurBench(p, 0, SkBlurMaskFilter::kNormal_BlurStyle); }
+
+static BenchRegistry gReg00(Fact00);
+static BenchRegistry gReg01(Fact01);
+static BenchRegistry gReg02(Fact02);
+static BenchRegistry gReg03(Fact03);
+
+static BenchRegistry gReg10(Fact10);
+static BenchRegistry gReg11(Fact11);
+static BenchRegistry gReg12(Fact12);
+static BenchRegistry gReg13(Fact13);
+
+static BenchRegistry gReg20(Fact20);
+static BenchRegistry gReg21(Fact21);
+static BenchRegistry gReg22(Fact22);
+static BenchRegistry gReg23(Fact23);
+
+static BenchRegistry gRegNone(FactNone);
+
diff --git a/bench/ChecksumBench.cpp b/bench/ChecksumBench.cpp
new file mode 100644
index 0000000..818b9e3
--- /dev/null
+++ b/bench/ChecksumBench.cpp
@@ -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.
+ */
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkChecksum.h"
+#include "SkRandom.h"
+
+class ComputeChecksumBench : public SkBenchmark {
+    enum {
+        U32COUNT  = 256,
+        SIZE      = U32COUNT * 4,
+        N         = SkBENCHLOOP(100000),
+    };
+    uint32_t    fData[U32COUNT];
+
+public:
+    ComputeChecksumBench(void* param) : INHERITED(param) {
+        SkRandom rand;
+        for (int i = 0; i < U32COUNT; ++i) {
+            fData[i] = rand.nextU();
+        }
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return "compute_checksum";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        for (int i = 0; i < N; i++) {
+            volatile uint32_t result = SkChecksum::Compute(fData, sizeof(fData));
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact0(void* p) { return new ComputeChecksumBench(p); }
+
+static BenchRegistry gReg0(Fact0);
diff --git a/bench/ChromeBench.cpp b/bench/ChromeBench.cpp
new file mode 100644
index 0000000..f045573
--- /dev/null
+++ b/bench/ChromeBench.cpp
@@ -0,0 +1,501 @@
+/*
+ * Copyright 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 "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkString.h"
+
+/**
+   Benchmarks that try to emulate a particular Skia call pattern observed in Chrome.
+*/
+
+/// blitRect() calls emitted by Chrome while scrolling through gmail: count, width, height.
+int gmailScrollingRectSpec [431*3] = {
+      1, 1254, 1160,
+      1, 64, 112,
+      1, 1034, 261,
+      1, 1166, 1,
+      1, 1166, 20,
+      1, 1254, 40,
+      1, 140, 20,
+      1, 22, 30,
+      1, 22, 39,
+      1, 294, 29,
+      1, 336, 25,
+      1, 336, 5,
+      1, 37, 3,
+      1, 37, 4,
+      1, 37, 5,
+      1, 41, 29,
+      1, 57, 15,
+      1, 72, 5,
+      1, 72, 8,
+      1, 76, 29,
+      1, 981, 88,
+      1, 990, 2,
+      1, 990, 6,
+      2, 220, 88,
+      2, 294, 1,
+      2, 37, 6,
+      2, 391, 55,
+      2, 57, 11,
+      2, 57, 14,
+      2, 57, 7,
+      2, 981, 30,
+      2, 990, 15,
+      2, 990, 19,
+      3, 114, 16,
+      3, 1166, 39,
+      3, 1254, 154,
+      3, 12, 12,
+      3, 162, 7,
+      3, 164, 479,
+      3, 167, 449,
+      3, 16, 24,
+      3, 204, 497,
+      3, 205, 434,
+      3, 220, 1127,
+      3, 220, 1132,
+      3, 220, 931,
+      3, 220, 933,
+      3, 220, 934,
+      3, 297, 8,
+      3, 72, 25,
+      3, 87, 30,
+      3, 981, 1,
+      3, 981, 126,
+      3, 990, 27,
+      3, 990, 36,
+      3, 991, 29,
+      4, 1254, 306,
+      4, 1254, 36,
+      4, 1, 1,
+      4, 1, 14,
+      4, 1, 19,
+      4, 1, 7,
+      4, 21, 21,
+      4, 220, 30,
+      4, 46, 949,
+      4, 509, 30,
+      4, 57, 2,
+      4, 57, 6,
+      4, 990, 11,
+      5, 13, 8,
+      5, 198, 24,
+      5, 24, 24,
+      5, 25, 24,
+      5, 2, 24,
+      5, 37, 33,
+      5, 57, 4,
+      5, 599, 24,
+      5, 90, 24,
+      5, 981, 19,
+      5, 990, 23,
+      5, 990, 8,
+      6, 101, 29,
+      6, 117, 29,
+      6, 1254, 88,
+      6, 139, 29,
+      6, 13, 12,
+      6, 15, 15,
+      6, 164, 25,
+      6, 16, 16,
+      6, 198, 7,
+      6, 1, 12,
+      6, 1, 15,
+      6, 1, 27,
+      6, 220, 936,
+      6, 24, 7,
+      6, 25, 7,
+      6, 2, 7,
+      6, 326, 29,
+      6, 336, 29,
+      6, 599, 7,
+      6, 86, 29,
+      6, 90, 7,
+      6, 96, 29,
+      6, 991, 31,
+      7, 198, 12,
+      7, 198, 20,
+      7, 198, 33,
+      7, 198, 35,
+      7, 24, 12,
+      7, 24, 20,
+      7, 24, 33,
+      7, 24, 35,
+      7, 25, 12,
+      7, 25, 20,
+      7, 25, 33,
+      7, 25, 35,
+      7, 2, 12,
+      7, 2, 20,
+      7, 2, 33,
+      7, 2, 35,
+      7, 304, 1,
+      7, 38, 29,
+      7, 51, 29,
+      7, 599, 12,
+      7, 599, 20,
+      7, 599, 33,
+      7, 599, 35,
+      7, 90, 12,
+      7, 90, 20,
+      7, 90, 33,
+      7, 90, 35,
+      8, 13, 5,
+      8, 198, 13,
+      8, 198, 23,
+      8, 220, 1,
+      8, 24, 13,
+      8, 24, 23,
+      8, 25, 13,
+      8, 25, 23,
+      8, 2, 13,
+      8, 2, 23,
+      8, 329, 28,
+      8, 57, 10,
+      8, 599, 13,
+      8, 599, 23,
+      8, 90, 13,
+      8, 90, 23,
+      9, 198, 17,
+      9, 198, 19,
+      9, 198, 37,
+      9, 198, 5,
+      9, 198, 8,
+      9, 24, 17,
+      9, 24, 19,
+      9, 24, 37,
+      9, 24, 5,
+      9, 24, 8,
+      9, 25, 17,
+      9, 25, 19,
+      9, 25, 37,
+      9, 25, 5,
+      9, 25, 8,
+      9, 2, 17,
+      9, 2, 19,
+      9, 2, 37,
+      9, 2, 5,
+      9, 2, 8,
+      9, 599, 17,
+      9, 599, 19,
+      9, 599, 37,
+      9, 599, 5,
+      9, 599, 8,
+      9, 72, 29,
+      9, 90, 17,
+      9, 90, 19,
+      9, 90, 37,
+      9, 90, 5,
+      9, 90, 8,
+     10, 13, 11,
+     10, 13, 9,
+     10, 198, 26,
+     10, 198, 28,
+     10, 1, 23,
+     10, 1, 4,
+     10, 1, 6,
+     10, 24, 26,
+     10, 24, 28,
+     10, 25, 26,
+     10, 25, 28,
+     10, 26, 24,
+     10, 2, 26,
+     10, 2, 28,
+     10, 599, 26,
+     10, 599, 28,
+     10, 90, 26,
+     10, 90, 28,
+     11, 198, 27,
+     11, 24, 27,
+     11, 25, 27,
+     11, 2, 27,
+     11, 599, 27,
+     11, 90, 27,
+     12, 198, 14,
+     12, 198, 21,
+     12, 198, 3,
+     12, 1, 11,
+     12, 1, 2,
+     12, 1, 8,
+     12, 24, 14,
+     12, 24, 21,
+     12, 24, 3,
+     12, 25, 14,
+     12, 25, 21,
+     12, 25, 3,
+     12, 26, 7,
+     12, 2, 14,
+     12, 2, 21,
+     12, 2, 3,
+     12, 329, 14,
+     12, 38, 2,
+     12, 599, 14,
+     12, 599, 21,
+     12, 599, 3,
+     12, 90, 14,
+     12, 90, 21,
+     12, 90, 3,
+     13, 198, 11,
+     13, 198, 15,
+     13, 198, 31,
+     13, 24, 11,
+     13, 24, 15,
+     13, 24, 31,
+     13, 25, 11,
+     13, 25, 15,
+     13, 25, 31,
+     13, 2, 11,
+     13, 2, 15,
+     13, 2, 31,
+     13, 57, 13,
+     13, 599, 11,
+     13, 599, 15,
+     13, 599, 31,
+     13, 71, 29,
+     13, 90, 11,
+     13, 90, 15,
+     13, 90, 31,
+     14, 13, 2,
+     14, 198, 10,
+     14, 24, 10,
+     14, 25, 10,
+     14, 26, 12,
+     14, 26, 20,
+     14, 26, 33,
+     14, 26, 35,
+     14, 2, 10,
+     14, 336, 1,
+     14, 45, 29,
+     14, 599, 10,
+     14, 63, 29,
+     14, 90, 10,
+     15, 13, 3,
+     15, 198, 2,
+     15, 198, 29,
+     15, 198, 34,
+     15, 24, 2,
+     15, 24, 29,
+     15, 24, 34,
+     15, 25, 2,
+     15, 25, 29,
+     15, 25, 34,
+     15, 2, 2,
+     15, 2, 29,
+     15, 2, 34,
+     15, 599, 2,
+     15, 599, 29,
+     15, 599, 34,
+     15, 90, 2,
+     15, 90, 29,
+     15, 90, 34,
+     16, 13, 4,
+     16, 13, 6,
+     16, 198, 16,
+     16, 198, 9,
+     16, 1, 10,
+     16, 24, 16,
+     16, 24, 9,
+     16, 25, 16,
+     16, 25, 9,
+     16, 26, 13,
+     16, 26, 23,
+     16, 2, 16,
+     16, 2, 9,
+     16, 599, 16,
+     16, 599, 9,
+     16, 90, 16,
+     16, 90, 9,
+     17, 13, 7,
+     17, 198, 18,
+     17, 24, 18,
+     17, 25, 18,
+     17, 2, 18,
+     17, 599, 18,
+     17, 90, 18,
+     18, 198, 22,
+     18, 198, 32,
+     18, 198, 36,
+     18, 198, 4,
+     18, 24, 22,
+     18, 24, 32,
+     18, 24, 36,
+     18, 24, 4,
+     18, 25, 22,
+     18, 25, 32,
+     18, 25, 36,
+     18, 25, 4,
+     18, 26, 17,
+     18, 26, 19,
+     18, 26, 37,
+     18, 26, 5,
+     18, 26, 8,
+     18, 2, 22,
+     18, 2, 32,
+     18, 2, 36,
+     18, 2, 4,
+     18, 599, 22,
+     18, 599, 32,
+     18, 599, 36,
+     18, 599, 4,
+     18, 90, 22,
+     18, 90, 32,
+     18, 90, 36,
+     18, 90, 4,
+     19, 13, 10,
+     20, 1254, 30,
+     20, 16, 1007,
+     20, 26, 26,
+     20, 26, 28,
+     21, 198, 6,
+     21, 24, 6,
+     21, 25, 6,
+     21, 2, 6,
+     21, 599, 6,
+     21, 90, 6,
+     22, 198, 38,
+     22, 22, 40,
+     22, 24, 38,
+     22, 25, 38,
+     22, 26, 27,
+     22, 2, 38,
+     22, 599, 38,
+     22, 90, 38,
+     23, 1254, 1160,
+     24, 220, 930,
+     24, 26, 14,
+     24, 26, 21,
+     24, 26, 3,
+     26, 11, 11,
+     26, 1, 13,
+     26, 26, 11,
+     26, 26, 15,
+     26, 26, 31,
+     28, 26, 10,
+     30, 176, 60,
+     30, 26, 2,
+     30, 26, 29,
+     30, 26, 34,
+     32, 26, 16,
+     32, 26, 9,
+     34, 26, 18,
+     36, 26, 22,
+     36, 26, 32,
+     36, 26, 36,
+     36, 26, 4,
+     36, 37, 26,
+     42, 26, 6,
+     43, 115, 29,
+     44, 198, 25,
+     44, 24, 25,
+     44, 25, 25,
+     44, 26, 38,
+     44, 2, 25,
+     44, 599, 25,
+     44, 90, 25,
+     46, 22, 1,
+     47, 198, 30,
+     47, 25, 30,
+     47, 2, 30,
+     47, 599, 30,
+     47, 90, 30,
+     48, 24, 30,
+     52, 176, 30,
+     58, 140, 24,
+     58, 4, 30,
+     63, 990, 29,
+     64, 1254, 1,
+     88, 26, 25,
+     92, 198, 39,
+     92, 25, 39,
+     92, 2, 39,
+     92, 599, 39,
+     92, 90, 39,
+     93, 24, 39,
+     94, 26, 30,
+    108, 1254, 1051,
+    117, 140, 1,
+    119, 160, 1,
+    126, 1, 29,
+    132, 135, 16,
+    147, 72, 16,
+    184, 26, 39,
+    238, 990, 1,
+    376, 11, 1007,
+    380, 11, 487,
+   1389, 1034, 1007,
+   1870, 57, 16,
+   4034, 1, 16,
+   8521, 198, 40,
+   8521, 25, 40,
+   8521, 2, 40,
+   8521, 599, 40,
+   8521, 90, 40,
+   8543, 24, 40,
+   8883, 13, 13,
+  17042, 26, 40,
+  17664, 198, 1,
+  17664, 25, 1,
+  17664, 2, 1,
+  17664, 599, 1,
+  17664, 90, 1,
+  17710, 24, 1,
+  35328, 26, 1,
+};
+
+/// Emulates the mix of rects blitted by gmail during scrolling
+class ScrollGmailBench : public SkBenchmark {
+    enum {
+        W = 1254,
+        H = 1160,
+        N = 431
+    };
+public:
+    ScrollGmailBench(void* param) : INHERITED(param) { }
+
+protected:
+
+    virtual const char* onGetName() { return "chrome_scrollGmail"; }
+    virtual void onDraw(SkCanvas* canvas) {
+        SkDEBUGCODE(this->validateBounds(canvas));
+        SkPaint paint;
+        this->setupPaint(&paint);
+        for (int i = 0; i < N; i++) {
+            SkRect current;
+            setRectangle(current, i);
+            for (int j = 0; j < SkBENCHLOOP(gmailScrollingRectSpec[i*3]); j++) {
+                canvas->drawRect(current, paint);
+            }
+        }
+    }
+    virtual SkIPoint onGetSize() { return SkIPoint::Make(W, H); }
+
+    void setRectangle(SkRect& current, int i) {
+        current.set(0, 0,
+                    SkIntToScalar(gmailScrollingRectSpec[i*3+1]), SkIntToScalar(gmailScrollingRectSpec[i*3+2]));
+    }
+    void validateBounds(SkCanvas* canvas) {
+        SkIRect bounds;
+        canvas->getClipDeviceBounds(&bounds);
+        SkASSERT(bounds.right()-bounds.left() >= W);
+        SkASSERT(bounds.bottom()-bounds.top() >= H);
+    }
+
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+static SkBenchmark* ScrollGmailFactory(void* p) {
+    return SkNEW_ARGS(ScrollGmailBench, (p));
+}
+
+// Disabled this benchmark: it takes 15x longer than any other benchmark
+// and is probably not giving us important information.
+//static BenchRegistry gScrollGmailReg(ScrollGmailFactory);
+
diff --git a/bench/DashBench.cpp b/bench/DashBench.cpp
new file mode 100644
index 0000000..d766eb1
--- /dev/null
+++ b/bench/DashBench.cpp
@@ -0,0 +1,295 @@
+
+/*
+ * Copyright 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);
+            dst.rewind();
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+/*
+ *  We try to special case square dashes (intervals are equal to strokewidth).
+ */
+class DashLineBench : public SkBenchmark {
+    SkString fName;
+    SkPath   fPath;
+    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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkScalar gDots[] = { SK_Scalar1, SK_Scalar1 };
+
+#define PARAM(array)    array, SK_ARRAY_COUNT(array)
+
+static SkBenchmark* gF0(void* p) { return new DashBench(p, PARAM(gDots), 0); }
+static SkBenchmark* gF1(void* p) { return new DashBench(p, PARAM(gDots), 1); }
+static SkBenchmark* gF2(void* p) { return new DashBench(p, PARAM(gDots), 1, true); }
+static SkBenchmark* gF3(void* p) { return new DashBench(p, PARAM(gDots), 4); }
+static SkBenchmark* gF4(void* p) { return new MakeDashBench(p, make_poly, "poly"); }
+static SkBenchmark* gF5(void* p) { return new MakeDashBench(p, make_quad, "quad"); }
+static SkBenchmark* gF6(void* p) { return new MakeDashBench(p, make_cubic, "cubic"); }
+static SkBenchmark* gF700(void* p) { return new DashLineBench(p, 0, false); }
+static SkBenchmark* gF710(void* p) { return new DashLineBench(p, SK_Scalar1, false); }
+static SkBenchmark* gF720(void* p) { return new DashLineBench(p, 2 * SK_Scalar1, false); }
+static SkBenchmark* gF701(void* p) { return new DashLineBench(p, 0, true); }
+static SkBenchmark* gF711(void* p) { return new DashLineBench(p, SK_Scalar1, true); }
+static SkBenchmark* gF721(void* p) { return new DashLineBench(p, 2 * SK_Scalar1, true); }
+
+static BenchRegistry gR0(gF0);
+static BenchRegistry gR1(gF1);
+static BenchRegistry gR2(gF2);
+static BenchRegistry gR3(gF3);
+static BenchRegistry gR4(gF4);
+static BenchRegistry gR5(gF5);
+static BenchRegistry gR6(gF6);
+static BenchRegistry gR700(gF700);
+static BenchRegistry gR710(gF710);
+static BenchRegistry gR720(gF720);
+static BenchRegistry gR701(gF701);
+static BenchRegistry gR711(gF711);
+static BenchRegistry gR721(gF721);
diff --git a/bench/DecodeBench.cpp b/bench/DecodeBench.cpp
new file mode 100644
index 0000000..6593d10
--- /dev/null
+++ b/bench/DecodeBench.cpp
@@ -0,0 +1,63 @@
+
+/*
+ * Copyright 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 "SkImageDecoder.h"
+#include "SkString.h"
+
+static const char* gConfigName[] = {
+    "ERROR", "a1", "a8", "index8", "565", "4444", "8888"
+};
+
+class DecodeBench : public SkBenchmark {
+    const char* fFilename;
+    SkBitmap::Config fPrefConfig;
+    SkString fName;
+    enum { N = SkBENCHLOOP(10) };
+public:
+    DecodeBench(void* param, SkBitmap::Config c) : SkBenchmark(param) {
+        fFilename = this->findDefine("decode-filename");
+        fPrefConfig = c;
+
+        const char* fname = NULL;
+        if (fFilename) {
+            fname = strrchr(fFilename, '/');
+            if (fname) {
+                fname += 1; // skip the slash
+            }
+        }
+        fName.printf("decode_%s_%s", gConfigName[c], fname);
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (fFilename) {
+            for (int i = 0; i < N; i++) {
+                SkBitmap bm;
+                SkImageDecoder::DecodeFile(fFilename, &bm, fPrefConfig,
+                                           SkImageDecoder::kDecodePixels_Mode);
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+static SkBenchmark* Fact0(void* p) { return new DecodeBench(p, SkBitmap::kARGB_8888_Config); }
+static SkBenchmark* Fact1(void* p) { return new DecodeBench(p, SkBitmap::kRGB_565_Config); }
+static SkBenchmark* Fact2(void* p) { return new DecodeBench(p, SkBitmap::kARGB_4444_Config); }
+
+static BenchRegistry gReg0(Fact0);
+static BenchRegistry gReg1(Fact1);
+static BenchRegistry gReg2(Fact2);
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/FontScalerBench.cpp b/bench/FontScalerBench.cpp
new file mode 100644
index 0000000..4ac6a35
--- /dev/null
+++ b/bench/FontScalerBench.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 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 "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+extern bool gSkSuppressFontCachePurgeSpew;
+
+class FontScalerBench : public SkBenchmark {
+    SkString fName;
+    SkString fText;
+    bool     fDoLCD;
+public:
+    FontScalerBench(void* param, bool doLCD) : INHERITED(param) {
+        fName.printf("fontscaler_%s", doLCD ? "lcd" : "aa");
+        fText.set("abcdefghijklmnopqrstuvwxyz01234567890");
+        fDoLCD = doLCD;
+    }
+
+protected:
+    virtual const char* onGetName() { return fName.c_str(); }
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        this->setupPaint(&paint);
+        paint.setLCDRenderText(fDoLCD);
+
+        bool prev = gSkSuppressFontCachePurgeSpew;
+        gSkSuppressFontCachePurgeSpew = true;
+
+        // this is critical - we want to time the creation process, so we
+        // explicitly flush our cache before each run
+        SkGraphics::PurgeFontCache();
+
+        for (int ps = 9; ps <= 24; ps += 2) {
+            paint.setTextSize(SkIntToScalar(ps));
+            canvas->drawText(fText.c_str(), fText.size(),
+                             0, SkIntToScalar(20), paint);
+        }
+
+        gSkSuppressFontCachePurgeSpew = prev;
+    }
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact0(void* p) { return SkNEW_ARGS(FontScalerBench, (p, false)); }
+static SkBenchmark* Fact1(void* p) { return SkNEW_ARGS(FontScalerBench, (p, true)); }
+
+static BenchRegistry gReg0(Fact0);
+static BenchRegistry gReg1(Fact1);
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
new file mode 100644
index 0000000..d7ce74d
--- /dev/null
+++ b/bench/GradientBench.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 "SkBenchmark.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkGradientShader.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+#include "SkString.h"
+#include "SkUnitMapper.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 }
+};
+
+/// Ignores scale
+static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
+                            SkShader::TileMode tm, SkUnitMapper* mapper,
+                            float scale) {
+    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,
+                            float scale) {
+    SkPoint center;
+    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
+               SkScalarAve(pts[0].fY, pts[1].fY));
+    return SkGradientShader::CreateRadial(center, center.fX * scale,
+                                          data.fColors,
+                                          data.fPos, data.fCount, tm, mapper);
+}
+
+/// Ignores scale
+static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
+                           SkShader::TileMode tm, SkUnitMapper* mapper,
+                           float scale) {
+    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);
+}
+
+/// Ignores scale
+static SkShader* Make2Radial(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::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);
+}
+
+/// 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,
+                               SkShader::TileMode tm, SkUnitMapper* mapper,
+                               float scale);
+
+static const struct {
+    GradMaker   fMaker;
+    const char* fName;
+    int         fRepeat;
+} gGrads[] = {
+    { MakeLinear,   "linear",  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,
+    kConical_GradType
+};
+
+enum GeomType {
+    kRect_GeomType,
+    kOval_GeomType
+};
+
+static const char* tilemodename(SkShader::TileMode tm) {
+    switch (tm) {
+        case SkShader::kClamp_TileMode:
+            return "clamp";
+        case SkShader::kRepeat_TileMode:
+            return "repeat";
+        case SkShader::kMirror_TileMode:
+            return "mirror";
+        default:
+            SkASSERT(!"unknown tilemode");
+            return "error";
+    }
+}
+
+static const char* geomtypename(GeomType gt) {
+    switch (gt) {
+        case kRect_GeomType:
+            return "rectangle";
+        case kOval_GeomType:
+            return "oval";
+        default:
+            SkASSERT(!"unknown geometry type");
+            return "error";
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GradientBench : public SkBenchmark {
+    SkString fName;
+    SkShader* fShader;
+    int      fCount;
+    enum {
+        W   = 400,
+        H   = 400,
+        N   = 1
+    };
+public:
+    GradientBench(void* param, GradType gradType,
+                  SkShader::TileMode tm = SkShader::kClamp_TileMode,
+                  GeomType geomType = kRect_GeomType,
+                  float scale = 1.0f)
+        : INHERITED(param) {
+        fName.printf("gradient_%s_%s", gGrads[gradType].fName,
+                     tilemodename(tm));
+        if (geomType != kRect_GeomType) {
+            fName.append("_");
+            fName.append(geomtypename(geomType));
+        }
+
+        const SkPoint pts[2] = {
+            { 0, 0 },
+            { SkIntToScalar(W), SkIntToScalar(H) }
+        };
+
+        fCount = SkBENCHLOOP(N * gGrads[gradType].fRepeat);
+        fShader = gGrads[gradType].fMaker(pts, gGradData[0], tm, NULL, scale);
+        fGeomType = geomType;
+    }
+
+    virtual ~GradientBench() {
+        fShader->unref();
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        this->setupPaint(&paint);
+
+        paint.setShader(fShader);
+
+        SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
+        for (int i = 0; i < fCount; i++) {
+            switch (fGeomType) {
+               case kRect_GeomType:
+                   canvas->drawRect(r, paint);
+                   break;
+               case kOval_GeomType:
+                   canvas->drawOval(r, paint);
+                   break;
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+
+    GeomType fGeomType;
+};
+
+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 },
+            { SkIntToScalar(100), SkIntToScalar(100) },
+        };
+
+        for (int i = 0; i < SkBENCHLOOP(1000); i++) {
+            const int a = i % 256;
+            SkColor colors[] = {
+                SK_ColorBLACK,
+                SkColorSetARGB(a, a, a, a),
+                SK_ColorWHITE };
+            SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL,
+                                                         SK_ARRAY_COUNT(colors),
+                                                         SkShader::kClamp_TileMode);
+            paint.setShader(s)->unref();
+            canvas->drawRect(r, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+static SkBenchmark* Fact0(void* p) { return new GradientBench(p, kLinear_GradType); }
+static SkBenchmark* Fact01(void* p) { return new GradientBench(p, kLinear_GradType, SkShader::kMirror_TileMode); }
+
+// Draw a radial gradient of radius 1/2 on a rectangle; half the lines should
+// be completely pinned, the other half should pe partially pinned
+static SkBenchmark* Fact1(void* p) { return new GradientBench(p, kRadial_GradType, SkShader::kClamp_TileMode, kRect_GeomType, 0.5f); }
+
+// Draw a radial gradient on a circle of equal size; all the lines should
+// hit the unpinned fast path (so long as GradientBench.W == H)
+static SkBenchmark* Fact1o(void* p) { return new GradientBench(p, kRadial_GradType, SkShader::kClamp_TileMode, kOval_GeomType); }
+
+static SkBenchmark* Fact11(void* p) { return new GradientBench(p, kRadial_GradType, SkShader::kMirror_TileMode); }
+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); }
+
+static BenchRegistry gReg0(Fact0);
+static BenchRegistry gReg01(Fact01);
+static BenchRegistry gReg1(Fact1);
+static BenchRegistry gReg1o(Fact1o);
+static BenchRegistry gReg11(Fact11);
+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..b689c72
--- /dev/null
+++ b/bench/InterpBench.cpp
@@ -0,0 +1,167 @@
+#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/MathBench.cpp b/bench/MathBench.cpp
new file mode 100644
index 0000000..bdb89ca
--- /dev/null
+++ b/bench/MathBench.cpp
@@ -0,0 +1,408 @@
+#include "SkBenchmark.h"
+#include "SkColorPriv.h"
+#include "SkMatrix.h"
+#include "SkRandom.h"
+#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,
+        kLoop   = 10000
+    };
+    SkString    fName;
+    float       fSrc[kBuffer], fDst[kBuffer];
+public:
+    MathBench(void* param, const char name[]) : INHERITED(param) {
+        fName.printf("math_%s", name);
+
+        SkRandom rand;
+        for (int i = 0; i < kBuffer; ++i) {
+            fSrc[i] = rand.nextSScalar1();
+        }
+
+        fIsRendering = false;
+    }
+
+    virtual void performTest(float* SK_RESTRICT dst,
+                              const float* SK_RESTRICT src,
+                              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, fSrc, kBuffer);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class MathBenchU32 : public MathBench {
+public:
+    MathBenchU32(void* param, const char name[]) : INHERITED(param, name) {}
+
+protected:
+    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);
+    }
+private:
+    typedef MathBench INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class NoOpMathBench : public MathBench {
+public:
+    NoOpMathBench(void* param) : INHERITED(param, "noOp") {}
+protected:
+    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;
+        }
+    }
+private:
+    typedef MathBench INHERITED;
+};
+
+class SlowISqrtMathBench : public MathBench {
+public:
+    SlowISqrtMathBench(void* param) : INHERITED(param, "slowIsqrt") {}
+protected:
+    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]);
+        }
+    }
+private:
+    typedef MathBench INHERITED;
+};
+
+static inline float SkFastInvSqrt(float x) {
+    float xhalf = 0.5f*x;
+    int i = *SkTCast<int*>(&x);
+    i = 0x5f3759df - (i>>1);
+    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;
+}
+
+class FastISqrtMathBench : public MathBench {
+public:
+    FastISqrtMathBench(void* param) : INHERITED(param, "fastIsqrt") {}
+protected:
+    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]);
+        }
+    }
+private:
+    typedef MathBench INHERITED;
+};
+
+static inline uint32_t QMul64(uint32_t value, U8CPU alpha) {
+    SkASSERT((uint8_t)alpha == alpha);
+    const uint32_t mask = 0xFF00FF;
+
+    uint64_t tmp = value;
+    tmp = (tmp & mask) | ((tmp & ~mask) << 24);
+    tmp *= alpha;
+    return (uint32_t) (((tmp >> 8) & mask) | ((tmp >> 32) & ~mask));
+}
+
+class QMul64Bench : public MathBenchU32 {
+public:
+    QMul64Bench(void* param) : INHERITED(param, "qmul64") {}
+protected:
+    virtual void performITest(uint32_t* SK_RESTRICT dst,
+                              const uint32_t* SK_RESTRICT src,
+                              int count) SK_OVERRIDE {
+        for (int i = 0; i < count; ++i) {
+            dst[i] = QMul64(src[i], (uint8_t)i);
+        }
+    }
+private:
+    typedef MathBenchU32 INHERITED;
+};
+
+class QMul32Bench : public MathBenchU32 {
+public:
+    QMul32Bench(void* param) : INHERITED(param, "qmul32") {}
+protected:
+    virtual void performITest(uint32_t* SK_RESTRICT dst,
+                              const uint32_t* SK_RESTRICT src,
+                              int count) SK_OVERRIDE {
+        for (int i = 0; i < count; ++i) {
+            dst[i] = SkAlphaMulQ(src[i], (uint8_t)i);
+        }
+    }
+private:
+    typedef MathBenchU32 INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool isFinite_int(float x) {
+    uint32_t bits = SkFloat2Bits(x);    // need unsigned for our shifts
+    int exponent = bits << 1 >> 24;
+    return exponent != 0xFF;
+}
+
+static bool isFinite_float(float x) {
+    return SkToBool(sk_float_isfinite(x));
+}
+
+static bool isFinite_mulzero(float x) {
+    float y = x * 0;
+    return y == y;
+}
+
+static bool isfinite_and_int(const float data[4]) {
+    return  isFinite_int(data[0]) && isFinite_int(data[1]) && isFinite_int(data[2]) && isFinite_int(data[3]);
+}
+
+static bool isfinite_and_float(const float data[4]) {
+    return  isFinite_float(data[0]) && isFinite_float(data[1]) && isFinite_float(data[2]) && isFinite_float(data[3]);
+}
+
+static bool isfinite_and_mulzero(const float data[4]) {
+    return  isFinite_mulzero(data[0]) && isFinite_mulzero(data[1]) && isFinite_mulzero(data[2]) && isFinite_mulzero(data[3]);
+}
+
+#define mulzeroadd(data)    (data[0]*0 + data[1]*0 + data[2]*0 + data[3]*0)
+
+static bool isfinite_plus_int(const float data[4]) {
+    return  isFinite_int(mulzeroadd(data));
+}
+
+static bool isfinite_plus_float(const float data[4]) {
+    return  !sk_float_isnan(mulzeroadd(data));
+}
+
+static bool isfinite_plus_mulzero(const float data[4]) {
+    float x = mulzeroadd(data);
+    return x == x;
+}
+
+typedef bool (*IsFiniteProc)(const float[]);
+
+#define MAKEREC(name)   { name, #name }
+
+static const struct {
+    IsFiniteProc    fProc;
+    const char*     fName;
+} gRec[] = {
+    MAKEREC(isfinite_and_int),
+    MAKEREC(isfinite_and_float),
+    MAKEREC(isfinite_and_mulzero),
+    MAKEREC(isfinite_plus_int),
+    MAKEREC(isfinite_plus_float),
+    MAKEREC(isfinite_plus_mulzero),
+};
+
+#undef MAKEREC
+
+static bool isFinite(const SkRect& r) {
+    // 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;
+}
+
+class IsFiniteBench : public SkBenchmark {
+    enum {
+        N = SkBENCHLOOP(1000),
+        NN = SkBENCHLOOP(1000),
+    };
+    float fData[N];
+public:
+
+    IsFiniteBench(void* param, int index) : INHERITED(param) {
+        SkRandom rand;
+
+        for (int i = 0; i < N; ++i) {
+            fData[i] = rand.nextSScalar1();
+        }
+
+        if (index < 0) {
+            fProc = NULL;
+            fName = "isfinite_rect";
+        } else {
+            fProc = gRec[index].fProc;
+            fName = gRec[index].fName;
+        }
+        fIsRendering = false;
+    }
+
+protected:
+    virtual void onDraw(SkCanvas* canvas) {
+        IsFiniteProc proc = fProc;
+        const float* data = fData;
+        // do this so the compiler won't throw away the function call
+        int counter = 0;
+
+        if (proc) {
+            for (int j = 0; j < NN; ++j) {
+                for (int i = 0; i < N - 4; ++i) {
+                    counter += proc(&data[i]);
+                }
+            }
+        } else {
+            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);
+        }
+    }
+
+    virtual const char* onGetName() {
+        return fName;
+    }
+
+private:
+    IsFiniteProc    fProc;
+    const char*     fName;
+
+    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); }
+static SkBenchmark* M1(void* p) { return new SlowISqrtMathBench(p); }
+static SkBenchmark* M2(void* p) { return new FastISqrtMathBench(p); }
+static SkBenchmark* M3(void* p) { return new QMul64Bench(p); }
+static SkBenchmark* M4(void* p) { return new QMul32Bench(p); }
+
+static SkBenchmark* M5neg1(void* p) { return new IsFiniteBench(p, -1); }
+static SkBenchmark* M50(void* p) { return new IsFiniteBench(p, 0); }
+static SkBenchmark* M51(void* p) { return new IsFiniteBench(p, 1); }
+static SkBenchmark* M52(void* p) { return new IsFiniteBench(p, 2); }
+static SkBenchmark* M53(void* p) { return new IsFiniteBench(p, 3); }
+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);
+static BenchRegistry gReg3(M3);
+static BenchRegistry gReg4(M4);
+
+static BenchRegistry gReg5neg1(M5neg1);
+static BenchRegistry gReg50(M50);
+static BenchRegistry gReg51(M51);
+static BenchRegistry gReg52(M52);
+static BenchRegistry gReg53(M53);
+static BenchRegistry gReg54(M54);
+static BenchRegistry gReg55(M55);
+
+static BenchRegistry gRF0(F0);
+static BenchRegistry gRF1(F1);
diff --git a/bench/MatrixBench.cpp b/bench/MatrixBench.cpp
new file mode 100644
index 0000000..b4715ba
--- /dev/null
+++ b/bench/MatrixBench.cpp
@@ -0,0 +1,466 @@
+
+/*
+ * Copyright 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 "SkMatrix.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+class MatrixBench : public SkBenchmark {
+    SkString    fName;
+    enum { N = 100000 };
+public:
+    MatrixBench(void* param, const char name[]) : INHERITED(param) {
+        fName.printf("matrix_%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;
+};
+
+// we want to stop the compiler from eliminating code that it thinks is a no-op
+// so we have a non-static global we increment, hoping that will convince the
+// compiler to execute everything
+int gMatrixBench_NonStaticGlobal;
+
+#define always_do(pred)                     \
+    do {                                    \
+        if (pred) {                         \
+            ++gMatrixBench_NonStaticGlobal; \
+        }                                   \
+    } while (0)
+
+class EqualsMatrixBench : public MatrixBench {
+public:
+    EqualsMatrixBench(void* param) : INHERITED(param, "equals") {}
+protected:
+    virtual void performTest() {
+        SkMatrix m0, m1, m2;
+
+        m0.reset();
+        m1.reset();
+        m2.reset();
+        always_do(m0 == m1);
+        always_do(m1 == m2);
+        always_do(m2 == m0);
+    }
+private:
+    typedef MatrixBench INHERITED;
+};
+
+class ScaleMatrixBench : public MatrixBench {
+public:
+    ScaleMatrixBench(void* param) : INHERITED(param, "scale") {
+        fSX = fSY = SkFloatToScalar(1.5f);
+        fM0.reset();
+        fM1.setScale(fSX, fSY);
+        fM2.setTranslate(fSX, fSY);
+    }
+protected:
+    virtual void performTest() {
+        SkMatrix m;
+        m = fM0; m.preScale(fSX, fSY);
+        m = fM1; m.preScale(fSX, fSY);
+        m = fM2; m.preScale(fSX, fSY);
+    }
+private:
+    SkMatrix fM0, fM1, fM2;
+    SkScalar fSX, fSY;
+    typedef MatrixBench INHERITED;
+};
+
+// having unknown values in our arrays can throw off the timing a lot, perhaps
+// handling NaN values is a lot slower. Anyway, this guy is just meant to put
+// reasonable values in our arrays.
+template <typename T> void init9(T array[9]) {
+    SkRandom rand;
+    for (int i = 0; i < 9; i++) {
+        array[i] = rand.nextSScalar1();
+    }
+}
+
+// Test the performance of setConcat() non-perspective case:
+// using floating point precision only.
+class FloatConcatMatrixBench : public MatrixBench {
+public:
+    FloatConcatMatrixBench(void* p) : INHERITED(p, "concat_floatfloat") {
+        init9(mya);
+        init9(myb);
+        init9(myr);
+    }
+protected:
+    virtual int mulLoopCount() const { return 4; }
+
+    static inline void muladdmul(float a, float b, float c, float d,
+                                   float* result) {
+      *result = a * b + c * d;
+    }
+    virtual void performTest() {
+        const float* a = mya;
+        const float* b = myb;
+        float* r = myr;
+        muladdmul(a[0], b[0], a[1], b[3], &r[0]);
+        muladdmul(a[0], b[1], a[1], b[4], &r[1]);
+        muladdmul(a[0], b[2], a[1], b[5], &r[2]);
+        r[2] += a[2];
+        muladdmul(a[3], b[0], a[4], b[3], &r[3]);
+        muladdmul(a[3], b[1], a[4], b[4], &r[4]);
+        muladdmul(a[3], b[2], a[4], b[5], &r[5]);
+        r[5] += a[5];
+        r[6] = r[7] = 0.0f;
+        r[8] = 1.0f;
+    }
+private:
+    float mya [9];
+    float myb [9];
+    float myr [9];
+    typedef MatrixBench INHERITED;
+};
+
+static inline float SkDoubleToFloat(double x) {
+    return static_cast<float>(x);
+}
+
+// Test the performance of setConcat() non-perspective case:
+// using floating point precision but casting up to float for
+// intermediate results during computations.
+class FloatDoubleConcatMatrixBench : public MatrixBench {
+public:
+    FloatDoubleConcatMatrixBench(void* p) : INHERITED(p, "concat_floatdouble") {
+        init9(mya);
+        init9(myb);
+        init9(myr);
+    }
+protected:
+    virtual int mulLoopCount() const { return 4; }
+
+    static inline void muladdmul(float a, float b, float c, float d,
+                                   float* result) {
+      *result = SkDoubleToFloat((double)a * b + (double)c * d);
+    }
+    virtual void performTest() {
+        const float* a = mya;
+        const float* b = myb;
+        float* r = myr;
+        muladdmul(a[0], b[0], a[1], b[3], &r[0]);
+        muladdmul(a[0], b[1], a[1], b[4], &r[1]);
+        muladdmul(a[0], b[2], a[1], b[5], &r[2]);
+        r[2] += a[2];
+        muladdmul(a[3], b[0], a[4], b[3], &r[3]);
+        muladdmul(a[3], b[1], a[4], b[4], &r[4]);
+        muladdmul(a[3], b[2], a[4], b[5], &r[5]);
+        r[5] += a[5];
+        r[6] = r[7] = 0.0f;
+        r[8] = 1.0f;
+    }
+private:
+    float mya [9];
+    float myb [9];
+    float myr [9];
+    typedef MatrixBench INHERITED;
+};
+
+// Test the performance of setConcat() non-perspective case:
+// using double precision only.
+class DoubleConcatMatrixBench : public MatrixBench {
+public:
+    DoubleConcatMatrixBench(void* p) : INHERITED(p, "concat_double") {
+        init9(mya);
+        init9(myb);
+        init9(myr);
+    }
+protected:
+    virtual int mulLoopCount() const { return 4; }
+
+    static inline void muladdmul(double a, double b, double c, double d,
+                                   double* result) {
+      *result = a * b + c * d;
+    }
+    virtual void performTest() {
+        const double* a = mya;
+        const double* b = myb;
+        double* r = myr;
+        muladdmul(a[0], b[0], a[1], b[3], &r[0]);
+        muladdmul(a[0], b[1], a[1], b[4], &r[1]);
+        muladdmul(a[0], b[2], a[1], b[5], &r[2]);
+        r[2] += a[2];
+        muladdmul(a[3], b[0], a[4], b[3], &r[3]);
+        muladdmul(a[3], b[1], a[4], b[4], &r[4]);
+        muladdmul(a[3], b[2], a[4], b[5], &r[5]);
+        r[5] += a[5];
+        r[6] = r[7] = 0.0;
+        r[8] = 1.0;
+    }
+private:
+    double mya [9];
+    double myb [9];
+    double myr [9];
+    typedef MatrixBench INHERITED;
+};
+
+class GetTypeMatrixBench : public MatrixBench {
+public:
+    GetTypeMatrixBench(void* param)
+        : INHERITED(param, "gettype") {
+        fArray[0] = (float) fRnd.nextS();
+        fArray[1] = (float) fRnd.nextS();
+        fArray[2] = (float) fRnd.nextS();
+        fArray[3] = (float) fRnd.nextS();
+        fArray[4] = (float) fRnd.nextS();
+        fArray[5] = (float) fRnd.nextS();
+        fArray[6] = (float) fRnd.nextS();
+        fArray[7] = (float) fRnd.nextS();
+        fArray[8] = (float) fRnd.nextS();
+    }
+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() {
+        fMatrix.setAll(fArray[0], fArray[1], fArray[2],
+                       fArray[3], fArray[4], fArray[5],
+                       fArray[6], fArray[7], fArray[8]);
+        always_do(fMatrix.getType());
+        fMatrix.dirtyMatrixTypeCache();
+        always_do(fMatrix.getType());
+        fMatrix.dirtyMatrixTypeCache();
+        always_do(fMatrix.getType());
+        fMatrix.dirtyMatrixTypeCache();
+        always_do(fMatrix.getType());
+        fMatrix.dirtyMatrixTypeCache();
+        always_do(fMatrix.getType());
+        fMatrix.dirtyMatrixTypeCache();
+        always_do(fMatrix.getType());
+        fMatrix.dirtyMatrixTypeCache();
+        always_do(fMatrix.getType());
+        fMatrix.dirtyMatrixTypeCache();
+        always_do(fMatrix.getType());
+    }
+private:
+    SkMatrix fMatrix;
+    float fArray[9];
+    SkRandom fRnd;
+    typedef MatrixBench INHERITED;
+};
+
+#ifdef SK_SCALAR_IS_FLOAT
+class ScaleTransMixedMatrixBench : public MatrixBench {
+ public:
+    ScaleTransMixedMatrixBench(void* p) : INHERITED(p, "scaletrans_mixed"), fCount (16) {
+        fMatrix.setAll(fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1(),
+                       fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1(),
+                       fRandom.nextSScalar1(), fRandom.nextSScalar1(), fRandom.nextSScalar1());
+        int i;
+        for (i = 0; i < SkBENCHLOOP(fCount); i++) {
+            fSrc[i].fX = fRandom.nextSScalar1();
+            fSrc[i].fY = fRandom.nextSScalar1();
+            fDst[i].fX = fRandom.nextSScalar1();
+            fDst[i].fY = fRandom.nextSScalar1();
+        }
+    }
+ protected:
+    virtual void performTest() {
+        SkPoint* dst = fDst;
+        const SkPoint* src = fSrc;
+        int count = SkBENCHLOOP(fCount);
+        float mx = fMatrix[SkMatrix::kMScaleX];
+        float my = fMatrix[SkMatrix::kMScaleY];
+        float tx = fMatrix[SkMatrix::kMTransX];
+        float ty = fMatrix[SkMatrix::kMTransY];
+        do {
+            dst->fY = SkScalarMulAdd(src->fY, my, ty);
+            dst->fX = SkScalarMulAdd(src->fX, mx, tx);
+            src += 1;
+            dst += 1;
+        } while (--count);
+    }
+ private:
+    SkMatrix fMatrix;
+    SkPoint fSrc [16];
+    SkPoint fDst [16];
+    int fCount;
+    SkRandom fRandom;
+    typedef MatrixBench INHERITED;
+};
+
+class ScaleTransDoubleMatrixBench : public MatrixBench {
+ public:
+    ScaleTransDoubleMatrixBench(void* p) : INHERITED(p, "scaletrans_double"), fCount (16) {
+        init9(fMatrix);
+        int i;
+        for (i = 0; i < SkBENCHLOOP(fCount); i++) {
+            fSrc[i].fX = fRandom.nextSScalar1();
+            fSrc[i].fY = fRandom.nextSScalar1();
+            fDst[i].fX = fRandom.nextSScalar1();
+            fDst[i].fY = fRandom.nextSScalar1();
+        }
+    }
+ protected:
+    virtual void performTest() {
+        SkPoint* dst = fDst;
+        const SkPoint* src = fSrc;
+        int count = SkBENCHLOOP(fCount);
+        // As doubles, on Z600 Linux systems this is 2.5x as expensive as mixed mode
+        float mx = (float) fMatrix[SkMatrix::kMScaleX];
+        float my = (float) fMatrix[SkMatrix::kMScaleY];
+        float tx = (float) fMatrix[SkMatrix::kMTransX];
+        float ty = (float) fMatrix[SkMatrix::kMTransY];
+        do {
+            dst->fY = src->fY * my + ty;
+            dst->fX = src->fX * mx + tx;
+            src += 1;
+            dst += 1;
+        } while (--count);
+    }
+ private:
+    double fMatrix [9];
+    SkPoint fSrc [16];
+    SkPoint fDst [16];
+    int fCount;
+    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;
+};
+
+
+
+
+static SkBenchmark* M0(void* p) { return new EqualsMatrixBench(p); }
+static SkBenchmark* M1(void* p) { return new ScaleMatrixBench(p); }
+static SkBenchmark* M2(void* p) { return new FloatConcatMatrixBench(p); }
+static SkBenchmark* M3(void* p) { return new FloatDoubleConcatMatrixBench(p); }
+static SkBenchmark* M4(void* p) { return new DoubleConcatMatrixBench(p); }
+static SkBenchmark* M5(void* p) { return new GetTypeMatrixBench(p); }
+static SkBenchmark* M6(void* p) {
+    return new InvertMapRectMatrixBench(p,
+        "invert_maprect_identity", 0);
+}
+static SkBenchmark* M7(void* p) {
+    return new InvertMapRectMatrixBench(p,
+        "invert_maprect_rectstaysrect",
+        InvertMapRectMatrixBench::kScale_Flag |
+        InvertMapRectMatrixBench::kTranslate_Flag);
+}
+static SkBenchmark* M8(void* p) {
+    return new InvertMapRectMatrixBench(p,
+        "invert_maprect_nonpersp",
+        InvertMapRectMatrixBench::kScale_Flag |
+        InvertMapRectMatrixBench::kRotate_Flag |
+        InvertMapRectMatrixBench::kTranslate_Flag);
+}
+static SkBenchmark* M9(void* p) {
+    return new InvertMapRectMatrixBench(p,
+        "invert_maprect_persp",
+        InvertMapRectMatrixBench::kPerspective_Flag);
+}
+static SkBenchmark* M10(void* p) {
+    return new InvertMapRectMatrixBench(p,
+        "invert_maprect_typemask_rectstaysrect",
+        InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
+        InvertMapRectMatrixBench::kScale_Flag |
+        InvertMapRectMatrixBench::kTranslate_Flag);
+}
+static SkBenchmark* M11(void* p) {
+    return new InvertMapRectMatrixBench(p,
+        "invert_maprect_typemask_nonpersp",
+        InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
+        InvertMapRectMatrixBench::kScale_Flag |
+        InvertMapRectMatrixBench::kRotate_Flag |
+        InvertMapRectMatrixBench::kTranslate_Flag);
+}
+
+static BenchRegistry gReg0(M0);
+static BenchRegistry gReg1(M1);
+static BenchRegistry gReg2(M2);
+static BenchRegistry gReg3(M3);
+static BenchRegistry gReg4(M4);
+static BenchRegistry gReg5(M5);
+static BenchRegistry gReg6(M6);
+static BenchRegistry gReg7(M7);
+static BenchRegistry gReg8(M8);
+static BenchRegistry gReg9(M9);
+static BenchRegistry gReg10(M10);
+static BenchRegistry gReg11(M11);
+
+#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
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..30b9ea3
--- /dev/null
+++ b/bench/MemoryBench.cpp
@@ -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 "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..b6e9c92
--- /dev/null
+++ b/bench/MorphologyBench.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 "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
new file mode 100644
index 0000000..dfdb792
--- /dev/null
+++ b/bench/MutexBench.cpp
@@ -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.
+ */
+#include "SkBenchmark.h"
+#include "SkThread.h"
+
+class MutexBench : public SkBenchmark {
+    enum {
+        N = SkBENCHLOOP(80),
+        M = SkBENCHLOOP(200)
+    };
+public:
+    MutexBench(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+protected:
+    virtual const char* onGetName() {
+        return "mutex";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        for (int i = 0; i < N; i++) {
+            SK_DECLARE_STATIC_MUTEX(mu);
+            for (int j = 0; j < M; j++) {
+                mu.acquire();
+                mu.release();
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact(void* p) { return new MutexBench(p); }
+
+static BenchRegistry gReg01(Fact);
+
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
new file mode 100644
index 0000000..12e456a
--- /dev/null
+++ b/bench/PathBench.cpp
@@ -0,0 +1,772 @@
+
+/*
+ * Copyright 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"
+#include "SkTArray.h"
+
+
+enum Flags {
+    kStroke_Flag = 1 << 0,
+    kBig_Flag    = 1 << 1
+};
+
+#define FLAGS00  Flags(0)
+#define FLAGS01  Flags(kStroke_Flag)
+#define FLAGS10  Flags(kBig_Flag)
+#define FLAGS11  Flags(kStroke_Flag | kBig_Flag)
+
+class PathBench : public SkBenchmark {
+    SkPaint     fPaint;
+    SkString    fName;
+    Flags       fFlags;
+    enum { N = SkBENCHLOOP(1000) };
+public:
+    PathBench(void* param, Flags flags) : INHERITED(param), fFlags(flags) {
+        fPaint.setStyle(flags & kStroke_Flag ? SkPaint::kStroke_Style :
+                        SkPaint::kFill_Style);
+        fPaint.setStrokeWidth(SkIntToScalar(5));
+        fPaint.setStrokeJoin(SkPaint::kBevel_Join);
+    }
+
+    virtual void appendName(SkString*) = 0;
+    virtual void makePath(SkPath*) = 0;
+    virtual int complexity() { return 0; }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        fName.printf("path_%s_%s_",
+                     fFlags & kStroke_Flag ? "stroke" : "fill",
+                     fFlags & kBig_Flag ? "big" : "small");
+        this->appendName(&fName);
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkPaint paint(fPaint);
+        this->setupPaint(&paint);
+
+        SkPath path;
+        this->makePath(&path);
+        if (fFlags & kBig_Flag) {
+            SkMatrix m;
+            m.setScale(SkIntToScalar(10), SkIntToScalar(10));
+            path.transform(m);
+        }
+
+        int count = N;
+        if (fFlags & kBig_Flag) {
+            count >>= 2;
+        }
+        count >>= (3 * complexity());
+
+        for (int i = 0; i < count; i++) {
+            canvas->drawPath(path, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class TrianglePathBench : public PathBench {
+public:
+    TrianglePathBench(void* param, Flags flags) : INHERITED(param, flags) {}
+
+    virtual void appendName(SkString* name) SK_OVERRIDE {
+        name->append("triangle");
+    }
+    virtual void makePath(SkPath* path) SK_OVERRIDE {
+        static const int gCoord[] = {
+            10, 10, 15, 5, 20, 20
+        };
+        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();
+    }
+private:
+    typedef PathBench INHERITED;
+};
+
+class RectPathBench : public PathBench {
+public:
+    RectPathBench(void* param, Flags flags) : INHERITED(param, flags) {}
+
+    virtual void appendName(SkString* name) SK_OVERRIDE {
+        name->append("rect");
+    }
+    virtual void makePath(SkPath* path) SK_OVERRIDE {
+        SkRect r = { 10, 10, 20, 20 };
+        path->addRect(r);
+    }
+private:
+    typedef PathBench INHERITED;
+};
+
+class OvalPathBench : public PathBench {
+public:
+    OvalPathBench(void* param, Flags flags) : INHERITED(param, flags) {}
+
+    virtual void appendName(SkString* name) SK_OVERRIDE {
+        name->append("oval");
+    }
+    virtual void makePath(SkPath* path) SK_OVERRIDE {
+        SkRect r = { 10, 10, 20, 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) SK_OVERRIDE {
+        name->append("sawtooth");
+    }
+    virtual void makePath(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();
+    }
+    virtual int complexity() SK_OVERRIDE { return 1; }
+private:
+    typedef PathBench INHERITED;
+};
+
+class LongCurvedPathBench : public PathBench {
+public:
+    LongCurvedPathBench(void * param, Flags flags)
+        : INHERITED(param, flags) {
+    }
+
+    virtual void appendName(SkString* name) SK_OVERRIDE {
+        name->append("long_curved");
+    }
+    virtual void makePath(SkPath* path) SK_OVERRIDE {
+        SkRandom rand (12);
+        int i;
+        for (i = 0; i < 100; i++) {
+            path->quadTo(SkScalarMul(rand.nextUScalar1(), SkIntToScalar(640)),
+                         SkScalarMul(rand.nextUScalar1(), SkIntToScalar(480)),
+                         SkScalarMul(rand.nextUScalar1(), SkIntToScalar(640)),
+                         SkScalarMul(rand.nextUScalar1(), SkIntToScalar(480)));
+        }
+        path->close();
+    }
+    virtual int complexity() SK_OVERRIDE { return 2; }
+private:
+    typedef PathBench INHERITED;
+};
+
+class LongLinePathBench : public PathBench {
+public:
+    LongLinePathBench(void * param, Flags flags)
+        : INHERITED(param, flags) {
+    }
+
+    virtual void appendName(SkString* name) SK_OVERRIDE {
+        name->append("long_line");
+    }
+    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() 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;
+
+    enum {
+        N = SkBENCHLOOP(100)
+    };
+public:
+    CirclesBench(void* param) : INHERITED(param) {
+        fName.printf("circles");
+    }
+
+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);
+
+        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;
+
+            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;
+};
+
+static SkBenchmark* FactT00(void* p) { return new TrianglePathBench(p, FLAGS00); }
+static SkBenchmark* FactT01(void* p) { return new TrianglePathBench(p, FLAGS01); }
+static SkBenchmark* FactT10(void* p) { return new TrianglePathBench(p, FLAGS10); }
+static SkBenchmark* FactT11(void* p) { return new TrianglePathBench(p, FLAGS11); }
+
+static SkBenchmark* FactR00(void* p) { return new RectPathBench(p, FLAGS00); }
+static SkBenchmark* FactR01(void* p) { return new RectPathBench(p, FLAGS01); }
+static SkBenchmark* FactR10(void* p) { return new RectPathBench(p, FLAGS10); }
+static SkBenchmark* FactR11(void* p) { return new RectPathBench(p, FLAGS11); }
+
+static SkBenchmark* FactO00(void* p) { return new OvalPathBench(p, FLAGS00); }
+static SkBenchmark* FactO01(void* p) { return new OvalPathBench(p, FLAGS01); }
+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); }
+
+static SkBenchmark* FactLC00(void* p) {
+    return new LongCurvedPathBench(p, FLAGS00);
+}
+static SkBenchmark* FactLC01(void* p) {
+    return new LongCurvedPathBench(p, FLAGS01);
+}
+
+static SkBenchmark* FactLL00(void* p) {
+    return new LongLinePathBench(p, FLAGS00);
+}
+
+static SkBenchmark* FactLL01(void* p) {
+    return new LongLinePathBench(p, FLAGS01);
+}
+
+static BenchRegistry gRegT00(FactT00);
+static BenchRegistry gRegT01(FactT01);
+static BenchRegistry gRegT10(FactT10);
+static BenchRegistry gRegT11(FactT11);
+
+static BenchRegistry gRegR00(FactR00);
+static BenchRegistry gRegR01(FactR01);
+static BenchRegistry gRegR10(FactR10);
+static BenchRegistry gRegR11(FactR11);
+
+static BenchRegistry gRegO00(FactO00);
+static BenchRegistry gRegO01(FactO01);
+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);
+
+static BenchRegistry gRegLC00(FactLC00);
+static BenchRegistry gRegLC01(FactLC01);
+
+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); }
+static BenchRegistry gRegCirclesTest(CirclesTest);
+
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
new file mode 100644
index 0000000..7cdbccf
--- /dev/null
+++ b/bench/PicturePlaybackBench.cpp
@@ -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.
+ */
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkString.h"
+
+// This is designed to emulate about 4 screens of textual content
+
+
+class PicturePlaybackBench : public SkBenchmark {
+public:
+    PicturePlaybackBench(void* param, const char name[]) : INHERITED(param) {
+        fName.printf("picture_playback_%s", name);
+        fPictureWidth = SkIntToScalar(PICTURE_WIDTH);
+        fPictureHeight = SkIntToScalar(PICTURE_HEIGHT);
+        fTextSize = SkIntToScalar(TEXT_SIZE);
+    }
+
+    enum {
+        N = SkBENCHLOOP(200),   // number of times to playback the picture
+        PICTURE_WIDTH = 1000,
+        PICTURE_HEIGHT = 4000,
+        TEXT_SIZE = 10
+    };
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        SkPicture picture;
+
+        SkCanvas* pCanvas = picture.beginRecording(PICTURE_WIDTH, PICTURE_HEIGHT);
+        recordCanvas(pCanvas);
+        picture.endRecording();
+
+        const SkPoint translateDelta = getTranslateDelta();
+
+        for (int i = 0; i < N; i++) {
+            picture.draw(canvas);
+            canvas->translate(translateDelta.fX, translateDelta.fY);
+        }
+    }
+
+    virtual void recordCanvas(SkCanvas* canvas) = 0;
+    virtual SkPoint getTranslateDelta() {
+        SkIPoint canvasSize = onGetSize();
+        return SkPoint::Make(SkIntToScalar((PICTURE_WIDTH - canvasSize.fX)/N),
+                             SkIntToScalar((PICTURE_HEIGHT- canvasSize.fY)/N));
+    }
+
+    SkString fName;
+    SkScalar fPictureWidth;
+    SkScalar fPictureHeight;
+    SkScalar fTextSize;
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+
+class TextPlaybackBench : public PicturePlaybackBench {
+public:
+    TextPlaybackBench(void* param) : INHERITED(param, "drawText") { }
+protected:
+    virtual void recordCanvas(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setTextSize(fTextSize);
+        paint.setColor(SK_ColorBLACK);
+
+        const char* text = "Hamburgefons";
+        size_t len = strlen(text);
+        const SkScalar textWidth = paint.measureText(text, len);
+
+        for (SkScalar x = 0; x < fPictureWidth; x += textWidth) {
+            for (SkScalar y = 0; y < fPictureHeight; y += fTextSize) {
+                canvas->drawText(text, len, x, y, paint);
+            }
+        }
+    }
+private:
+    typedef PicturePlaybackBench INHERITED;
+};
+
+class PosTextPlaybackBench : public PicturePlaybackBench {
+public:
+    PosTextPlaybackBench(void* param, bool drawPosH)
+        : INHERITED(param, drawPosH ? "drawPosTextH" : "drawPosText")
+        , fDrawPosH(drawPosH) { }
+protected:
+    virtual void recordCanvas(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setTextSize(fTextSize);
+        paint.setColor(SK_ColorBLACK);
+
+        const char* text = "Hamburgefons";
+        size_t len = strlen(text);
+        const SkScalar textWidth = paint.measureText(text, len);
+
+        SkScalar* adv = new SkScalar[len];
+        paint.getTextWidths(text, len, adv);
+
+        for (SkScalar x = 0; x < fPictureWidth; x += textWidth) {
+            for (SkScalar y = 0; y < fPictureHeight; y += fTextSize) {
+
+                SkPoint* pos = new SkPoint[len];
+                SkScalar advX = 0;
+
+                for (size_t i = 0; i < len; i++) {
+                    if (fDrawPosH)
+                        pos[i].set(x + advX, y);
+                    else
+                        pos[i].set(x + advX, y + i);
+                    advX += adv[i];
+                }
+
+                canvas->drawPosText(text, len, pos, paint);
+                delete[] pos;
+            }
+        }
+        delete[] adv;
+    }
+private:
+    bool fDrawPosH;
+    typedef PicturePlaybackBench INHERITED;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact0(void* p) { return new TextPlaybackBench(p); }
+static SkBenchmark* Fact1(void* p) { return new PosTextPlaybackBench(p, true); }
+static SkBenchmark* Fact2(void* p) { return new PosTextPlaybackBench(p, false); }
+
+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..e1885c3
--- /dev/null
+++ b/bench/RTreeBench.cpp
@@ -0,0 +1,218 @@
+
+/*
+ * Copyright 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 SkIRect make_simple_rect(SkRandom&, int index, int numRects) {
+    SkIRect out = {0, 0, GENERATE_EXTENTS, GENERATE_EXTENTS};
+    return out;
+}
+
+static SkIRect make_concentric_rects_increasing(SkRandom&, int index, int numRects) {
+    SkIRect out = {0, 0, index + 1, index + 1};
+    return out;
+}
+
+static SkIRect make_concentric_rects_decreasing(SkRandom&, int index, int numRects) {
+    SkIRect out = {0, 0, numRects - index, numRects - index};
+    return out;
+}
+
+static SkIRect make_point_rects(SkRandom& rand, int index, int numRects) {
+    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 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 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 SkBenchmark* Fact0(void* p) {
+    return SkNEW_ARGS(BBoxBuildBench, (p, "random", &make_random_rects, true,
+                      SkRTree::Create(5, 16)));
+}
+static SkBenchmark* Fact1(void* p) {
+    return SkNEW_ARGS(BBoxBuildBench, (p, "random", &make_random_rects, false,
+                      SkRTree::Create(5, 16)));
+}
+static SkBenchmark* Fact2(void* p) {
+    return SkNEW_ARGS(BBoxBuildBench, (p, "concentric",
+                      &make_concentric_rects_increasing, true, SkRTree::Create(5, 16)));
+}
+static SkBenchmark* Fact3(void* p) {
+    return SkNEW_ARGS(BBoxQueryBench, (p, "random", &make_random_rects, true,
+                      BBoxQueryBench::kRandom_QueryType, SkRTree::Create(5, 16)));
+}
+static SkBenchmark* Fact4(void* p) {
+    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
new file mode 100644
index 0000000..992b8f9
--- /dev/null
+++ b/bench/RectBench.cpp
@@ -0,0 +1,269 @@
+
+/*
+ * Copyright 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 "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkString.h"
+#include "SkShader.h"
+
+class RectBench : public SkBenchmark {
+public:
+    int fShift, fStroke;
+    enum {
+        W = 640,
+        H = 480,
+        N = SkBENCHLOOP(300)
+    };
+    SkRect  fRects[N];
+    SkColor fColors[N];
+
+    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++) {
+            int x = rand.nextU() % W;
+            int y = rand.nextU() % H;
+            int w = rand.nextU() % W;
+            int h = rand.nextU() % H;
+            w >>= shift;
+            h >>= shift;
+            x -= w/2;
+            y -= h/2;
+            fRects[i].set(SkIntToScalar(x), SkIntToScalar(y),
+                          SkIntToScalar(x+w), SkIntToScalar(y+h));
+            fRects[i].offset(offset, offset);
+            fColors[i] = rand.nextU() | 0xFF808080;
+        }
+    }
+
+    SkString fName;
+    const char* computeName(const char root[]) {
+        fName.printf("%s_%d", root, fShift);
+        if (fStroke > 0) {
+            fName.appendf("_stroke_%d", fStroke);
+        }
+        return fName.c_str();
+    }
+
+protected:
+    virtual void drawThisRect(SkCanvas* c, const SkRect& r, const SkPaint& p) {
+        c->drawRect(r, p);
+    }
+
+    virtual const char* onGetName() { return computeName("rects"); }
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        if (fStroke > 0) {
+            paint.setStyle(SkPaint::kStroke_Style);
+            paint.setStrokeWidth(SkIntToScalar(fStroke));
+        }
+        for (int i = 0; i < N; i++) {
+            paint.setColor(fColors[i]);
+            this->setupPaint(&paint);
+            this->drawThisRect(canvas, fRects[i], paint);
+        }
+    }
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class OvalBench : public RectBench {
+public:
+    OvalBench(void* param, int shift) : RectBench(param, shift) {}
+protected:
+    virtual void drawThisRect(SkCanvas* c, const SkRect& r, const SkPaint& p) {
+        c->drawOval(r, p);
+    }
+    virtual const char* onGetName() { return computeName("ovals"); }
+};
+
+class RRectBench : public RectBench {
+public:
+    RRectBench(void* param, int shift) : RectBench(param, shift) {}
+protected:
+    virtual void drawThisRect(SkCanvas* c, const SkRect& r, const SkPaint& p) {
+        c->drawRoundRect(r, r.width() / 4, r.height() / 4, p);
+    }
+    virtual const char* onGetName() { return computeName("rrects"); }
+};
+
+class PointsBench : public RectBench {
+public:
+    SkCanvas::PointMode fMode;
+    const char* fName;
+
+    PointsBench(void* param, SkCanvas::PointMode mode, const char* name) :
+        RectBench(param, 2), fMode(mode) {
+        fName = name;
+    }
+
+protected:
+    virtual void onDraw(SkCanvas* canvas) {
+        SkScalar gSizes[] = {
+            SkIntToScalar(7), 0
+        };
+        size_t sizes = SK_ARRAY_COUNT(gSizes);
+
+        if (this->hasStrokeWidth()) {
+            gSizes[0] = this->getStrokeWidth();
+            sizes = 1;
+        }
+
+        SkPaint paint;
+        paint.setStrokeCap(SkPaint::kRound_Cap);
+
+        for (size_t i = 0; i < sizes; i++) {
+            paint.setStrokeWidth(gSizes[i]);
+            this->setupPaint(&paint);
+            canvas->drawPoints(fMode, N * 2, SkTCast<SkPoint*>(fRects), paint);
+            paint.setColor(fColors[i]);
+        }
+    }
+    virtual const char* onGetName() { return fName; }
+};
+
+/*******************************************************************************
+ * to bench BlitMask [Opaque, Black, color, shader]
+ *******************************************************************************/
+
+class BlitMaskBench : public RectBench {
+public:
+    enum kMaskType {
+        kMaskOpaque = 0,
+        kMaskBlack,
+        kMaskColor,
+        KMaskShader
+    };
+    SkCanvas::PointMode fMode;
+    const char* fName;
+
+    BlitMaskBench(void* param, SkCanvas::PointMode mode,
+                  BlitMaskBench::kMaskType type, const char* name) :
+                  RectBench(param, 2), fMode(mode), _type(type) {
+        fName = name;
+    }
+
+protected:
+    virtual void onDraw(SkCanvas* canvas) {
+        SkScalar gSizes[] = {
+            SkIntToScalar(13), SkIntToScalar(24)
+        };
+        size_t sizes = SK_ARRAY_COUNT(gSizes);
+
+        if (this->hasStrokeWidth()) {
+            gSizes[0] = this->getStrokeWidth();
+            sizes = 1;
+        }
+        SkRandom rand;
+        SkColor color = 0xFF000000;
+        U8CPU alpha = 0xFF;
+        SkPaint paint;
+        paint.setStrokeCap(SkPaint::kRound_Cap);
+        if (_type == KMaskShader) {
+            SkBitmap srcBM;
+            srcBM.setConfig(SkBitmap::kARGB_8888_Config, 10, 1);
+            srcBM.allocPixels();
+            srcBM.eraseColor(0xFF00FF00);
+
+            SkShader* s;
+            s  = SkShader::CreateBitmapShader(srcBM, SkShader::kClamp_TileMode,
+                                              SkShader::kClamp_TileMode);
+            paint.setShader(s)->unref();
+        }
+        for (size_t i = 0; i < sizes; i++) {
+            switch (_type) {
+                case kMaskOpaque:
+                    color = fColors[i];
+                    alpha = 0xFF;
+                    break;
+                case kMaskBlack:
+                    alpha = 0xFF;
+                    color = 0xFF000000;
+                    break;
+                case kMaskColor:
+                    color = fColors[i];
+                    alpha = rand.nextU() & 255;
+                    break;
+                case KMaskShader:
+                    break;
+            }
+            paint.setStrokeWidth(gSizes[i]);
+            this->setupPaint(&paint);
+            paint.setColor(color);
+            paint.setAlpha(alpha);
+            canvas->drawPoints(fMode, N * 2, SkTCast<SkPoint*>(fRects), paint);
+       }
+    }
+    virtual const char* onGetName() { return fName; }
+private:
+    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"));
+}
+
+/* init the blitmask bench
+ */
+static SkBenchmark* BlitMaskOpaqueFactory(void* p) {
+    return SkNEW_ARGS(BlitMaskBench,
+                      (p, SkCanvas::kPoints_PointMode,
+                      BlitMaskBench::kMaskOpaque, "maskopaque")
+                      );
+}
+static SkBenchmark* BlitMaskBlackFactory(void* p) {
+    return SkNEW_ARGS(BlitMaskBench,
+                      (p, SkCanvas::kPoints_PointMode,
+                      BlitMaskBench::kMaskBlack, "maskblack")
+                      );
+}
+static SkBenchmark* BlitMaskColorFactory(void* p) {
+    return SkNEW_ARGS(BlitMaskBench,
+                      (p, SkCanvas::kPoints_PointMode,
+                      BlitMaskBench::kMaskColor, "maskcolor")
+                      );
+}
+static SkBenchmark* BlitMaskShaderFactory(void* p) {
+    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/RepeatTileBench.cpp b/bench/RepeatTileBench.cpp
new file mode 100644
index 0000000..da36205
--- /dev/null
+++ b/bench/RepeatTileBench.cpp
@@ -0,0 +1,145 @@
+
+/*
+ * Copyright 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 "SkShader.h"
+#include "SkString.h"
+
+static const char* gConfigName[] = {
+    "ERROR", "a1", "a8", "index8", "565", "4444", "8888"
+};
+
+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);
+}
+
+static int conv6ToByte(int x) {
+    return x * 0xFF / 5;
+}
+
+static int convByteTo6(int x) {
+    return x * 5 / 255;
+}
+
+static uint8_t compute666Index(SkPMColor c) {
+    int r = SkGetPackedR32(c);
+    int g = SkGetPackedG32(c);
+    int b = SkGetPackedB32(c);
+
+    return convByteTo6(r) * 36 + convByteTo6(g) * 6 + convByteTo6(b);
+}
+
+static void convertToIndex666(const SkBitmap& src, SkBitmap* dst) {
+    SkColorTable* ctable = new SkColorTable(216);
+    SkPMColor* colors = ctable->lockColors();
+    // rrr ggg bbb
+    for (int r = 0; r < 6; r++) {
+        int rr = conv6ToByte(r);
+        for (int g = 0; g < 6; g++) {
+            int gg = conv6ToByte(g);
+            for (int b = 0; b < 6; b++) {
+                int bb = conv6ToByte(b);
+                *colors++ = SkPreMultiplyARGB(0xFF, rr, gg, bb);
+            }
+        }
+    }
+    ctable->unlockColors(true);
+    dst->setConfig(SkBitmap::kIndex8_Config, src.width(), src.height());
+    dst->allocPixels(ctable);
+    ctable->unref();
+
+    SkAutoLockPixels alps(src);
+    SkAutoLockPixels alpd(*dst);
+
+    for (int y = 0; y < src.height(); y++) {
+        const SkPMColor* srcP = src.getAddr32(0, y);
+        uint8_t* dstP = dst->getAddr8(0, y);
+        for (int x = src.width() - 1; x >= 0; --x) {
+            *dstP++ = compute666Index(*srcP++);
+        }
+    }
+}
+
+class RepeatTileBench : public SkBenchmark {
+    SkPaint     fPaint;
+    SkString    fName;
+    enum { N = SkBENCHLOOP(20) };
+public:
+    RepeatTileBench(void* param, SkBitmap::Config c) : INHERITED(param) {
+        const int w = 50;
+        const int h = 50;
+        SkBitmap bm;
+
+        if (SkBitmap::kIndex8_Config == c) {
+            bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+        } else {
+            bm.setConfig(c, w, h);
+        }
+        bm.allocPixels();
+        bm.eraseColor(0);
+
+        drawIntoBitmap(bm);
+
+        if (SkBitmap::kIndex8_Config == c) {
+            SkBitmap tmp;
+            convertToIndex666(bm, &tmp);
+            bm = tmp;
+        }
+
+        SkShader* s = SkShader::CreateBitmapShader(bm,
+                                                   SkShader::kRepeat_TileMode,
+                                                   SkShader::kRepeat_TileMode);
+        fPaint.setShader(s)->unref();
+        fName.printf("repeatTile_%s", gConfigName[bm.config()]);
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint(fPaint);
+        this->setupPaint(&paint);
+
+        for (int i = 0; i < N; i++) {
+            canvas->drawPaint(paint);
+        }
+    }
+
+private:
+    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);
diff --git a/bench/ScalarBench.cpp b/bench/ScalarBench.cpp
new file mode 100644
index 0000000..333dd22
--- /dev/null
+++ b/bench/ScalarBench.cpp
@@ -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.
+ */
+#include "SkBenchmark.h"
+#include "SkFloatBits.h"
+#include "SkRandom.h"
+#include "SkRect.h"
+#include "SkString.h"
+
+class ScalarBench : public SkBenchmark {
+    SkString    fName;
+    enum { N = 100000 };
+public:
+    ScalarBench(void* param, const char name[]) : INHERITED(param) {
+        fName.printf("scalar_%s", name);
+        fIsRendering = false;
+    }
+
+    virtual void performTest() = 0;
+
+protected:
+    virtual int mulLoopCount() const { return 1; }
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        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;
+};
+
+// we want to stop the compiler from eliminating code that it thinks is a no-op
+// so we have a non-static global we increment, hoping that will convince the
+// compiler to execute everything
+int gScalarBench_NonStaticGlobal;
+
+#define always_do(pred)                     \
+    do {                                    \
+        if (pred) {                         \
+            ++gScalarBench_NonStaticGlobal; \
+        }                                   \
+    } while (0)
+
+// having unknown values in our arrays can throw off the timing a lot, perhaps
+// handling NaN values is a lot slower. Anyway, this guy is just meant to put
+// reasonable values in our arrays.
+template <typename T> void init9(T array[9]) {
+    SkRandom rand;
+    for (int i = 0; i < 9; i++) {
+        array[i] = rand.nextSScalar1();
+    }
+}
+
+class FloatComparisonBench : public ScalarBench {
+public:
+    FloatComparisonBench(void* param) : INHERITED(param, "compare_float") {
+        init9(fArray);
+    }
+protected:
+    virtual int mulLoopCount() const { return 4; }
+    virtual void performTest() {
+        always_do(fArray[6] != 0.0f || fArray[7] != 0.0f || fArray[8] != 1.0f);
+        always_do(fArray[2] != 0.0f || fArray[5] != 0.0f);
+    }
+private:
+    float fArray[9];
+    typedef ScalarBench INHERITED;
+};
+
+class ForcedIntComparisonBench : public ScalarBench {
+public:
+    ForcedIntComparisonBench(void* param)
+    : INHERITED(param, "compare_forced_int") {
+        init9(fArray);
+    }
+protected:
+    virtual int mulLoopCount() const { return 4; }
+    virtual void performTest() {
+        always_do(SkScalarAs2sCompliment(fArray[6]) |
+                  SkScalarAs2sCompliment(fArray[7]) |
+                  (SkScalarAs2sCompliment(fArray[8]) - kPersp1Int));
+        always_do(SkScalarAs2sCompliment(fArray[2]) |
+                  SkScalarAs2sCompliment(fArray[5]));
+    }
+private:
+    static const int32_t kPersp1Int = 0x3f800000;
+    SkScalar fArray[9];
+    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
new file mode 100644
index 0000000..ad27fcb
--- /dev/null
+++ b/bench/ShaderMaskBench.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 "SkCanvas.h"
+#include "SkColorShader.h"
+#include "SkFontHost.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkString.h"
+#include "SkTemplates.h"
+
+#define STR     "Hamburgefons"
+
+enum FontQuality {
+    kBW,
+    kAA,
+    kLCD
+};
+
+static const char* fontQualityName(const SkPaint& paint) {
+    if (!paint.isAntiAlias()) {
+        return "BW";
+    }
+    if (paint.isLCDRenderText()) {
+        return "LCD";
+    }
+    return "AA";
+}
+
+class ShaderMaskBench : public SkBenchmark {
+    SkPaint     fPaint;
+    SkString    fText;
+    SkString    fName;
+    FontQuality fFQ;
+    enum { N = SkBENCHLOOP(500) };
+public:
+    ShaderMaskBench(void* param, bool isOpaque, FontQuality fq) : INHERITED(param) {
+        fFQ = fq;
+        fText.set(STR);
+
+        fPaint.setAntiAlias(kBW != fq);
+        fPaint.setLCDRenderText(kLCD == fq);
+        fPaint.setAlpha(isOpaque ? 0xFF : 0x80);
+        fPaint.setShader(new SkColorShader)->unref();
+    }
+
+protected:
+    virtual const char* onGetName() {
+        fName.printf("shadermask");
+        fName.appendf("_%s", fontQualityName(fPaint));
+        fName.appendf("_%02X", fPaint.getAlpha());
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        const SkIPoint dim = this->getSize();
+        SkRandom rand;
+
+        SkPaint paint(fPaint);
+        this->setupPaint(&paint);
+        // explicitly need these
+        paint.setAlpha(fPaint.getAlpha());
+        paint.setAntiAlias(kBW != fFQ);
+        paint.setLCDRenderText(kLCD == fFQ);
+
+        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;
+            SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
+            canvas->drawText(fText.c_str(), fText.size(), x, y, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact00(void* p) { return new ShaderMaskBench(p, true,  kBW); }
+static SkBenchmark* Fact01(void* p) { return new ShaderMaskBench(p, false, kBW); }
+static SkBenchmark* Fact10(void* p) { return new ShaderMaskBench(p, true,  kAA); }
+static SkBenchmark* Fact11(void* p) { return new ShaderMaskBench(p, false, kAA); }
+static SkBenchmark* Fact20(void* p) { return new ShaderMaskBench(p, true,  kLCD); }
+static SkBenchmark* Fact21(void* p) { return new ShaderMaskBench(p, false, kLCD); }
+
+static BenchRegistry gReg00(Fact00);
+static BenchRegistry gReg01(Fact01);
+static BenchRegistry gReg10(Fact10);
+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
new file mode 100644
index 0000000..6afcd8e
--- /dev/null
+++ b/bench/SkBenchmark.cpp
@@ -0,0 +1,87 @@
+
+/*
+ * Copyright 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 "SkPaint.h"
+#include "SkParse.h"
+
+SK_DEFINE_INST_COUNT(SkBenchmark)
+
+template BenchRegistry* BenchRegistry::gHead;
+
+SkBenchmark::SkBenchmark(void* defineDict) {
+    fDict = reinterpret_cast<const SkTDict<const char*>*>(defineDict);
+    fForceAlpha = 0xFF;
+    fForceAA = true;
+    fDither = SkTriState::kDefault;
+    fHasStrokeWidth = false;
+    fIsRendering = true;
+}
+
+const char* SkBenchmark::getName() {
+    return this->onGetName();
+}
+
+SkIPoint SkBenchmark::getSize() {
+    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);
+    paint->setFilterBitmap(fForceFilter);
+
+    if (SkTriState::kDefault != fDither) {
+        paint->setDither(SkTriState::kTrue == fDither);
+    }
+}
+
+const char* SkBenchmark::findDefine(const char* key) const {
+    if (fDict) {
+        const char* value;
+        if (fDict->find(key, &value)) {
+            return value;
+        }
+    }
+    return NULL;
+}
+
+bool SkBenchmark::findDefine32(const char* key, int32_t* value) const {
+    const char* valueStr = this->findDefine(key);
+    if (valueStr) {
+        SkParse::FindS32(valueStr, value);
+        return true;
+    }
+    return false;
+}
+
+bool SkBenchmark::findDefineScalar(const char* key, SkScalar* value) const {
+    const char* valueStr = this->findDefine(key);
+    if (valueStr) {
+        SkParse::FindScalar(valueStr, value);
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkIPoint SkBenchmark::onGetSize() {
+    return SkIPoint::Make(640, 480);
+}
diff --git a/bench/SkBenchmark.h b/bench/SkBenchmark.h
new file mode 100644
index 0000000..b72162f
--- /dev/null
+++ b/bench/SkBenchmark.h
@@ -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.
+ */
+#ifndef SkBenchmark_DEFINED
+#define SkBenchmark_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkPoint.h"
+#include "SkTDict.h"
+#include "SkTRegistry.h"
+
+
+#define SK_MACRO_CONCAT(X, Y)       SK_MACRO_CONCAT_IMPL(X, Y)
+#define SK_MACRO_CONCAT_IMPL(X, Y)  X ## Y
+
+#define SK_MACRO_APPEND_LINE(name)  SK_MACRO_CONCAT(name, __LINE__)
+
+#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
+    #define SkBENCHLOOP(n) n
+#endif
+
+class SkCanvas;
+class SkPaint;
+
+class SkTriState {
+public:
+    enum State {
+        kDefault,
+        kTrue,
+        kFalse
+    };
+};
+
+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;
+    }
+
+    void setStrokeWidth(SkScalar width) {
+      strokeWidth = width;
+      fHasStrokeWidth = true;
+    }
+
+    SkScalar getStrokeWidth() {
+      return strokeWidth;
+    }
+
+    bool hasStrokeWidth() {
+      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 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;
+    int     fForceAlpha;
+    bool    fForceAA;
+    bool    fForceFilter;
+    SkTriState::State  fDither;
+    bool    fHasStrokeWidth;
+    SkScalar strokeWidth;
+
+    typedef SkRefCnt INHERITED;
+};
+
+typedef SkTRegistry<SkBenchmark*, void*> BenchRegistry;
+
+#endif
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
new file mode 100644
index 0000000..9334c33
--- /dev/null
+++ b/bench/TextBench.cpp
@@ -0,0 +1,160 @@
+
+/*
+ * Copyright 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 "SkCanvas.h"
+#include "SkFontHost.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkString.h"
+#include "SkTemplates.h"
+
+enum FontQuality {
+    kBW,
+    kAA,
+    kLCD
+};
+
+static const char* fontQualityName(const SkPaint& paint) {
+    if (!paint.isAntiAlias()) {
+        return "BW";
+    }
+    if (paint.isLCDRenderText()) {
+        return "LCD";
+    }
+    return "AA";
+}
+
+/*  Some considerations for performance:
+        short -vs- long strings (measuring overhead)
+        tiny -vs- large pointsize (measure blit -vs- overhead)
+        1 -vs- many point sizes (measure cache lookup)
+        normal -vs- subpixel -vs- lineartext (minor)
+        force purge after each draw to measure scaler
+        textencoding?
+        text -vs- postext - pathtext
+ */
+class TextBench : public SkBenchmark {
+    SkPaint     fPaint;
+    SkString    fText;
+    SkString    fName;
+    FontQuality fFQ;
+    bool        fDoPos;
+    SkPoint*    fPos;
+    enum { N = SkBENCHLOOP(800) };
+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);
+
+        fPaint.setAntiAlias(kBW != fq);
+        fPaint.setLCDRenderText(kLCD == fq);
+        fPaint.setTextSize(SkIntToScalar(ps));
+        fPaint.setColor(color);
+
+        if (doPos) {
+            size_t len = strlen(text);
+            SkScalar* adv = new SkScalar[len];
+            fPaint.getTextWidths(text, len, adv);
+            fPos = new SkPoint[len];
+            SkScalar x = 0;
+            for (size_t i = 0; i < len; ++i) {
+                fPos[i].set(x, SkIntToScalar(50));
+                x += adv[i];
+            }
+            delete[] adv;
+        }
+    }
+
+    virtual ~TextBench() {
+        delete[] fPos;
+    }
+
+protected:
+    virtual const char* onGetName() {
+        fName.printf("text_%g", SkScalarToFloat(fPaint.getTextSize()));
+        if (fDoPos) {
+            fName.append("_pos");
+        }
+        fName.appendf("_%s", fontQualityName(fPaint));
+        if (SK_ColorBLACK != fPaint.getColor()) {
+            fName.appendf("_%02X", fPaint.getAlpha());
+        } else {
+            fName.append("_BK");
+        }
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        const SkIPoint dim = this->getSize();
+        SkRandom rand;
+
+        SkPaint paint(fPaint);
+        this->setupPaint(&paint);
+        // explicitly need these
+        paint.setColor(fPaint.getColor());
+        paint.setAntiAlias(kBW != fFQ);
+        paint.setLCDRenderText(kLCD == fFQ);
+
+        const SkScalar x0 = SkIntToScalar(-10);
+        const SkScalar y0 = SkIntToScalar(-10);
+
+        if (fDoPos) {
+            // realistically, the matrix is often at least translated, so we
+            // do that since it exercises different code in drawPosText.
+            canvas->translate(SK_Scalar1, SK_Scalar1);
+        }
+
+        for (int i = 0; i < N; i++) {
+            if (fDoPos) {
+                canvas->drawPosText(fText.c_str(), fText.size(), fPos, paint);
+            } else {
+                SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
+                SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
+                canvas->drawText(fText.c_str(), fText.size(), x, y, paint);
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define STR     "Hamburgefons"
+
+static SkBenchmark* Fact01(void* p) { return new TextBench(p, STR, 16, 0xFF000000, kBW); }
+static SkBenchmark* Fact02(void* p) { return new TextBench(p, STR, 16, 0xFFFF0000, kBW); }
+static SkBenchmark* Fact03(void* p) { return new TextBench(p, STR, 16, 0x88FF0000, kBW); }
+
+static SkBenchmark* Fact11(void* p) { return new TextBench(p, STR, 16, 0xFF000000, kAA); }
+static SkBenchmark* Fact12(void* p) { return new TextBench(p, STR, 16, 0xFFFF0000, kAA); }
+static SkBenchmark* Fact13(void* p) { return new TextBench(p, STR, 16, 0x88FF0000, kAA); }
+
+static SkBenchmark* Fact21(void* p) { return new TextBench(p, STR, 16, 0xFF000000, kLCD); }
+static SkBenchmark* Fact22(void* p) { return new TextBench(p, STR, 16, 0xFFFF0000, kLCD); }
+static SkBenchmark* Fact23(void* p) { return new TextBench(p, STR, 16, 0x88FF0000, kLCD); }
+
+static SkBenchmark* Fact111(void* p) { return new TextBench(p, STR, 16, 0xFF000000, kAA, true); }
+
+static BenchRegistry gReg01(Fact01);
+static BenchRegistry gReg02(Fact02);
+static BenchRegistry gReg03(Fact03);
+
+static BenchRegistry gReg11(Fact11);
+static BenchRegistry gReg12(Fact12);
+static BenchRegistry gReg13(Fact13);
+
+static BenchRegistry gReg21(Fact21);
+static BenchRegistry gReg22(Fact22);
+static BenchRegistry gReg23(Fact23);
+
+static BenchRegistry gReg111(Fact111);
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
new file mode 100644
index 0000000..e5053d9
--- /dev/null
+++ b/bench/VertBench.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 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 "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkString.h"
+#include "SkShader.h"
+
+enum VertFlags {
+    kColors_VertFlag,
+    kTexture_VertFlag,
+};
+
+class VertBench : public SkBenchmark {
+    SkString fName;
+    enum {
+        W = 640,
+        H = 480,
+        ROW = 20,
+        COL = 20,
+        PTS = (ROW + 1) * (COL + 1),
+        IDX = ROW * COL * 6,
+        N = SkBENCHLOOP(10)
+    };
+
+    SkPoint fPts[PTS];
+    SkColor fColors[PTS];
+    SkPoint fTex[PTS];
+    uint16_t fIdx[IDX];
+
+    static void load_2_tris(uint16_t idx[], int x, int y, int rb) {
+        int n = y * rb + x;
+        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;
+        const SkScalar dy = SkIntToScalar(H) / COL;
+
+        SkPoint* pts = fPts;
+        uint16_t* idx = fIdx;
+
+        SkScalar yy = 0;
+        for (int y = 0; y <= ROW; y++) {
+            SkScalar xx = 0;
+            for (int x = 0; x <= COL; ++x) {
+                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++) {
+                        SkASSERT(idx[i] < PTS);
+                    }
+                    idx += 6;
+                }
+            }
+            yy += dy;
+        }
+        SkASSERT(PTS == pts - fPts);
+        SkASSERT(IDX == idx - fIdx);
+
+        SkRandom rand;
+        for (int i = 0; i < PTS; ++i) {
+            fColors[i] = rand.nextU() | (0xFF << 24);
+        }
+
+        fName.set("verts");
+    }
+
+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++) {
+            canvas->drawVertices(SkCanvas::kTriangles_VertexMode, PTS,
+                                 fPts, NULL, fColors, NULL, fIdx, IDX, paint);
+        }
+    }
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact(void* p) { return SkNEW_ARGS(VertBench, (p)); }
+
+static BenchRegistry gReg(Fact);
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
new file mode 100644
index 0000000..d853358
--- /dev/null
+++ b/bench/bench_compare.py
@@ -0,0 +1,158 @@
+'''
+Created on May 16, 2011
+
+@author: bungeman
+'''
+import sys
+import getopt
+import bench_util
+
+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'
+    print '  c: config'
+    print '  t: time type'
+    print '  o: old time'
+    print '  n: new time'
+    print '  d: diff'
+    print '  p: percent diff'
+    
+class BenchDiff:
+    """A compare between data points produced by bench.
+    
+    (BenchDataPoint, BenchDataPoint)"""
+    def __init__(self, old, new):
+        self.old = old
+        self.new = new
+        self.diff = old.time - new.time
+        diffp = 0
+        if old.time != 0:
+            diffp = self.diff / old.time
+        self.diffp = diffp
+    
+    def __repr__(self):
+        return "BenchDiff(%s, %s)" % (
+                   str(self.new),
+                   str(self.old),
+               )
+        
+def main():
+    """Parses command line and writes output."""
+    
+    try:
+        opts, _ = getopt.getopt(sys.argv[1:], "f:o:n:s:h")
+    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"
+    
+    for option, value in opts:
+        if option == "-o":
+            old = value
+        elif option == "-n":
+            new = value
+        elif option == "-h":
+            header = True
+        elif option == "-f":
+            columns = value
+        elif option == "-s":
+            stat_type = value
+        else:
+            usage()
+            assert False, "unhandled option"
+    
+    if old is None or new is None:
+        usage()
+        sys.exit(2)
+    
+    for column_char in columns:
+        if column_formats[column_char]:
+            column_format += column_formats[column_char]
+            header_format += header_formats[column_char]
+        else:
+            usage()
+            sys.exit(2)
+    
+    if header:
+        print header_format.format(
+            bench='bench'
+            , config='conf'
+            , time_type='time'
+            , old_time='old'
+            , new_time='new'
+            , diff='diff'
+            , diffp='diffP'
+        )
+    
+    old_benches = bench_util.parse({}, open(old, 'r'), stat_type)
+    new_benches = bench_util.parse({}, open(new, 'r'), stat_type)
+    
+    bench_diffs = []
+    for old_bench in old_benches:
+        #filter new_benches for benches that match old_bench
+        new_bench_match = [bench for bench in new_benches
+            if old_bench.bench == bench.bench and
+               old_bench.config == bench.config and
+               old_bench.time_type == bench.time_type
+        ]
+        if (len(new_bench_match) < 1):
+            continue
+        bench_diffs.append(BenchDiff(old_bench, new_bench_match[0]))
+    
+    bench_diffs.sort(key=lambda d : [d.diffp,
+                                     d.old.bench,
+                                     d.old.config,
+                                     d.old.time_type,
+                                    ])
+    for bench_diff in bench_diffs:
+        print column_format.format(
+            bench=bench_diff.old.bench.strip()
+            , config=bench_diff.old.config.strip()
+            , time_type=bench_diff.old.time_type
+            , old_time=bench_diff.old.time
+            , new_time=bench_diff.new.time
+            , 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..4c6f05f
--- /dev/null
+++ b/bench/bench_expectations.txt
@@ -0,0 +1,12 @@
+# 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.
+
+picture_playback_drawText_GPU_c,Mac_Float_NoDebug_64-25th,160,150,170
diff --git a/bench/bench_graph_svg.py b/bench/bench_graph_svg.py
new file mode 100644
index 0000000..5a9978a
--- /dev/null
+++ b/bench/bench_graph_svg.py
@@ -0,0 +1,954 @@
+'''
+Created on May 16, 2011
+
+@author: bungeman
+'''
+import sys
+import getopt
+import re
+import os
+import bench_util
+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 '-b <bench> the bench to show.'
+    print '-c <config> the config to show (GPU, 8888, 565, etc).'
+    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 '--default-setting <setting>[=<value>] setting for those without.'
+    
+
+class Label:
+    """The information in a label.
+    
+    (str, str, str, str, {str:str})"""
+    def __init__(self, bench, config, time_type, settings):
+        self.bench = bench
+        self.config = config
+        self.time_type = time_type
+        self.settings = settings
+    
+    def __repr__(self):
+        return "Label(%s, %s, %s, %s)" % (
+                   str(self.bench),
+                   str(self.config),
+                   str(self.time_type),
+                   str(self.settings),
+               )
+    
+    def __str__(self):
+        return "%s_%s_%s_%s" % (
+                   str(self.bench),
+                   str(self.config),
+                   str(self.time_type),
+                   str(self.settings),
+               )
+    
+    def __eq__(self, other):
+        return (self.bench == other.bench and
+                self.config == other.config and
+                self.time_type == other.time_type and
+                self.settings == other.settings)
+    
+    def __hash__(self):
+        return (hash(self.bench) ^
+                hash(self.config) ^
+                hash(self.time_type) ^
+                hash(frozenset(self.settings.iteritems())))
+
+def get_latest_revision(directory):
+    """Returns the latest revision number found within this directory.
+    """
+    latest_revision_found = -1
+    for bench_file in os.listdir(directory):
+        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))
+        if revision > latest_revision_found:
+            latest_revision_found = revision
+    if latest_revision_found < 0:
+        return None
+    else:
+        return latest_revision_found
+
+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]}"""
+    revision_data_points = {} # {revision : [BenchDataPoints]}
+    for bench_file in os.listdir(directory):
+        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, 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
+               , time_to_ignore):
+    """Convert revision data into sorted line data.
+    
+    ({int:[BenchDataPoints]}, {str:str}, str?, str?, str?)
+        -> {Label:[(x,y)] | [n].x <= [n+1].x}"""
+    revisions = revision_data_points.keys()
+    revisions.sort()
+    lines = {} # {Label:[(x,y)] | x[n] <= x[n+1]}
+    for revision in revisions:
+        for point in revision_data_points[revision]:
+            if (bench_of_interest is not None and
+                not bench_of_interest == point.bench):
+                continue
+            
+            if (config_of_interest is not None and
+                not config_of_interest == point.config):
+                continue
+            
+            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():
+                if key in point.settings and point.settings[key] != value:
+                    skip = True
+                    break
+            if skip:
+                continue
+            
+            line_name = Label(point.bench
+                            , point.config
+                            , point.time_type
+                            , point.settings)
+            
+            if line_name not in lines:
+                lines[line_name] = []
+            
+            lines[line_name].append((revision, point.time))
+            
+    return lines
+
+def bounds(lines):
+    """Finds the bounding rectangle for the lines.
+    
+    {Label:[(x,y)]} -> ((min_x, min_y),(max_x,max_y))"""
+    min_x = bench_util.Max
+    min_y = bench_util.Max
+    max_x = bench_util.Min
+    max_y = bench_util.Min
+    
+    for line in lines.itervalues():
+        for x, y in line:
+            min_x = min(min_x, x)
+            min_y = min(min_y, y)
+            max_x = max(max_x, x)
+            max_y = max(max_y, y)
+            
+    return ((min_x, min_y), (max_x, max_y))
+
+def create_regressions(lines, start_x, end_x):
+    """Creates regression data from line segments.
+    
+    ({Label:[(x,y)] | [n].x <= [n+1].x}, Number, Number)
+        -> {Label:LinearRegression}"""
+    regressions = {} # {Label : LinearRegression}
+    
+    for label, line in lines.iteritems():
+        regression_line = [p for p in line if start_x <= p[0] <= end_x]
+        
+        if (len(regression_line) < 2):
+            continue
+        regression = bench_util.LinearRegression(regression_line)
+        regressions[label] = regression
+    
+    return regressions
+
+def bounds_slope(regressions):
+    """Finds the extreme up and down slopes of a set of linear regressions.
+    
+    ({Label:LinearRegression}) -> (max_up_slope, min_down_slope)"""
+    max_up_slope = 0
+    min_down_slope = 0
+    for regression in regressions.itervalues():
+        min_slope = regression.find_min_slope()
+        max_up_slope = max(max_up_slope, min_slope)
+        min_down_slope = min(min_down_slope, min_slope)
+    
+    return (max_up_slope, min_down_slope)
+
+def main():
+    """Parses command line and writes output."""
+    
+    try:
+        opts, _ = getopt.getopt(sys.argv[1:]
+                                 , "b:c:d:e:f:i:l:m:o:r:s:t:x:y:"
+                                 , "default-setting=")
+    except getopt.GetoptError, err:
+        print str(err) 
+        usage()
+        sys.exit(2)
+    
+    directory = None
+    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
+    requested_height = None
+    requested_width = None
+    title = 'Bench graph'
+    settings = {}
+    default_settings = {}
+
+    def parse_range(range):
+        """Takes '<old>[:<new>]' as a string and returns (old, new).
+        Any revision numbers that are dependent on the latest revision number
+        will be filled in based on latest_revision.
+        """
+        old, _, new = range.partition(":")
+        old = int(old)
+        if old < 0:
+            old += latest_revision;
+        if not new:
+            new = latest_revision;
+        new = int(new)
+        if new < 0:
+            new += latest_revision;
+        return (old, new)
+
+    def add_setting(settings, setting):
+        """Takes <key>[=<value>] adds {key:value} or {key:True} to settings."""
+        name, _, value = setting.partition('=')
+        if not value:
+            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 == "-b":
+                bench_of_interest = value
+            elif option == "-c":
+                config_of_interest = 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 == "--default-setting":
+                add_setting(default_settings, value)
+            else:
+                usage()
+                assert False, "unhandled option"
+    except ValueError:
+        usage()
+        sys.exit(2)
+
+    if directory is None:
+        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)
+
+    unfiltered_revision_data_points = parse_dir(directory
+                                   , default_settings
+                                   , oldest_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 = allowed_revision_data_points.keys()
+    oldest_revision = min(all_revision_numbers)
+    newest_revision = max(all_revision_numbers)
+
+    lines = create_lines(allowed_revision_data_points
+                   , settings
+                   , bench_of_interest
+                   , config_of_interest
+                   , time_of_interest
+                   , time_to_ignore)
+
+    regressions = create_regressions(lines
+                                   , oldest_regression
+                                   , newest_regression)
+
+    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))
+def qe(out):
+    """Stringify input and escape as xml data."""
+    return xml.sax.saxutils.escape(str(out))
+
+def create_select(qualifier, lines, select_id=None):
+    """Output select with options showing lines which qualifier maps to it.
+    
+    ((Label) -> str, {Label:_}, str?) -> _"""
+    options = {} #{ option : [Label]}
+    for label in lines.keys():
+        option = qualifier(label)
+        if (option not in options):
+            options[option] = []
+        options[option].append(label)
+    option_list = list(options.keys())
+    option_list.sort()
+    print '<select class="lines"',
+    if select_id is not None:
+        print 'id=%s' % qa(select_id)
+    print 'multiple="true" size="10" onchange="updateSvg();">'
+    for option in option_list:
+        print '<option value=' + qa('[' + 
+        reduce(lambda x,y:x+json.dumps(str(y))+',',options[option],"")[0:-1]
+        + ']') + '>'+qe(option)+'</option>'
+    print '</select>'
+
+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>' % qe(title)
+    print '</head>'
+    print '<body>'
+    
+    output_svg(lines, regressions, requested_width, requested_height)
+
+    #output the manipulation controls
+    print """
+<script type="text/javascript">//<![CDATA[
+    function getElementsByClass(node, searchClass, tag) {
+        var classElements = new Array();
+        var elements = node.getElementsByTagName(tag);
+        var pattern = new RegExp("^|\\s"+searchClass+"\\s|$");
+        for (var i = 0, elementsFound = 0; i < elements.length; ++i) {
+            if (pattern.test(elements[i].className)) {
+                classElements[elementsFound] = elements[i];
+                ++elementsFound;
+            }
+        }
+        return classElements;
+    }
+    function getAllLines() {
+        var selectElem = document.getElementById('benchSelect');
+        var linesObj = {};
+        for (var i = 0; i < selectElem.options.length; ++i) {
+            var lines = JSON.parse(selectElem.options[i].value);
+            for (var j = 0; j < lines.length; ++j) {
+                linesObj[lines[j]] = true;
+            }
+        }
+        return linesObj;
+    }
+    function getOptions(selectElem) {
+        var linesSelectedObj = {};
+        for (var i = 0; i < selectElem.options.length; ++i) {
+            if (!selectElem.options[i].selected) continue;
+            
+            var linesSelected = JSON.parse(selectElem.options[i].value);
+            for (var j = 0; j < linesSelected.length; ++j) {
+                linesSelectedObj[linesSelected[j]] = true;
+            }
+        }
+        return linesSelectedObj;
+    }
+    function objectEmpty(obj) {
+        for (var p in obj) {
+            return false;
+        }
+        return true;
+    }
+    function markSelectedLines(selectElem, allLines) {
+        var linesSelected = getOptions(selectElem);
+        if (!objectEmpty(linesSelected)) {
+            for (var line in allLines) {
+                allLines[line] &= (linesSelected[line] == true);
+            }
+        }
+    }
+    function updateSvg() {
+        var allLines = getAllLines();
+        
+        var selects = getElementsByClass(document, 'lines', 'select');
+        for (var i = 0; i < selects.length; ++i) {
+            markSelectedLines(selects[i], allLines);
+        }
+        
+        for (var line in allLines) {
+            var svgLine = document.getElementById(line);
+            var display = (allLines[line] ? 'inline' : 'none');
+            svgLine.setAttributeNS(null,'display', display);
+        }
+    }
+    
+    function mark(markerId) {
+        for (var line in getAllLines()) {
+            var svgLineGroup = document.getElementById(line);
+            var display = svgLineGroup.getAttributeNS(null,'display');
+            if (display == null || display == "" || display != "none") {
+                var svgLine = document.getElementById(line+'_line');
+                if (markerId == null) {
+                    svgLine.removeAttributeNS(null,'marker-mid');
+                } else {
+                    svgLine.setAttributeNS(null,'marker-mid', markerId);
+                }
+            }
+        }
+    }
+//]]></script>"""
+
+    all_settings = {}
+    variant_settings = set()
+    for label in lines.keys():
+        for key, value  in label.settings.items():
+            if key not in all_settings:
+                all_settings[key] = value
+            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:
+        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 """
+</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
+graph lines.</p>
+<p>To highlight revision numbers, hold down SHIFT and mouse over
+the graph area.</p>
+<p>To only show certain tests on the graph, select any combination of
+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>
+</table>
+</body>
+</html>"""
+    
+def compute_size(requested_width, requested_height, rev_width, time_height):
+    """Converts potentially empty requested size into a concrete size.
+    
+    (Number?,  Number?) -> (Number, Number)"""
+    pic_width = 0
+    pic_height = 0
+    if (requested_width is not None and requested_height is not None):
+        pic_height = requested_height
+        pic_width = requested_width
+    
+    elif (requested_width is not None):
+        pic_width = requested_width
+        pic_height = pic_width * (float(time_height) / rev_width)
+        
+    elif (requested_height is not None):
+        pic_height = requested_height
+        pic_width = pic_height * (float(rev_width) / time_height)
+        
+    else:
+        pic_height = 800
+        pic_width = max(rev_width*3
+                      , pic_height * (float(rev_width) / time_height))
+    
+    return (pic_width, pic_height)
+
+def output_svg(lines, regressions, requested_width, requested_height):
+    """Outputs an svg view of the data."""
+    
+    (global_min_x, _), (global_max_x, global_max_y) = bounds(lines)
+    max_up_slope, min_down_slope = bounds_slope(regressions)
+    
+    #output
+    global_min_y = 0
+    x = global_min_x
+    y = global_min_y
+    w = global_max_x - global_min_x
+    h = global_max_y - global_min_y
+    font_size = 16
+    line_width = 2
+    
+    pic_width, pic_height = compute_size(requested_width, requested_height
+                                       , w, h)
+    
+    def cw(w1):
+        """Converts a revision difference to display width."""
+        return (pic_width / float(w)) * w1
+    def cx(x):
+        """Converts a revision to a horizontal display position."""
+        return cw(x - global_min_x)
+
+    def ch(h1):
+        """Converts a time difference to a display height."""
+        return -(pic_height / float(h)) * h1
+    def cy(y):
+        """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')
+    print 'viewBox="0 0 %s %s"' % (str(pic_width), str(pic_height))
+    print 'onclick=%s' % qa(
+            "var event = arguments[0] || window.event;"
+            " if (event.shiftKey) { highlightRevision(null); }"
+            " if (event.ctrlKey) { highlight(null); }"
+            " return false;")
+    print 'xmlns="http://www.w3.org/2000/svg"'
+    print 'xmlns:xlink="http://www.w3.org/1999/xlink">'
+    
+    print """
+<defs>
+    <marker id="circleMark"
+      viewBox="0 0 2 2" refX="1" refY="1"
+      markerUnits="strokeWidth"
+      markerWidth="2" markerHeight="2"
+      orient="0">
+      <circle cx="1" cy="1" r="1"/>
+    </marker>
+</defs>"""
+    
+    #output the revisions
+    print """
+<script type="text/javascript">//<![CDATA[
+    var previousRevision;
+    var previousRevisionFill;
+    var previousRevisionStroke
+    function highlightRevision(id) {
+        if (previousRevision == id) return;
+
+        document.getElementById('revision').firstChild.nodeValue = 'r' + id;
+        document.getElementById('rev_link').setAttribute('xlink:href',
+            'http://code.google.com/p/skia/source/detail?r=' + id);
+        
+        var preRevision = document.getElementById(previousRevision);
+        if (preRevision) {
+            preRevision.setAttributeNS(null,'fill', previousRevisionFill);
+            preRevision.setAttributeNS(null,'stroke', previousRevisionStroke);
+        }
+        
+        var revision = document.getElementById(id);
+        previousRevision = id;
+        if (revision) {
+            previousRevisionFill = revision.getAttributeNS(null,'fill');
+            revision.setAttributeNS(null,'fill','rgb(100%, 95%, 95%)');
+            
+            previousRevisionStroke = revision.getAttributeNS(null,'stroke');
+            revision.setAttributeNS(null,'stroke','rgb(100%, 90%, 90%)');
+        }
+    }
+//]]></script>"""
+    
+    def print_rect(x, y, w, h, revision):
+        """Outputs a revision rectangle in display space,
+           taking arguments in revision space."""
+        disp_y = cy(y)
+        disp_h = ch(h)
+        if disp_h < 0:
+            disp_y += disp_h
+            disp_h = -disp_h
+        
+        print '<rect id=%s x=%s y=%s' % (qa(revision), qa(cx(x)), qa(disp_y),),
+        print 'width=%s height=%s' % (qa(cw(w)), qa(disp_h),),
+        print 'fill="white"',
+        print 'stroke="rgb(98%%,98%%,88%%)" stroke-width=%s' % qa(line_width),
+        print 'onmouseover=%s' % qa(
+                "var event = arguments[0] || window.event;"
+                " if (event.shiftKey) {"
+                    " highlightRevision('"+str(revision)+"');"
+                    " return false;"
+                " }"),
+        print ' />'
+    
+    xes = set()
+    for line in lines.itervalues():
+        for point in line:
+            xes.add(point[0])
+    revisions = list(xes)
+    revisions.sort()
+    
+    left = x
+    current_revision = revisions[0]
+    for next_revision in revisions[1:]:
+        width = (((next_revision - current_revision) / 2.0)
+                 + (current_revision - left))
+        print_rect(left, y, width, h, current_revision)
+        left += width
+        current_revision = next_revision
+    print_rect(left, y, x+w - left, h, current_revision)
+
+    #output the lines
+    print """
+<script type="text/javascript">//<![CDATA[
+    var previous;
+    var previousColor;
+    var previousOpacity;
+    function highlight(id) {
+        if (previous == id) return;
+
+        document.getElementById('label').firstChild.nodeValue = id;
+
+        var preGroup = document.getElementById(previous);
+        if (preGroup) {
+            var preLine = document.getElementById(previous+'_line');
+            preLine.setAttributeNS(null,'stroke', previousColor);
+            preLine.setAttributeNS(null,'opacity', previousOpacity);
+
+            var preSlope = document.getElementById(previous+'_linear');
+            if (preSlope) {
+                preSlope.setAttributeNS(null,'visibility', 'hidden');
+            }
+        }
+
+        var group = document.getElementById(id);
+        previous = id;
+        if (group) {
+            group.parentNode.appendChild(group);
+            
+            var line = document.getElementById(id+'_line');
+            previousColor = line.getAttributeNS(null,'stroke');
+            previousOpacity = line.getAttributeNS(null,'opacity');
+            line.setAttributeNS(null,'stroke', 'blue');
+            line.setAttributeNS(null,'opacity', '1');
+            
+            var slope = document.getElementById(id+'_linear');
+            if (slope) {
+                slope.setAttributeNS(null,'visibility', 'visible');
+            }
+        }
+    }
+//]]></script>"""
+    for label, line in lines.items():
+        print '<g id=%s>' % qa(label)
+        r = 128
+        g = 128
+        b = 128
+        a = .10
+        if label in regressions:
+            regression = regressions[label]
+            min_slope = regression.find_min_slope()
+            if min_slope < 0:
+                d = max(0, (min_slope / min_down_slope))
+                g += int(d*128)
+                a += d*0.9
+            elif min_slope > 0:
+                d = max(0, (min_slope / max_up_slope))
+                r += int(d*128)
+                a += d*0.9
+            
+            slope = regression.slope
+            intercept = regression.intercept
+            min_x = regression.min_x
+            max_x = regression.max_x
+            print '<polyline id=%s' % qa(str(label)+'_linear'),
+            print 'fill="none" stroke="yellow"',
+            print 'stroke-width=%s' % qa(abs(ch(regression.serror*2))),
+            print 'opacity="0.5" pointer-events="none" visibility="hidden"',
+            print 'points="',
+            print '%s,%s' % (str(cx(min_x)), str(cy(slope*min_x + intercept))),
+            print '%s,%s' % (str(cx(max_x)), str(cy(slope*max_x + intercept))),
+            print '"/>'
+        
+        print '<polyline id=%s' % qa(str(label)+'_line'),
+        print 'onmouseover=%s' % qa(
+                "var event = arguments[0] || window.event;"
+                " if (event.ctrlKey) {"
+                    " highlight('"+str(label).replace("'", "\\'")+"');"
+                    " return false;"
+                " }"),
+        print 'fill="none" stroke="rgb(%s,%s,%s)"' % (str(r), str(g), str(b)),
+        print 'stroke-width=%s' % qa(line_width),
+        print 'opacity=%s' % qa(a),
+        print 'points="',
+        for point in line:
+            print '%s,%s' % (str(cx(point[0])), str(cy(point[1]))),
+        print '"/>'
+
+        print '</g>'
+
+    #output the labels
+    print '<text id="label" x="0" y=%s' % qa(font_size),
+    print 'font-size=%s> </text>' % qa(font_size)
+
+    print '<a id="rev_link" xlink:href="" target="_top">'
+    print '<text id="revision" x="0" y=%s style="' % qa(font_size*2)
+    print 'font-size: %s; ' % qe(font_size)
+    print 'stroke: #0000dd; text-decoration: underline; '
+    print '"> </text></a>'
+
+    print '</svg>'
+
+if __name__ == "__main__":
+    main()
diff --git a/bench/bench_util.py b/bench/bench_util.py
new file mode 100644
index 0000000..82a16f0
--- /dev/null
+++ b/bench/bench_util.py
@@ -0,0 +1,221 @@
+'''
+Created on May 19, 2011
+
+@author: bungeman
+'''
+
+import re
+import math
+
+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
+        self.config = config
+        self.time_type = time_type
+        self.time = time
+        self.settings = settings
+    
+    def __repr__(self):
+        return "BenchDataPoint(%s, %s, %s, %s, %s)" % (
+                   str(self.bench),
+                   str(self.config),
+                   str(self.time_type),
+                   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")
+
+class _ListAlgorithm(object):
+    """Algorithm for selecting the representation value from a given list.
+    representation is one of 'avg', 'min', 'med', '25th' (average, minimum,
+    median, 25th percentile)"""
+    def __init__(self, data, representation=None):
+        if not representation:
+            representation = 'avg'  # default algorithm is average
+        self._data = data
+        self._len = len(data)
+        if representation == 'avg':
+            self._rep = sum(self._data) / self._len
+        else:
+            self._data.sort()
+            if representation == 'min':
+                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 == 'med':
+                    x = int(round(0.5 * self._len + 0.5))
+                elif representation == '25th':
+                    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 parse(settings, lines, representation='avg'):
+    """Parses bench output into a useful data structure.
+    
+    ({str:str}, __iter__ -> str) -> [BenchDataPoint]
+    representation should match one of those defined in class _ListAlgorithm."""
+    
+    benches = []
+    current_bench = None
+    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+)(?:,\d+\.\d+)*)'
+    config_re = '(\S+): ((?:' + time_re + '\s+)+)'
+    
+    for line in lines:
+        
+        #see if this line is a settings line
+        settingsMatch = re.search(settings_re, line)
+        if (settingsMatch):
+            settings = dict(settings)
+            for settingMatch in re.finditer(setting_re, settingsMatch.group(1)):
+                if (settingMatch.group(2)):
+                    settings[settingMatch.group(1)] = settingMatch.group(2)
+                else:
+                    settings[settingMatch.group(1)] = True
+                
+        #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
+        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)
+                    iters = [float(i) for i in
+                             new_time.group(2).strip().split(',')]
+                    benches.append(BenchDataPoint(
+                            current_bench
+                            , current_config
+                            , current_time_type
+                            , _ListAlgorithm(iters, representation).compute()
+                            , 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
+        Sxy = 0.0
+        Syy = 0.0
+        for point in points:
+            x = point[0]
+            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
+        
+        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 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))
+        self.serror_slope = math.sqrt(max(0, sB2))
+        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),
+                   str(self.intercept),
+                   str(self.serror),
+                   str(self.serror_slope),
+                   str(self.serror_intercept),
+               )
+    
+    def find_min_slope(self):
+        """Finds the minimal slope given one standard deviation."""
+        slope = self.slope
+        intercept = self.intercept
+        error = self.serror
+        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):
+    """Returns HTML displaying the given revision number and linking to
+    that revision's change page at code.google.com, e.g.
+    http://code.google.com/p/skia/source/detail?r=2056
+    """
+    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
new file mode 100644
index 0000000..eb8c4ed
--- /dev/null
+++ b/bench/benchmain.cpp
@@ -0,0 +1,917 @@
+
+/*
+ * Copyright 2011 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"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrRenderTarget.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"
+
+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);
+    } else {
+        bm.eraseColor(SK_ColorWHITE);
+    }
+}
+
+#if 0
+static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
+    if (bm1.width() != bm2.width() ||
+        bm1.height() != bm2.height() ||
+        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)) {
+            return false;
+        }
+    }
+    return true;
+}
+#endif
+
+class Iter {
+public:
+    Iter(void* param) {
+        fBench = BenchRegistry::Head();
+        fParam = param;
+    }
+
+    SkBenchmark* next() {
+        if (fBench) {
+            BenchRegistry::Factory f = fBench->factory();
+            fBench = fBench->next();
+            return f(fParam);
+        }
+        return NULL;
+    }
+
+private:
+    const BenchRegistry* fBench;
+    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++) {
+        switch (name[i]) {
+            case '/':
+            case '\\':
+            case ' ':
+            case ':':
+                path->writable_str()[i] = '-';
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+static void saveFile(const char name[], const char config[], const char dir[],
+                     const SkBitmap& bm) {
+    SkBitmap copy;
+    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;
+        SkPMColor* p = copy.getAddr32(0, 0);
+        for (size_t i = 0; i < size; i++) {
+            int c = (*p >> SK_A32_SHIFT) & 0xFF;
+            c = 255 - c;
+            c |= (c << 24) | (c << 16) | (c << 8);
+            *p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
+        }
+    }
+
+    SkString str;
+    make_filename(name, &str);
+    str.appendf("_%s.png", config);
+    str.prepend(dir);
+    ::remove(str.c_str());
+    SkImageEncoder::EncodeFile(str.c_str(), copy, SkImageEncoder::kPNG_Type,
+                               100);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+    canvas->translate(-x, -y);
+}
+
+static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
+    if (argv < stop) {
+        *var = atoi(*argv) != 0;
+        return true;
+    }
+    return false;
+}
+
+enum Backend {
+    kRaster_Backend,
+    kGPU_Backend,
+    kPDF_Backend,
+};
+
+#if SK_SUPPORT_GPU
+class GLHelper {
+public:
+    GLHelper() {
+    }
+
+    bool init(SkGLContext* glCtx, int width, int height) {
+        GrContext* grCtx;
+        if (!glCtx->init(width, height)) {
+            return false;
+        }
+        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();
+            GrRenderTarget* rt = grCtx->createPlatformRenderTarget(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();
+    }
+
+    SkGLContext* glContext() {
+        return fGLContext.get();
+    }
+
+    GrRenderTarget* renderTarget() {
+        return fRenderTarget.get();
+    }
+
+    GrContext* grContext() {
+        return fGrContext.get();
+    }
+private:
+    SkAutoTUnref<SkGLContext> fGLContext;
+    SkAutoTUnref<GrContext> fGrContext;
+    SkAutoTUnref<GrRenderTarget> fRenderTarget;
+};
+
+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");
+    }
+    return device;
+}
+
+static const struct {
+    SkBitmap::Config    fConfig;
+    const char*         fName;
+    Backend             fBackend;
+    GLHelper*           fGLHelper;
+} 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[]) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
+        if (!strcmp(config, gConfigs[i].fName)) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+static void determine_gpu_context_size(SkTDict<const char*>& defineDict,
+                                       int* contextWidth,
+                                       int* contextHeight) {
+    Iter iter(&defineDict);
+    SkBenchmark* bench;
+    while ((bench = iter.next()) != NULL) {
+        SkIPoint dim = bench->getSize();
+        if (*contextWidth < dim.fX) {
+            *contextWidth = dim.fX;
+        }
+        if (*contextHeight < dim.fY) {
+            *contextHeight = dim.fY;
+        }
+        bench->unref();
+    }
+}
+
+static bool skip_name(const SkTDArray<const char*> array, const char name[]) {
+    if (0 == array.count()) {
+        // no names, so don't skip anything
+        return false;
+    }
+    for (int i = 0; i < array.count(); ++i) {
+        if (strstr(name, array[i])) {
+            // found the name, so don't skip
+            return false;
+        }
+    }
+    return true;
+}
+
+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) {
+#ifdef 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
+    SkTDArray<int> configs;
+    bool userConfig = false;
+
+    SkBenchLogger logger;
+
+    char* const* stop = argv + argc;
+    for (++argv; argv < stop; ++argv) {
+        if (strcmp(*argv, "-o") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                outDir.set(*argv);
+                if (outDir.c_str()[outDir.size() - 1] != '/') {
+                    outDir.append("/");
+                }
+            }
+        } else if (strcmp(*argv, "--repeat") == 0) {
+            argv++;
+            if (argv < stop) {
+                repeatDraw = atoi(*argv);
+                if (repeatDraw < 1) {
+                    repeatDraw = 1;
+                }
+            } else {
+                logger.logError("missing arg for --repeat\n");
+                help();
+                return -1;
+            }
+        } 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 {
+                logger.logError("missing arg for --timers\n");
+                help();
+                return -1;
+            }
+        } else if (!strcmp(*argv, "--rotate")) {
+            doRotate = true;
+        } else if (!strcmp(*argv, "--scale")) {
+            doScale = true;
+        } else if (!strcmp(*argv, "--clip")) {
+            doClip = true;
+        } else if (!strcmp(*argv, "--min")) {
+            printMin = true;
+        } else if (strcmp(*argv, "--forceAA") == 0) {
+            if (!parse_bool_arg(++argv, stop, &forceAA)) {
+                logger.logError("missing arg for --forceAA\n");
+                help();
+                return -1;
+            }
+        } else if (strcmp(*argv, "--forceFilter") == 0) {
+            if (!parse_bool_arg(++argv, stop, &forceFilter)) {
+                logger.logError("missing arg for --forceFilter\n");
+                help();
+                return -1;
+            }
+        } else if (strcmp(*argv, "--forceDither") == 0) {
+            bool tmp;
+            if (!parse_bool_arg(++argv, stop, &tmp)) {
+                logger.logError("missing arg for --forceDither\n");
+                help();
+                return -1;
+            }
+            forceDither = tmp ? SkTriState::kTrue : SkTriState::kFalse;
+        } else if (strcmp(*argv, "--forceBlend") == 0) {
+            bool wantAlpha = false;
+            if (!parse_bool_arg(++argv, stop, &wantAlpha)) {
+                logger.logError("missing arg for --forceBlend\n");
+                help();
+                return -1;
+            }
+            forceAlpha = wantAlpha ? 0x80 : 0xFF;
+        } 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) {
+                  logger.logError("bad arg for --strokeWidth\n");
+                  help();
+                  return -1;
+                }
+                hasStrokeWidth = true;
+            } else {
+                logger.logError("missing arg for --strokeWidth\n");
+                help();
+                return -1;
+            }
+        } else if (strcmp(*argv, "--match") == 0) {
+            argv++;
+            if (argv < stop) {
+                *fMatches.append() = *argv;
+            } else {
+                logger.logError("missing arg for --match\n");
+                help();
+                return -1;
+            }
+        } else if (strcmp(*argv, "--config") == 0) {
+            argv++;
+            if (argv < stop) {
+                int index = findConfig(*argv);
+                if (index >= 0) {
+                    *configs.append() = index;
+                    userConfig = true;
+                } else {
+                    SkString str;
+                    str.printf("unrecognized config %s\n", *argv);
+                    logger.logError(str);
+                    help();
+                    return -1;
+                }
+            } else {
+                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) {
+            argv++;
+            if (argv < stop) {
+                defineDict.set(argv[-1] + 2, *argv);
+            } else {
+                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);
+            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;
+        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;
+            case SkTriState::kTrue: ditherName = "true"; break;
+            case SkTriState::kFalse: ditherName = "false"; break;
+            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)
+        str.append(" scalar=fixed");
+#endif
+
+#if defined(SK_BUILD_FOR_WIN32)
+        str.append(" system=WIN32");
+#elif defined(SK_BUILD_FOR_MAC)
+        str.append(" system=MAC");
+#elif defined(SK_BUILD_FOR_ANDROID)
+        str.append(" system=ANDROID");
+#elif defined(SK_BUILD_FOR_UNIX)
+        str.append(" system=UNIX");
+#else
+        str.append(" system=other");
+#endif
+
+#if defined(SK_DEBUG)
+        str.append(" DEBUG");
+#endif
+        str.append("\n");
+        logger.logProgress(str);
+    }
+
+    SkGLContext* timerCtx = NULL;
+    //Don't do GL when 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);
+    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);
+        bench->setDither(forceDither);
+        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());
+            logger.logProgress(str);
+        }
+
+        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 = 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);
+                    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);
+                    bench->draw(tempCanvas);
+                    pictureRecordFrom.endRecording();
+                    canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY);
+                    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);
+            }
+            if (doScale) {
+                performScale(canvas, dim.fX, dim.fY);
+            }
+            if (doRotate) {
+                performRotate(canvas, dim.fX, dim.fY);
+            }
+
+            // warm up caches if needed
+            if (repeatDraw > 1) {
+#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
+            }
+
+            // record timer values for each repeat, and their sum
+            TimerData timerData(perIterTimeformat, normalTimeFormat);
+            for (int i = 0; i < repeatDraw; i++) {
+                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);
+                }
+
+                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 (repeatDraw > 1) {
+                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));
+            }
+        }
+        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
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
+
diff --git a/debugger/QT/SkCanvasWidget.cpp b/debugger/QT/SkCanvasWidget.cpp
new file mode 100644
index 0000000..2f714d7
--- /dev/null
+++ b/debugger/QT/SkCanvasWidget.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.
+ */
+
+
+#include "SkCanvasWidget.h"
+
+SkCanvasWidget::SkCanvasWidget(QWidget* parent,
+        SkDebugger* debugger) : QWidget(parent)
+    , fHorizontalLayout(this)
+    , fRasterWidget(debugger)
+    , fGLWidget(debugger)
+{
+
+    fDebugger = debugger;
+
+    fHorizontalLayout.setSpacing(6);
+    fHorizontalLayout.setContentsMargins(0,0,0,0);
+    fRasterWidget.setSizePolicy(QSizePolicy::Expanding,
+            QSizePolicy::Expanding);
+    fGLWidget.setSizePolicy(QSizePolicy::Expanding,
+            QSizePolicy::Expanding);
+
+    fHorizontalLayout.addWidget(&fRasterWidget);
+    fHorizontalLayout.addWidget(&fGLWidget);
+
+    fPreviousPoint.set(0,0);
+    fUserOffset.set(0,0);
+    fUserScaleFactor = 1.0;
+
+    setWidgetVisibility(kGPU_WidgetType, true);
+    connect(&fRasterWidget, SIGNAL(drawComplete()),
+            this->parentWidget(), SLOT(drawComplete()));
+}
+
+SkCanvasWidget::~SkCanvasWidget() {}
+
+void SkCanvasWidget::drawTo(int index) {
+    fDebugger->setIndex(index);
+    fRasterWidget.draw();
+    fGLWidget.draw();
+    emit commandChanged(fDebugger->index());
+}
+
+void SkCanvasWidget::mouseMoveEvent(QMouseEvent* event) {
+    SkIPoint eventPoint = SkIPoint::Make(event->globalX(), event->globalY());
+    fUserOffset += eventPoint - fPreviousPoint;
+    fPreviousPoint = eventPoint;
+    fDebugger->setUserOffset(fUserOffset);
+    drawTo(fDebugger->index());
+}
+
+void SkCanvasWidget::mousePressEvent(QMouseEvent* event) {
+    fPreviousPoint.set(event->globalX(), event->globalY());
+    emit hitChanged(fDebugger->getCommandAtPoint(event->x(), event->y(),
+            fDebugger->index()));
+}
+
+void SkCanvasWidget::mouseDoubleClickEvent(QMouseEvent* event) {
+    resetWidgetTransform();
+}
+
+void SkCanvasWidget::resetWidgetTransform() {
+    fUserOffset.set(0,0);
+    fUserScaleFactor = 1.0;
+    fDebugger->setUserOffset(fUserOffset);
+    fDebugger->setUserScale(fUserScaleFactor);
+    emit scaleFactorChanged(fUserScaleFactor);
+    drawTo(fDebugger->index());
+}
+
+void SkCanvasWidget::setWidgetVisibility(WidgetType type, bool isHidden) {
+    if (type == kRaster_8888_WidgetType) {
+        fRasterWidget.setHidden(isHidden);
+    } else if (type == kGPU_WidgetType) {
+        fGLWidget.setHidden(isHidden);
+    }
+}
+
+void SkCanvasWidget::zoom(float zoomIncrement) {
+    fUserScaleFactor += zoomIncrement;
+
+    /* The range of the fUserScaleFactor crosses over the range -1,0,1 frequently.
+    * Based on the code below, -1 and 1 both scale the image to it's original
+    * size we do the following to never have a registered wheel scroll
+    * not effect the fUserScaleFactor. */
+    if (fUserScaleFactor == 0) {
+        fUserScaleFactor = 2 * zoomIncrement;
+    }
+    emit scaleFactorChanged(fUserScaleFactor);
+    fDebugger->setUserScale(fUserScaleFactor);
+    drawTo(fDebugger->index());
+}
diff --git a/debugger/QT/SkCanvasWidget.h b/debugger/QT/SkCanvasWidget.h
new file mode 100644
index 0000000..ab634f8
--- /dev/null
+++ b/debugger/QT/SkCanvasWidget.h
@@ -0,0 +1,72 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SKCANVASWIDGET_H_
+#define SKCANVASWIDGET_H_
+
+#include <QWidget>
+#include <QHBoxLayout>
+#include "SkStream.h"
+#include "SkRasterWidget.h"
+#include "SkGLWidget.h"
+#include "SkDebugger.h"
+
+class SkCanvasWidget : public QWidget {
+    Q_OBJECT
+
+public:
+    SkCanvasWidget(QWidget* parent, SkDebugger* debugger);
+
+    ~SkCanvasWidget();
+
+    enum WidgetType {
+        kRaster_8888_WidgetType = 1 << 0,
+        kGPU_WidgetType         = 1 << 1,
+    };
+
+    void drawTo(int index);
+
+    void setWidgetVisibility(WidgetType type, bool isHidden);
+
+    void zoom(float zoomIncrement);
+
+    void resetWidgetTransform();
+
+signals:
+    void scaleFactorChanged(float newScaleFactor);
+    void commandChanged(int newCommand);
+    void hitChanged(int hit);
+
+private slots:
+    void keyZoom(int zoomIncrement) {
+        zoom(zoomIncrement);
+    }
+
+private:
+    QHBoxLayout fHorizontalLayout;
+    SkRasterWidget fRasterWidget;
+    SkGLWidget fGLWidget;
+    SkDebugger* fDebugger;
+    SkIPoint fPreviousPoint;
+    SkIPoint fUserOffset;
+    float fUserScaleFactor;
+
+    void mouseMoveEvent(QMouseEvent* event);
+
+    void mousePressEvent(QMouseEvent* event);
+
+    void mouseDoubleClickEvent(QMouseEvent* event);
+
+    void wheelEvent(QWheelEvent* event) {
+        zoom(event->delta()/120);
+    }
+};
+
+
+#endif /* SKCANVASWIDGET_H_ */
diff --git a/debugger/QT/SkDebuggerGUI.cpp b/debugger/QT/SkDebuggerGUI.cpp
new file mode 100644
index 0000000..a03c878
--- /dev/null
+++ b/debugger/QT/SkDebuggerGUI.cpp
@@ -0,0 +1,661 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDebuggerGUI.h"
+#include "SkGraphics.h"
+#include <QListWidgetItem>
+
+SkDebuggerGUI::SkDebuggerGUI(QWidget *parent) :
+        QMainWindow(parent)
+    , fCentralWidget(this)
+    , fStatusBar(this)
+    , fToolBar(this)
+    , fActionOpen(this)
+    , fActionBreakpoint(this)
+    , fActionCancel(this)
+    , fActionClearBreakpoints(this)
+    , fActionClearDeletes(this)
+    , fActionClose(this)
+    , fActionCreateBreakpoint(this)
+    , fActionDelete(this)
+    , fActionDirectory(this)
+    , fActionGoToLine(this)
+    , fActionInspector(this)
+    , fActionPlay(this)
+    , fActionPause(this)
+    , fActionRewind(this)
+    , fActionSave(this)
+    , fActionSaveAs(this)
+    , fActionShowDeletes(this)
+    , fActionStepBack(this)
+    , fActionStepForward(this)
+    , fActionZoomIn(this)
+    , fActionZoomOut(this)
+    , fMapper(this)
+    , fListWidget(&fCentralWidget)
+    , fDirectoryWidget(&fCentralWidget)
+    , fCanvasWidget(this, &fDebugger)
+    , fMenuBar(this)
+    , fMenuFile(this)
+    , fMenuNavigate(this)
+    , fMenuView(this)
+    , fBreakpointsActivated(false)
+    , fDeletesActivated(false)
+    , fPause(false)
+    , fLoading(false)
+{
+    setupUi(this);
+    connect(&fListWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(registerListClick(QListWidgetItem *)));
+    connect(&fActionOpen, SIGNAL(triggered()), this, SLOT(openFile()));
+    connect(&fActionDirectory, SIGNAL(triggered()), this, SLOT(toggleDirectory()));
+    connect(&fDirectoryWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(loadFile(QListWidgetItem *)));
+    connect(&fActionDelete, SIGNAL(triggered()), this, SLOT(actionDelete()));
+    connect(&fListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(toggleBreakpoint()));
+    connect(&fActionRewind, SIGNAL(triggered()), this, SLOT(actionRewind()));
+    connect(&fActionPlay, SIGNAL(triggered()), this, SLOT(actionPlay()));
+    connect(&fActionStepBack, SIGNAL(triggered()), this, SLOT(actionStepBack()));
+    connect(&fActionStepForward, SIGNAL(triggered()), this, SLOT(actionStepForward()));
+    connect(&fActionBreakpoint, SIGNAL(triggered()), this, SLOT(actionBreakpoints()));
+    connect(&fActionInspector, SIGNAL(triggered()), this, SLOT(actionInspector()));
+    connect(&fActionInspector, SIGNAL(triggered()), this, SLOT(actionSettings()));
+    connect(&fFilter, SIGNAL(activated(QString)), this, SLOT(toggleFilter(QString)));
+    connect(&fActionCancel, SIGNAL(triggered()), this, SLOT(actionCancel()));
+    connect(&fActionClearBreakpoints, SIGNAL(triggered()), this, SLOT(actionClearBreakpoints()));
+    connect(&fActionClearDeletes, SIGNAL(triggered()), this, SLOT(actionClearDeletes()));
+    connect(&fActionClose, SIGNAL(triggered()), this, SLOT(actionClose()));
+    connect(fSettingsWidget.getVisibilityButton(), SIGNAL(toggled(bool)), this, SLOT(actionCommandFilter()));
+    connect(fSettingsWidget.getGLCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionGLWidget(bool)));
+    connect(fSettingsWidget.getRasterCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionRasterWidget(bool)));
+    connect(&fActionPause, SIGNAL(toggled(bool)), this, SLOT(pauseDrawing(bool)));
+    connect(&fActionCreateBreakpoint, SIGNAL(activated()), this, SLOT(toggleBreakpoint()));
+    connect(&fActionShowDeletes, SIGNAL(triggered()), this, SLOT(showDeletes()));
+    connect(&fCanvasWidget, SIGNAL(hitChanged(int)), this, SLOT(selectCommand(int)));
+    connect(&fCanvasWidget, SIGNAL(hitChanged(int)), &fSettingsWidget, SLOT(updateHit(int)));
+    connect(&fCanvasWidget, SIGNAL(scaleFactorChanged(float)), this, SLOT(actionScale(float)));
+    connect(&fCanvasWidget, SIGNAL(commandChanged(int)), &fSettingsWidget, SLOT(updateCommand(int)));
+    connect(&fActionSaveAs, SIGNAL(triggered()), this, SLOT(actionSaveAs()));
+    connect(&fActionSave, SIGNAL(triggered()), this, SLOT(actionSave()));
+
+    fMapper.setMapping(&fActionZoomIn, 1);
+    fMapper.setMapping(&fActionZoomOut, -1);
+
+    connect(&fActionZoomIn, SIGNAL(triggered()), &fMapper, SLOT(map()));
+    connect(&fActionZoomOut, SIGNAL(triggered()), &fMapper, SLOT(map()));
+    connect(&fMapper, SIGNAL(mapped(int)), &fCanvasWidget, SLOT(keyZoom(int)));
+
+    fInspectorWidget.setDisabled(true);
+    fMenuEdit.setDisabled(true);
+    fMenuNavigate.setDisabled(true);
+    fMenuView.setDisabled(true);
+
+    SkGraphics::Init();
+}
+
+SkDebuggerGUI::~SkDebuggerGUI() {
+    SkGraphics::Term();
+}
+
+void SkDebuggerGUI::actionBreakpoints() {
+    fBreakpointsActivated = !fBreakpointsActivated;
+    for (int row = 0; row < fListWidget.count(); row++) {
+        QListWidgetItem *item = fListWidget.item(row);
+        item->setHidden(item->checkState() == Qt::Unchecked && fBreakpointsActivated);
+    }
+}
+
+void SkDebuggerGUI::showDeletes() {
+    fDeletesActivated = !fDeletesActivated;
+    for (int row = 0; row < fListWidget.count(); row++) {
+        QListWidgetItem *item = fListWidget.item(row);
+        item->setHidden(fDebugger.isCommandVisible(row)
+                && fDeletesActivated);
+    }
+}
+
+void SkDebuggerGUI::actionCancel() {
+    for (int row = 0; row < fListWidget.count(); row++) {
+        fListWidget.item(row)->setHidden(false);
+    }
+}
+
+void SkDebuggerGUI::actionClearBreakpoints() {
+    for (int row = 0; row < fListWidget.count(); row++) {
+        QListWidgetItem* item = fListWidget.item(row);
+        item->setCheckState(Qt::Unchecked);
+        item->setData(Qt::DecorationRole,
+                QPixmap(":/images/Icons/blank.png"));
+    }
+}
+
+void SkDebuggerGUI::actionClearDeletes() {
+    for (int row = 0; row < fListWidget.count(); row++) {
+        QListWidgetItem* item = fListWidget.item(row);
+        item->setData(Qt::UserRole + 2, QPixmap(":/images/Icons/blank.png"));
+        fDebugger.setCommandVisible(row, true);
+    }
+    if (fPause) {
+        fCanvasWidget.drawTo(fPausedRow);
+    } else {
+        fCanvasWidget.drawTo(fListWidget.currentRow());
+    }
+}
+
+void SkDebuggerGUI::actionCommandFilter() {
+    fDebugger.highlightCurrentCommand(
+            fSettingsWidget.getVisibilityButton()->isChecked());
+    fCanvasWidget.drawTo(fListWidget.currentRow());
+}
+
+void SkDebuggerGUI::actionClose() {
+    this->close();
+}
+
+void SkDebuggerGUI::actionDelete() {
+    int currentRow = fListWidget.currentRow();
+    QListWidgetItem* item = fListWidget.currentItem();
+
+    if (fDebugger.isCommandVisible(currentRow)) {
+        item->setData(Qt::UserRole + 2, QPixmap(":/images/Icons/delete.png"));
+        fDebugger.setCommandVisible(currentRow, false);
+    } else {
+        item->setData(Qt::UserRole + 2, QPixmap(":/images/Icons/blank.png"));
+        fDebugger.setCommandVisible(currentRow, true);
+    }
+
+    if (fPause) {
+        fCanvasWidget.drawTo(fPausedRow);
+    } else {
+        fCanvasWidget.drawTo(currentRow);
+    }
+}
+
+void SkDebuggerGUI::actionGLWidget(bool isToggled) {
+    fCanvasWidget.setWidgetVisibility(SkCanvasWidget::kGPU_WidgetType, !isToggled);
+}
+
+void SkDebuggerGUI::actionInspector() {
+    if (fInspectorWidget.isHidden()) {
+        fInspectorWidget.setHidden(false);
+    } else {
+        fInspectorWidget.setHidden(true);
+    }
+}
+
+void SkDebuggerGUI::actionPlay() {
+    for (int row = fListWidget.currentRow() + 1; row < fListWidget.count();
+            row++) {
+        QListWidgetItem *item = fListWidget.item(row);
+        if (item->checkState() == Qt::Checked) {
+            fListWidget.setCurrentItem(item);
+            return;
+        }
+    }
+    fListWidget.setCurrentRow(fListWidget.count() - 1);
+}
+
+void SkDebuggerGUI::actionRasterWidget(bool isToggled) {
+    fCanvasWidget.setWidgetVisibility(SkCanvasWidget::kRaster_8888_WidgetType, !isToggled);
+}
+
+void SkDebuggerGUI::actionRewind() {
+    fListWidget.setCurrentRow(0);
+}
+
+void SkDebuggerGUI::actionSave() {
+    QString filename;
+    filename.append(fPath);
+    filename.append("/");
+    filename.append(fDirectoryWidget.currentItem()->text());
+    saveToFile(filename);
+}
+
+void SkDebuggerGUI::actionSaveAs() {
+    QString filename = QFileDialog::getSaveFileName(this, "Save File", "",
+            "Skia Picture (*skp)");
+    if (!filename.endsWith(".skp", Qt::CaseInsensitive)) {
+        filename.append(".skp");
+    }
+    saveToFile(filename);
+}
+
+void SkDebuggerGUI::actionScale(float scaleFactor) {
+    fSettingsWidget.setZoomText(scaleFactor);
+}
+
+void SkDebuggerGUI::actionSettings() {
+    if (fSettingsWidget.isHidden()) {
+        fSettingsWidget.setHidden(false);
+    } else {
+        fSettingsWidget.setHidden(true);
+    }
+}
+
+void SkDebuggerGUI::actionStepBack() {
+    int currentRow = fListWidget.currentRow();
+    if (currentRow != 0) {
+        fListWidget.setCurrentRow(currentRow - 1);
+    }
+}
+
+void SkDebuggerGUI::actionStepForward() {
+    int currentRow = fListWidget.currentRow();
+    QString curRow = QString::number(currentRow);
+    QString curCount = QString::number(fListWidget.count());
+    if (currentRow < fListWidget.count() - 1) {
+        fListWidget.setCurrentRow(currentRow + 1);
+    }
+}
+
+void SkDebuggerGUI::drawComplete() {
+    fInspectorWidget.setMatrix(fDebugger.getCurrentMatrix());
+    fInspectorWidget.setClip(fDebugger.getCurrentClip());
+}
+
+void SkDebuggerGUI::saveToFile(QString filename) {
+    SkFILEWStream file(filename.toAscii());
+    fDebugger.makePicture()->serialize(&file);
+}
+
+void SkDebuggerGUI::loadFile(QListWidgetItem *item) {
+    if (fDirectoryWidgetActive) {
+        QString fileName;
+        fileName.append(fPath);
+        fileName.append("/");
+        fileName.append(item->text());
+        loadPicture(fileName);
+    }
+}
+
+void SkDebuggerGUI::openFile() {
+    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
+            tr("Files (*.*)"));
+    fDirectoryWidgetActive = false;
+    if (!fileName.isNull()) {
+        QFileInfo pathInfo(fileName);
+        fPath = pathInfo.path();
+        loadPicture(fileName);
+        setupDirectoryWidget();
+    }
+    fDirectoryWidgetActive = true;
+}
+
+void SkDebuggerGUI::pauseDrawing(bool isPaused) {
+    fPause = isPaused;
+    fPausedRow = fListWidget.currentRow();
+    fCanvasWidget.drawTo(fPausedRow);
+}
+
+void SkDebuggerGUI::registerListClick(QListWidgetItem *item) {
+    if(!fLoading) {
+        int currentRow = fListWidget.currentRow();
+
+        if (currentRow != -1) {
+            if (!fPause) {
+                fCanvasWidget.drawTo(currentRow);
+            }
+            SkTDArray<SkString*> *currInfo = fDebugger.getCommandInfo(
+                    currentRow);
+
+            /* TODO(chudy): Add command type before parameters. Rename v
+             * to something more informative. */
+            if (currInfo) {
+                QString info;
+                info.append("<b>Parameters: </b><br/>");
+                for (int i = 0; i < currInfo->count(); i++) {
+
+                    info.append(QString((*currInfo)[i]->c_str()));
+                    info.append("<br/>");
+                }
+                fInspectorWidget.setText(info, SkInspectorWidget::kDetail_TabType);
+                fInspectorWidget.setDisabled(false);
+            }
+        }
+
+    }
+}
+
+void SkDebuggerGUI::selectCommand(int command) {
+    if (fPause) {
+        fListWidget.setCurrentRow(command);
+    }
+}
+
+void SkDebuggerGUI::toggleBreakpoint() {
+    QListWidgetItem* item = fListWidget.currentItem();
+    if (item->checkState() == Qt::Unchecked) {
+        item->setCheckState(Qt::Checked);
+        item->setData(Qt::DecorationRole,
+                QPixmap(":/images/Icons/breakpoint_16x16.png"));
+    } else {
+        item->setCheckState(Qt::Unchecked);
+        item->setData(Qt::DecorationRole,
+                QPixmap(":/images/Icons/blank.png"));
+    }
+}
+
+void SkDebuggerGUI::toggleDirectory() {
+    fDirectoryWidget.setHidden(!fDirectoryWidget.isHidden());
+}
+
+void SkDebuggerGUI::toggleFilter(QString string) {
+    for (int row = 0; row < fListWidget.count(); row++) {
+        QListWidgetItem *item = fListWidget.item(row);
+        item->setHidden(item->text() != string);
+    }
+}
+
+void SkDebuggerGUI::setupUi(QMainWindow *SkDebuggerGUI) {
+    QIcon windowIcon;
+    windowIcon.addFile(QString::fromUtf8(":/images/Icons/skia.png"), QSize(),
+            QIcon::Normal, QIcon::Off);
+    SkDebuggerGUI->setObjectName(QString::fromUtf8("SkDebuggerGUI"));
+    SkDebuggerGUI->resize(1200, 1000);
+    SkDebuggerGUI->setWindowIcon(windowIcon);
+    SkDebuggerGUI->setWindowTitle("Skia Debugger");
+
+    fActionOpen.setShortcuts(QKeySequence::Open);
+    fActionOpen.setText("Open");
+
+    QIcon breakpoint;
+    breakpoint.addFile(QString::fromUtf8(":/images/Icons/breakpoint.png"),
+            QSize(), QIcon::Normal, QIcon::Off);
+    fActionBreakpoint.setShortcut(QKeySequence(tr("Ctrl+B")));
+    fActionBreakpoint.setIcon(breakpoint);
+    fActionBreakpoint.setText("Breakpoints");
+
+    QIcon cancel;
+    cancel.addFile(QString::fromUtf8(":/images/Ico/reload.png"), QSize(),
+            QIcon::Normal, QIcon::Off);
+    fActionCancel.setIcon(cancel);
+    fActionCancel.setText("Clear Filter");
+
+    fActionClearBreakpoints.setShortcut(QKeySequence(tr("Alt+B")));
+    fActionClearBreakpoints.setText("Clear Breakpoints");
+
+    fActionClearDeletes.setShortcut(QKeySequence(tr("Alt+X")));
+    fActionClearDeletes.setText("Clear Deletes");
+
+    fActionClose.setShortcuts(QKeySequence::Quit);
+    fActionClose.setText("Exit");
+
+    fActionCreateBreakpoint.setShortcut(QKeySequence(tr("B")));
+    fActionCreateBreakpoint.setText("Set Breakpoint");
+
+    fActionDelete.setShortcut(QKeySequence(tr("X")));
+    fActionDelete.setText("Delete Command");
+
+    fActionDirectory.setShortcut(QKeySequence(tr("Ctrl+D")));
+    fActionDirectory.setText("Directory");
+
+    QIcon inspector;
+    inspector.addFile(QString::fromUtf8(":/images/Ico/inspector.png"),
+            QSize(), QIcon::Normal, QIcon::Off);
+    fActionInspector.setShortcut(QKeySequence(tr("Ctrl+I")));
+    fActionInspector.setIcon(inspector);
+    fActionInspector.setText("Inspector");
+
+    QIcon play;
+    play.addFile(QString::fromUtf8(":/images/Ico/play.png"), QSize(),
+            QIcon::Normal, QIcon::Off);
+    fActionPlay.setShortcut(QKeySequence(tr("Ctrl+P")));
+    fActionPlay.setIcon(play);
+    fActionPlay.setText("Play");
+
+    QIcon pause;
+    pause.addFile(QString::fromUtf8(":/images/Ico/pause.png"), QSize(),
+            QIcon::Normal, QIcon::Off);
+    fActionPause.setShortcut(QKeySequence(tr("Space")));
+    fActionPause.setCheckable(true);
+    fActionPause.setIcon(pause);
+    fActionPause.setText("Pause");
+
+    QIcon rewind;
+    rewind.addFile(QString::fromUtf8(":/images/Ico/rewind.png"), QSize(),
+            QIcon::Normal, QIcon::Off);
+    fActionRewind.setShortcut(QKeySequence(tr("Ctrl+R")));
+    fActionRewind.setIcon(rewind);
+    fActionRewind.setText("Rewind");
+
+    fActionSave.setShortcut(QKeySequence::Save);
+    fActionSave.setText("Save");
+    fActionSave.setDisabled(true);
+    fActionSaveAs.setShortcut(QKeySequence::SaveAs);
+    fActionSaveAs.setText("Save As");
+    fActionSaveAs.setDisabled(true);
+
+    fActionShowDeletes.setShortcut(QKeySequence(tr("Ctrl+X")));
+    fActionShowDeletes.setText("Deleted Commands");
+
+    QIcon stepBack;
+    stepBack.addFile(QString::fromUtf8(":/images/Ico/previous.png"), QSize(),
+            QIcon::Normal, QIcon::Off);
+    fActionStepBack.setShortcut(QKeySequence(tr("[")));
+    fActionStepBack.setIcon(stepBack);
+    fActionStepBack.setText("Step Back");
+
+    QIcon stepForward;
+    stepForward.addFile(QString::fromUtf8(":/images/Ico/next.png"),
+            QSize(), QIcon::Normal, QIcon::Off);
+    fActionStepForward.setShortcut(QKeySequence(tr("]")));
+    fActionStepForward.setIcon(stepForward);
+    fActionStepForward.setText("Step Forward");
+
+    fActionZoomIn.setShortcut(QKeySequence(tr("Ctrl+=")));
+    fActionZoomIn.setText("Zoom In");
+    fActionZoomOut.setShortcut(QKeySequence(tr("Ctrl+-")));
+    fActionZoomOut.setText("Zoom Out");
+
+    fListWidget.setItemDelegate(new SkListWidget(&fListWidget));
+    fListWidget.setObjectName(QString::fromUtf8("listWidget"));
+    fListWidget.setMaximumWidth(250);
+
+    fFilter.addItem("--Filter By Available Commands--");
+
+    fDirectoryWidget.setMaximumWidth(250);
+    fDirectoryWidget.setStyleSheet("QListWidget::Item {padding: 5px;}");
+
+    fCanvasWidget.setSizePolicy(QSizePolicy::Expanding,
+            QSizePolicy::Expanding);
+
+    fInspectorWidget.setSizePolicy(QSizePolicy::Expanding,
+            QSizePolicy::Expanding);
+    fInspectorWidget.setMaximumHeight(300);
+
+    fSettingsWidget.setSizePolicy(QSizePolicy::Expanding,
+            QSizePolicy::Expanding);
+    fSettingsWidget.setMaximumWidth(250);
+
+    fLeftColumnLayout.setSpacing(6);
+    fLeftColumnLayout.addWidget(&fListWidget);
+    fLeftColumnLayout.addWidget(&fDirectoryWidget);
+
+    fCanvasAndSettingsLayout.setSpacing(6);
+    fCanvasAndSettingsLayout.addWidget(&fCanvasWidget);
+    fCanvasAndSettingsLayout.addWidget(&fSettingsWidget);
+
+    fMainAndRightColumnLayout.setSpacing(6);
+    fMainAndRightColumnLayout.addLayout(&fCanvasAndSettingsLayout);
+    fMainAndRightColumnLayout.addWidget(&fInspectorWidget);
+
+    fCentralWidget.setLayout(&fContainerLayout);
+    fContainerLayout.setSpacing(6);
+    fContainerLayout.setContentsMargins(11, 11, 11, 11);
+    fContainerLayout.addLayout(&fLeftColumnLayout);
+    fContainerLayout.addLayout(&fMainAndRightColumnLayout);
+
+    SkDebuggerGUI->setCentralWidget(&fCentralWidget);
+    SkDebuggerGUI->setStatusBar(&fStatusBar);
+
+    fToolBar.setIconSize(QSize(32, 32));
+    fToolBar.setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
+    SkDebuggerGUI->addToolBar(Qt::TopToolBarArea, &fToolBar);
+
+    fSpacer.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+
+    fToolBar.addAction(&fActionRewind);
+    fToolBar.addAction(&fActionStepBack);
+    fToolBar.addAction(&fActionPause);
+    fToolBar.addAction(&fActionStepForward);
+    fToolBar.addAction(&fActionPlay);
+    fToolBar.addSeparator();
+    fToolBar.addAction(&fActionInspector);
+    fToolBar.addSeparator();
+    fToolBar.addWidget(&fSpacer);
+    fToolBar.addWidget(&fFilter);
+    fToolBar.addAction(&fActionCancel);
+
+    // TODO(chudy): Remove static call.
+    fDirectoryWidgetActive = false;
+    fPath = "/usr/local/google/home/chudy/trunk-git/trunk/skp";
+    setupDirectoryWidget();
+    fDirectoryWidgetActive = true;
+
+    // Menu Bar
+    fMenuFile.setTitle("File");
+    fMenuFile.addAction(&fActionOpen);
+    fMenuFile.addAction(&fActionSave);
+    fMenuFile.addAction(&fActionSaveAs);
+    fMenuFile.addAction(&fActionClose);
+
+    fMenuEdit.setTitle("Edit");
+    fMenuEdit.addAction(&fActionDelete);
+    fMenuEdit.addAction(&fActionClearDeletes);
+    fMenuEdit.addSeparator();
+    fMenuEdit.addAction(&fActionCreateBreakpoint);
+    fMenuEdit.addAction(&fActionClearBreakpoints);
+
+    fMenuNavigate.setTitle("Navigate");
+    fMenuNavigate.addAction(&fActionRewind);
+    fMenuNavigate.addAction(&fActionStepBack);
+    fMenuNavigate.addAction(&fActionStepForward);
+    fMenuNavigate.addAction(&fActionPlay);
+    fMenuNavigate.addAction(&fActionPause);
+    fMenuNavigate.addAction(&fActionGoToLine);
+
+    fMenuView.setTitle("View");
+    fMenuView.addAction(&fActionBreakpoint);
+    fMenuView.addAction(&fActionShowDeletes);
+    fMenuView.addAction(&fActionZoomIn);
+    fMenuView.addAction(&fActionZoomOut);
+
+    fMenuWindows.setTitle("Window");
+    fMenuWindows.addAction(&fActionInspector);
+    fMenuWindows.addAction(&fActionDirectory);
+
+    fActionGoToLine.setText("Go to Line...");
+    fActionGoToLine.setDisabled(true);
+    fMenuBar.addAction(fMenuFile.menuAction());
+    fMenuBar.addAction(fMenuEdit.menuAction());
+    fMenuBar.addAction(fMenuView.menuAction());
+    fMenuBar.addAction(fMenuNavigate.menuAction());
+    fMenuBar.addAction(fMenuWindows.menuAction());
+
+    fPause = false;
+
+    SkDebuggerGUI->setMenuBar(&fMenuBar);
+    QMetaObject::connectSlotsByName(SkDebuggerGUI);
+}
+
+void SkDebuggerGUI::setupDirectoryWidget() {
+    QDir dir(fPath);
+    QRegExp r(".skp");
+    fDirectoryWidget.clear();
+    const QStringList files = dir.entryList();
+    foreach (QString f, files) {
+        if (f.contains(r))
+            fDirectoryWidget.addItem(f);
+    }
+}
+
+void SkDebuggerGUI::loadPicture(QString fileName) {
+    fLoading = true;
+    SkStream* stream = new SkFILEStream(fileName.toAscii());
+    SkPicture* picture = new SkPicture(stream);
+    fCanvasWidget.resetWidgetTransform();
+    fDebugger.loadPicture(picture);
+
+    SkSafeUnref(stream);
+    SkSafeUnref(picture);
+
+    // Will this automatically clear out due to nature of refcnt?
+    SkTDArray<SkString*>* commands = fDebugger.getDrawCommands();
+
+    /* fDebugCanvas is reinitialized every load picture. Need it to retain value
+     * of the visibility filter.
+     * TODO(chudy): This should be deprecated since fDebugger is not
+     * recreated.
+     * */
+    fDebugger.highlightCurrentCommand(fSettingsWidget.getVisibilityButton()->isChecked());
+
+    setupListWidget(commands);
+    setupComboBox(commands);
+    fInspectorWidget.setDisabled(false);
+    fSettingsWidget.setDisabled(false);
+    fMenuEdit.setDisabled(false);
+    fMenuNavigate.setDisabled(false);
+    fMenuView.setDisabled(false);
+    fActionSave.setDisabled(false);
+    fActionSaveAs.setDisabled(false);
+    fLoading = false;
+    actionPlay();
+}
+
+void SkDebuggerGUI::setupListWidget(SkTDArray<SkString*>* command) {
+    fListWidget.clear();
+    int counter = 0;
+    for (int i = 0; i < command->count(); i++) {
+        QListWidgetItem *item = new QListWidgetItem();
+        item->setData(Qt::DisplayRole, (*command)[i]->c_str());
+        item->setData(Qt::UserRole + 1, counter++);
+        fListWidget.addItem(item);
+    }
+}
+
+void SkDebuggerGUI::setupComboBox(SkTDArray<SkString*>* command) {
+    fFilter.clear();
+    fFilter.addItem("--Filter By Available Commands--");
+
+    std::map<std::string, int> map;
+    for (int i = 0; i < command->count(); i++) {
+        map[(*command)[i]->c_str()]++;
+    }
+
+    QString overview;
+    int counter = 0;
+    for (std::map<std::string, int>::iterator it = map.begin(); it != map.end();
+            ++it) {
+        overview.append((it->first).c_str());
+        overview.append(": ");
+        overview.append(QString::number(it->second));
+        overview.append("<br/>");
+        counter += it->second;
+        fFilter.addItem((it->first).c_str());
+    }
+    QString total;
+    total.append("Total Draw Commands: ");
+    total.append(QString::number(counter));
+    total.append("<br/>");
+    overview.insert(0, total);
+
+    overview.append("<br/>");
+    overview.append("SkPicture Width: ");
+    // NOTE(chudy): This is where we can pull out the SkPictures width.
+    overview.append(QString::number(fDebugger.pictureWidth()));
+    overview.append("px<br/>");
+    overview.append("SkPicture Height: ");
+    overview.append(QString::number(fDebugger.pictureHeight()));
+    overview.append("px");
+    fInspectorWidget.setText(overview, SkInspectorWidget::kOverview_TabType);
+
+    // NOTE(chudy): Makes first item unselectable.
+    QStandardItemModel* model = qobject_cast<QStandardItemModel*>(
+            fFilter.model());
+    QModelIndex firstIndex = model->index(0, fFilter.modelColumn(),
+            fFilter.rootModelIndex());
+    QStandardItem* firstItem = model->itemFromIndex(firstIndex);
+    firstItem->setSelectable(false);
+}
diff --git a/debugger/QT/SkDebuggerGUI.h b/debugger/QT/SkDebuggerGUI.h
new file mode 100644
index 0000000..f460067
--- /dev/null
+++ b/debugger/QT/SkDebuggerGUI.h
@@ -0,0 +1,295 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKDEBUGGERUI_H
+#define SKDEBUGGERUI_H
+
+
+#include "SkCanvas.h"
+#include "SkCanvasWidget.h"
+#include "SkDebugger.h"
+#include "SkGLWidget.h"
+#include "SkListWidget.h"
+#include "SkInspectorWidget.h"
+#include "SkRasterWidget.h"
+#include "SkSettingsWidget.h"
+#include <QtCore/QVariant>
+#include <QtGui/QAction>
+#include <QtGui/QApplication>
+#include <QtGui/QButtonGroup>
+#include <QtGui/QHBoxLayout>
+#include <QtGui/QHeaderView>
+#include <QtGui/QListView>
+#include <QtGui/QListWidget>
+#include <QtGui/QMainWindow>
+#include <QtGui/QStatusBar>
+#include <QtGui/QToolBar>
+#include <QtGui/QVBoxLayout>
+#include <QtGui/QWidget>
+#include <QtGui/QMenu>
+#include <QtGui/QMenuBar>
+#include <vector>
+
+/** \class SkDebuggerGUI
+
+    Container for the UI and it's functions.
+ */
+class SkDebuggerGUI : public QMainWindow {
+    Q_OBJECT
+
+public:
+    /**
+        Constructs the view of the application.
+        @param parent  The parent container of this widget.
+     */
+    SkDebuggerGUI(QWidget *parent = 0);
+
+    ~SkDebuggerGUI();
+
+signals:
+    void commandChanged(int command);
+
+private slots:
+    /**
+        Toggles breakpoint view in the list widget.
+     */
+    void actionBreakpoints();
+
+    /**
+        Cancels the command filter in the list widget.
+     */
+    void actionCancel();
+
+    /**
+        Clears the breakpoint state off of all commands marked as breakpoints.
+     */
+    void actionClearBreakpoints();
+
+    /**
+        Clears the deleted state off of all commands marked as deleted.
+     */
+    void actionClearDeletes();
+
+    /**
+        Applies a visible filter to all drawing commands other than the previous.
+     */
+    void actionCommandFilter();
+
+    /**
+        Closes the application.
+     */
+    void actionClose();
+
+    /**
+        Deletes the command in question.
+     */
+    void actionDelete();
+
+    /**
+        Toggles the visibility of the GL canvas widget.
+     */
+    void actionGLWidget(bool isToggled);
+
+    /**
+        Toggles the visibility of the inspector widget.
+     */
+    void actionInspector();
+
+    /**
+        Plays from the current step to the next breakpoint if it exists, otherwise
+        executes all remaining draw commands.
+     */
+    void actionPlay();
+
+    /**
+        Toggles the visibility of the raster canvas widget.
+     */
+    void actionRasterWidget(bool isToggled);
+
+    /**
+        Rewinds from the current step back to the start of the commands.
+     */
+    void actionRewind();
+
+    /**
+        Saves the current SKP with all modifications.
+     */
+    void actionSave();
+
+    /**
+        Saves the current SKP under a different name and/or location.
+     */
+    void actionSaveAs();
+
+    /**
+        Sends the scale factor information to the settings widget.
+     */
+    void actionScale(float scaleFactor);
+
+    /**
+        Toggles the settings widget visibility.
+     */
+    void actionSettings();
+
+    /**
+        Steps forward to the next draw command.
+     */
+    void actionStepBack();
+
+    /**
+        Steps backwards to the next draw command.
+     */
+    void actionStepForward();
+
+    /**
+        Called when the canvas is done being drawn to by SkCanvasWidget.
+     */
+    void drawComplete();
+
+    /**
+        Loads an skpicture selected from the directory.
+     */
+    void loadFile(QListWidgetItem *item);
+
+    /**
+        Toggles a dialog with a file browser for navigating to a skpicture. Loads
+        the seleced file.
+     */
+    void openFile();
+
+    /**
+        Toggles whether drawing to a new command requires a double click
+        or simple focus.
+     */
+    void pauseDrawing(bool isPaused = true);
+
+    /**
+        Executes draw commands up to the selected command
+     */
+    void registerListClick(QListWidgetItem *item);
+
+    /**
+        Sets the command to active in the list widget.
+     */
+    void selectCommand(int command);
+
+    /**
+        Toggles the exclusive listing of commands set as deleted.
+     */
+    void showDeletes();
+
+    /**
+        Toggles a breakpoint on the current step in the list widget.
+     */
+    void toggleBreakpoint();
+
+    /**
+        Toggles the visibility of the directory widget.
+     */
+    void toggleDirectory();
+
+    /**
+        Filters the list widgets command visibility based on the currently
+        active selection.
+     */
+    void toggleFilter(QString string);
+
+private:
+    QWidget fCentralWidget;
+    QStatusBar fStatusBar;
+    QToolBar fToolBar;
+
+    QAction fActionOpen;
+    QAction fActionBreakpoint;
+    QAction fActionCancel;
+    QAction fActionClearBreakpoints;
+    QAction fActionClearDeletes;
+    QAction fActionClose;
+    QAction fActionCreateBreakpoint;
+    QAction fActionDelete;
+    QAction fActionDirectory;
+    QAction fActionGoToLine;
+    QAction fActionInspector;
+    QAction fActionPlay;
+    QAction fActionPause;
+    QAction fActionRewind;
+    QAction fActionSave;
+    QAction fActionSaveAs;
+    QAction fActionShowDeletes;
+    QAction fActionStepBack;
+    QAction fActionStepForward;
+    QAction fActionZoomIn;
+    QAction fActionZoomOut;
+    QSignalMapper fMapper;
+
+    QWidget fSpacer;
+    QComboBox fFilter;
+
+    QHBoxLayout fContainerLayout;
+    QVBoxLayout fLeftColumnLayout;
+    QVBoxLayout fMainAndRightColumnLayout;
+    QHBoxLayout fCanvasAndSettingsLayout;
+
+    QListWidget fListWidget;
+    QListWidget fDirectoryWidget;
+
+    SkDebugger fDebugger;
+    SkCanvasWidget fCanvasWidget;
+    SkInspectorWidget fInspectorWidget;
+    SkSettingsWidget fSettingsWidget;
+
+    QString fPath;
+    bool fDirectoryWidgetActive;
+
+    QMenuBar fMenuBar;
+    QMenu fMenuFile;
+    QMenu fMenuEdit;
+    QMenu fMenuNavigate;
+    QMenu fMenuView;
+    QMenu fMenuWindows;
+
+    bool fBreakpointsActivated;
+    bool fDeletesActivated;
+    bool fPause;
+    bool fLoading;
+    int fPausedRow;
+
+    /**
+        Creates the entire UI.
+     */
+    void setupUi(QMainWindow *SkDebuggerGUI);
+
+    /**
+        Pipes a QString in with the location of the filename, proceeds to updating
+        the listwidget, combowidget and inspectorwidget.
+     */
+    void loadPicture(QString fileName);
+
+    /**
+        Creates a picture of the current canvas.
+     */
+    void saveToFile(QString filename);
+
+    /**
+        Populates the list widget with the vector of strings passed in.
+     */
+    void setupListWidget(SkTDArray<SkString*>* command);
+
+    /**
+        Populates the combo box widget with the vector of strings passed in.
+     */
+    void setupComboBox(SkTDArray<SkString*>* command);
+
+    /**
+        Updates the directory widget with the latest directory path stored in
+        the global class variable fPath.
+     */
+    void setupDirectoryWidget();
+};
+
+#endif // SKDEBUGGERUI_H
diff --git a/debugger/QT/SkGLWidget.cpp b/debugger/QT/SkGLWidget.cpp
new file mode 100644
index 0000000..a9a625a
--- /dev/null
+++ b/debugger/QT/SkGLWidget.cpp
@@ -0,0 +1,72 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkGLWidget.h"
+
+SkGLWidget::SkGLWidget(SkDebugger* debugger) : QGLWidget() {
+    this->setStyleSheet("QWidget {background-color: white; border: 1px solid #cccccc;}");
+    fDebugger = debugger;
+    fCurIntf = NULL;
+    fCurContext = NULL;
+    fGpuDevice = NULL;
+    fCanvas = NULL;
+}
+
+SkGLWidget::~SkGLWidget() {
+    SkSafeUnref(fCurIntf);
+    SkSafeUnref(fCurContext);
+    SkSafeUnref(fGpuDevice);
+    SkSafeUnref(fCanvas);
+}
+
+void SkGLWidget::initializeGL() {
+    fCurIntf = GrGLCreateNativeInterface();
+    fCurContext = GrContext::Create(kOpenGL_Shaders_GrEngine, (GrPlatform3DContext) fCurIntf);
+    GrRenderTarget* curRenderTarget = fCurContext->createPlatformRenderTarget(getDesc(this->width(), this->height()));
+    fGpuDevice = new SkGpuDevice(fCurContext, curRenderTarget);
+    fCanvas = new SkCanvas(fGpuDevice);
+    curRenderTarget->unref();
+
+    glClearColor(1, 1, 1, 0);
+    glClearStencil(0);
+    glClear(GL_STENCIL_BUFFER_BIT);
+}
+
+void SkGLWidget::resizeGL(int w, int h) {
+    GrRenderTarget* curRenderTarget = fCurContext->createPlatformRenderTarget(getDesc(w,h));
+    SkSafeUnref(fGpuDevice);
+    SkSafeUnref(fCanvas);
+    fGpuDevice = new SkGpuDevice(fCurContext, curRenderTarget);
+    fCanvas = new SkCanvas(fGpuDevice);
+    fDebugger->resize(w, h);
+    draw();
+}
+
+void SkGLWidget::paintGL() {
+    if (!this->isHidden()) {
+        fDebugger->draw(fCanvas);
+        // TODO(chudy): Implement an optional flush button in Gui.
+        fCanvas->flush();
+        emit drawComplete();
+    }
+}
+
+GrPlatformRenderTargetDesc SkGLWidget::getDesc(int w, int h) {
+    GrPlatformRenderTargetDesc desc;
+    desc.fWidth = SkScalarRound(this->width());
+    desc.fHeight = SkScalarRound(this->height());
+    desc.fConfig = kSkia8888_PM_GrPixelConfig;
+    GR_GL_GetIntegerv(fCurIntf, GR_GL_SAMPLES, &desc.fSampleCnt);
+    GR_GL_GetIntegerv(fCurIntf, GR_GL_STENCIL_BITS, &desc.fStencilBits);
+    GrGLint buffer;
+    GR_GL_GetIntegerv(fCurIntf, GR_GL_FRAMEBUFFER_BINDING, &buffer);
+    desc.fRenderTargetHandle = buffer;
+
+    return desc;
+}
diff --git a/debugger/QT/SkGLWidget.h b/debugger/QT/SkGLWidget.h
new file mode 100644
index 0000000..e31d09c
--- /dev/null
+++ b/debugger/QT/SkGLWidget.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 SKGLWIDGET_H_
+#define SKGLWIDGET_H_
+
+#include <QtOpenGL/QGLWidget>
+#include "SkDebugCanvas.h"
+#include "SkDebugger.h"
+#include "SkDevice.h"
+#include "SkGpuDevice.h"
+
+#include "GrContext.h"
+#include "gl/GrGLInterface.h"
+#include "gl/GrGLUtil.h"
+#include "GrRenderTarget.h"
+
+class SkGLWidget : public QGLWidget {
+Q_OBJECT
+
+public:
+    SkGLWidget(SkDebugger* debugger);
+
+    ~SkGLWidget();
+
+    void draw() {
+        this->updateGL();
+    }
+
+signals:
+    void drawComplete();
+
+protected:
+    void initializeGL();
+    void resizeGL(int w, int h);
+    void paintGL();
+
+
+private:
+    const GrGLInterface* fCurIntf;
+    GrContext* fCurContext;
+    SkGpuDevice* fGpuDevice;
+    SkCanvas* fCanvas;
+    SkDebugger* fDebugger;
+    GrPlatformRenderTargetDesc getDesc(int w, int h);
+};
+
+#endif /* SKGLWIDGET_H_ */
diff --git a/debugger/QT/SkIcons.qrc b/debugger/QT/SkIcons.qrc
new file mode 100644
index 0000000..53f456f
--- /dev/null
+++ b/debugger/QT/SkIcons.qrc
@@ -0,0 +1,18 @@
+<RCC>
+    <qresource prefix="/images">
+        <file>Icons/package-br32.png</file>
+        <file>Icons/drawer-open-icon.png</file>
+        <file>Icons/rewind.png</file>
+        <file>Icons/back.png</file>
+        <file>Icons/go-next.png</file>
+        <file>Icons/play.png</file>
+        <file>Icons/breakpoint.png</file>
+        <file>Icons/breakpoint_16x16.png</file>
+        <file>Icons/inspector.png</file>
+        <file>Icons/reset.png</file>
+        <file>Icons/skia.png</file>
+        <file>Icons/delete.png</file>
+        <file>Icons/reload.png</file>
+        <file>Icons/settings.png</file>
+    </qresource>
+</RCC>
diff --git a/debugger/QT/SkInspectorWidget.cpp b/debugger/QT/SkInspectorWidget.cpp
new file mode 100644
index 0000000..6cf1212
--- /dev/null
+++ b/debugger/QT/SkInspectorWidget.cpp
@@ -0,0 +1,114 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkInspectorWidget.h"
+#include <iostream>
+
+SkInspectorWidget::SkInspectorWidget() : QWidget()
+    , fHorizontalLayout(this)
+    , fMatrixAndClipWidget(this)
+    , fVerticalLayout(&fMatrixAndClipWidget)
+    , fMatrixLabel(this)
+    , fClipLabel(this) {
+
+    fHorizontalLayout.setSpacing(6);
+    fHorizontalLayout.setContentsMargins(11, 11, 11, 11);
+
+    QString tabNames[kTotalTabCount];
+    tabNames[kOverview_TabType] = "Overview";
+    tabNames[kDetail_TabType] = "Details";
+
+    for (int i = 0; i < kTotalTabCount; i++) {
+        fTabTexts[i].setReadOnly(true);
+        fTabLayouts[i].setSpacing(6);
+        fTabLayouts[i].setContentsMargins(11, 11, 11, 11);
+        fTabLayouts[i].addWidget(&fTabTexts[i]);
+        fTabs[i].setLayout(&fTabLayouts[i]);
+        fTabWidget.addTab(&fTabs[i], tabNames[i]);
+    }
+
+    fHorizontalLayout.setAlignment(Qt::AlignTop);
+    fHorizontalLayout.addWidget(&fTabWidget);
+
+    /* NOTE(chudy): We add all the line edits to (this). Then we lay them out
+     * by adding them to horizontal layouts.
+     *
+     * We will have 1 big vertical layout, 3 horizontal layouts and then 3
+     * line edits in each horizontal layout. */
+    fMatrixAndClipWidget.setFixedSize(260,300);
+    fMatrixAndClipWidget.setDisabled(true);
+
+    fVerticalLayout.setAlignment(Qt::AlignVCenter);
+    fVerticalLayout.addLayout(setupMatrix());
+    fVerticalLayout.addLayout(setupClip());
+    fHorizontalLayout.addWidget(&fMatrixAndClipWidget);
+}
+
+void SkInspectorWidget::setText(QString text, TabType type) {
+    fTabTexts[type].setHtml(text);
+}
+
+void SkInspectorWidget::setMatrix(const SkMatrix& matrix) {
+    for(int i=0; i<9; i++) {
+        fMatrixEntry[i].setText(QString::number(matrix.get(i)));
+    }
+}
+
+void SkInspectorWidget::setClip(const SkIRect& clip) {
+    fClipEntry[0].setText(QString::number(clip.left()));
+    fClipEntry[1].setText(QString::number(clip.top()));
+    fClipEntry[2].setText(QString::number(clip.right()));
+    fClipEntry[3].setText(QString::number(clip.bottom()));
+}
+
+QVBoxLayout* SkInspectorWidget::setupMatrix() {
+    fMatrixLabel.setText("Current Matrix");
+    fMatrixLabel.setAlignment(Qt::AlignHCenter);
+
+    fMatrixLayout.setSpacing(6);
+    fMatrixLayout.setContentsMargins(11,11,11,0);
+    fMatrixLayout.setAlignment(Qt::AlignTop | Qt::AlignHCenter);
+    fMatrixLayout.addWidget(&fMatrixLabel);
+
+    for(int i =0; i<9; i++) {
+        fMatrixEntry[i].setMinimumSize(QSize(70,25));
+        fMatrixEntry[i].setMaximumSize(QSize(70,16777215));
+        fMatrixEntry[i].setReadOnly(true);
+
+        fMatrixRow[i/3].addWidget(&fMatrixEntry[i]);
+        if(i%3 == 2) {
+            fMatrixLayout.addLayout(&fMatrixRow[i/3]);
+        }
+    }
+
+    return &fMatrixLayout;
+}
+
+QVBoxLayout* SkInspectorWidget::setupClip() {
+    fClipLabel.setText("Current Clip");
+    fClipLabel.setAlignment(Qt::AlignHCenter);
+
+    fClipLayout.setSpacing(6);
+    fClipLayout.setContentsMargins(11,11,11,0);
+    fClipLayout.setAlignment(Qt::AlignTop | Qt::AlignHCenter);
+    fClipLayout.addWidget(&fClipLabel);
+
+    for(int i =0; i<4; i++) {
+        fClipEntry[i].setMinimumSize(QSize(70,25));
+        fClipEntry[i].setMaximumSize(QSize(70,16777215));
+        fClipEntry[i].setReadOnly(true);
+
+        fClipRow[i/2].addWidget(&fClipEntry[i]);
+        if(i%2 == 1) {
+            fClipLayout.addLayout(&fClipRow[i/2]);
+        }
+    }
+
+    return &fClipLayout;
+}
diff --git a/debugger/QT/SkInspectorWidget.h b/debugger/QT/SkInspectorWidget.h
new file mode 100644
index 0000000..1b96235
--- /dev/null
+++ b/debugger/QT/SkInspectorWidget.h
@@ -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.
+ */
+
+
+#ifndef SKINSPECTORWIDGET_H_
+#define SKINSPECTORWIDGET_H_
+
+#include "SkMatrix.h"
+
+#include <QWidget>
+#include <QTabWidget>
+#include <QTextEdit>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QLineEdit>
+
+/** \class SkInspectorWidget
+
+    The InspectorWidget contains the overview and details tab. These contain
+    information about the whole picture and details about each draw command.
+ */
+class SkInspectorWidget : public QWidget {
+    Q_OBJECT
+
+public:
+    enum TabType {
+        kOverview_TabType,
+        kDetail_TabType,
+        kTotalTabCount,
+    };
+
+    /**
+        Constructs a widget with the specified parent for layout purposes.
+        @param parent  The parent container of this widget
+     */
+    SkInspectorWidget();
+
+    void setDisabled(bool isDisabled) {
+        fMatrixAndClipWidget.setDisabled(isDisabled);
+    }
+
+    /**
+        Sets the text in tab at the specified index.
+        @param text
+     */
+    void setText(QString text, TabType type);
+
+    /**
+        Sets the text in the current matrix.
+        @param matrixValues
+     */
+    void setMatrix(const SkMatrix& matrix);
+
+    /**
+        Sets the text in the current clip.
+        @param clipValues
+     */
+    void setClip(const SkIRect& clip);
+
+    class Tab : public QWidget {
+        QWidget fTab;
+        QHBoxLayout fTabLayout;
+        QTextEdit fTabText;
+        QString fName;
+
+        Tab(const char* name) {
+            fTabText.setReadOnly(true);
+            fTabLayout.setSpacing(6);
+            fTabLayout.setContentsMargins(11, 11, 11, 11);
+            fTabLayout.addWidget(&fTabText);
+            fTab.setLayout(&fTabLayout);
+            fName = QString(name);
+        }
+    };
+
+private:
+    QHBoxLayout fHorizontalLayout;
+    QTabWidget fTabWidget;
+
+    QWidget fTabs[kTotalTabCount];
+    QHBoxLayout fTabLayouts[kTotalTabCount];
+    QTextEdit fTabTexts[kTotalTabCount];
+
+    QWidget fMatrixAndClipWidget;
+    QVBoxLayout fVerticalLayout;
+
+    QLabel fMatrixLabel;
+    QVBoxLayout fMatrixLayout;
+    QHBoxLayout fMatrixRow[3];
+    QLineEdit fMatrixEntry[9];
+
+    QLabel fClipLabel;
+    QVBoxLayout fClipLayout;
+    QHBoxLayout fClipRow[2];
+    QLineEdit fClipEntry[4];
+
+    QVBoxLayout* setupMatrix();
+    QVBoxLayout* setupClip();
+};
+
+#endif
diff --git a/debugger/QT/SkListWidget.cpp b/debugger/QT/SkListWidget.cpp
new file mode 100644
index 0000000..669f225
--- /dev/null
+++ b/debugger/QT/SkListWidget.cpp
@@ -0,0 +1,101 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkListWidget.h"
+
+SkListWidget::SkListWidget(QObject *parent) {}
+
+SkListWidget::~SkListWidget() {}
+
+void SkListWidget::paint (QPainter *painter,
+        const QStyleOptionViewItem &option,
+        const QModelIndex &index) const {
+    /* We adjust the initial position of the list item so that
+     * we don't have overlapping top and bottom borders of concurrent
+     * widget items. */
+    QRect r = option.rect;
+    r.adjust(-1,-1,1,0);
+
+    QPen linePen(QColor::fromRgb(211,211,211), 1, Qt::SolidLine);
+    QPen fontPen(QColor::fromRgb(51,51,51), 1, Qt::SolidLine);
+    QPen fontMarkedPen(Qt::white, 1, Qt::SolidLine);
+
+    // If selected
+    if(option.state & QStyle::State_Selected){
+        QLinearGradient gradientSelected(r.left(),r.top(),r.left(),r.height()+r.top());
+        gradientSelected.setColorAt(0.0, QColor::fromRgb(119,213,247));
+        gradientSelected.setColorAt(0.9, QColor::fromRgb(27,134,183));
+        gradientSelected.setColorAt(1.0, QColor::fromRgb(0,120,174));
+        painter->setBrush(gradientSelected);
+        painter->drawRect(r);
+
+        painter->setPen(linePen);
+        painter->drawLine(r.topLeft(),r.topRight());
+        painter->drawLine(r.topRight(),r.bottomRight());
+        painter->drawLine(r.bottomLeft(),r.bottomRight());
+        painter->drawLine(r.topLeft(),r.bottomLeft());
+
+        painter->setPen(fontMarkedPen);
+
+    } else {
+        // Alternating background
+        painter->setBrush((index.row() % 2) ? Qt::white : QColor(252,252,252));
+        painter->drawRect(r);
+
+        painter->setPen(linePen);
+        painter->drawLine(r.topLeft(),r.topRight());
+        painter->drawLine(r.topRight(),r.bottomRight());
+        painter->drawLine(r.bottomLeft(),r.bottomRight());
+        painter->drawLine(r.topLeft(),r.bottomLeft());
+
+        painter->setPen(fontPen);
+    }
+
+    QIcon breakpointIcon =
+            QIcon(qvariant_cast<QPixmap>(index.data(Qt::DecorationRole)));
+    QIcon deleteIcon =
+            QIcon(qvariant_cast<QPixmap>(index.data(Qt::UserRole + 2)));
+
+    QString drawCommandText = index.data(Qt::DisplayRole).toString();
+    QString drawCommandNumber = index.data(Qt::UserRole + 1).toString();
+
+    /* option.rect is a struct that Qt uses as a target to draw into. Following
+     * the format (x1,y1,x2,y2) x1 and y1 represent where the painter can start
+     * drawing. x2 and y2 represent where the drawing area has to terminate
+     * counting from the bottom right corner of each list item styled with this
+     * widget. A (x1,y1,0,0) rect would mean that the item being drawn would
+     * be pushed down into that bottom corner. Negative values in the x2,y2
+     * spot act as a margin for the bottom and right sides. Positive values in
+     * x1,y1 act as a margin for the top and left. The target area will not
+     * affect size of text but will scale icons. */
+    int imageSpace = 35;
+
+    // Breakpoint Icon
+    r = option.rect.adjusted(5, 10, -10, -10);
+    breakpointIcon.paint(painter, r, Qt::AlignVCenter|Qt::AlignLeft);
+
+    // Delete Icon
+    r = option.rect.adjusted(19, 10, -10, -10);
+    deleteIcon.paint(painter, r, Qt::AlignVCenter|Qt::AlignLeft);
+
+    // Draw Command
+    r = option.rect.adjusted(imageSpace, 0, -10, -7);
+    painter->drawText(r.left(), r.top(), r.width(), r.height(),
+            Qt::AlignBottom|Qt::AlignRight, drawCommandText, &r);
+
+    // Draw Command Number
+    r = option.rect.adjusted(imageSpace, 0, -10, -7);
+    painter->drawText(r.left(), r.top(), r.width(), r.height(),
+            Qt::AlignBottom|Qt::AlignLeft, drawCommandNumber, &r);
+}
+
+QSize SkListWidget::sizeHint ( const QStyleOptionViewItem & option,
+        const QModelIndex & index ) const{
+    return QSize(200, 30);
+}
diff --git a/debugger/QT/SkListWidget.h b/debugger/QT/SkListWidget.h
new file mode 100644
index 0000000..12a2120
--- /dev/null
+++ b/debugger/QT/SkListWidget.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 SKLISTWIDGET_H_
+#define SKLISTWIDGET_H_
+
+#include <QAbstractItemDelegate>
+#include <QPainter>
+
+/** \class SkListWidget
+
+    This widget contains the draw commands.
+ */
+class SkListWidget : public QAbstractItemDelegate {
+public:
+    /**
+        Constructs the list widget with the specified parent for layout purposes.
+        @param parent  The parent container of this widget
+     */
+    SkListWidget(QObject* parent = NULL);
+
+    ~SkListWidget();
+
+    /**
+        Draws the current state of the widget. Overriden from QWidget.
+     */
+    void paint (QPainter* painter, const QStyleOptionViewItem& option,
+            const QModelIndex& index ) const;
+
+    /**
+        Returns the default size of the widget. Overriden from QWidget.
+     */
+    QSize sizeHint (const QStyleOptionViewItem& option,
+            const QModelIndex& index) const;
+};
+
+#endif
diff --git a/debugger/QT/SkRasterWidget.cpp b/debugger/QT/SkRasterWidget.cpp
new file mode 100644
index 0000000..4f8537d
--- /dev/null
+++ b/debugger/QT/SkRasterWidget.cpp
@@ -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.
+ */
+
+
+#include "SkRasterWidget.h"
+
+SkRasterWidget::SkRasterWidget(SkDebugger *debugger) : QWidget() {
+    fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 800, 800);
+    fBitmap.allocPixels();
+    fBitmap.eraseColor(0);
+    fDevice = new SkDevice(fBitmap);
+    fDebugger = debugger;
+    fCanvas = new SkCanvas(fDevice);
+    this->setStyleSheet("QWidget {background-color: white; border: 1px solid #cccccc;}");
+}
+
+SkRasterWidget::~SkRasterWidget() {
+    SkSafeUnref(fCanvas);
+    SkSafeUnref(fDevice);
+}
+
+void SkRasterWidget::resizeEvent(QResizeEvent* event) {
+    fBitmap.setConfig(SkBitmap::kARGB_8888_Config, event->size().width(), event->size().height());
+    fBitmap.allocPixels();
+    SkSafeUnref(fCanvas);
+    SkSafeUnref(fDevice);
+    fDevice = new SkDevice(fBitmap);
+    fCanvas = new SkCanvas(fDevice);
+    fDebugger->resize(event->size().width(), event->size().height());
+    this->update();
+}
+
+void SkRasterWidget::paintEvent(QPaintEvent* event) {
+    if (!this->isHidden()) {
+        fDebugger->draw(fCanvas);
+        QPainter painter(this);
+        QStyleOption opt;
+        opt.init(this);
+
+        style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
+
+        QPoint origin(0,0);
+        QImage image((uchar *)fBitmap.getPixels(), fBitmap.width(),
+                fBitmap.height(), QImage::Format_ARGB32_Premultiplied);
+
+        painter.drawImage(origin, image);
+        painter.end();
+        emit drawComplete();
+    }
+}
diff --git a/debugger/QT/SkRasterWidget.h b/debugger/QT/SkRasterWidget.h
new file mode 100644
index 0000000..a0355c1
--- /dev/null
+++ b/debugger/QT/SkRasterWidget.h
@@ -0,0 +1,47 @@
+/*
+ * SkRasterWidget.h
+ *
+ *  Created on: Jul 28, 2012
+ *      Author: chudy
+ */
+
+
+#ifndef SKRASTERWIDGET_H_
+#define SKRASTERWIDGET_H_
+
+#include "SkGpuDevice.h"
+#include "SkDevice.h"
+#include "SkDebugger.h"
+
+#include <QApplication>
+#include <QtGui>
+#include <QWidget>
+
+class  SkRasterWidget : public QWidget {
+    Q_OBJECT
+
+public:
+    SkRasterWidget(SkDebugger* debugger);
+
+    ~SkRasterWidget();
+
+    void draw() {
+        this->update();
+    }
+
+signals:
+    void drawComplete();
+
+protected:
+    void paintEvent(QPaintEvent* event);
+
+    void resizeEvent(QResizeEvent* event);
+
+private:
+    SkBitmap fBitmap;
+    SkDebugger* fDebugger;
+    SkCanvas* fCanvas;
+    SkDevice* fDevice;
+};
+
+#endif /* SKRASTERWIDGET_H_ */
diff --git a/debugger/QT/SkSettingsWidget.cpp b/debugger/QT/SkSettingsWidget.cpp
new file mode 100644
index 0000000..d9268be
--- /dev/null
+++ b/debugger/QT/SkSettingsWidget.cpp
@@ -0,0 +1,162 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkSettingsWidget.h"
+#include <iostream>
+#include <math.h>
+
+// TODO(chudy): See if the layout can't be attached to the frame post construction.
+SkSettingsWidget::SkSettingsWidget() : QWidget()
+    , mainFrameLayout(this)
+    , fVerticalLayout(&mainFrame)
+    , fVisibleFrameLayout(&fVisibleFrame)
+    , fVisibleOn(&fVisibleFrame)
+    , fVisibleOff(&fVisibleFrame)
+    , fCommandLayout(&fCommandFrame)
+    , fCurrentCommandBox(&fCommandFrame)
+    , fCommandHitBox(&fCommandFrame)
+    , fCanvasLayout(&fCanvasFrame)
+    , fZoomLayout(&fZoomFrame)
+    , fZoomBox(&fZoomFrame)
+{
+    // Sets up the container and it's alignment around the settings widget.
+    mainFrame.setFrameShape(QFrame::StyledPanel);
+    mainFrame.setFrameShadow(QFrame::Raised);
+    mainFrameLayout.setSpacing(6);
+    mainFrameLayout.setContentsMargins(0,0,0,0);
+    mainFrameLayout.addWidget(&mainFrame);
+
+    // Vertical Layout is the alignment inside of the main frame.
+    fVerticalLayout.setContentsMargins(11,11,11,11);
+    fVerticalLayout.setAlignment(Qt::AlignTop);
+
+    // Visible Toggle
+    fVisibileText.setText("Visibility Filter");
+    fVisibleFrame.setFrameShape(QFrame::StyledPanel);
+    fVisibleFrame.setFrameShadow(QFrame::Raised);
+    fVisibleOn.setText("On");
+    fVisibleOff.setText("Off");
+    fVisibleOff.setChecked(true);
+    fVisibleFrameLayout.setSpacing(6);
+    fVisibleFrameLayout.setContentsMargins(11,11,11,11);
+    fVisibleFrameLayout.addWidget(&fVisibleOn);
+    fVisibleFrameLayout.addWidget(&fVisibleOff);
+
+    // Canvas
+    fCanvasToggle.setText("Render Targets");
+    fCanvasFrame.setFrameShape(QFrame::StyledPanel);
+    fCanvasFrame.setFrameShadow(QFrame::Raised);
+
+    fRasterLabel.setText("Raster: ");
+    fRasterLabel.setMinimumWidth(178);
+    fRasterLabel.setMaximumWidth(178);
+
+    fRasterCheckBox.setChecked(true);
+
+    fGLLabel.setText("OpenGL: ");
+    fGLLabel.setMinimumWidth(178);
+    fGLLabel.setMaximumWidth(178);
+
+    fRasterLayout.addWidget(&fRasterLabel);
+    fRasterLayout.addWidget(&fRasterCheckBox);
+
+    fGLLayout.addWidget(&fGLLabel);
+    fGLLayout.addWidget(&fGLCheckBox);
+
+    fCanvasLayout.setSpacing(6);
+    fCanvasLayout.setContentsMargins(11,11,11,11);
+    fCanvasLayout.addLayout(&fRasterLayout);
+    fCanvasLayout.addLayout(&fGLLayout);
+
+    // Command Toggle
+    fCommandToggle.setText("Command Scrolling Preferences");
+    fCommandFrame.setFrameShape(QFrame::StyledPanel);
+    fCommandFrame.setFrameShadow(QFrame::Raised);
+
+    fCurrentCommandLabel.setText("Current Command: ");
+    fCurrentCommandLabel.setMinimumWidth(178);
+    fCurrentCommandLabel.setMaximumWidth(178);
+    fCurrentCommandBox.setText("0");
+    fCurrentCommandBox.setMinimumSize(QSize(50,25));
+    fCurrentCommandBox.setMaximumSize(QSize(50,25));
+    fCurrentCommandBox.setAlignment(Qt::AlignRight);
+
+    fCurrentCommandLayout.setSpacing(0);
+    fCurrentCommandLayout.setContentsMargins(0,0,0,0);
+    fCurrentCommandLayout.setAlignment(Qt::AlignLeft);
+    fCurrentCommandLayout.addWidget(&fCurrentCommandLabel);
+    fCurrentCommandLayout.addWidget(&fCurrentCommandBox);
+
+    fCommandHitLabel.setText("Command HitBox: ");
+    fCommandHitLabel.setMinimumWidth(178);
+    fCommandHitLabel.setMaximumWidth(178);
+    fCommandHitBox.setText("0");
+    fCommandHitBox.setMinimumSize(QSize(50,25));
+    fCommandHitBox.setMaximumSize(QSize(50,25));
+    fCommandHitBox.setAlignment(Qt::AlignRight);
+    fCommandHitLayout.setSpacing(0);
+    fCommandHitLayout.setContentsMargins(0,0,0,0);
+    fCommandHitLayout.setAlignment(Qt::AlignLeft);
+    fCommandHitLayout.addWidget(&fCommandHitLabel);
+    fCommandHitLayout.addWidget(&fCommandHitBox);
+
+    fCommandLayout.setSpacing(6);
+    fCommandLayout.setContentsMargins(11,11,11,11);
+    fCommandLayout.addLayout(&fCurrentCommandLayout);
+    fCommandLayout.addLayout(&fCommandHitLayout);
+
+    // Zoom Info
+    fZoomSetting.setText("Zoom Level: ");
+    fZoomSetting.setMinimumWidth(178);
+    fZoomSetting.setMaximumWidth(178);
+    fZoomFrame.setFrameShape(QFrame::StyledPanel);
+    fZoomFrame.setFrameShadow(QFrame::Raised);
+    fZoomBox.setText("100%");
+    fZoomBox.setMinimumSize(QSize(50,25));
+    fZoomBox.setMaximumSize(QSize(50,25));
+    fZoomBox.setAlignment(Qt::AlignRight);
+    fZoomLayout.setSpacing(6);
+    fZoomLayout.setContentsMargins(11,11,11,11);
+    fZoomLayout.addWidget(&fZoomSetting);
+    fZoomLayout.addWidget(&fZoomBox);
+
+    // Adds all widgets to settings container
+    fVerticalLayout.addWidget(&fVisibileText);
+    fVerticalLayout.addWidget(&fVisibleFrame);
+    fVerticalLayout.addWidget(&fCommandToggle);
+    fVerticalLayout.addWidget(&fCommandFrame);
+    fVerticalLayout.addWidget(&fCanvasToggle);
+    fVerticalLayout.addWidget(&fCanvasFrame);
+    fVerticalLayout.addWidget(&fZoomFrame);
+
+    this->setDisabled(true);
+}
+
+
+void SkSettingsWidget::updateCommand(int newCommand) {
+    fCurrentCommandBox.setText(QString::number(newCommand));
+}
+
+void SkSettingsWidget::updateHit(int newHit) {
+    fCommandHitBox.setText(QString::number(newHit));
+}
+
+QRadioButton* SkSettingsWidget::getVisibilityButton() {
+    return &fVisibleOn;
+}
+
+void SkSettingsWidget::setZoomText(int scaleFactor) {
+    if(scaleFactor == 1 || scaleFactor == -1) {
+        fZoomBox.setText("100%");
+    } else if (scaleFactor > 1) {
+        fZoomBox.setText(QString::number(scaleFactor*100).append("%"));
+    } else if (scaleFactor < -1) {
+        fZoomBox.setText(QString::number(100 / pow(2.0f, (-scaleFactor - 1))).append("%"));
+    }
+}
diff --git a/debugger/QT/SkSettingsWidget.h b/debugger/QT/SkSettingsWidget.h
new file mode 100644
index 0000000..fdb99d1
--- /dev/null
+++ b/debugger/QT/SkSettingsWidget.h
@@ -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.
+ */
+
+
+#ifndef SKSETTINGSWIDGET_H_
+#define SKSETTINGSWIDGET_H_
+
+#include <QWidget>
+#include <QHBoxLayout>
+#include <QTextEdit>
+#include <QFrame>
+#include <QLabel>
+#include <QRadioButton>
+#include <QCheckBox>
+#include <QLineEdit>
+
+/** \class SkSettingsWidget
+
+    The SettingsWidget contains multiple checkboxes and toggles for altering
+    the visibility.
+ */
+class SkSettingsWidget : public QWidget {
+    Q_OBJECT
+
+public:
+    /**
+        Constructs a widget with the specified parent for layout purposes.
+        @param parent  The parent container of this widget
+     */
+    SkSettingsWidget();
+
+    void setZoomText(int scaleFactor);
+
+    QRadioButton* getVisibilityButton();
+
+    QCheckBox* getGLCheckBox() {
+        return &fGLCheckBox;
+    }
+
+    QCheckBox* getRasterCheckBox() {
+        return &fRasterCheckBox;
+    }
+
+private slots:
+    void updateCommand(int newCommand);
+    void updateHit(int newHit);
+
+signals:
+    void scrollingPreferences(bool isStickyActivate);
+    void showStyle(bool isSingleCommand);
+    void visibilityFilter(bool isEnabled);
+
+private:
+    QVBoxLayout mainFrameLayout;
+    QFrame mainFrame;
+    QVBoxLayout fVerticalLayout;
+
+    QLabel fVisibileText;
+    QFrame fVisibleFrame;
+    QVBoxLayout fVisibleFrameLayout;
+    QRadioButton fVisibleOn;
+    QRadioButton fVisibleOff;
+
+    QLabel fCommandToggle;
+    QFrame fCommandFrame;
+    QVBoxLayout fCommandLayout;
+
+    QHBoxLayout fCurrentCommandLayout;
+    QLabel fCurrentCommandLabel;
+    QLineEdit fCurrentCommandBox;
+
+    QHBoxLayout fCommandHitLayout;
+    QLabel fCommandHitLabel;
+    QLineEdit fCommandHitBox;
+
+    QFrame fCanvasFrame;
+    QVBoxLayout fCanvasLayout;
+    QLabel fCanvasToggle;
+
+    QHBoxLayout fRasterLayout;
+    QLabel fRasterLabel;
+    QCheckBox fRasterCheckBox;
+
+    QHBoxLayout fGLLayout;
+    QLabel fGLLabel;
+    QCheckBox fGLCheckBox;
+
+    QFrame fZoomFrame;
+    QHBoxLayout fZoomLayout;
+    QLabel fZoomSetting;
+    QLineEdit fZoomBox;
+};
+
+#endif /* SKSETTINGSWIDGET_H_ */
diff --git a/debugger/QT/moc_4.8.1_SkCanvasWidget.cpp b/debugger/QT/moc_4.8.1_SkCanvasWidget.cpp
new file mode 100644
index 0000000..8187549
--- /dev/null
+++ b/debugger/QT/moc_4.8.1_SkCanvasWidget.cpp
@@ -0,0 +1,125 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkCanvasWidget.h'
+**
+** Created: Mon Sep 10 14:33:43 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.1)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkCanvasWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkCanvasWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.1. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkCanvasWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       4,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       3,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      31,   16,   15,   15, 0x05,
+      68,   57,   15,   15, 0x05,
+      92,   88,   15,   15, 0x05,
+
+ // slots: signature, parameters, type, tag, flags
+     122,  108,   15,   15, 0x08,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkCanvasWidget[] = {
+    "SkCanvasWidget\0\0newScaleFactor\0"
+    "scaleFactorChanged(float)\0newCommand\0"
+    "commandChanged(int)\0hit\0hitChanged(int)\0"
+    "zoomIncrement\0keyZoom(int)\0"
+};
+
+void SkCanvasWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkCanvasWidget *_t = static_cast<SkCanvasWidget *>(_o);
+        switch (_id) {
+        case 0: _t->scaleFactorChanged((*reinterpret_cast< float(*)>(_a[1]))); break;
+        case 1: _t->commandChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 2: _t->hitChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 3: _t->keyZoom((*reinterpret_cast< int(*)>(_a[1]))); break;
+        default: ;
+        }
+    }
+}
+
+const QMetaObjectExtraData SkCanvasWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkCanvasWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkCanvasWidget,
+      qt_meta_data_SkCanvasWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkCanvasWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkCanvasWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkCanvasWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkCanvasWidget))
+        return static_cast<void*>(const_cast< SkCanvasWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkCanvasWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 4)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 4;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkCanvasWidget::scaleFactorChanged(float _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+
+// SIGNAL 1
+void SkCanvasWidget::commandChanged(int _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 1, _a);
+}
+
+// SIGNAL 2
+void SkCanvasWidget::hitChanged(int _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 2, _a);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.1_SkDebuggerGUI.cpp b/debugger/QT/moc_4.8.1_SkDebuggerGUI.cpp
new file mode 100644
index 0000000..46d9f70
--- /dev/null
+++ b/debugger/QT/moc_4.8.1_SkDebuggerGUI.cpp
@@ -0,0 +1,175 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkDebuggerGUI.h'
+**
+** Created: Mon Sep 10 14:34:20 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.1)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkDebuggerGUI.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkDebuggerGUI.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.1. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkDebuggerGUI[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+      30,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       1,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      23,   15,   14,   14, 0x05,
+
+ // slots: signature, parameters, type, tag, flags
+      43,   14,   14,   14, 0x08,
+      63,   14,   14,   14, 0x08,
+      78,   14,   14,   14, 0x08,
+     103,   14,   14,   14, 0x08,
+     124,   14,   14,   14, 0x08,
+     146,   14,   14,   14, 0x08,
+     160,   14,   14,   14, 0x08,
+     185,  175,   14,   14, 0x08,
+     206,   14,   14,   14, 0x08,
+     224,   14,   14,   14, 0x08,
+     237,  175,   14,   14, 0x08,
+     262,   14,   14,   14, 0x08,
+     277,   14,   14,   14, 0x08,
+     290,   14,   14,   14, 0x08,
+     317,  305,   14,   14, 0x08,
+     336,   14,   14,   14, 0x08,
+     353,   14,   14,   14, 0x08,
+     370,   14,   14,   14, 0x08,
+     390,   14,   14,   14, 0x08,
+     410,  405,   14,   14, 0x08,
+     437,   14,   14,   14, 0x08,
+     457,  448,   14,   14, 0x08,
+     476,   14,   14,   14, 0x28,
+     491,  405,   14,   14, 0x08,
+     527,   15,   14,   14, 0x08,
+     546,   14,   14,   14, 0x08,
+     560,   14,   14,   14, 0x08,
+     579,   14,   14,   14, 0x08,
+     604,  597,   14,   14, 0x08,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkDebuggerGUI[] = {
+    "SkDebuggerGUI\0\0command\0commandChanged(int)\0"
+    "actionBreakpoints()\0actionCancel()\0"
+    "actionClearBreakpoints()\0actionClearDeletes()\0"
+    "actionCommandFilter()\0actionClose()\0"
+    "actionDelete()\0isToggled\0actionGLWidget(bool)\0"
+    "actionInspector()\0actionPlay()\0"
+    "actionRasterWidget(bool)\0actionRewind()\0"
+    "actionSave()\0actionSaveAs()\0scaleFactor\0"
+    "actionScale(float)\0actionSettings()\0"
+    "actionStepBack()\0actionStepForward()\0"
+    "drawComplete()\0item\0loadFile(QListWidgetItem*)\0"
+    "openFile()\0isPaused\0pauseDrawing(bool)\0"
+    "pauseDrawing()\0registerListClick(QListWidgetItem*)\0"
+    "selectCommand(int)\0showDeletes()\0"
+    "toggleBreakpoint()\0toggleDirectory()\0"
+    "string\0toggleFilter(QString)\0"
+};
+
+void SkDebuggerGUI::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkDebuggerGUI *_t = static_cast<SkDebuggerGUI *>(_o);
+        switch (_id) {
+        case 0: _t->commandChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 1: _t->actionBreakpoints(); break;
+        case 2: _t->actionCancel(); break;
+        case 3: _t->actionClearBreakpoints(); break;
+        case 4: _t->actionClearDeletes(); break;
+        case 5: _t->actionCommandFilter(); break;
+        case 6: _t->actionClose(); break;
+        case 7: _t->actionDelete(); break;
+        case 8: _t->actionGLWidget((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 9: _t->actionInspector(); break;
+        case 10: _t->actionPlay(); break;
+        case 11: _t->actionRasterWidget((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 12: _t->actionRewind(); break;
+        case 13: _t->actionSave(); break;
+        case 14: _t->actionSaveAs(); break;
+        case 15: _t->actionScale((*reinterpret_cast< float(*)>(_a[1]))); break;
+        case 16: _t->actionSettings(); break;
+        case 17: _t->actionStepBack(); break;
+        case 18: _t->actionStepForward(); break;
+        case 19: _t->drawComplete(); break;
+        case 20: _t->loadFile((*reinterpret_cast< QListWidgetItem*(*)>(_a[1]))); break;
+        case 21: _t->openFile(); break;
+        case 22: _t->pauseDrawing((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 23: _t->pauseDrawing(); break;
+        case 24: _t->registerListClick((*reinterpret_cast< QListWidgetItem*(*)>(_a[1]))); break;
+        case 25: _t->selectCommand((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 26: _t->showDeletes(); break;
+        case 27: _t->toggleBreakpoint(); break;
+        case 28: _t->toggleDirectory(); break;
+        case 29: _t->toggleFilter((*reinterpret_cast< QString(*)>(_a[1]))); break;
+        default: ;
+        }
+    }
+}
+
+const QMetaObjectExtraData SkDebuggerGUI::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkDebuggerGUI::staticMetaObject = {
+    { &QMainWindow::staticMetaObject, qt_meta_stringdata_SkDebuggerGUI,
+      qt_meta_data_SkDebuggerGUI, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkDebuggerGUI::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkDebuggerGUI::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkDebuggerGUI::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkDebuggerGUI))
+        return static_cast<void*>(const_cast< SkDebuggerGUI*>(this));
+    return QMainWindow::qt_metacast(_clname);
+}
+
+int SkDebuggerGUI::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QMainWindow::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 30)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 30;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkDebuggerGUI::commandChanged(int _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.1_SkGLWidget.cpp b/debugger/QT/moc_4.8.1_SkGLWidget.cpp
new file mode 100644
index 0000000..fccde24
--- /dev/null
+++ b/debugger/QT/moc_4.8.1_SkGLWidget.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkGLWidget.h'
+**
+** Created: Mon Sep 10 14:34:37 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.1)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkGLWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkGLWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.1. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkGLWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       1,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       1,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      12,   11,   11,   11, 0x05,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkGLWidget[] = {
+    "SkGLWidget\0\0drawComplete()\0"
+};
+
+void SkGLWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkGLWidget *_t = static_cast<SkGLWidget *>(_o);
+        switch (_id) {
+        case 0: _t->drawComplete(); break;
+        default: ;
+        }
+    }
+    Q_UNUSED(_a);
+}
+
+const QMetaObjectExtraData SkGLWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkGLWidget::staticMetaObject = {
+    { &QGLWidget::staticMetaObject, qt_meta_stringdata_SkGLWidget,
+      qt_meta_data_SkGLWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkGLWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkGLWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkGLWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkGLWidget))
+        return static_cast<void*>(const_cast< SkGLWidget*>(this));
+    return QGLWidget::qt_metacast(_clname);
+}
+
+int SkGLWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QGLWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 1)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 1;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkGLWidget::drawComplete()
+{
+    QMetaObject::activate(this, &staticMetaObject, 0, 0);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.1_SkInspectorWidget.cpp b/debugger/QT/moc_4.8.1_SkInspectorWidget.cpp
new file mode 100644
index 0000000..236768d
--- /dev/null
+++ b/debugger/QT/moc_4.8.1_SkInspectorWidget.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkInspectorWidget.h'
+**
+** Created: Mon Sep 10 14:34:53 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.1)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkInspectorWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkInspectorWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.1. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkInspectorWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       0,    0, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       0,       // signalCount
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkInspectorWidget[] = {
+    "SkInspectorWidget\0"
+};
+
+void SkInspectorWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    Q_UNUSED(_o);
+    Q_UNUSED(_id);
+    Q_UNUSED(_c);
+    Q_UNUSED(_a);
+}
+
+const QMetaObjectExtraData SkInspectorWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkInspectorWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkInspectorWidget,
+      qt_meta_data_SkInspectorWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkInspectorWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkInspectorWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkInspectorWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkInspectorWidget))
+        return static_cast<void*>(const_cast< SkInspectorWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkInspectorWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    return _id;
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.1_SkRasterWidget.cpp b/debugger/QT/moc_4.8.1_SkRasterWidget.cpp
new file mode 100644
index 0000000..1036986
--- /dev/null
+++ b/debugger/QT/moc_4.8.1_SkRasterWidget.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkRasterWidget.h'
+**
+** Created: Mon Sep 10 14:35:07 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.1)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkRasterWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkRasterWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.1. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkRasterWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       1,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       1,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      16,   15,   15,   15, 0x05,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkRasterWidget[] = {
+    "SkRasterWidget\0\0drawComplete()\0"
+};
+
+void SkRasterWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkRasterWidget *_t = static_cast<SkRasterWidget *>(_o);
+        switch (_id) {
+        case 0: _t->drawComplete(); break;
+        default: ;
+        }
+    }
+    Q_UNUSED(_a);
+}
+
+const QMetaObjectExtraData SkRasterWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkRasterWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkRasterWidget,
+      qt_meta_data_SkRasterWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkRasterWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkRasterWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkRasterWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkRasterWidget))
+        return static_cast<void*>(const_cast< SkRasterWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkRasterWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 1)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 1;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkRasterWidget::drawComplete()
+{
+    QMetaObject::activate(this, &staticMetaObject, 0, 0);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.1_SkSettingsWidget.cpp b/debugger/QT/moc_4.8.1_SkSettingsWidget.cpp
new file mode 100644
index 0000000..e5c89bc
--- /dev/null
+++ b/debugger/QT/moc_4.8.1_SkSettingsWidget.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkSettingsWidget.h'
+**
+** Created: Mon Sep 10 14:35:18 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.1)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkSettingsWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkSettingsWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.1. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkSettingsWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       5,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       3,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      35,   18,   17,   17, 0x05,
+      78,   62,   17,   17, 0x05,
+     104,   94,   17,   17, 0x05,
+
+ // slots: signature, parameters, type, tag, flags
+     138,  127,   17,   17, 0x08,
+     164,  157,   17,   17, 0x08,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkSettingsWidget[] = {
+    "SkSettingsWidget\0\0isStickyActivate\0"
+    "scrollingPreferences(bool)\0isSingleCommand\0"
+    "showStyle(bool)\0isEnabled\0"
+    "visibilityFilter(bool)\0newCommand\0"
+    "updateCommand(int)\0newHit\0updateHit(int)\0"
+};
+
+void SkSettingsWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkSettingsWidget *_t = static_cast<SkSettingsWidget *>(_o);
+        switch (_id) {
+        case 0: _t->scrollingPreferences((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 1: _t->showStyle((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 2: _t->visibilityFilter((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 3: _t->updateCommand((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 4: _t->updateHit((*reinterpret_cast< int(*)>(_a[1]))); break;
+        default: ;
+        }
+    }
+}
+
+const QMetaObjectExtraData SkSettingsWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkSettingsWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkSettingsWidget,
+      qt_meta_data_SkSettingsWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkSettingsWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkSettingsWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkSettingsWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkSettingsWidget))
+        return static_cast<void*>(const_cast< SkSettingsWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkSettingsWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 5)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 5;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkSettingsWidget::scrollingPreferences(bool _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+
+// SIGNAL 1
+void SkSettingsWidget::showStyle(bool _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 1, _a);
+}
+
+// SIGNAL 2
+void SkSettingsWidget::visibilityFilter(bool _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 2, _a);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.2_SkCanvasWidget.cpp b/debugger/QT/moc_4.8.2_SkCanvasWidget.cpp
new file mode 100644
index 0000000..94528c2
--- /dev/null
+++ b/debugger/QT/moc_4.8.2_SkCanvasWidget.cpp
@@ -0,0 +1,125 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkCanvasWidget.h'
+**
+** Created: Thu Sep 6 11:16:07 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkCanvasWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkCanvasWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.2. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkCanvasWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       4,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       3,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      31,   16,   15,   15, 0x05,
+      68,   57,   15,   15, 0x05,
+      92,   88,   15,   15, 0x05,
+
+ // slots: signature, parameters, type, tag, flags
+     122,  108,   15,   15, 0x08,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkCanvasWidget[] = {
+    "SkCanvasWidget\0\0newScaleFactor\0"
+    "scaleFactorChanged(float)\0newCommand\0"
+    "commandChanged(int)\0hit\0hitChanged(int)\0"
+    "zoomIncrement\0keyZoom(int)\0"
+};
+
+void SkCanvasWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkCanvasWidget *_t = static_cast<SkCanvasWidget *>(_o);
+        switch (_id) {
+        case 0: _t->scaleFactorChanged((*reinterpret_cast< float(*)>(_a[1]))); break;
+        case 1: _t->commandChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 2: _t->hitChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 3: _t->keyZoom((*reinterpret_cast< int(*)>(_a[1]))); break;
+        default: ;
+        }
+    }
+}
+
+const QMetaObjectExtraData SkCanvasWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkCanvasWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkCanvasWidget,
+      qt_meta_data_SkCanvasWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkCanvasWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkCanvasWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkCanvasWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkCanvasWidget))
+        return static_cast<void*>(const_cast< SkCanvasWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkCanvasWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 4)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 4;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkCanvasWidget::scaleFactorChanged(float _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+
+// SIGNAL 1
+void SkCanvasWidget::commandChanged(int _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 1, _a);
+}
+
+// SIGNAL 2
+void SkCanvasWidget::hitChanged(int _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 2, _a);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.2_SkDebuggerGUI.cpp b/debugger/QT/moc_4.8.2_SkDebuggerGUI.cpp
new file mode 100644
index 0000000..99c75da
--- /dev/null
+++ b/debugger/QT/moc_4.8.2_SkDebuggerGUI.cpp
@@ -0,0 +1,175 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkDebuggerGUI.h'
+**
+** Created: Thu Sep 6 11:16:07 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkDebuggerGUI.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkDebuggerGUI.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.2. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkDebuggerGUI[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+      30,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       1,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      23,   15,   14,   14, 0x05,
+
+ // slots: signature, parameters, type, tag, flags
+      43,   14,   14,   14, 0x08,
+      63,   14,   14,   14, 0x08,
+      78,   14,   14,   14, 0x08,
+     103,   14,   14,   14, 0x08,
+     124,   14,   14,   14, 0x08,
+     146,   14,   14,   14, 0x08,
+     160,   14,   14,   14, 0x08,
+     185,  175,   14,   14, 0x08,
+     206,   14,   14,   14, 0x08,
+     224,   14,   14,   14, 0x08,
+     237,  175,   14,   14, 0x08,
+     262,   14,   14,   14, 0x08,
+     277,   14,   14,   14, 0x08,
+     290,   14,   14,   14, 0x08,
+     317,  305,   14,   14, 0x08,
+     336,   14,   14,   14, 0x08,
+     353,   14,   14,   14, 0x08,
+     370,   14,   14,   14, 0x08,
+     390,   14,   14,   14, 0x08,
+     410,  405,   14,   14, 0x08,
+     437,   14,   14,   14, 0x08,
+     457,  448,   14,   14, 0x08,
+     476,   14,   14,   14, 0x28,
+     491,  405,   14,   14, 0x08,
+     527,   15,   14,   14, 0x08,
+     546,   14,   14,   14, 0x08,
+     560,   14,   14,   14, 0x08,
+     579,   14,   14,   14, 0x08,
+     604,  597,   14,   14, 0x08,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkDebuggerGUI[] = {
+    "SkDebuggerGUI\0\0command\0commandChanged(int)\0"
+    "actionBreakpoints()\0actionCancel()\0"
+    "actionClearBreakpoints()\0actionClearDeletes()\0"
+    "actionCommandFilter()\0actionClose()\0"
+    "actionDelete()\0isToggled\0actionGLWidget(bool)\0"
+    "actionInspector()\0actionPlay()\0"
+    "actionRasterWidget(bool)\0actionRewind()\0"
+    "actionSave()\0actionSaveAs()\0scaleFactor\0"
+    "actionScale(float)\0actionSettings()\0"
+    "actionStepBack()\0actionStepForward()\0"
+    "drawComplete()\0item\0loadFile(QListWidgetItem*)\0"
+    "openFile()\0isPaused\0pauseDrawing(bool)\0"
+    "pauseDrawing()\0registerListClick(QListWidgetItem*)\0"
+    "selectCommand(int)\0showDeletes()\0"
+    "toggleBreakpoint()\0toggleDirectory()\0"
+    "string\0toggleFilter(QString)\0"
+};
+
+void SkDebuggerGUI::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkDebuggerGUI *_t = static_cast<SkDebuggerGUI *>(_o);
+        switch (_id) {
+        case 0: _t->commandChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 1: _t->actionBreakpoints(); break;
+        case 2: _t->actionCancel(); break;
+        case 3: _t->actionClearBreakpoints(); break;
+        case 4: _t->actionClearDeletes(); break;
+        case 5: _t->actionCommandFilter(); break;
+        case 6: _t->actionClose(); break;
+        case 7: _t->actionDelete(); break;
+        case 8: _t->actionGLWidget((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 9: _t->actionInspector(); break;
+        case 10: _t->actionPlay(); break;
+        case 11: _t->actionRasterWidget((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 12: _t->actionRewind(); break;
+        case 13: _t->actionSave(); break;
+        case 14: _t->actionSaveAs(); break;
+        case 15: _t->actionScale((*reinterpret_cast< float(*)>(_a[1]))); break;
+        case 16: _t->actionSettings(); break;
+        case 17: _t->actionStepBack(); break;
+        case 18: _t->actionStepForward(); break;
+        case 19: _t->drawComplete(); break;
+        case 20: _t->loadFile((*reinterpret_cast< QListWidgetItem*(*)>(_a[1]))); break;
+        case 21: _t->openFile(); break;
+        case 22: _t->pauseDrawing((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 23: _t->pauseDrawing(); break;
+        case 24: _t->registerListClick((*reinterpret_cast< QListWidgetItem*(*)>(_a[1]))); break;
+        case 25: _t->selectCommand((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 26: _t->showDeletes(); break;
+        case 27: _t->toggleBreakpoint(); break;
+        case 28: _t->toggleDirectory(); break;
+        case 29: _t->toggleFilter((*reinterpret_cast< QString(*)>(_a[1]))); break;
+        default: ;
+        }
+    }
+}
+
+const QMetaObjectExtraData SkDebuggerGUI::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkDebuggerGUI::staticMetaObject = {
+    { &QMainWindow::staticMetaObject, qt_meta_stringdata_SkDebuggerGUI,
+      qt_meta_data_SkDebuggerGUI, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkDebuggerGUI::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkDebuggerGUI::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkDebuggerGUI::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkDebuggerGUI))
+        return static_cast<void*>(const_cast< SkDebuggerGUI*>(this));
+    return QMainWindow::qt_metacast(_clname);
+}
+
+int SkDebuggerGUI::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QMainWindow::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 30)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 30;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkDebuggerGUI::commandChanged(int _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.2_SkGLWidget.cpp b/debugger/QT/moc_4.8.2_SkGLWidget.cpp
new file mode 100644
index 0000000..2dca3ae
--- /dev/null
+++ b/debugger/QT/moc_4.8.2_SkGLWidget.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkglWidget.h'
+**
+** Created: Thu Sep 6 11:16:07 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkglWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkglWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.2. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkGLWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       1,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       1,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      12,   11,   11,   11, 0x05,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkGLWidget[] = {
+    "SkGLWidget\0\0drawComplete()\0"
+};
+
+void SkGLWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkGLWidget *_t = static_cast<SkGLWidget *>(_o);
+        switch (_id) {
+        case 0: _t->drawComplete(); break;
+        default: ;
+        }
+    }
+    Q_UNUSED(_a);
+}
+
+const QMetaObjectExtraData SkGLWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkGLWidget::staticMetaObject = {
+    { &QGLWidget::staticMetaObject, qt_meta_stringdata_SkGLWidget,
+      qt_meta_data_SkGLWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkGLWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkGLWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkGLWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkGLWidget))
+        return static_cast<void*>(const_cast< SkGLWidget*>(this));
+    return QGLWidget::qt_metacast(_clname);
+}
+
+int SkGLWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QGLWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 1)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 1;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkGLWidget::drawComplete()
+{
+    QMetaObject::activate(this, &staticMetaObject, 0, 0);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.2_SkInspectorWidget.cpp b/debugger/QT/moc_4.8.2_SkInspectorWidget.cpp
new file mode 100644
index 0000000..548d041
--- /dev/null
+++ b/debugger/QT/moc_4.8.2_SkInspectorWidget.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkInspectorWidget.h'
+**
+** Created: Thu Sep 6 11:16:07 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkInspectorWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkInspectorWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.2. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkInspectorWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       0,    0, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       0,       // signalCount
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkInspectorWidget[] = {
+    "SkInspectorWidget\0"
+};
+
+void SkInspectorWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    Q_UNUSED(_o);
+    Q_UNUSED(_id);
+    Q_UNUSED(_c);
+    Q_UNUSED(_a);
+}
+
+const QMetaObjectExtraData SkInspectorWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkInspectorWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkInspectorWidget,
+      qt_meta_data_SkInspectorWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkInspectorWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkInspectorWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkInspectorWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkInspectorWidget))
+        return static_cast<void*>(const_cast< SkInspectorWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkInspectorWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    return _id;
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.2_SkRasterWidget.cpp b/debugger/QT/moc_4.8.2_SkRasterWidget.cpp
new file mode 100644
index 0000000..fd7ed8e
--- /dev/null
+++ b/debugger/QT/moc_4.8.2_SkRasterWidget.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkrasterWidget.h'
+**
+** Created: Thu Sep 6 11:16:07 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkrasterWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkrasterWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.2. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkRasterWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       1,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       1,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      16,   15,   15,   15, 0x05,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkRasterWidget[] = {
+    "SkRasterWidget\0\0drawComplete()\0"
+};
+
+void SkRasterWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkRasterWidget *_t = static_cast<SkRasterWidget *>(_o);
+        switch (_id) {
+        case 0: _t->drawComplete(); break;
+        default: ;
+        }
+    }
+    Q_UNUSED(_a);
+}
+
+const QMetaObjectExtraData SkRasterWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkRasterWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkRasterWidget,
+      qt_meta_data_SkRasterWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkRasterWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkRasterWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkRasterWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkRasterWidget))
+        return static_cast<void*>(const_cast< SkRasterWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkRasterWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 1)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 1;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkRasterWidget::drawComplete()
+{
+    QMetaObject::activate(this, &staticMetaObject, 0, 0);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.2_SkSettingsWidget.cpp b/debugger/QT/moc_4.8.2_SkSettingsWidget.cpp
new file mode 100644
index 0000000..f9ffd28
--- /dev/null
+++ b/debugger/QT/moc_4.8.2_SkSettingsWidget.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkSettingsWidget.h'
+**
+** Created: Thu Sep 6 11:16:07 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkSettingsWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkSettingsWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.2. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkSettingsWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       5,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       3,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      35,   18,   17,   17, 0x05,
+      78,   62,   17,   17, 0x05,
+     104,   94,   17,   17, 0x05,
+
+ // slots: signature, parameters, type, tag, flags
+     138,  127,   17,   17, 0x08,
+     164,  157,   17,   17, 0x08,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkSettingsWidget[] = {
+    "SkSettingsWidget\0\0isStickyActivate\0"
+    "scrollingPreferences(bool)\0isSingleCommand\0"
+    "showStyle(bool)\0isEnabled\0"
+    "visibilityFilter(bool)\0newCommand\0"
+    "updateCommand(int)\0newHit\0updateHit(int)\0"
+};
+
+void SkSettingsWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkSettingsWidget *_t = static_cast<SkSettingsWidget *>(_o);
+        switch (_id) {
+        case 0: _t->scrollingPreferences((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 1: _t->showStyle((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 2: _t->visibilityFilter((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 3: _t->updateCommand((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 4: _t->updateHit((*reinterpret_cast< int(*)>(_a[1]))); break;
+        default: ;
+        }
+    }
+}
+
+const QMetaObjectExtraData SkSettingsWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkSettingsWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkSettingsWidget,
+      qt_meta_data_SkSettingsWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkSettingsWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkSettingsWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkSettingsWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkSettingsWidget))
+        return static_cast<void*>(const_cast< SkSettingsWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkSettingsWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 5)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 5;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkSettingsWidget::scrollingPreferences(bool _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+
+// SIGNAL 1
+void SkSettingsWidget::showStyle(bool _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 1, _a);
+}
+
+// SIGNAL 2
+void SkSettingsWidget::visibilityFilter(bool _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 2, _a);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.3_SkCanvasWidget.cpp b/debugger/QT/moc_4.8.3_SkCanvasWidget.cpp
new file mode 100644
index 0000000..55adb77
--- /dev/null
+++ b/debugger/QT/moc_4.8.3_SkCanvasWidget.cpp
@@ -0,0 +1,125 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkCanvasWidget.h'
+**
+** Created: Mon Sep 24 14:49:44 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.3)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkCanvasWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkCanvasWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.3. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkCanvasWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       4,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       3,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      31,   16,   15,   15, 0x05,
+      68,   57,   15,   15, 0x05,
+      92,   88,   15,   15, 0x05,
+
+ // slots: signature, parameters, type, tag, flags
+     122,  108,   15,   15, 0x08,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkCanvasWidget[] = {
+    "SkCanvasWidget\0\0newScaleFactor\0"
+    "scaleFactorChanged(float)\0newCommand\0"
+    "commandChanged(int)\0hit\0hitChanged(int)\0"
+    "zoomIncrement\0keyZoom(int)\0"
+};
+
+void SkCanvasWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkCanvasWidget *_t = static_cast<SkCanvasWidget *>(_o);
+        switch (_id) {
+        case 0: _t->scaleFactorChanged((*reinterpret_cast< float(*)>(_a[1]))); break;
+        case 1: _t->commandChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 2: _t->hitChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 3: _t->keyZoom((*reinterpret_cast< int(*)>(_a[1]))); break;
+        default: ;
+        }
+    }
+}
+
+const QMetaObjectExtraData SkCanvasWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkCanvasWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkCanvasWidget,
+      qt_meta_data_SkCanvasWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkCanvasWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkCanvasWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkCanvasWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkCanvasWidget))
+        return static_cast<void*>(const_cast< SkCanvasWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkCanvasWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 4)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 4;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkCanvasWidget::scaleFactorChanged(float _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+
+// SIGNAL 1
+void SkCanvasWidget::commandChanged(int _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 1, _a);
+}
+
+// SIGNAL 2
+void SkCanvasWidget::hitChanged(int _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 2, _a);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.3_SkDebuggerGUI.cpp b/debugger/QT/moc_4.8.3_SkDebuggerGUI.cpp
new file mode 100644
index 0000000..97ab700
--- /dev/null
+++ b/debugger/QT/moc_4.8.3_SkDebuggerGUI.cpp
@@ -0,0 +1,175 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkDebuggerGUI.h'
+**
+** Created: Mon Sep 24 14:50:01 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.3)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkDebuggerGUI.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkDebuggerGUI.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.3. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkDebuggerGUI[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+      30,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       1,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      23,   15,   14,   14, 0x05,
+
+ // slots: signature, parameters, type, tag, flags
+      43,   14,   14,   14, 0x08,
+      63,   14,   14,   14, 0x08,
+      78,   14,   14,   14, 0x08,
+     103,   14,   14,   14, 0x08,
+     124,   14,   14,   14, 0x08,
+     146,   14,   14,   14, 0x08,
+     160,   14,   14,   14, 0x08,
+     185,  175,   14,   14, 0x08,
+     206,   14,   14,   14, 0x08,
+     224,   14,   14,   14, 0x08,
+     237,  175,   14,   14, 0x08,
+     262,   14,   14,   14, 0x08,
+     277,   14,   14,   14, 0x08,
+     290,   14,   14,   14, 0x08,
+     317,  305,   14,   14, 0x08,
+     336,   14,   14,   14, 0x08,
+     353,   14,   14,   14, 0x08,
+     370,   14,   14,   14, 0x08,
+     390,   14,   14,   14, 0x08,
+     410,  405,   14,   14, 0x08,
+     437,   14,   14,   14, 0x08,
+     457,  448,   14,   14, 0x08,
+     476,   14,   14,   14, 0x28,
+     491,  405,   14,   14, 0x08,
+     527,   15,   14,   14, 0x08,
+     546,   14,   14,   14, 0x08,
+     560,   14,   14,   14, 0x08,
+     579,   14,   14,   14, 0x08,
+     604,  597,   14,   14, 0x08,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkDebuggerGUI[] = {
+    "SkDebuggerGUI\0\0command\0commandChanged(int)\0"
+    "actionBreakpoints()\0actionCancel()\0"
+    "actionClearBreakpoints()\0actionClearDeletes()\0"
+    "actionCommandFilter()\0actionClose()\0"
+    "actionDelete()\0isToggled\0actionGLWidget(bool)\0"
+    "actionInspector()\0actionPlay()\0"
+    "actionRasterWidget(bool)\0actionRewind()\0"
+    "actionSave()\0actionSaveAs()\0scaleFactor\0"
+    "actionScale(float)\0actionSettings()\0"
+    "actionStepBack()\0actionStepForward()\0"
+    "drawComplete()\0item\0loadFile(QListWidgetItem*)\0"
+    "openFile()\0isPaused\0pauseDrawing(bool)\0"
+    "pauseDrawing()\0registerListClick(QListWidgetItem*)\0"
+    "selectCommand(int)\0showDeletes()\0"
+    "toggleBreakpoint()\0toggleDirectory()\0"
+    "string\0toggleFilter(QString)\0"
+};
+
+void SkDebuggerGUI::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkDebuggerGUI *_t = static_cast<SkDebuggerGUI *>(_o);
+        switch (_id) {
+        case 0: _t->commandChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 1: _t->actionBreakpoints(); break;
+        case 2: _t->actionCancel(); break;
+        case 3: _t->actionClearBreakpoints(); break;
+        case 4: _t->actionClearDeletes(); break;
+        case 5: _t->actionCommandFilter(); break;
+        case 6: _t->actionClose(); break;
+        case 7: _t->actionDelete(); break;
+        case 8: _t->actionGLWidget((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 9: _t->actionInspector(); break;
+        case 10: _t->actionPlay(); break;
+        case 11: _t->actionRasterWidget((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 12: _t->actionRewind(); break;
+        case 13: _t->actionSave(); break;
+        case 14: _t->actionSaveAs(); break;
+        case 15: _t->actionScale((*reinterpret_cast< float(*)>(_a[1]))); break;
+        case 16: _t->actionSettings(); break;
+        case 17: _t->actionStepBack(); break;
+        case 18: _t->actionStepForward(); break;
+        case 19: _t->drawComplete(); break;
+        case 20: _t->loadFile((*reinterpret_cast< QListWidgetItem*(*)>(_a[1]))); break;
+        case 21: _t->openFile(); break;
+        case 22: _t->pauseDrawing((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 23: _t->pauseDrawing(); break;
+        case 24: _t->registerListClick((*reinterpret_cast< QListWidgetItem*(*)>(_a[1]))); break;
+        case 25: _t->selectCommand((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 26: _t->showDeletes(); break;
+        case 27: _t->toggleBreakpoint(); break;
+        case 28: _t->toggleDirectory(); break;
+        case 29: _t->toggleFilter((*reinterpret_cast< QString(*)>(_a[1]))); break;
+        default: ;
+        }
+    }
+}
+
+const QMetaObjectExtraData SkDebuggerGUI::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkDebuggerGUI::staticMetaObject = {
+    { &QMainWindow::staticMetaObject, qt_meta_stringdata_SkDebuggerGUI,
+      qt_meta_data_SkDebuggerGUI, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkDebuggerGUI::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkDebuggerGUI::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkDebuggerGUI::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkDebuggerGUI))
+        return static_cast<void*>(const_cast< SkDebuggerGUI*>(this));
+    return QMainWindow::qt_metacast(_clname);
+}
+
+int SkDebuggerGUI::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QMainWindow::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 30)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 30;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkDebuggerGUI::commandChanged(int _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.3_SkGLWidget.cpp b/debugger/QT/moc_4.8.3_SkGLWidget.cpp
new file mode 100644
index 0000000..ff70c84
--- /dev/null
+++ b/debugger/QT/moc_4.8.3_SkGLWidget.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkGLWidget.h'
+**
+** Created: Mon Sep 24 14:51:07 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.3)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkGLWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkGLWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.3. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkGLWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       1,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       1,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      12,   11,   11,   11, 0x05,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkGLWidget[] = {
+    "SkGLWidget\0\0drawComplete()\0"
+};
+
+void SkGLWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkGLWidget *_t = static_cast<SkGLWidget *>(_o);
+        switch (_id) {
+        case 0: _t->drawComplete(); break;
+        default: ;
+        }
+    }
+    Q_UNUSED(_a);
+}
+
+const QMetaObjectExtraData SkGLWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkGLWidget::staticMetaObject = {
+    { &QGLWidget::staticMetaObject, qt_meta_stringdata_SkGLWidget,
+      qt_meta_data_SkGLWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkGLWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkGLWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkGLWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkGLWidget))
+        return static_cast<void*>(const_cast< SkGLWidget*>(this));
+    return QGLWidget::qt_metacast(_clname);
+}
+
+int SkGLWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QGLWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 1)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 1;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkGLWidget::drawComplete()
+{
+    QMetaObject::activate(this, &staticMetaObject, 0, 0);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.3_SkInspectorWidget.cpp b/debugger/QT/moc_4.8.3_SkInspectorWidget.cpp
new file mode 100644
index 0000000..44dc33e
--- /dev/null
+++ b/debugger/QT/moc_4.8.3_SkInspectorWidget.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkInspectorWidget.h'
+**
+** Created: Mon Sep 24 14:50:20 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.3)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkInspectorWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkInspectorWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.3. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkInspectorWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       0,    0, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       0,       // signalCount
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkInspectorWidget[] = {
+    "SkInspectorWidget\0"
+};
+
+void SkInspectorWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    Q_UNUSED(_o);
+    Q_UNUSED(_id);
+    Q_UNUSED(_c);
+    Q_UNUSED(_a);
+}
+
+const QMetaObjectExtraData SkInspectorWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkInspectorWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkInspectorWidget,
+      qt_meta_data_SkInspectorWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkInspectorWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkInspectorWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkInspectorWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkInspectorWidget))
+        return static_cast<void*>(const_cast< SkInspectorWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkInspectorWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    return _id;
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.3_SkRasterWidget.cpp b/debugger/QT/moc_4.8.3_SkRasterWidget.cpp
new file mode 100644
index 0000000..f051078
--- /dev/null
+++ b/debugger/QT/moc_4.8.3_SkRasterWidget.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkRasterWidget.h'
+**
+** Created: Mon Sep 24 14:50:52 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.3)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkRasterWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkRasterWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.3. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkRasterWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       1,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       1,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      16,   15,   15,   15, 0x05,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkRasterWidget[] = {
+    "SkRasterWidget\0\0drawComplete()\0"
+};
+
+void SkRasterWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkRasterWidget *_t = static_cast<SkRasterWidget *>(_o);
+        switch (_id) {
+        case 0: _t->drawComplete(); break;
+        default: ;
+        }
+    }
+    Q_UNUSED(_a);
+}
+
+const QMetaObjectExtraData SkRasterWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkRasterWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkRasterWidget,
+      qt_meta_data_SkRasterWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkRasterWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkRasterWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkRasterWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkRasterWidget))
+        return static_cast<void*>(const_cast< SkRasterWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkRasterWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 1)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 1;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkRasterWidget::drawComplete()
+{
+    QMetaObject::activate(this, &staticMetaObject, 0, 0);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/moc_4.8.3_SkSettingsWidget.cpp b/debugger/QT/moc_4.8.3_SkSettingsWidget.cpp
new file mode 100644
index 0000000..9388958
--- /dev/null
+++ b/debugger/QT/moc_4.8.3_SkSettingsWidget.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'SkSettingsWidget.h'
+**
+** Created: Mon Sep 24 14:50:37 2012
+**      by: The Qt Meta Object Compiler version 63 (Qt 4.8.3)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "SkSettingsWidget.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'SkSettingsWidget.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 63
+#error "This file was generated using the moc from 4.8.3. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+QT_BEGIN_MOC_NAMESPACE
+static const uint qt_meta_data_SkSettingsWidget[] = {
+
+ // content:
+       6,       // revision
+       0,       // classname
+       0,    0, // classinfo
+       5,   14, // methods
+       0,    0, // properties
+       0,    0, // enums/sets
+       0,    0, // constructors
+       0,       // flags
+       3,       // signalCount
+
+ // signals: signature, parameters, type, tag, flags
+      35,   18,   17,   17, 0x05,
+      78,   62,   17,   17, 0x05,
+     104,   94,   17,   17, 0x05,
+
+ // slots: signature, parameters, type, tag, flags
+     138,  127,   17,   17, 0x08,
+     164,  157,   17,   17, 0x08,
+
+       0        // eod
+};
+
+static const char qt_meta_stringdata_SkSettingsWidget[] = {
+    "SkSettingsWidget\0\0isStickyActivate\0"
+    "scrollingPreferences(bool)\0isSingleCommand\0"
+    "showStyle(bool)\0isEnabled\0"
+    "visibilityFilter(bool)\0newCommand\0"
+    "updateCommand(int)\0newHit\0updateHit(int)\0"
+};
+
+void SkSettingsWidget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        Q_ASSERT(staticMetaObject.cast(_o));
+        SkSettingsWidget *_t = static_cast<SkSettingsWidget *>(_o);
+        switch (_id) {
+        case 0: _t->scrollingPreferences((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 1: _t->showStyle((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 2: _t->visibilityFilter((*reinterpret_cast< bool(*)>(_a[1]))); break;
+        case 3: _t->updateCommand((*reinterpret_cast< int(*)>(_a[1]))); break;
+        case 4: _t->updateHit((*reinterpret_cast< int(*)>(_a[1]))); break;
+        default: ;
+        }
+    }
+}
+
+const QMetaObjectExtraData SkSettingsWidget::staticMetaObjectExtraData = {
+    0,  qt_static_metacall
+};
+
+const QMetaObject SkSettingsWidget::staticMetaObject = {
+    { &QWidget::staticMetaObject, qt_meta_stringdata_SkSettingsWidget,
+      qt_meta_data_SkSettingsWidget, &staticMetaObjectExtraData }
+};
+
+#ifdef Q_NO_DATA_RELOCATION
+const QMetaObject &SkSettingsWidget::getStaticMetaObject() { return staticMetaObject; }
+#endif //Q_NO_DATA_RELOCATION
+
+const QMetaObject *SkSettingsWidget::metaObject() const
+{
+    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
+}
+
+void *SkSettingsWidget::qt_metacast(const char *_clname)
+{
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_SkSettingsWidget))
+        return static_cast<void*>(const_cast< SkSettingsWidget*>(this));
+    return QWidget::qt_metacast(_clname);
+}
+
+int SkSettingsWidget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+    _id = QWidget::qt_metacall(_c, _id, _a);
+    if (_id < 0)
+        return _id;
+    if (_c == QMetaObject::InvokeMetaMethod) {
+        if (_id < 5)
+            qt_static_metacall(this, _c, _id, _a);
+        _id -= 5;
+    }
+    return _id;
+}
+
+// SIGNAL 0
+void SkSettingsWidget::scrollingPreferences(bool _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+
+// SIGNAL 1
+void SkSettingsWidget::showStyle(bool _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 1, _a);
+}
+
+// SIGNAL 2
+void SkSettingsWidget::visibilityFilter(bool _t1)
+{
+    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+    QMetaObject::activate(this, &staticMetaObject, 2, _a);
+}
+QT_END_MOC_NAMESPACE
diff --git a/debugger/QT/qrc_SkIcons.cpp b/debugger/QT/qrc_SkIcons.cpp
new file mode 100644
index 0000000..2e873b0
--- /dev/null
+++ b/debugger/QT/qrc_SkIcons.cpp
@@ -0,0 +1,1146 @@
+/****************************************************************************
+** Resource object code
+**
+** Created: Mon Jul 16 13:14:59 2012
+**      by: The Resource Compiler for Qt version 4.8.1
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include <QtCore/qglobal.h>
+
+static const unsigned char qt_resource_data[] = {
+  // /usr/local/google/home/chudy/Icons/skia.png
+  0x0,0x0,0x4,0x1e,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x3,0x0,0x0,0x0,0x44,0xa4,0x8a,0xc6,
+  0x0,0x0,0x1,0x80,0x50,0x4c,0x54,0x45,0xff,0xff,0xff,0x0,0x55,0xac,0x2,0x64,
+  0xc4,0x8d,0x8d,0x83,0x0,0x61,0xbb,0x8d,0x8d,0x83,0x8d,0x8d,0x83,0x8d,0x8d,0x83,
+  0x7,0x86,0xfd,0x5,0x76,0xe1,0x1,0x5b,0xb4,0x5,0x7a,0xe7,0x3,0x6b,0xcf,0x0,
+  0x55,0xac,0x6,0x84,0xf8,0x6,0x7d,0xee,0x5,0x7a,0xe7,0x6,0x80,0xf1,0x3,0x6e,
+  0xd5,0x3,0x6b,0xcf,0x0,0x58,0xad,0x6,0x80,0xf1,0x4,0x74,0xde,0x6,0x7d,0xee,
+  0x3,0x6e,0xd5,0xb6,0xb6,0xb3,0xb6,0xb6,0xb3,0x95,0x95,0x8c,0x8d,0x8d,0x83,0x7,
+  0x86,0xfd,0xb6,0xb6,0xb3,0xb6,0xb6,0xb3,0x9c,0x9c,0x94,0x7,0x86,0xfd,0x1,0x72,
+  0xce,0x2,0x6b,0xc5,0x3,0x6b,0xcf,0x35,0xa3,0xdc,0x5,0x7a,0xe7,0x6,0x7d,0xee,
+  0x4,0x74,0xde,0x3a,0xa6,0xdf,0x6,0x80,0xf1,0x0,0x69,0xbc,0xff,0xce,0xa,0xfc,
+  0xb8,0x29,0x3,0x6e,0xd5,0x5,0x76,0xe1,0x2e,0xa8,0x46,0x3a,0xae,0xed,0x36,0x9f,
+  0xd6,0x0,0x55,0xac,0x6,0x84,0xf8,0x0,0x84,0x3d,0x2,0x9a,0x3a,0x0,0x61,0xbb,
+  0x95,0x95,0x8c,0xae,0xae,0xa9,0x6d,0xb6,0xdf,0x69,0xac,0xcf,0x18,0x92,0x86,0xe,
+  0x75,0xcd,0x3b,0xb2,0xf1,0x83,0xd0,0xff,0x2,0x7b,0xd5,0x8d,0x8d,0x83,0x42,0xbb,
+  0xff,0x3,0x73,0xd5,0x0,0x58,0xad,0x2,0x64,0xc4,0x6f,0xca,0xff,0x39,0xaa,0xe6,
+  0x59,0xbc,0x9b,0x31,0xad,0xd0,0x43,0xb0,0x57,0x59,0x98,0xd0,0x22,0xa4,0x43,0x0,
+  0x8c,0x3b,0x96,0xd7,0xff,0x3f,0xb9,0xfd,0x87,0xbf,0xdf,0x8c,0xd3,0xff,0x33,0xaa,
+  0x48,0xe,0x71,0xc6,0x75,0xcc,0xff,0xd,0x83,0x32,0x99,0xab,0x8e,0x33,0x99,0xcc,
+  0x70,0xad,0xcf,0x51,0xc0,0xff,0x0,0x94,0x3a,0x0,0x7b,0x3f,0x0,0x62,0xb6,0x9,
+  0x9c,0x3c,0x83,0xaf,0xc7,0x66,0xb4,0xdf,0xf,0x9e,0x3d,0xa5,0xa5,0x9f,0x59,0xc3,
+  0xff,0x1,0x5b,0xb4,0x57,0xaf,0xdf,0x9c,0x9c,0x94,0x13,0x7d,0xd8,0x1a,0x84,0xe0,
+  0x47,0x75,0x9e,0x3d,0xb5,0xf6,0x18,0xa5,0x82,0x27,0x8c,0x39,0xa,0x82,0x5c,0x0,
+  0x7b,0xac,0xc,0x6b,0xbc,0x39,0xac,0x4a,0x7,0x86,0xfd,0x29,0xa6,0x45,0x1,0x94,
+  0x66,0x63,0xc6,0xff,0x82,0xb3,0xcf,0x7b,0xce,0xff,0x1a,0xa2,0x41,0x22,0x8b,0xe8,
+  0x0,0x7b,0x67,0xfe,0xc1,0x17,0x1,0x8d,0x66,0x0,0x82,0x67,0x6a,0xc8,0xff,0x49,
+  0xbe,0xff,0x3e,0xb6,0xf7,0x13,0x9f,0x3f,0xa2,0xb1,0x27,0x7c,0x0,0x0,0x0,0x22,
+  0x74,0x52,0x4e,0x53,0x0,0x44,0x44,0x44,0x44,0x66,0xbb,0xdd,0x44,0x44,0x44,0x88,
+  0x88,0x22,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x88,0x88,0x88,0x88,0x99,0xbb,0x22,
+  0x22,0x22,0x22,0x33,0x33,0x33,0xc3,0x29,0x6b,0x68,0x0,0x0,0x2,0x2b,0x49,0x44,
+  0x41,0x54,0x78,0x5e,0x95,0x93,0x65,0x73,0xdb,0x40,0x18,0x6,0x65,0x3b,0x55,0x92,
+  0xda,0x29,0xdb,0x49,0x53,0x48,0x2b,0x66,0x33,0x33,0x33,0x63,0x98,0x99,0xa9,0xc,
+  0x7f,0xbd,0xef,0x9d,0x9a,0xa8,0x93,0x49,0x3b,0xd3,0xd5,0xc7,0xdd,0x79,0x4e,0x9a,
+  0x39,0x11,0xff,0xc9,0x5c,0x38,0x3c,0xf7,0x2f,0x3d,0xbb,0x1d,0x4a,0x6f,0xcf,0xbe,
+  0xfb,0xab,0x5e,0x4f,0x93,0x16,0xb,0x9,0x89,0xe5,0x5e,0x1d,0xe,0x91,0x58,0x40,
+  0x12,0x22,0xef,0xae,0xbc,0x7d,0x19,0xe,0x8d,0x3f,0xb8,0x89,0x1f,0x8c,0x87,0x42,
+  0xe4,0xab,0x7b,0xb5,0x91,0xa4,0xd3,0xe4,0x6b,0xdd,0xbe,0xb7,0xdb,0x47,0xd5,0xea,
+  0x9e,0xdd,0x6e,0x9f,0x9e,0xb6,0x5a,0xad,0xe,0x87,0x63,0x66,0xc6,0x66,0xb3,0xed,
+  0x1,0x36,0xdb,0x14,0x41,0xbc,0x39,0x43,0x48,0x67,0x3e,0x49,0xe2,0x79,0x8e,0x73,
+  0x3a,0x5,0x41,0xd3,0x18,0x86,0x66,0xf2,0xc1,0xa0,0xd7,0x9b,0x9b,0x22,0x26,0x40,
+  0xfb,0x0,0xe9,0x36,0xc8,0xa2,0x80,0xc9,0x43,0xb0,0xb9,0xe9,0x35,0x11,0x13,0x3e,
+  0xf0,0x1f,0x11,0xbb,0xc0,0xe,0x10,0xdf,0x89,0xc7,0xbb,0xdd,0x93,0x93,0x6e,0x1e,
+  0x7,0x8f,0xc0,0x57,0xaf,0x5a,0xad,0x56,0x24,0x12,0x59,0x5d,0xdd,0xda,0x12,0x45,
+  0x91,0xf5,0x78,0x3c,0x43,0x60,0x57,0xc6,0x1,0xcc,0xaf,0x34,0x1a,0x8d,0x76,0x3b,
+  0x79,0x5d,0xf8,0x76,0xb9,0x31,0xff,0x3d,0xd3,0xfc,0xb1,0xef,0xe,0xab,0xa5,0xa2,
+  0x48,0xe5,0xf4,0x40,0xba,0x42,0x41,0x32,0xd9,0x2b,0x5c,0xa2,0xa0,0x9,0xc1,0x40,
+  0x51,0x3e,0x47,0xcb,0x59,0x14,0x3c,0xf5,0x49,0x10,0xb4,0xc1,0x5f,0xf7,0xd0,0xc0,
+  0x7c,0xa6,0xd9,0xdc,0x4f,0x28,0x8a,0xa2,0xe,0x3c,0x29,0x1c,0xc0,0xeb,0x8f,0x74,
+  0x6f,0x9c,0x90,0x50,0x15,0x55,0x5d,0x19,0xa6,0xbc,0x38,0xe0,0xf9,0x51,0xf2,0xc6,
+  0x6f,0xcc,0x67,0x70,0x10,0x1d,0xa8,0xeb,0x2c,0x8b,0x83,0xc7,0x3c,0xcf,0x47,0x41,
+  0xf7,0x4a,0xa5,0x4a,0xe7,0xbc,0x76,0x50,0xce,0x80,0x4f,0xb8,0x45,0x37,0x2b,0xb2,
+  0x14,0xe,0x38,0x9e,0x8b,0x16,0x22,0xa5,0x4a,0xa5,0x3,0xfe,0xc3,0xcf,0xe5,0x3,
+  0x8,0xdc,0xee,0x62,0xb1,0xc8,0xb2,0x59,0x14,0x3c,0xe1,0x38,0xae,0x5a,0xaa,0x9c,
+  0x9e,0x76,0x5c,0xd8,0x7,0x2,0x17,0xe5,0x84,0xee,0x61,0x41,0xf,0x9c,0x2e,0xa4,
+  0x5d,0x35,0xe4,0x97,0x17,0x80,0x58,0xb9,0x58,0x14,0x59,0x76,0x28,0xa3,0xaf,0x98,
+  0x74,0x3a,0x9d,0x87,0x2e,0xd7,0x39,0xcc,0x23,0x1f,0x0,0x5f,0xaf,0xd7,0x8f,0x62,
+  0xb1,0xd8,0xd1,0x22,0x5e,0x98,0x14,0x4,0xe1,0xb0,0x56,0x43,0x7a,0xd,0xf6,0x75,
+  0xef,0xf7,0xfb,0x17,0x1,0x3d,0xc8,0xa,0x42,0x1f,0x2c,0xe8,0xbb,0x5e,0xf,0x9e,
+  0x9,0x9a,0xd0,0x5f,0x3,0x8b,0xb5,0xe1,0x8d,0x40,0xd3,0xb4,0x7e,0x0,0xa1,0x6b,
+  0xc3,0x1b,0x41,0xd6,0xbf,0xb0,0xa0,0x6b,0xc3,0x63,0x18,0x14,0x3c,0x87,0xdb,0x73,
+  0x7c,0x81,0xf9,0x82,0xf8,0xa,0x7c,0xd2,0x39,0xe,0x7a,0x73,0x10,0x50,0xc,0x43,
+  0xa5,0x7e,0x43,0xa5,0x28,0x8a,0xa2,0x29,0x9a,0xce,0xa3,0x7,0xee,0xe4,0x92,0x89,
+  0x30,0x33,0x29,0xa,0xc1,0xd0,0x18,0x59,0xe,0xca,0x4b,0xb7,0xc8,0xb0,0x40,0xe4,
+  0x69,0x86,0xa2,0x6f,0x34,0xf6,0x6,0x39,0x2f,0x1,0x98,0xc7,0xcc,0xe6,0x31,0xcc,
+  0x43,0xe0,0x85,0xe9,0xf,0xe0,0xb7,0xf8,0x5,0xfe,0x47,0xd1,0xc6,0x31,0x88,0x99,
+  0xe7,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
+    // /usr/local/google/home/chudy/Icons/delete.png
+  0x0,0x0,0x5,0x87,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0x73,0x7a,0x7a,0xf4,
+  0x0,0x0,0x0,0x4,0x73,0x42,0x49,0x54,0x8,0x8,0x8,0x8,0x7c,0x8,0x64,0x88,
+  0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xa,0xf0,0x0,0x0,0xa,0xf0,
+  0x1,0x42,0xac,0x34,0x98,0x0,0x0,0x0,0x18,0x74,0x45,0x58,0x74,0x53,0x6f,0x66,
+  0x74,0x77,0x61,0x72,0x65,0x0,0x41,0x64,0x6f,0x62,0x65,0x20,0x46,0x69,0x72,0x65,
+  0x77,0x6f,0x72,0x6b,0x73,0x4f,0xb3,0x1f,0x4e,0x0,0x0,0x5,0x5,0x49,0x44,0x41,
+  0x54,0x58,0x85,0xc5,0x97,0x5d,0x6c,0x54,0x45,0x14,0xc7,0xff,0x67,0xe6,0xee,0x6e,
+  0x3f,0xb7,0xd0,0x52,0xa,0xba,0x18,0x40,0x81,0x2,0x9a,0xd0,0xa6,0x2d,0xf1,0xb,
+  0x5b,0xe5,0x81,0x25,0x25,0x6b,0xfb,0x62,0x8c,0x10,0xa3,0xc4,0xf8,0x80,0x31,0x68,
+  0xa2,0x4,0x9f,0x4c,0x48,0xd4,0x7,0x13,0x24,0x7e,0x24,0x3e,0xa8,0x60,0xc4,0x68,
+  0x4c,0x1b,0x4d,0xb0,0x54,0x25,0x88,0x89,0x89,0x1f,0x58,0x8a,0xa9,0x2,0x96,0x96,
+  0x60,0xd5,0x7e,0xb1,0xbb,0x94,0x76,0x5b,0xee,0xee,0x9d,0x99,0xe3,0x43,0xdb,0xb5,
+  0xbb,0xbd,0xfb,0x51,0x8d,0xe1,0x24,0x27,0xb9,0xb9,0x33,0xe7,0xfc,0x7f,0x33,0x73,
+  0x66,0xcf,0x5d,0x62,0x66,0xdc,0x48,0x13,0x37,0x54,0x1d,0x80,0x95,0x69,0xe0,0x57,
+  0x6f,0x49,0x69,0x41,0xed,0xca,0x97,0x49,0x8a,0x4a,0x15,0x8d,0xbd,0xba,0xf6,0xfc,
+  0xa5,0xd3,0xb,0x4d,0xde,0xdf,0xb0,0xf1,0x45,0xe1,0xf3,0x6c,0x54,0x31,0xfb,0xe8,
+  0x9a,0xee,0xb,0x9f,0xba,0xcd,0x21,0xb7,0x23,0xe8,0xb9,0x29,0x50,0x56,0xbc,0x7e,
+  0xf9,0x9,0x63,0x74,0x1d,0xc,0x83,0x3c,0x32,0xe1,0x84,0xc7,0xb7,0x55,0x9f,0xed,
+  0xfd,0x3a,0x5f,0xf1,0xbe,0x2d,0x9b,0xda,0xc9,0xb2,0x5a,0x58,0x29,0x8,0xaf,0x7,
+  0xda,0x76,0x76,0xaf,0xf9,0xf6,0xec,0xbb,0x39,0x1,0xba,0x56,0xac,0x28,0xf3,0xaf,
+  0x5f,0x7e,0x42,0x8d,0xc5,0xea,0xe2,0xa3,0x57,0x1,0x66,0x58,0xfe,0x62,0xf8,0xaa,
+  0x16,0x27,0x9c,0x68,0x2c,0xb8,0xe1,0xcc,0x85,0x93,0xb9,0xc4,0x7b,0x9b,0x6a,0xdb,
+  0x8d,0xa3,0x5a,0xe2,0x83,0x61,0xb0,0x36,0x10,0x5,0x3e,0x14,0x6,0x2a,0x61,0x14,
+  0xef,0x5e,0xf7,0x4d,0x57,0xa,0x44,0xa,0xc0,0xd9,0x9a,0xea,0xb2,0xc2,0x25,0xfe,
+  0xaf,0x12,0x63,0xb1,0xfa,0x78,0x78,0xc,0xc2,0x92,0x0,0x0,0xd6,0x6,0xb2,0xc8,
+  0x87,0xc2,0xaa,0xf2,0x44,0x22,0x1a,0xb,0xde,0xd1,0xed,0xe,0x41,0x20,0x9c,0x6f,
+  0xac,0x69,0xd7,0x8e,0x6a,0xb1,0x87,0x22,0x80,0x10,0x20,0x2,0xd8,0x30,0x48,0xa,
+  0x14,0x5,0x96,0x82,0x41,0x3b,0xd7,0x9f,0x3c,0x7d,0x74,0x36,0x26,0xa5,0x8,0x65,
+  0x49,0xe1,0x63,0xd7,0xaf,0xc6,0xea,0x27,0xaf,0x8c,0x41,0x4b,0x9,0x87,0x1,0x87,
+  0x1,0x25,0x4,0xec,0xc9,0x38,0x26,0x86,0xa3,0x5e,0x59,0x5e,0xd2,0x71,0xa6,0x66,
+  0xdd,0xfd,0x6e,0x0,0xbf,0x34,0xd6,0xb4,0x25,0x12,0xaa,0x65,0x62,0x30,0x2,0x25,
+  0x4,0x14,0x66,0xe2,0x89,0xe0,0x28,0x8d,0x89,0xbf,0xae,0x40,0x25,0x12,0x87,0xba,
+  0x9b,0x6a,0xa5,0x2b,0x40,0xec,0xca,0xd8,0xd4,0x64,0x74,0x1c,0xc6,0x92,0xd0,0x40,
+  0x8a,0x1b,0x29,0x10,0x9f,0x8a,0x63,0x62,0x38,0xea,0xf3,0x96,0xfb,0x3b,0x7e,0xaa,
+  0xa9,0x4e,0x42,0x10,0x8,0x3d,0x8d,0xb5,0xed,0x4e,0x42,0xb5,0x4e,0xc,0x45,0x60,
+  0xa4,0x98,0x17,0xaf,0x85,0x98,0x86,0x18,0x8e,0x4e,0xe9,0x71,0x9b,0x5d,0x1,0x74,
+  0x49,0xe9,0x11,0x23,0x45,0xa7,0x2,0xe0,0xe6,0x5a,0xa,0xd8,0x53,0x36,0xc6,0x87,
+  0x23,0x3e,0x6f,0x79,0x69,0xc7,0xf7,0xb5,0xeb,0xee,0x5,0x80,0xee,0xc6,0x9a,0xb6,
+  0x84,0xa3,0x5a,0xae,0xd,0x85,0xa1,0x25,0xb9,0xc6,0x2a,0x0,0x8a,0x48,0x1b,0xcb,
+  0x7a,0xa2,0xae,0xeb,0x9c,0x49,0xc2,0xa7,0x17,0x61,0xe7,0xe6,0xd,0xd2,0x17,0x9d,
+  0x38,0xc6,0x86,0xb7,0xb9,0x6d,0x33,0x8,0x60,0x65,0xe0,0x29,0xf2,0xc1,0xbf,0xac,
+  0x62,0xc,0xc0,0xcf,0x8e,0x1d,0xbf,0x6f,0x7c,0x30,0xc,0x92,0xd2,0x35,0x64,0x26,
+  0x4e,0x93,0x10,0xa1,0xa6,0xde,0x81,0xcf,0x53,0x5e,0xbb,0x5d,0xc3,0xe3,0x9b,0x1b,
+  0xa4,0x27,0x32,0x74,0x8c,0x39,0x3,0x4,0xa6,0xb,0xd3,0x5b,0x5c,0x80,0x2,0x7f,
+  0x31,0xae,0xd,0x45,0x20,0x84,0x0,0x28,0xa3,0xbc,0x26,0x21,0x43,0x5b,0x2f,0xfe,
+  0xfe,0x79,0xfa,0x80,0x2b,0x0,0x0,0x7c,0x76,0x4f,0xbd,0xf4,0xe,0xf,0x1f,0x43,
+  0x36,0x8,0xc3,0x60,0x63,0x92,0xb7,0x25,0xa3,0xb8,0x94,0xa1,0x6d,0xbd,0xf3,0xc5,
+  0xb3,0x2,0x0,0x40,0xdb,0xdd,0x75,0xd2,0x3b,0x32,0x92,0x15,0x22,0x87,0x69,0x92,
+  0x32,0xd4,0x9c,0x41,0x3c,0x27,0x0,0x0,0x7c,0xdc,0x50,0x23,0xbd,0x91,0xf0,0xbf,
+  0x81,0xd0,0x24,0x45,0xe8,0xc1,0x8b,0x3,0x19,0xc5,0x81,0x3c,0x9a,0xd1,0x43,0x3f,
+  0x76,0xeb,0x68,0xf3,0x23,0x41,0x4d,0xd4,0x9e,0xb1,0xba,0xe7,0x7b,0xcc,0x48,0xf9,
+  0x40,0x2e,0xf1,0xbc,0x0,0x0,0x60,0xf7,0xa1,0x57,0xa0,0x8,0x71,0xd,0x46,0x9e,
+  0x6e,0x12,0xc4,0x53,0xf9,0xe4,0xce,0x79,0x4,0x0,0xf0,0xc1,0xad,0x81,0x76,0x30,
+  0xb7,0x2c,0xe4,0xcb,0x81,0x80,0x38,0x84,0xc,0xee,0xec,0x1b,0xc8,0xda,0xc0,0x72,
+  0x2,0x1c,0x59,0x1d,0x68,0x63,0xe6,0xd6,0x5,0x68,0xcf,0xb5,0x38,0x49,0x19,0x7c,
+  0x34,0xb,0x44,0x56,0x80,0x77,0x56,0x2f,0x7c,0xe5,0xf3,0x4,0xa6,0x21,0xb6,0x3f,
+  0xde,0x37,0xe0,0xde,0xc0,0x32,0x1,0xbc,0xfd,0xdf,0x56,0x9e,0x2a,0x42,0xd3,0xc7,
+  0xf1,0xa4,0xcb,0x4e,0xb8,0x2,0xbc,0xb1,0x2a,0xd0,0x86,0x7c,0xc4,0x9,0x93,0x44,
+  0x74,0x8e,0xd,0xd7,0xe7,0x31,0x37,0x4e,0x42,0x6,0xf7,0xf4,0xa7,0x42,0xa4,0xdc,
+  0x82,0xf7,0xef,0xaa,0xb3,0xe,0xad,0xa,0xb4,0x69,0x36,0xad,0xd9,0xaa,0x5c,0x81,
+  0xa1,0x0,0x6d,0x84,0x6c,0xdd,0x73,0xe9,0xcf,0x6,0x4d,0xd4,0xa9,0x72,0xdd,0xc,
+  0x66,0x9f,0xd2,0xfa,0xf8,0xeb,0xb7,0xdd,0xb2,0x25,0x23,0x40,0x64,0x74,0x74,0x97,
+  0x62,0xd3,0x9a,0xeb,0x9e,0x6b,0x40,0xb3,0x14,0xa1,0xa7,0xfb,0x7,0xbe,0x4,0x0,
+  0x5e,0xba,0x78,0x87,0x21,0xca,0xd8,0x45,0xff,0x71,0xf6,0x39,0x5a,0x1f,0x7e,0xad,
+  0x7a,0x75,0x52,0x37,0x5,0xc0,0x61,0x2e,0x77,0x72,0x26,0x81,0x36,0x52,0x86,0x9e,
+  0xe9,0xff,0x23,0xf9,0x23,0xb3,0xf7,0x87,0x1e,0x65,0x2f,0x5b,0xd6,0xac,0x89,0x3a,
+  0x73,0xc5,0x3b,0xcc,0x8b,0xe2,0xb6,0x9d,0x6c,0x5b,0x29,0x0,0x54,0x50,0x78,0xd8,
+  0x8,0xd9,0xe3,0x30,0xa0,0xd2,0xdc,0x61,0x40,0x81,0x8c,0x11,0x32,0xf4,0xdc,0x1c,
+  0xf1,0x59,0x7b,0xe1,0xbb,0x2e,0x6d,0x57,0x54,0x36,0x1b,0x50,0xa7,0x5b,0xbc,0x9a,
+  0x8e,0x7,0x7b,0xbc,0xcf,0xef,0xbb,0x3c,0xa8,0x93,0x9a,0xe9,0x45,0xf8,0xd6,0x9d,
+  0x75,0x15,0x91,0x91,0x91,0x53,0xac,0xf5,0xed,0x73,0x47,0x8,0x50,0x1e,0xcb,0xda,
+  0xb1,0xbf,0x7f,0xa0,0x33,0x5d,0x3c,0xdd,0xe,0xac,0xbc,0xb9,0x83,0x99,0x83,0x29,
+  0xf1,0x44,0xf0,0x78,0x7d,0x4f,0xed,0xef,0xbd,0xf4,0x66,0xca,0xa2,0x99,0x19,0x16,
+  0xd1,0x8c,0x6,0x84,0x62,0xd6,0x2f,0xd5,0x6d,0xaa,0xb4,0x23,0xe1,0x53,0xc4,0x66,
+  0x3,0x18,0x0,0x91,0x1e,0x37,0xfc,0xf0,0xc1,0x81,0xc1,0x4f,0x2c,0xa2,0xb2,0x99,
+  0xb9,0xe9,0xdd,0x9f,0x1,0x40,0x3,0x89,0x4d,0xcb,0xab,0xcc,0x76,0x8f,0xe8,0xb0,
+  0x88,0x9a,0xc0,0xc,0x10,0xc1,0x58,0x9e,0x67,0xf,0xf4,0x5d,0x3e,0x48,0x44,0x52,
+  0x2,0x6,0x0,0x2b,0xe6,0x24,0x40,0x1,0x80,0xa,0x0,0xa5,0x0,0x7c,0x1a,0x88,
+  0xaf,0xf5,0x97,0x6,0xb6,0xfa,0x8b,0xf7,0x59,0xc0,0xa2,0xdf,0xec,0xc4,0x87,0x5f,
+  0x84,0xa3,0x27,0x24,0xb0,0x64,0xce,0xb1,0xb9,0x2,0x0,0x60,0xd,0x5c,0x2f,0xb0,
+  0x2c,0xb9,0xab,0xaa,0x62,0x6f,0x11,0xd1,0xca,0x11,0x6d,0x8e,0x7f,0x34,0x34,0xfa,
+  0x1e,0x80,0x62,0x9,0x38,0x0,0x26,0x0,0x44,0x14,0xb3,0x3d,0xb,0x60,0x1,0x28,
+  0x2,0xe0,0x3,0xe0,0x1,0xe0,0xd1,0xd3,0xdf,0x92,0x71,0x0,0x12,0x80,0x47,0x4e,
+  0xc3,0xcd,0xa,0xd3,0x9c,0xe7,0x14,0xf1,0xb9,0x3b,0x31,0x23,0xc4,0x0,0xc,0x1,
+  0x45,0x62,0xa6,0xe,0x67,0xf2,0x4e,0x29,0x66,0x95,0x57,0x33,0x2,0x30,0x7b,0x4c,
+  0xe9,0x96,0xe,0x90,0x34,0x95,0x67,0xde,0xbc,0x1,0xfe,0x2f,0xbb,0xe1,0xff,0x8e,
+  0xff,0x6,0xd,0xb3,0xf2,0x0,0x51,0x1b,0x2c,0x34,0x0,0x0,0x0,0x0,0x49,0x45,
+  0x4e,0x44,0xae,0x42,0x60,0x82,
+    // /usr/local/google/home/chudy/Icons/breakpoint_16x16.png
+  0x0,0x0,0x4,0x54,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,
+  0x0,0x0,0x4,0x1b,0x49,0x44,0x41,0x54,0x38,0x11,0x1,0x10,0x4,0xef,0xfb,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd3,0x40,0xc,
+  0xcf,0x14,0x4d,0x5c,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0xe4,0xdf,
+  0x0,0x1e,0xe4,0xb9,0x3,0x1,0xab,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd5,0x39,0xe,0x12,0xe3,0x7e,
+  0x57,0xff,0xf4,0xd5,0xcb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xde,0x68,
+  0x3c,0xff,0xcc,0x3d,0xb,0xed,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xcd,0x49,0x1c,0xfd,0xa,
+  0x16,0x19,0x2,0x14,0x2d,0x30,0x0,0xf5,0xda,0xd1,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x0,0xc,
+  0x58,0x79,0x0,0xf6,0xbb,0xa0,0x0,0xe2,0xbb,0xbb,0x31,0x3b,0xcb,0xf5,0xd0,0x0,
+  0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0xbe,0x33,0xa,0x4a,0x12,0x25,0x28,0x2,
+  0x23,0x92,0xb7,0x0,0xf2,0xd5,0xce,0x0,0xfd,0xfb,0xfd,0x0,0xfd,0xfb,0xfd,0x0,
+  0xfd,0xfb,0xfd,0x0,0xfd,0xfb,0xfd,0x0,0xfd,0xfb,0xfd,0x0,0xfc,0xfa,0xfc,0x0,
+  0xfe,0xdb,0xc8,0x0,0xf2,0xe1,0xe1,0x0,0x4,0xc,0x9,0xcf,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,0x6f,0x3c,0x29,0xda,0xfe,0xd,0x16,
+  0x0,0xf8,0xb8,0x9c,0x0,0xfb,0xf8,0xf9,0x0,0xfe,0xfe,0xfe,0x0,0xfe,0xfe,0xfe,
+  0x0,0xfe,0xfe,0xfe,0x0,0xfe,0xfe,0xfe,0x0,0xfe,0xfe,0xfe,0x0,0xfe,0xfe,0xfe,
+  0x0,0xf7,0xe1,0xdc,0x0,0x1c,0x8a,0xb0,0x0,0xff,0xda,0xc9,0x0,0x4d,0xfb,0xe8,
+  0xf9,0xa3,0xe8,0xfc,0xc4,0x2,0xb4,0x2c,0xa,0x9d,0x7,0x5,0x4,0x0,0x16,0x5d,
+  0x70,0x0,0xf6,0xef,0xf3,0x0,0xfc,0xfa,0xfd,0x0,0xfc,0xfa,0xfd,0x0,0xfc,0xfa,
+  0xfd,0x0,0xfc,0xfa,0xfd,0x0,0xfc,0xfa,0xfd,0x0,0xfc,0xfa,0xfd,0x0,0xfc,0xfa,
+  0xfd,0x0,0xfc,0xfa,0xfd,0x0,0xe8,0x84,0x62,0x0,0xef,0xf1,0xfc,0x0,0xd,0x1a,
+  0x1c,0x87,0x0,0x0,0x0,0x0,0x1,0xc7,0x50,0x2c,0xff,0x11,0x4a,0x5b,0x0,0xfa,
+  0xb9,0xa3,0x0,0xfd,0xfe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x13,0x7f,0xa0,0x0,0xef,
+  0x83,0x63,0x0,0xda,0xd4,0xdc,0xc4,0x4,0xea,0xe7,0xed,0x0,0x4,0xa3,0x8d,0x0,
+  0xf2,0xb,0x17,0x0,0x7,0xe9,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfd,0xff,0x0,0x0,0xa,0xdb,0xd2,0x0,
+  0xfd,0xfe,0xff,0x0,0xfa,0xfe,0x2,0x6d,0x2,0xe6,0xe5,0xf0,0x1b,0xfa,0xa,0x13,
+  0x0,0x15,0x6d,0x81,0x0,0xf9,0xfb,0xff,0x0,0xfa,0xfb,0xff,0x0,0xfa,0xfb,0xff,
+  0x0,0xfa,0xfb,0xff,0x0,0xfa,0xfb,0xff,0x0,0xfa,0xfb,0xff,0x0,0xfa,0xfb,0xff,
+  0x0,0xfa,0xfb,0xff,0x0,0xfa,0xfb,0xff,0x0,0x5,0x59,0x70,0x0,0xe7,0x83,0x6f,
+  0x0,0xcf,0xd0,0xdd,0xf4,0x5b,0xdb,0xf5,0xd0,0x2,0x69,0xe4,0xf7,0xe6,0xce,0xd6,
+  0xe2,0xde,0xeb,0x74,0x5e,0x0,0x0,0x48,0x5b,0x0,0xfc,0xfc,0x0,0x0,0xfc,0xfc,
+  0x0,0x0,0xfc,0xfc,0x0,0x0,0xfc,0xfc,0x0,0x0,0xfc,0xfc,0x0,0x0,0xfc,0xfc,
+  0x0,0x0,0xfc,0xfc,0x0,0x0,0xf6,0xfa,0x0,0x0,0x4,0xe5,0xdb,0x0,0xfc,0x16,
+  0x1d,0x0,0xd,0xa,0xf7,0x13,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x0,0x6d,
+  0xe3,0xf7,0x23,0xf6,0x13,0x1b,0x0,0x6,0xfc,0xf8,0x0,0xf8,0xfa,0x0,0x0,0xfb,
+  0xfc,0x0,0x0,0xfb,0xfc,0x0,0x0,0xfb,0xfc,0x0,0x0,0xfb,0xfc,0x0,0x0,0xfb,
+  0xfc,0x0,0x0,0xfb,0xfc,0x0,0x0,0xe,0x8c,0xa4,0x0,0xef,0xba,0xb8,0x0,0xd1,
+  0xd4,0xdf,0xc4,0x56,0xd5,0x0,0xfa,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x2d,0xf6,0xf5,0x18,0xf,0xde,0xda,0x34,0x5,0x7a,0x8c,0x0,
+  0xeb,0xb9,0xb9,0x0,0xf6,0xfa,0x0,0x0,0xf6,0xfa,0x0,0x0,0xf6,0xfa,0x0,0x0,
+  0xf6,0xfa,0x0,0x0,0xf9,0x8,0xe,0x0,0x9,0xc6,0xb8,0x0,0xe9,0xfc,0x2,0x0,
+  0x68,0xe0,0xf2,0x1f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x9d,0x24,0x14,0xff,0x1e,0xfc,0xf8,
+  0x0,0x4,0x2,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf2,0x2,0x6,0x0,0xcf,0xef,0xf4,
+  0x6d,0x80,0xed,0xf9,0x94,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x12,0x0,0x0,0x0,0xa,0x5b,0xa,0x5,0x48,0x22,0x6,
+  0x1,0x9b,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x83,0xf0,
+  0xfa,0x27,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0xf2,0x1,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0xfa,0x0,0x0,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,
+  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe1,
+  0xf,0x4b,0x5c,0x44,0xc0,0xce,0x74,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,
+  0x42,0x60,0x82,
+    // /usr/local/google/home/chudy/Icons/blank.png
+  0x0,0x0,0x0,0x92,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x10,0x0,0x0,0x0,0x10,0x8,0x6,0x0,0x0,0x0,0x1f,0xf3,0xff,0x61,
+  0x0,0x0,0x0,0x1,0x73,0x52,0x47,0x42,0x0,0xae,0xce,0x1c,0xe9,0x0,0x0,0x0,
+  0x6,0x62,0x4b,0x47,0x44,0x0,0xff,0x0,0xff,0x0,0xff,0xa0,0xbd,0xa7,0x93,0x0,
+  0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,
+  0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdc,0x6,0xb,
+  0x14,0xb,0x34,0x28,0x6,0x22,0x87,0x0,0x0,0x0,0x12,0x49,0x44,0x41,0x54,0x38,
+  0xcb,0x63,0x60,0x18,0x5,0xa3,0x60,0x14,0x8c,0x2,0x8,0x0,0x0,0x4,0x10,0x0,
+  0x1,0x85,0x3f,0xaa,0x72,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,
+  0x82,
+    // /usr/local/google/home/chudy/Icons/breakpoint.png
+  0x0,0x0,0x8,0xc7,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x1,0x4,0x7d,0x4a,0x62,
+  0x0,0x0,0x0,0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,
+  0x1,0x0,0x9a,0x9c,0x18,0x0,0x0,0x0,0x4,0x67,0x41,0x4d,0x41,0x0,0x0,0xd7,
+  0xfb,0x6f,0x33,0x18,0x1,0x0,0x0,0x0,0x20,0x63,0x48,0x52,0x4d,0x0,0x0,0x6c,
+  0x9b,0x0,0x0,0x72,0x6f,0x0,0x0,0xf6,0xae,0x0,0x0,0x85,0x83,0x0,0x0,0x6e,
+  0xf7,0x0,0x0,0xe8,0x41,0x0,0x0,0x31,0x74,0x0,0x0,0x17,0x6c,0xa1,0xb6,0x2d,
+  0x9b,0x0,0x0,0x8,0x3d,0x49,0x44,0x41,0x54,0x78,0xda,0x62,0xfc,0xff,0xff,0x3f,
+  0x3,0x32,0x60,0x61,0x60,0x60,0x60,0xf8,0x7a,0x62,0xeb,0xff,0xd7,0x73,0x1a,0x19,
+  0x7e,0x89,0xaa,0x30,0x30,0x2e,0x53,0xe3,0x3c,0xac,0x22,0x23,0x64,0xc3,0xcc,0xcd,
+  0xcb,0xf0,0xe7,0xd3,0x7b,0x6,0x86,0xff,0xff,0xff,0x33,0xec,0x5b,0xb7,0xfa,0xff,
+  0x16,0x45,0xae,0xff,0x3b,0x54,0xb8,0xfe,0x3,0x0,0x0,0x0,0xff,0xff,0x62,0x44,
+  0x37,0x83,0xf1,0x98,0x25,0xf7,0x16,0x49,0x2d,0x2d,0x6f,0x6,0x6,0x6,0x6,0x85,
+  0x39,0xa7,0x18,0x19,0xaf,0x2f,0x9c,0xf4,0xff,0xeb,0xf2,0x9,0x10,0x1b,0x64,0x94,
+  0x18,0x18,0xbb,0x44,0x59,0xd,0xb4,0x78,0x58,0xcf,0x33,0x30,0x30,0x30,0x78,0xdf,
+  0xfb,0xca,0x8,0x0,0x0,0x0,0xff,0xff,0x82,0x9b,0xf1,0xb8,0xcc,0xfb,0xff,0xcb,
+  0x3b,0x4f,0xe0,0x66,0xf1,0xfe,0xff,0xc8,0xa0,0xbe,0xfe,0x1,0x23,0xe3,0xff,0xff,
+  0xff,0x19,0x2e,0x5,0x6b,0xff,0x67,0x63,0x63,0x86,0xda,0xc2,0x8,0xa5,0x18,0x19,
+  0x18,0xf8,0x4,0x19,0x18,0xff,0xff,0xff,0xcf,0xb0,0xdf,0x50,0xe0,0xff,0xbf,0x9f,
+  0xbf,0x51,0x5c,0xf3,0x53,0x42,0x8e,0xe1,0xf7,0xe7,0xf,0x10,0x5,0x8c,0x8c,0x8c,
+  0x4c,0x6b,0xc,0x95,0xfe,0xf2,0xb2,0xb2,0xc1,0x15,0x1c,0xbd,0x73,0xef,0x5e,0xe3,
+  0xdb,0x9f,0xca,0x0,0x0,0x0,0x0,0xff,0xff,0xc2,0xf0,0x7,0x3a,0x60,0x61,0x60,
+  0x60,0x60,0xb8,0x64,0xcd,0x9d,0xf9,0x4b,0x5c,0x65,0x1a,0x4c,0x90,0x9b,0x87,0x9d,
+  0x41,0x73,0xd1,0x29,0x46,0x6,0x6,0x6,0x88,0x15,0xf7,0x52,0x2d,0xff,0x33,0xfd,
+  0xff,0x8b,0xd0,0xc5,0xcd,0xc5,0xf0,0xed,0xf3,0x77,0x6,0xb5,0x79,0x27,0x19,0x99,
+  0x76,0x59,0x8,0x7f,0xe6,0xd0,0xb3,0x62,0xf8,0xf1,0xed,0x27,0xc3,0x8f,0xaf,0x3f,
+  0x18,0x7e,0x7c,0xfd,0xc1,0xf0,0xe5,0xf5,0x7b,0x6,0x16,0xbb,0x70,0x86,0x2d,0x6,
+  0x5c,0xd2,0x8c,0xc7,0x8c,0xb8,0xff,0x33,0x71,0x72,0x32,0xb0,0xf0,0xf2,0x31,0x30,
+  0x32,0x33,0x33,0xfc,0xff,0xfb,0x97,0xe1,0xff,0xdf,0x7f,0xc,0x5f,0x3f,0x7e,0x66,
+  0x60,0x97,0x96,0x61,0x60,0xba,0xfc,0xe6,0x5f,0x25,0x7b,0xf3,0x32,0x86,0xf,0xf,
+  0x5f,0x30,0xbc,0xbf,0xf7,0x94,0xe1,0xc3,0xc3,0x17,0xc,0x1f,0x9f,0xbc,0x62,0x60,
+  0x65,0x60,0x60,0xb0,0x58,0x77,0x1,0x12,0x50,0x7b,0x4c,0xa4,0xff,0xff,0x7c,0xf7,
+  0x1,0xee,0x86,0xff,0xbe,0x91,0xc,0x37,0xd7,0x2c,0xff,0x5b,0xfc,0xf4,0x2b,0xb,
+  0xe3,0xff,0xff,0xff,0x19,0x14,0x19,0x19,0x5,0x66,0x9a,0x69,0xbc,0x47,0xf6,0x9e,
+  0xfb,0xa9,0x1b,0xec,0xff,0xff,0xff,0xff,0x5,0x20,0x94,0xec,0x41,0x12,0xa,0xa3,
+  0x30,0xfc,0x7c,0xf8,0x57,0x69,0xa4,0x9,0xa1,0x4b,0x4e,0x12,0x54,0x50,0x12,0x41,
+  0x63,0x21,0x41,0xb9,0x38,0x35,0x44,0x63,0x5b,0x18,0x8d,0xd5,0x52,0x20,0x84,0x53,
+  0x53,0x83,0x6e,0x6d,0x77,0x8,0x22,0xa8,0xa0,0xa1,0x1f,0x34,0x70,0xac,0x1c,0xc2,
+  0x96,0x74,0xc8,0x8a,0x9b,0x95,0x18,0x7a,0x43,0x3,0xfd,0x5a,0xf2,0x56,0x28,0xf5,
+  0xc2,0xe1,0x3d,0xcb,0x79,0x87,0xe7,0x3d,0x4d,0x1c,0xd2,0xe3,0xb6,0xbf,0xc1,0x48,
+  0xe1,0xef,0x4f,0x94,0xce,0xf4,0xfe,0x7f,0x6,0x64,0x43,0x7e,0x29,0x5d,0x5e,0x3a,
+  0x5e,0x6e,0x40,0xd6,0x9b,0x6e,0x7b,0x36,0xe,0x78,0xc,0xcf,0xa1,0x26,0xe3,0xd1,
+  0xd1,0xa4,0xb6,0xa0,0x7,0xa4,0x7,0x85,0xd9,0x34,0x39,0x5d,0xed,0xa,0xcc,0x53,
+  0x56,0x22,0xd4,0x1b,0xa1,0xd,0x17,0x42,0xdf,0x5d,0xcb,0x31,0xd4,0xcd,0x45,0x72,
+  0xa9,0xeb,0xdb,0x89,0xb,0xcd,0x2b,0xf6,0x6,0xac,0xc3,0x7d,0x63,0x23,0x57,0xce,
+  0x95,0x18,0xf9,0xd5,0x19,0x40,0x2,0xa2,0x85,0x7f,0xcb,0x3e,0x35,0x4b,0xfe,0x50,
+  0xe1,0x3e,0x9b,0xab,0x9,0x29,0x25,0x89,0xa0,0x4f,0x3a,0x3c,0x1e,0x9e,0x4e,0x8f,
+  0x31,0x9b,0x4,0xff,0xc9,0xbd,0x1d,0xe7,0x39,0x14,0x20,0xa3,0x96,0x14,0x9d,0x81,
+  0xe2,0x73,0xcb,0xde,0x4e,0x23,0x5,0xad,0x8e,0x45,0x2b,0xb6,0xe6,0x7,0xd8,0xc3,
+  0x51,0xaa,0x5b,0x6b,0xa4,0xb2,0x6a,0x70,0xe9,0xa1,0xb2,0xff,0xb,0x62,0xc4,0x6d,
+  0x7d,0x1d,0x6a,0x13,0xdd,0x86,0xd8,0x6e,0xeb,0xcf,0x3c,0xda,0xe1,0xfd,0xfc,0x84,
+  0xcb,0xcc,0x9d,0x73,0xfd,0x4d,0x16,0x74,0x88,0x42,0x8,0x23,0x60,0x5,0x6c,0x80,
+  0x3,0x68,0xff,0x1a,0xb,0x60,0x0,0x6a,0xc0,0x7,0x50,0x1,0x8a,0x80,0x6,0x14,
+  0xa5,0x94,0xe5,0x4f,0x42,0xaa,0x26,0x24,0xaa,0x28,0xa,0x7f,0x77,0x9c,0x9f,0xe7,
+  0xbc,0xe1,0x8d,0x3e,0x9d,0x81,0x71,0xb0,0xd2,0x46,0x14,0x4b,0x71,0x48,0x1c,0x6a,
+  0x84,0x82,0x6a,0x11,0x12,0x41,0xed,0x92,0x5c,0x95,0xe4,0xaa,0x88,0x5a,0x44,0x41,
+  0xf4,0xb3,0x88,0x70,0x21,0x24,0xe6,0x36,0x50,0x74,0xd3,0x26,0x6c,0xe3,0xa2,0xda,
+  0x48,0x50,0x10,0x95,0xda,0x8f,0x36,0xd,0x8a,0x39,0x68,0xcc,0xbc,0x19,0xc7,0xe7,
+  0x9b,0x77,0xdf,0x7d,0xf3,0x5a,0xcc,0x4f,0x4f,0xa6,0x71,0x2e,0x7c,0x9c,0x73,0x16,
+  0xe7,0xbb,0xdc,0xfb,0x9d,0xf3,0x55,0xdc,0x87,0x4a,0xc7,0x6a,0x2e,0x16,0x4e,0xb8,
+  0x4e,0x5b,0x80,0xd9,0xbd,0x1a,0xda,0xdf,0x6e,0x93,0xb2,0x4,0x5c,0xe0,0xd0,0xec,
+  0x4e,0x9a,0x82,0x52,0xfd,0x9f,0xf6,0xa6,0xe8,0xeb,0x68,0xc5,0x97,0x30,0x3f,0xd4,
+  0x39,0x27,0x3f,0x2b,0xf4,0x58,0xa,0xc9,0x87,0x30,0x3f,0x66,0xab,0xe6,0xd1,0x36,
+  0x3c,0xd,0xff,0x3e,0x37,0xfc,0x8d,0xc2,0xae,0xb8,0x3f,0xd8,0x86,0xf4,0x1f,0x9,
+  0x8e,0x3,0x7,0xc7,0xcc,0x97,0x16,0x9,0xc4,0xd0,0xf1,0x21,0xeb,0x91,0x33,0x88,
+  0x3d,0x1c,0x80,0x2a,0xab,0x25,0x90,0x57,0xa2,0x68,0x79,0x34,0x1,0xae,0x56,0xc4,
+  0xbb,0x10,0x3f,0xb3,0x8b,0xe0,0x4d,0x88,0x5f,0xd6,0x36,0x62,0x70,0x8a,0x6e,0x68,
+  0xaa,0x6,0xa6,0x31,0x30,0xa6,0xe7,0xa2,0x9,0xab,0x37,0xfb,0xa0,0x35,0x6,0x21,
+  0x76,0x75,0xf7,0x7d,0x3d,0x4c,0xec,0x0,0x60,0x99,0xe9,0x72,0xfa,0xfd,0x3d,0xbd,
+  0x81,0xba,0xeb,0x23,0xd8,0x98,0x1a,0x5,0xa3,0xc,0x4c,0xd3,0xc1,0x54,0x2d,0x97,
+  0x17,0x6a,0xca,0xa0,0x2a,0x14,0xee,0xee,0x30,0x58,0x4a,0x42,0xd4,0xea,0x8c,0x3,
+  0x0,0x79,0x15,0xf6,0x31,0x8f,0xc7,0x53,0x55,0xdf,0xde,0xa,0x79,0x69,0xd1,0x34,
+  0xf7,0x66,0x79,0x49,0xb1,0x26,0x84,0xc0,0x75,0xf9,0x1,0x32,0x2f,0x46,0xf1,0x6d,
+  0xee,0x7d,0xd0,0x5a,0xdf,0xe0,0xaf,0x12,0x8e,0x9e,0xc4,0xfa,0xf4,0x38,0x6c,0x9c,
+  0x3,0xc4,0xce,0xc1,0x62,0xcd,0x89,0x43,0xf2,0x46,0x6a,0x68,0x14,0x0,0x90,0xa5,
+  0x2a,0xc,0x4a,0xc1,0xd7,0x78,0x21,0x7d,0xfe,0x8,0x45,0xd3,0x9f,0x92,0xe7,0xcd,
+  0xdc,0x64,0x53,0x8d,0xed,0xa2,0xfb,0xee,0x38,0xa4,0xfb,0x83,0x15,0x7,0x87,0x3a,
+  0xdd,0x10,0x6b,0x5d,0x90,0x14,0x1d,0xa7,0x5e,0xff,0x24,0x96,0x81,0x88,0xd2,0x9f,
+  0x16,0xbc,0xa0,0x53,0x23,0x90,0xb3,0x76,0x30,0x6a,0xec,0x9,0xcf,0x93,0x49,0x28,
+  0xdb,0xa,0x7e,0x45,0xd6,0x6f,0x17,0x55,0x58,0x5e,0x5a,0x3b,0xb7,0x15,0x89,0x42,
+  0x1c,0x9e,0x0,0xcb,0xa2,0x2c,0x32,0x9d,0xbd,0x90,0xae,0x9e,0x45,0x24,0x96,0xc4,
+  0xe0,0xca,0xce,0xe3,0x22,0xc1,0xb5,0xdf,0x99,0x97,0xf3,0x12,0xd3,0xe5,0x1b,0xfd,
+  0x90,0x7d,0x1,0x30,0x8a,0x12,0x50,0x15,0x70,0x5,0x7b,0x60,0x8,0x1e,0x6c,0x26,
+  0x33,0xc1,0x92,0x51,0x4e,0xc6,0x93,0x5e,0x99,0x6f,0x88,0xbb,0x2e,0x5c,0x82,0xd1,
+  0xd2,0xf1,0xdf,0xf7,0xa7,0xae,0x9c,0xc7,0xbc,0x9a,0x4d,0xdc,0xd9,0xa4,0x9f,0x4a,
+  0x4c,0x95,0x10,0x62,0xbb,0x27,0x3a,0xbe,0x1f,0xb,0x34,0x35,0x97,0xfb,0xc0,0x44,
+  0x2a,0x85,0x5b,0x3f,0x62,0x75,0x6b,0xb9,0x55,0xce,0x9a,0xfd,0xa0,0x1a,0x80,0x90,
+  0xf7,0x3,0x21,0xef,0x5,0x1c,0x0,0x5b,0xbe,0x57,0x3,0xa0,0x2,0x50,0x0,0x24,
+  0x0,0x6c,0x1,0x48,0x1b,0x86,0xa1,0xfd,0x65,0xbd,0x5c,0x43,0x9a,0x8c,0xc2,0x38,
+  0xfe,0x3f,0xdb,0xde,0xdd,0xde,0x6d,0xba,0x4d,0x27,0x39,0x15,0xd3,0xd6,0xc5,0x12,
+  0x2c,0x56,0x94,0xd4,0x87,0xca,0xbe,0x74,0x81,0x12,0x8a,0x40,0x30,0xc8,0xa0,0x12,
+  0x24,0xca,0x3e,0xa4,0x41,0xd1,0x45,0xa4,0x48,0xa9,0x28,0xb,0x53,0x8c,0xa2,0xa2,
+  0x24,0xe8,0xe6,0x97,0xae,0x94,0x62,0x11,0x6a,0x66,0x77,0x67,0x99,0xe8,0xa6,0x6e,
+  0x66,0x6d,0xee,0x7d,0xdb,0xfb,0xee,0x72,0xfa,0x90,0xc5,0xa2,0x2c,0xe7,0x7a,0xe0,
+  0x7c,0x3a,0xf0,0xfc,0x1f,0xce,0xf3,0x9c,0xff,0xef,0x9c,0xa8,0xfd,0x20,0xda,0x90,
+  0x8d,0xb9,0xb3,0x8e,0x48,0x5f,0xba,0x34,0x2d,0xfa,0xa5,0xab,0xb3,0xa4,0x6a,0xcd,
+  0x84,0x92,0x8b,0x7d,0xdd,0xf0,0xb4,0x35,0xb5,0xcf,0x32,0x71,0x56,0x5c,0x9,0xe3,
+  0x77,0xf8,0x1d,0xf9,0xd3,0x9,0xbc,0xce,0x8e,0x31,0xf8,0x59,0xc6,0x66,0xcc,0xce,
+  0x31,0xf0,0x7d,0xdd,0x30,0x17,0x9f,0x4,0x9b,0x61,0x8d,0x48,0xdc,0x7e,0xae,0x2,
+  0xe2,0xe3,0x6b,0x90,0x19,0xe2,0xe1,0x6c,0xbc,0x3d,0xac,0xa,0x48,0x2d,0x19,0xcd,
+  0xee,0xe1,0x7f,0x16,0xd0,0x3c,0x4f,0x93,0xa5,0x8c,0xd3,0xb7,0x24,0xcc,0xb6,0x4a,
+  0x47,0xec,0x76,0xa4,0x95,0xd7,0xc3,0x73,0xb1,0xc,0x5c,0xe7,0xf3,0x71,0x8b,0x13,
+  0x42,0x10,0xbb,0x66,0x33,0x2,0xa,0x3,0x86,0xcf,0x95,0x41,0x39,0xc9,0xc,0xc7,
+  0xa3,0xfb,0xc1,0x80,0x10,0xb4,0x66,0x3f,0xf5,0xb6,0x8f,0x59,0xc0,0x3,0x2b,0xbb,
+  0x22,0x36,0x25,0xe9,0x96,0x3e,0x39,0x5,0x1c,0xef,0xc7,0xd4,0x8a,0xab,0x70,0x1e,
+  0x2a,0x80,0xd0,0xdf,0x37,0xa1,0x16,0xe8,0x16,0x2c,0x83,0x6a,0x49,0x1e,0x7a,0xf6,
+  0xe6,0x43,0x93,0x68,0xc6,0x40,0xcb,0x13,0x70,0x1e,0x7e,0xe5,0xe2,0x16,0xae,0xe1,
+  0xb7,0x2,0xee,0xce,0x51,0xef,0x32,0x4e,0xb1,0x94,0x6b,0x63,0x35,0x10,0x54,0xf1,
+  0x48,0xdd,0x75,0x1c,0xae,0x7d,0xeb,0x21,0x72,0x7c,0x54,0x43,0xc6,0xa6,0x4d,0x43,
+  0xcc,0xe6,0xc3,0xe8,0xda,0x99,0xb,0x5d,0x9c,0x1e,0x43,0x9d,0x36,0x78,0x9c,0xae,
+  0x92,0x9c,0xb6,0xef,0x4e,0x40,0x28,0xa5,0x68,0xc8,0x64,0x6b,0x12,0xb3,0x32,0xb,
+  0x14,0x21,0x3f,0xa8,0x65,0x2e,0x4c,0x2b,0xd7,0x63,0xb8,0x72,0x1b,0x82,0xc1,0x50,
+  0xe4,0x8a,0x3f,0x38,0x16,0x16,0x72,0xa3,0x9,0xc6,0xdd,0x75,0xf8,0x50,0x9a,0xf,
+  0x56,0x26,0xc2,0x3d,0x38,0x84,0xa1,0x1e,0x7b,0xed,0x8a,0x17,0xdc,0x26,0x52,0x3f,
+  0x4d,0xd5,0x98,0x34,0x23,0x7d,0xa1,0x32,0x28,0x40,0xb5,0x6a,0x23,0xe4,0x71,0x93,
+  0xe0,0xbd,0x70,0x18,0x13,0xd1,0xfe,0x5b,0x30,0x8c,0x1c,0xba,0xd2,0x6a,0xb8,0x2f,
+  0x1e,0x43,0xe8,0x7d,0x7,0xbc,0x5f,0x5,0x38,0xde,0xf7,0x36,0xc9,0xc4,0x90,0xa4,
+  0xc8,0xd9,0xff,0xe9,0x59,0x62,0x8c,0x12,0x23,0xe7,0x2b,0x60,0xa8,0xbc,0x1,0x17,
+  0x47,0xa0,0x18,0xfc,0xf8,0xdf,0xc4,0x83,0x14,0x50,0x2c,0xcf,0x3,0x63,0xeb,0x0,
+  0x77,0xef,0x3a,0xe4,0x29,0xa9,0x70,0x7c,0x74,0x40,0xc,0x49,0x8a,0x8,0xa5,0x14,
+  0xa7,0x93,0xd5,0x66,0x95,0x56,0xf1,0xce,0x92,0x92,0xc0,0x52,0xa7,0x1d,0xba,0x83,
+  0x67,0xe1,0xbc,0x52,0xb,0xd2,0xfa,0x10,0x24,0x5a,0xf1,0x10,0xc0,0x6e,0xd8,0xe,
+  0x75,0x88,0xc7,0x97,0x4b,0xd5,0x50,0x4e,0xb6,0xe0,0x55,0xc7,0x3b,0xaf,0xe8,0xc7,
+  0xf4,0x2d,0xbd,0xbc,0xfd,0xe7,0x10,0xd6,0xcf,0x22,0xf2,0x41,0x8f,0xea,0xf5,0x8c,
+  0x29,0x49,0xe9,0x64,0xc0,0xe,0x6d,0x61,0x29,0x46,0x7a,0x7a,0xe0,0xbf,0x79,0x1,
+  0x92,0x9,0x56,0x21,0x86,0x0,0xfd,0xee,0xa3,0x60,0x5a,0xef,0xc2,0x7d,0xa7,0x1,
+  0xc4,0x9c,0x8c,0x37,0x6f,0xbb,0xbb,0x12,0x62,0x84,0x99,0x6b,0x5f,0x52,0xf1,0x8f,
+  0x3e,0x50,0x99,0xa8,0x6a,0x9c,0x9a,0x9e,0xbc,0x90,0x71,0x3a,0xc0,0x2e,0xcf,0x5,
+  0x6f,0x4a,0x85,0x50,0x53,0x1,0xa9,0x24,0x42,0x71,0x9,0x3,0xe3,0x91,0x3a,0x90,
+  0x33,0x65,0xe0,0x6d,0x36,0xf8,0xb4,0x7a,0x74,0xf5,0xe,0x34,0xed,0xe8,0xf7,0x2d,
+  0xfa,0xa7,0x13,0x96,0xc7,0x2b,0xab,0x52,0xcd,0xf1,0x5b,0xb5,0xee,0x2f,0x60,0xe7,
+  0x2f,0x80,0xb0,0x34,0x17,0xae,0x3d,0xc5,0x18,0x2f,0x37,0xa4,0x3a,0x1d,0x4c,0x87,
+  0xaa,0x20,0x39,0x50,0x4,0x1f,0xef,0xc3,0x67,0x22,0x43,0xaf,0xcb,0x7d,0xaa,0xc4,
+  0xe5,0x2b,0x1c,0x97,0x15,0x3,0xc0,0x7e,0xa3,0x72,0x5b,0x92,0x41,0x7b,0xd4,0x20,
+  0x8,0xd0,0x48,0x29,0x64,0x11,0xf6,0xc1,0x17,0xa0,0x10,0xe5,0xa,0x38,0x44,0x3f,
+  0x6,0x39,0xa1,0x68,0xef,0x90,0xef,0xc4,0x98,0x2c,0x20,0x84,0x90,0xb0,0x27,0x35,
+  0x33,0xa,0x29,0x26,0x4f,0xcd,0xe4,0xa4,0xc9,0x49,0x75,0x34,0x43,0xd8,0x19,0xa0,
+  0x85,0x97,0xbd,0xfe,0x7b,0x0,0x2,0xa3,0xeb,0x7,0x96,0x47,0x28,0xa5,0x34,0x62,
+  0x1c,0x8f,0xb2,0x9f,0x9,0x7b,0xf0,0xfd,0xf2,0x4f,0xa6,0x94,0xfa,0x22,0xc9,0xf7,
+  0x6d,0x0,0xe,0x19,0xb9,0x2a,0xab,0x3b,0x72,0x2d,0x0,0x0,0x0,0x0,0x49,0x45,
+  0x4e,0x44,0xae,0x42,0x60,0x82,
+    // /usr/local/google/home/chudy/Ico/next.png
+  0x0,0x0,0x3,0xaa,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0x73,0x7a,0x7a,0xf4,
+  0x0,0x0,0x0,0x4,0x73,0x42,0x49,0x54,0x8,0x8,0x8,0x8,0x7c,0x8,0x64,0x88,
+  0x0,0x0,0x3,0x61,0x49,0x44,0x41,0x54,0x58,0x85,0xed,0x94,0xcf,0x6b,0x5c,0x55,
+  0x14,0xc7,0x3f,0xe7,0xce,0xbc,0x37,0x49,0x67,0xda,0x12,0xb1,0x58,0x1b,0x27,0xbf,
+  0x9a,0xa1,0x6a,0x2c,0xa9,0x82,0xc5,0x6c,0x5c,0x8a,0x8a,0x3f,0xa0,0xa2,0x36,0x41,
+  0xc1,0x85,0xe8,0xae,0x2b,0x45,0xff,0x5,0x75,0x33,0x71,0xab,0xd0,0xa0,0x55,0xa1,
+  0x25,0x14,0x71,0x21,0x76,0x84,0xce,0xc4,0x3f,0x41,0xb0,0x69,0x2b,0x46,0xd1,0x4a,
+  0xea,0xc2,0x4c,0x52,0xdb,0x79,0x3f,0xef,0x75,0x71,0xdf,0x1b,0x5e,0xa6,0x9d,0x8c,
+  0x29,0x63,0x5d,0x98,0x3,0x87,0xfb,0x2e,0xbc,0xf7,0xbe,0x9f,0xf3,0x3d,0xe7,0x5e,
+  0xd8,0x89,0x9d,0xe8,0x43,0x7c,0x39,0xc7,0xcc,0x17,0xb3,0xa5,0xb1,0xdb,0xf9,0x56,
+  0xf5,0x41,0xdf,0xdd,0xe3,0xf0,0xce,0xa1,0x89,0xb1,0xf9,0x8f,0x5e,0x28,0x4e,0xfc,
+  0x17,0x0,0xf9,0xc1,0x1c,0xf9,0xca,0xc3,0x8f,0x3f,0x37,0x33,0x7d,0xff,0xe2,0xc9,
+  0xe3,0x7b,0xf,0xde,0x69,0x0,0x44,0xc0,0x2d,0xc,0x32,0x7a,0x68,0xfa,0xc8,0x91,
+  0x83,0xf7,0x54,0x17,0xb6,0x1,0xd1,0x1f,0x0,0x5,0x98,0x10,0xb7,0x50,0x60,0x72,
+  0xea,0x91,0x67,0x8e,0x3e,0x58,0x5e,0x3c,0xf5,0xca,0xdd,0x93,0x77,0x1c,0x0,0x13,
+  0xe1,0xb8,0x3,0x94,0xc7,0x2b,0xd3,0x87,0xc7,0xf6,0xce,0x7f,0xf6,0xea,0xbe,0x9e,
+  0x10,0xf9,0xec,0xe6,0xab,0x17,0x99,0x19,0xd9,0xc5,0x30,0x62,0x6d,0x45,0xec,0xcf,
+  0x25,0x59,0x6f,0xda,0x3,0x1b,0x1,0x3,0x43,0x83,0xdc,0x8b,0xe,0xc0,0x44,0x8,
+  0x21,0x6e,0xc1,0x61,0xa2,0x32,0xf9,0xb4,0x5b,0x58,0x29,0x7f,0xfe,0x9a,0x3a,0x36,
+  0xb7,0x70,0xf5,0xc7,0xae,0xf0,0x99,0x67,0xb7,0x3e,0xcb,0xe9,0xa9,0xa3,0x4f,0x3c,
+  0x8f,0x52,0xa0,0x4,0x11,0xb1,0x1e,0x89,0x58,0x71,0x31,0x20,0x6,0x30,0x80,0x46,
+  0x30,0x8,0x9a,0xbc,0xd2,0xb8,0x77,0x95,0x41,0x47,0x60,0x22,0xd0,0x21,0x46,0x87,
+  0x84,0xde,0x35,0x56,0x7e,0x5a,0xf9,0xfa,0x87,0xdf,0x82,0x13,0xc7,0x4f,0xae,0xde,
+  0x12,0x22,0xeb,0x40,0x3e,0xf,0xf9,0xdd,0xfb,0x46,0x40,0x29,0xc8,0x25,0xa5,0xaa,
+  0x54,0x1c,0x2b,0xdc,0x6,0x88,0xc1,0x68,0x90,0x18,0x74,0x4c,0xea,0x40,0xa,0x21,
+  0x26,0xc0,0x75,0x72,0x8c,0x8f,0xee,0x7f,0xca,0x75,0xfe,0x38,0x7b,0xfa,0xf5,0xe1,
+  0x63,0x2f,0x7d,0x7c,0xe5,0x72,0x27,0xc0,0xe6,0x19,0x10,0xec,0x8f,0x74,0x0,0x71,
+  0x66,0x8d,0x3b,0xf6,0x3a,0x0,0x1d,0xda,0xbe,0xc7,0x41,0xd2,0xff,0x24,0x9,0xdb,
+  0xf3,0x0,0x11,0x8e,0x9b,0x63,0x78,0xff,0x9e,0x87,0x1e,0x38,0x60,0x3e,0x3c,0xf3,
+  0x46,0xb9,0xb2,0x35,0x0,0x40,0xe4,0x27,0xe9,0xd9,0x8c,0x3d,0x24,0xf6,0x30,0x69,
+  0x6a,0x1f,0x62,0xf,0xb4,0x97,0xac,0x1,0x68,0x3f,0xc9,0x4,0xce,0x4,0x6d,0x10,
+  0x31,0x21,0x8e,0xa3,0x18,0x3b,0x50,0x7a,0xf2,0xf0,0x7d,0xf1,0xd9,0x33,0x6f,0x8e,
+  0x6c,0x82,0xd8,0x34,0x84,0x22,0x60,0xe2,0x0,0x8c,0x20,0x28,0x30,0x2,0x46,0x30,
+  0xc9,0x40,0x22,0x26,0x59,0x35,0x90,0xa4,0x68,0x30,0x31,0x10,0xd9,0x96,0x68,0xb,
+  0x4a,0xd4,0x82,0xd8,0x87,0xd8,0xc7,0xe8,0x90,0x5c,0x1c,0x32,0xe4,0xb6,0xa6,0x4c,
+  0x34,0xf4,0x2c,0x30,0x6f,0x7b,0xd8,0x1,0x60,0x1d,0xf0,0x40,0x29,0x8c,0xb6,0xfd,
+  0x17,0x95,0xce,0x81,0x1,0x65,0xfb,0x6f,0x74,0x0,0x3a,0x11,0x8,0x9a,0x98,0xf8,
+  0x6,0x44,0xd7,0xad,0x2b,0xda,0x3,0x63,0xda,0xa7,0x8,0xb1,0x23,0xd3,0xbc,0x21,
+  0x7e,0xfd,0x62,0xf1,0x9b,0xb9,0x53,0x57,0x16,0xbb,0x3a,0xd0,0x9e,0x1,0x93,0x9c,
+  0xb9,0xb4,0x74,0x3,0x68,0x1f,0x13,0x6c,0x80,0xff,0x27,0x26,0xdc,0x80,0xb8,0x85,
+  0xa4,0xfd,0x4e,0x5e,0x27,0x73,0x54,0xc9,0x7c,0xea,0x45,0x50,0xbf,0x54,0x3c,0x57,
+  0x5d,0xa,0x3f,0x0,0x56,0xd3,0xea,0xbb,0x38,0xe0,0xdb,0x53,0xa0,0xd,0xe8,0x0,
+  0xed,0xaf,0x61,0xfc,0x35,0xf0,0xd7,0x10,0x22,0x2b,0xd2,0x99,0xdd,0xc4,0xc5,0x56,
+  0xde,0x58,0x2e,0xd6,0xe6,0x3e,0xfd,0xeb,0x6d,0xe0,0x67,0xc0,0xef,0xea,0xc0,0x7a,
+  0x8,0xab,0xbf,0x5c,0x0,0x95,0x9c,0x79,0xed,0x23,0x71,0xb,0x44,0xdb,0xb,0x28,
+  0x93,0x24,0xab,0xca,0xc1,0x40,0x1,0xa,0xb9,0x8c,0xb8,0xb2,0x95,0xfb,0x11,0x34,
+  0x2e,0x96,0x6a,0xd5,0x7a,0xf0,0xde,0xad,0xc4,0x53,0xd3,0xd3,0x70,0x81,0xa,0x30,
+  0x7a,0x93,0x2b,0x5b,0x87,0xf3,0xdd,0x9,0xde,0x7d,0x74,0x94,0xc7,0x52,0x47,0x8c,
+  0xc0,0x7a,0x4b,0xf9,0x8d,0xb,0xc5,0xda,0xcb,0xb,0xd7,0xde,0xea,0x26,0xde,0xe9,
+  0x40,0x0,0x5c,0x6,0x56,0xb6,0x9,0xb0,0x6b,0x77,0x81,0x66,0xb6,0xe7,0x7e,0x4,
+  0x4b,0xcb,0xc5,0x6f,0xab,0xe7,0xc3,0xf7,0xb7,0x12,0xef,0x4,0x48,0x21,0x82,0x6d,
+  0x2,0x90,0x57,0xc4,0xa9,0x78,0xb3,0x25,0xfe,0xd2,0x72,0xa9,0x67,0xe5,0xdd,0x0,
+  0x6e,0x2f,0x32,0x95,0x37,0x96,0x4b,0xb5,0xea,0xf9,0xee,0x3d,0xff,0x57,0x0,0x8c,
+  0x81,0xe6,0x75,0xf1,0xeb,0x97,0x8a,0xb5,0xd9,0x4f,0xfe,0x59,0xe5,0x7d,0x5,0x58,
+  0xf7,0xe0,0xfb,0xdf,0x8b,0xe7,0xe6,0x1b,0xbd,0x7b,0xde,0x19,0xd2,0xfb,0x95,0x9e,
+  0xe1,0x2,0xe3,0xd8,0xcb,0xe5,0xd7,0xed,0x88,0xf7,0x33,0x1c,0xfa,0x35,0x4f,0x3b,
+  0xf1,0xbf,0x8b,0xbf,0x1,0x89,0x9f,0x95,0xb4,0x21,0x16,0x51,0x5d,0x0,0x0,0x0,
+  0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
+    // /usr/local/google/home/chudy/Ico/inspector.png
+  0x0,0x0,0x7,0xa8,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0x73,0x7a,0x7a,0xf4,
+  0x0,0x0,0x0,0x4,0x73,0x42,0x49,0x54,0x8,0x8,0x8,0x8,0x7c,0x8,0x64,0x88,
+  0x0,0x0,0x7,0x5f,0x49,0x44,0x41,0x54,0x58,0x85,0xa5,0x97,0x6b,0x8c,0x5d,0x55,
+  0x15,0xc7,0x7f,0x6b,0xef,0x73,0xee,0xb9,0xf7,0xce,0x9d,0xb9,0xf3,0x68,0x87,0x4e,
+  0x5b,0x18,0x79,0x98,0x62,0xa9,0x5,0x1,0x9,0x55,0x24,0x41,0x20,0x3e,0x12,0x13,
+  0x88,0x89,0x68,0x34,0x4,0x5f,0x1,0x62,0x8c,0x86,0x4f,0x9a,0x60,0x8c,0x8f,0xa8,
+  0xf1,0x83,0x1f,0x14,0x4b,0x48,0x88,0xc1,0x47,0x14,0x92,0xfa,0xf8,0xa0,0x41,0x54,
+  0x24,0x1,0x11,0x63,0x0,0x5b,0xc6,0xa6,0x15,0xa4,0x85,0x4c,0x3b,0x7d,0xcc,0xe3,
+  0xce,0xdc,0xc7,0xb9,0xe7,0xb9,0x97,0x1f,0xce,0x9d,0x99,0x3b,0xcc,0x4c,0x8b,0xba,
+  0x92,0x95,0x93,0xbd,0xcf,0xda,0x6b,0xfd,0xd7,0x7f,0xaf,0xbd,0xce,0xd9,0xc2,0x79,
+  0x44,0x55,0xfb,0x87,0x2,0x18,0xc0,0xf6,0xd4,0x2c,0x9b,0x1,0x19,0x90,0x3,0xe,
+  0x50,0x11,0x39,0x9f,0xeb,0x15,0x87,0x6f,0x26,0xb8,0x1,0x4a,0xaa,0x5a,0x5,0x86,
+  0xe2,0x34,0xaf,0xbf,0x7e,0xb6,0x35,0x96,0x64,0x6e,0x40,0x4,0x7c,0x6b,0xc2,0xc9,
+  0xf1,0xc1,0xf9,0xc0,0xb7,0x4b,0x22,0xb2,0x4,0x84,0x40,0x2,0xb8,0xf3,0x1,0x39,
+  0x1f,0x0,0x51,0xd5,0x92,0x88,0xc,0xfd,0x7b,0x66,0x71,0xe7,0xb3,0x47,0x4f,0xef,
+  0x5b,0x68,0xc5,0x37,0x8a,0x70,0xf9,0x40,0xb9,0x34,0x56,0xd,0xbc,0x92,0x8,0x74,
+  0x93,0x3c,0xe9,0x44,0xe9,0x82,0xaa,0x1e,0x1d,0xad,0x95,0x9e,0xd9,0xb7,0x6b,0xdb,
+  0x73,0x97,0x6d,0x1f,0x9e,0x6,0x9a,0x40,0x7c,0x2e,0x46,0xd6,0xcd,0xf6,0x67,0xad,
+  0xaa,0x95,0xd3,0x8d,0xce,0xc4,0xfe,0xdf,0x4d,0x7d,0xa4,0xd9,0x4d,0x3f,0x31,0x39,
+  0x5e,0xbf,0x64,0x72,0xbc,0x5e,0xb2,0xc6,0x4a,0x96,0x17,0xbc,0x4b,0xcf,0x8b,0x67,
+  0x20,0xce,0x32,0x9d,0x9e,0x6d,0x26,0x33,0xf3,0xad,0xd7,0x6,0x2b,0xde,0xcf,0xef,
+  0x7e,0xff,0x9e,0x47,0xb7,0x8f,0xd5,0x4e,0x2,0xdd,0xcd,0xd8,0x58,0x37,0xe3,0x9c,
+  0x3,0x30,0xed,0x6e,0x52,0xdb,0xff,0xdb,0x43,0xd7,0x1d,0x3c,0x3e,0xff,0x9d,0x8b,
+  0x27,0x46,0xae,0xbe,0x7c,0xe7,0x56,0x89,0x33,0x68,0xb4,0x53,0xdc,0xda,0xba,0x58,
+  0x11,0x23,0xc2,0x48,0xcd,0x27,0xf0,0xe1,0x95,0x99,0x79,0x3d,0x3e,0xd3,0x38,0xf8,
+  0xf6,0xc9,0x91,0x2f,0xdf,0xf3,0xc1,0xbd,0x7f,0xab,0xd7,0xca,0xad,0x8d,0x40,0xac,
+  0x19,0xa9,0x2a,0xaa,0x2a,0xc7,0x4f,0x35,0x6,0xbe,0xf4,0xa3,0x67,0x6e,0x6f,0x27,
+  0xee,0xdb,0x37,0xee,0xbd,0x78,0xc7,0x40,0xb9,0xca,0x52,0x98,0xe2,0x74,0xed,0x22,
+  0x65,0xe3,0xb1,0x11,0xa8,0x57,0x7d,0xe2,0x34,0xe2,0xe9,0xa9,0xd7,0x66,0xaa,0x9e,
+  0xdc,0xff,0xcd,0x3b,0xdf,0x75,0xe0,0xd2,0x1d,0xa3,0x6d,0x63,0xcc,0xba,0xaa,0x5e,
+  0x93,0xfd,0xe1,0x63,0xa7,0x4a,0xf7,0xfe,0xe0,0x4f,0xb7,0x5b,0x3f,0x78,0xe0,0xd6,
+  0x6b,0x77,0x8d,0xe5,0x6a,0x25,0xc9,0x36,0xce,0xd8,0xb3,0x82,0x0,0x69,0xbe,0xf1,
+  0xfb,0x92,0x67,0x8,0x3c,0xa7,0x7f,0x7c,0xe1,0xe5,0x85,0x2c,0x89,0xbf,0xf0,0xfd,
+  0x7b,0xde,0x7b,0x60,0xef,0x5b,0x27,0xe2,0x7e,0x16,0xd6,0x1,0xd8,0xfb,0xa9,0x7,
+  0xdf,0x11,0xa9,0xfd,0xcd,0x6d,0xef,0xb9,0xf2,0x42,0xcf,0x2b,0x4b,0xee,0x36,0x76,
+  0x7e,0xc5,0x45,0x75,0x6e,0xbe,0x72,0x1b,0x0,0x4f,0xbd,0x74,0x9a,0xa9,0xd7,0x97,
+  0x36,0xb4,0xb3,0x56,0xb0,0x64,0xfa,0xcb,0xa7,0xf,0xcd,0x94,0x25,0xbb,0xed,0xef,
+  0xfb,0x3f,0xfd,0x7c,0xa9,0xe4,0x6f,0x0,0xe0,0x9a,0x2f,0xb2,0xa5,0x5e,0x9,0xa2,
+  0x4a,0xfd,0xf,0x1f,0xba,0xe1,0xaa,0x1b,0xb7,0x6f,0xdd,0x42,0xba,0x49,0xf0,0xc1,
+  0xb2,0xe1,0xbe,0xdb,0x76,0x33,0x3a,0x58,0x6,0xa0,0xd1,0x8e,0xf9,0xde,0xaf,0xf,
+  0xd3,0x8c,0xdc,0x86,0xf6,0xbe,0x35,0x2c,0xb6,0x16,0x39,0xf0,0xe7,0x17,0x9f,0x1b,
+  0x48,0xdb,0xb7,0xcc,0x3c,0xfe,0xb5,0x70,0xf9,0x9d,0x59,0xb1,0x5a,0x6c,0x32,0x97,
+  0xda,0x3b,0x2e,0x18,0xad,0xdf,0x30,0x36,0x32,0x4c,0x98,0x64,0xa4,0x59,0xbe,0xa1,
+  0xe2,0x1c,0x65,0xdf,0x2,0x45,0xdd,0x4,0xbe,0x5,0x75,0x24,0x9b,0xd8,0x87,0x71,
+  0x4a,0xbd,0x36,0xc8,0xce,0xf1,0x91,0xeb,0x67,0x42,0xfd,0x28,0x3b,0x3f,0xcc,0x7a,
+  0x0,0xf5,0xe1,0x21,0x44,0x3e,0x7f,0xd5,0xae,0xb7,0x98,0x6e,0x92,0x13,0x67,0x39,
+  0x71,0xda,0xd3,0x6c,0xed,0xf8,0x54,0xa3,0xcb,0xc1,0x63,0x73,0x38,0x55,0x14,0x78,
+  0xe9,0xd8,0x2c,0x33,0xb,0x21,0xc9,0x26,0xf6,0x71,0x96,0xb3,0x14,0xa6,0xec,0xdb,
+  0x73,0xa9,0x20,0xe6,0x73,0x4c,0x4c,0xe,0x2f,0x87,0xf5,0x8a,0xc7,0x5d,0x10,0x78,
+  0xd7,0x8d,0x8f,0xe,0xed,0xae,0xd,0xd4,0x88,0xd3,0x7c,0x43,0x2a,0xfb,0xe5,0xe1,
+  0x27,0x5e,0xe6,0xd9,0xc3,0x67,0x40,0xe0,0xc8,0xf4,0x12,0xa9,0x1a,0x70,0xe7,0x5e,
+  0x57,0xd,0x2,0x76,0x8e,0xd7,0xdf,0x76,0xe2,0x44,0x77,0x1f,0xdc,0xf5,0x38,0x3c,
+  0x82,0x47,0xed,0x3,0x70,0x81,0x8,0xa2,0xef,0xdb,0x71,0xc1,0xd6,0x4a,0x37,0xce,
+  0x36,0x75,0xa0,0xc0,0x60,0xd9,0x3,0x55,0x14,0xc3,0x91,0x93,0xad,0xde,0xbc,0x90,
+  0xbc,0x9,0xd0,0x49,0x9a,0x73,0xf1,0x8e,0xf1,0xf2,0x89,0x13,0x67,0x6e,0xe5,0x92,
+  0xfc,0x9,0x8e,0xe1,0x3c,0xaa,0x13,0x50,0xf2,0x4b,0xc0,0x35,0x95,0x6a,0x55,0x92,
+  0x6c,0xe3,0x42,0x2,0xc8,0xf3,0x9c,0xfd,0xf7,0x5e,0x8f,0x5d,0x3e,0x46,0xbd,0xc7,
+  0x42,0x2b,0xe6,0x1b,0x8f,0x1e,0x64,0xa9,0x9b,0xad,0x36,0x83,0x4d,0xa4,0x3e,0x38,
+  0x8,0xa2,0x57,0x53,0xaa,0x94,0x80,0xc8,0x23,0x48,0x21,0xa8,0xe,0x19,0x23,0xdb,
+  0xac,0xf5,0x88,0xb3,0xcd,0x32,0x11,0xb2,0x2c,0xc7,0x1a,0xc1,0x33,0xc2,0x40,0xd9,
+  0xc7,0x98,0x2,0x41,0x96,0x3b,0x52,0x7,0x71,0x2e,0xa0,0xae,0xe0,0x6a,0x93,0x6e,
+  0xe9,0x19,0x8f,0x72,0xc9,0x9b,0x88,0x2a,0x41,0xbd,0x0,0xe0,0x47,0x60,0xea,0x15,
+  0x3,0x95,0x5c,0x37,0xa0,0x52,0x64,0x45,0x1d,0x96,0xcf,0x3c,0xf0,0x1c,0x83,0x65,
+  0xcb,0x77,0xef,0xba,0x96,0xf1,0xe1,0xa,0x14,0xe1,0x48,0x72,0x25,0x71,0x2,0x2a,
+  0xc5,0x84,0x6a,0x1,0xa6,0xf,0x48,0xc9,0x33,0xa8,0x80,0xa0,0x15,0x84,0x2a,0x80,
+  0x87,0x96,0x41,0x11,0x55,0x95,0x38,0xcd,0x31,0x76,0xb9,0x6,0x7a,0x81,0x8d,0x29,
+  0x54,0x4,0xc4,0x72,0xb6,0xb,0x49,0x9e,0xb3,0xa6,0x41,0x29,0x24,0x39,0xc4,0xe,
+  0x44,0xd,0xe0,0x30,0x2a,0x18,0x31,0x78,0xc6,0x21,0xaa,0x2c,0x76,0x22,0x66,0x16,
+  0xda,0x9c,0x98,0x6d,0x42,0x94,0xca,0xf2,0x6,0x7a,0xcc,0x77,0xa0,0x5e,0x8f,0x9d,
+  0xd3,0xa8,0xd5,0x8d,0xc1,0x58,0x8c,0x11,0x44,0xc,0x18,0xb,0x8,0xd2,0x7,0x46,
+  0x11,0x92,0xde,0xf1,0x5b,0xfe,0x72,0x2a,0x80,0x31,0x88,0x15,0x8c,0x80,0xa8,0xa3,
+  0x15,0xc6,0x2c,0xb5,0xbb,0x34,0x9a,0x21,0xcd,0x56,0x48,0x9a,0xa5,0x3d,0xb0,0xa,
+  0x68,0x84,0xd3,0xa8,0x0,0xd0,0xfc,0x15,0xc4,0xf7,0x34,0x35,0x70,0xb3,0xaf,0x9e,
+  0x6a,0xec,0xf2,0xfc,0x10,0xcf,0x5a,0xfc,0x52,0x89,0x52,0x10,0x60,0x7d,0x1f,0xeb,
+  0xf9,0x78,0xbe,0x8f,0xef,0xb,0xc6,0x58,0x8c,0x3a,0xa2,0x28,0x22,0x8a,0x4,0x55,
+  0xa5,0xdd,0xe,0x39,0x36,0x1f,0x71,0xa2,0x2d,0x44,0x49,0x4a,0x1c,0xa5,0x68,0x9e,
+  0x82,0xcb,0x20,0xcb,0x8a,0x6d,0x41,0x58,0xa9,0x50,0xe7,0x66,0x89,0xb3,0xa5,0x2,
+  0x0,0x40,0xa2,0x31,0xea,0xa6,0xf2,0x3c,0x7b,0x77,0x8e,0x27,0x71,0xe,0xe4,0x39,
+  0x24,0xe9,0xea,0xcf,0x97,0x55,0xb0,0xe,0x8c,0x65,0x5b,0x25,0xa3,0x13,0x46,0x44,
+  0x95,0xa2,0x8,0xa3,0x24,0xa1,0xd1,0x75,0x2c,0xc6,0x1e,0x38,0x5b,0xd8,0x2e,0x8b,
+  0xd5,0x5e,0x51,0xba,0xa2,0x4f,0xa8,0x3,0x75,0x53,0x24,0x1a,0xc3,0x72,0x27,0x3c,
+  0xbb,0x98,0xe3,0xf4,0x49,0xba,0x4b,0x31,0x36,0x0,0xb1,0x5,0xfd,0xc6,0x82,0xf5,
+  0x7a,0xea,0xf7,0xb4,0x4,0xc6,0x5b,0x7f,0xda,0x8c,0x5,0xaf,0x4,0xde,0xb2,0x5d,
+  0x6f,0x9d,0xe9,0x53,0x2f,0x80,0x68,0x31,0xc1,0xe9,0x53,0x74,0xe6,0xb3,0x55,0x0,
+  0xad,0xc7,0x20,0x4e,0xff,0x4a,0xd4,0x3a,0x86,0xb8,0xde,0x42,0xdb,0x5b,0xd8,0x7b,
+  0xf6,0x83,0x30,0xde,0x1b,0xbe,0xa3,0xd2,0xb3,0xf1,0xc1,0xf8,0x6b,0x83,0x1a,0x5b,
+  0x14,0xb1,0xf5,0x40,0x50,0xa2,0xe6,0x71,0xb2,0xec,0x2f,0x9c,0x3e,0xc0,0x2a,0x0,
+  0x80,0x66,0xe7,0xc,0x59,0xfa,0x8,0xad,0x33,0x8a,0x57,0x2,0xe9,0x55,0xbf,0xe9,
+  0x63,0xc3,0xe5,0x8c,0x78,0x31,0xc3,0x1,0xab,0xcd,0x8,0x30,0x46,0x18,0xe,0x94,
+  0x11,0x1b,0x15,0xfb,0xbe,0x92,0x40,0x9f,0x7a,0x1,0xb4,0x4e,0x41,0x9a,0xfc,0x94,
+  0x46,0x34,0xd3,0x7,0xbd,0x27,0x95,0x3b,0xe1,0xb2,0xc1,0x2d,0xd4,0x2a,0x4f,0x31,
+  0x71,0xc5,0x1e,0xca,0xc3,0x5,0x8,0x2f,0x28,0x68,0xf5,0x4a,0x90,0xc4,0x1c,0xfe,
+  0xca,0x15,0x94,0x3c,0xc3,0x70,0xd5,0xc3,0xf6,0x1a,0x51,0xee,0x94,0xc5,0xde,0x1f,
+  0xd3,0xee,0xaf,0x4f,0x91,0xfb,0x55,0xc8,0x92,0x55,0x55,0x7,0xdd,0x45,0xe5,0xd4,
+  0x3f,0x8f,0xd2,0xee,0xdc,0xc4,0x2b,0xcd,0x33,0x44,0x3f,0x7b,0x3,0x3,0xdd,0x9f,
+  0xc0,0x91,0xa5,0x39,0xe2,0xe4,0x3e,0xce,0xbe,0x32,0x8b,0xcb,0xb4,0xa0,0x54,0xa,
+  0x20,0x62,0xc0,0x16,0x47,0x54,0x11,0x1a,0x61,0xce,0x5c,0x3b,0x63,0xae,0x93,0xd1,
+  0x8,0x73,0x14,0x41,0x44,0x7a,0x54,0x9b,0xbe,0x35,0x9e,0xe2,0x52,0x65,0xf6,0xe5,
+  0x5,0xe2,0xe8,0x3e,0x5e,0x5b,0xd,0xbe,0x96,0x81,0x65,0xd9,0x73,0xb7,0x4f,0xb9,
+  0xfc,0x59,0x6a,0xa3,0xdf,0x62,0xc7,0x95,0x43,0x94,0x87,0x4,0x31,0xe0,0x97,0x40,
+  0x85,0xad,0x83,0x16,0xe9,0xd5,0x84,0xf6,0x6a,0x41,0xf2,0x1c,0x5c,0x8a,0xe6,0x19,
+  0xb3,0xed,0xbc,0xe8,0x19,0x69,0x2f,0xf3,0xa8,0xa9,0x9c,0x3c,0xd8,0xa2,0x35,0x7f,
+  0x3f,0xdd,0xe8,0x21,0xe,0x3f,0x94,0xf4,0x87,0xb3,0xeb,0x0,0x84,0xef,0x74,0x54,
+  0x75,0xa,0x4d,0x1a,0x84,0x73,0xd7,0x53,0x19,0xae,0x52,0x5d,0xde,0xe,0x9f,0x50,
+  0xca,0x74,0xa8,0xd0,0x91,0x2a,0xa1,0x54,0x9,0x29,0xd3,0x71,0x1e,0x9d,0xdc,0x23,
+  0xcc,0x4d,0x2f,0x27,0x57,0xd8,0xb7,0xce,0x2a,0xd3,0x2f,0x34,0xe8,0x2c,0x7e,0x95,
+  0x30,0x7d,0x98,0xe9,0x3c,0x26,0x79,0x91,0x73,0x3,0x48,0x9e,0x87,0xee,0xd,0x19,
+  0xd5,0xe8,0x10,0x9a,0x1f,0xa2,0x73,0xe6,0x5a,0xe2,0xd6,0x18,0xd5,0x11,0xa1,0x3c,
+  0xc8,0x4a,0x8b,0x16,0x1,0xe9,0x3b,0xdf,0x2e,0x3,0xd7,0xb,0xdc,0x5d,0x52,0x4e,
+  0xfe,0x3,0x4e,0x1f,0x79,0x95,0x6e,0xe7,0x5e,0xba,0xf1,0x63,0x4c,0x57,0x63,0x5a,
+  0x3f,0x5c,0x17,0xee,0xdc,0xf7,0xa6,0xc9,0x5b,0x60,0x78,0xd7,0x76,0x9,0xfc,0xbb,
+  0xd5,0x2f,0x7d,0x9c,0xa1,0x6d,0x17,0x31,0x76,0x89,0x47,0x6d,0xb,0x4,0xb5,0xe2,
+  0xd8,0x1,0xe4,0x29,0xc4,0x1d,0x68,0xcf,0xc2,0xfc,0xf1,0x8c,0xe6,0xe9,0x69,0x49,
+  0xe3,0x5f,0x68,0x9c,0x3e,0x48,0xf3,0xd5,0x93,0x1c,0xff,0xfd,0xa6,0x21,0xce,0x7f,
+  0x83,0xbc,0xf0,0x63,0x50,0xab,0x7b,0x52,0xf1,0x27,0xf1,0xed,0x4d,0x18,0x7b,0xb3,
+  0x5a,0x7f,0x37,0x5e,0x30,0x86,0xf5,0x82,0x2,0x40,0x96,0x90,0xc5,0x73,0x92,0xa7,
+  0x47,0x71,0xee,0x49,0xb2,0xfc,0x49,0xed,0x66,0xaf,0xd3,0xa,0x33,0xa6,0x1f,0x39,
+  0xa7,0xfb,0x37,0x77,0x85,0x5d,0x96,0xcb,0x3f,0x9,0xb6,0x1c,0xe0,0xfb,0x83,0x66,
+  0xc0,0x1f,0x55,0xe7,0x6,0x0,0xc4,0x98,0xd0,0x85,0xe9,0x3c,0x49,0xd6,0x22,0xeb,
+  0xc4,0xfc,0xeb,0xc7,0xff,0x95,0xdb,0xff,0x5d,0xb6,0xdd,0x1,0x13,0x77,0xfc,0x5f,
+  0x2e,0xfe,0x3,0x8e,0x26,0xc0,0xa6,0x8,0xff,0x6,0x23,0x0,0x0,0x0,0x0,0x49,
+  0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
+    // /usr/local/google/home/chudy/Ico/reload.png
+  0x0,0x0,0x7,0x8c,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0x73,0x7a,0x7a,0xf4,
+  0x0,0x0,0x0,0x4,0x73,0x42,0x49,0x54,0x8,0x8,0x8,0x8,0x7c,0x8,0x64,0x88,
+  0x0,0x0,0x7,0x43,0x49,0x44,0x41,0x54,0x58,0x85,0xad,0x96,0x5b,0x6c,0x54,0xd7,
+  0x15,0x86,0xff,0xb5,0xf7,0x39,0x33,0x67,0x2e,0x67,0x6c,0x8f,0x1d,0x1b,0x63,0x63,
+  0x12,0x63,0x7,0xe2,0x81,0x70,0x89,0xcb,0x25,0x21,0x4d,0x68,0x9b,0xaa,0x17,0xa9,
+  0x8a,0xaa,0xaa,0x2f,0x51,0xa5,0x34,0xaa,0x54,0x45,0x6a,0x33,0xf,0xc9,0x4b,0xa4,
+  0x3e,0xb4,0x6a,0xa5,0x94,0x3e,0x54,0xa5,0xaa,0xa2,0x5e,0x22,0xb5,0xf,0x95,0xda,
+  0xaa,0x55,0xa4,0x84,0x40,0x9a,0x96,0x88,0x4b,0x1c,0xb0,0xc1,0x71,0x8d,0x31,0x36,
+  0x97,0x80,0x31,0xd8,0xd8,0x66,0x6c,0x8f,0xcd,0x8c,0x67,0xce,0x39,0xfb,0xd6,0x87,
+  0x31,0xc1,0x98,0x31,0x18,0xc8,0x92,0x8e,0x46,0xda,0xb3,0xf7,0xfa,0xbf,0xb5,0xd6,
+  0xd6,0x5e,0xb,0x78,0x40,0x73,0x53,0xe9,0xf5,0x6e,0x2a,0xbd,0xeb,0x7e,0xcf,0xb3,
+  0x7,0x5,0x0,0xd0,0x50,0x59,0x9d,0xfc,0xa9,0x9b,0x4a,0xb7,0xdd,0xcf,0x61,0xfe,
+  0xa0,0xea,0xe1,0xda,0xed,0x2d,0x2f,0xfe,0xf0,0x9b,0x3f,0xe,0xc7,0xe2,0x5f,0xcc,
+  0xb3,0xb6,0x8b,0xf9,0x91,0x23,0x43,0xf7,0x72,0xfe,0xf3,0xc8,0x0,0x38,0x1,0x5f,
+  0xfa,0xfa,0xb6,0x4d,0x8f,0x3f,0xf1,0xd8,0x2f,0xef,0x35,0x13,0xd6,0xbd,0x8a,0xb9,
+  0xa9,0x34,0x0,0x84,0x1,0xb8,0x0,0xa2,0x0,0x56,0x28,0xd,0xe4,0xb5,0x65,0x3d,
+  0xf7,0xfc,0xb3,0x4f,0x28,0x43,0x7f,0xe9,0x77,0x7f,0xf2,0xfa,0x44,0xe7,0x2f,0x3e,
+  0xfc,0x5c,0x1,0xdc,0x54,0x9a,0x0,0x34,0x2,0xf8,0x46,0x55,0x4d,0x72,0x73,0x53,
+  0xcb,0xea,0x95,0x55,0xc9,0x4a,0xd7,0xa,0x5b,0x35,0x89,0x64,0x32,0x9c,0xb,0xc,
+  0x2,0x29,0x69,0xe7,0x73,0xdb,0x37,0x1b,0x83,0x37,0xa,0xb9,0xf4,0x8b,0xb9,0xd3,
+  0x7b,0x6,0xee,0xe6,0x97,0x96,0x21,0x1c,0x6,0xb0,0xbe,0x79,0xed,0xea,0xd7,0x9a,
+  0x1e,0x69,0xf8,0xf2,0xba,0xd,0xad,0x55,0x15,0x55,0x71,0xae,0x89,0x93,0x34,0x80,
+  0x1,0xc1,0x80,0x21,0xe7,0x97,0xdc,0x31,0xc6,0x50,0x19,0xb5,0xcd,0xc1,0xbd,0x7,
+  0xbb,0x7,0x7a,0x4e,0xbf,0x7e,0xad,0xeb,0xce,0x99,0xb8,0xe3,0x25,0x74,0x53,0x69,
+  0xc4,0xe2,0xb1,0x57,0xbe,0xf0,0xe4,0xe3,0x3f,0xdf,0xf6,0xf4,0xa6,0x9d,0x2b,0x9b,
+  0x9b,0xe2,0xd2,0xe,0xf1,0x7c,0x0,0x2a,0x8,0x83,0x62,0x0,0x14,0x25,0xa1,0x28,
+  0x0,0x65,0x18,0xb4,0x21,0x48,0xcd,0x50,0x90,0xa0,0xe6,0xe6,0xc6,0x3a,0xaf,0xe0,
+  0xa5,0xa6,0xbd,0xc6,0x8f,0x83,0x4c,0x57,0xe6,0x9e,0x1,0xdc,0x54,0x7a,0x45,0x4d,
+  0x6d,0x72,0xf7,0xae,0xaf,0x6e,0x7f,0x75,0xcd,0xfa,0xd6,0x7a,0xdf,0x72,0xb8,0x2f,
+  0x41,0x81,0x4,0x94,0x22,0x28,0x5,0x28,0x4d,0x50,0x8a,0xa0,0x35,0x41,0x6a,0x82,
+  0x32,0x4,0x65,0x18,0x84,0x66,0xe0,0x56,0x98,0x6a,0xaa,0xdd,0xe4,0xc9,0x63,0x27,
+  0xa6,0xc3,0xb5,0x3b,0xba,0x82,0x4c,0x97,0x2c,0xa7,0xb3,0xe4,0x1d,0xa8,0xaa,0xa9,
+  0x7c,0xad,0x7d,0xc7,0x86,0x17,0x2a,0x1b,0xea,0x23,0x39,0x41,0xd0,0x46,0xc3,0x80,
+  0xc1,0x0,0x0,0x34,0xc,0x31,0x18,0x18,0x18,0x32,0x0,0x11,0xc,0xa8,0xf4,0xcb,
+  0x18,0x42,0x9c,0xe0,0x4f,0x67,0xfc,0x8f,0xf6,0x7f,0xf0,0xa1,0x3f,0xd1,0xf1,0x8e,
+  0x98,0xfa,0x5f,0xb0,0x94,0xce,0x6d,0x0,0x6e,0x2a,0xcd,0x39,0xe7,0x2f,0x6d,0x6a,
+  0x4f,0xbd,0x5c,0xdd,0xd0,0x10,0xc9,0xfb,0x6,0x30,0xaa,0x24,0x48,0x6,0x0,0x81,
+  0x19,0x3,0x32,0xda,0x68,0xa9,0x8c,0xd1,0x5a,0x47,0xa2,0x11,0x96,0xe3,0x9,0x66,
+  0x18,0xc1,0x81,0x32,0x62,0x26,0xe3,0xbf,0xf7,0xb7,0x7f,0xfc,0x3d,0x33,0xf0,0xce,
+  0x5b,0x72,0x76,0xf0,0x2c,0x0,0xbd,0x6c,0x0,0x0,0x6b,0x9a,0x9a,0x1b,0xbf,0x5f,
+  0xb7,0xaa,0x3e,0x52,0x90,0x20,0xd,0x55,0x8a,0x6e,0xde,0x78,0x50,0x30,0x23,0xc3,
+  0xa3,0xc5,0x89,0x91,0xd1,0xac,0x54,0x6a,0x58,0x4,0x52,0x6e,0xda,0xba,0x79,0x2b,
+  0xad,0xaa,0x70,0x6c,0xa3,0x51,0x9c,0x1c,0xb,0xba,0xf,0x1e,0x3a,0x9c,0x19,0x7c,
+  0xf7,0x2d,0x39,0x3b,0x78,0xa,0x80,0xb7,0x94,0x78,0x59,0x80,0x8a,0xaa,0x8a,0x97,
+  0x9a,0x5b,0x56,0x6d,0xc9,0xb,0x46,0xca,0x28,0x18,0x30,0x80,0x18,0xb8,0xf6,0x4c,
+  0xfe,0xda,0xb8,0xe8,0xef,0xe9,0x3b,0x3e,0x31,0xd4,0xf7,0x4f,0x6f,0xe4,0x83,0x63,
+  0x46,0xce,0x79,0x91,0xd5,0xcf,0x3f,0xd9,0xda,0xb6,0xb6,0x3d,0x21,0x7c,0x53,0x9c,
+  0x1c,0xb,0x3a,0xf6,0xed,0xfd,0x57,0xf6,0xdc,0xfe,0xdf,0xcb,0x99,0x81,0x4f,0xee,
+  0x26,0x7e,0x1b,0x80,0x9b,0x4a,0x53,0x3c,0x1e,0xf9,0x56,0xd8,0xad,0x8,0xf9,0x42,
+  0x3,0x6,0xa5,0x9a,0x93,0x46,0x61,0x72,0x5c,0xf6,0x7f,0x72,0xb2,0x73,0xac,0x7f,
+  0xdf,0xee,0x60,0xba,0xf7,0xc,0x8c,0x9c,0x2,0x20,0xb9,0x53,0xd7,0x22,0x95,0x42,
+  0xfe,0xea,0xb0,0xe8,0x3f,0x76,0xac,0x23,0x7b,0xee,0xfd,0x3f,0x88,0x99,0xd3,0x7d,
+  0xcb,0x11,0x2f,0x97,0x81,0x67,0x92,0xf,0x55,0xaf,0xe,0xc,0x27,0xe9,0xcb,0x52,
+  0xe1,0xc8,0x80,0x7,0x73,0x66,0xa0,0xf7,0xd4,0xd9,0xf1,0x73,0x47,0x7e,0x1d,0x4c,
+  0x75,0x1f,0x6,0x30,0x77,0xe3,0x0,0x59,0x8e,0xc8,0x8c,0x4f,0xca,0x2b,0x1d,0x47,
+  0xf7,0xce,0xe,0x1d,0x78,0x53,0xcc,0xf4,0x9f,0x58,0xae,0x78,0x39,0x80,0x8d,0xb1,
+  0x84,0x1b,0x12,0xc2,0x40,0xa2,0x94,0x7e,0x82,0x44,0x21,0x3b,0xa3,0x33,0xa3,0x57,
+  0xe,0x8a,0x6c,0xff,0xd1,0x85,0xe2,0x37,0x6c,0x68,0xf0,0x4c,0xf7,0xdc,0xc8,0xc1,
+  0x37,0x45,0xf6,0x54,0xef,0xbd,0x88,0x97,0x3,0xa8,0x27,0xc6,0x99,0x27,0x14,0xb4,
+  0x21,0x18,0xa3,0x41,0xd0,0x98,0x9a,0xc8,0x8,0xa3,0xc5,0x21,0xed,0x65,0x6e,0x7b,
+  0x50,0x54,0xfe,0xca,0xa0,0x97,0xfb,0xf4,0x67,0x22,0xdb,0x77,0x7c,0xa1,0xb8,0x9b,
+  0x4a,0x3f,0x8a,0xd2,0x3b,0x63,0x16,0x7d,0x79,0x0,0x63,0xb9,0xd3,0x7b,0xca,0x2,
+  0x24,0x8c,0x1,0x9,0xa1,0x61,0xe6,0x9f,0x59,0xe9,0x7b,0xb8,0x9e,0x9d,0x95,0xaa,
+  0x30,0xd1,0x5b,0x2e,0x82,0xc2,0xf0,0xdb,0x63,0x0,0x26,0x17,0x89,0x73,0xb7,0xba,
+  0xfa,0x8f,0xb5,0x4d,0x8f,0xc4,0x18,0xe7,0x9a,0x31,0xe,0xc6,0xb9,0x26,0xc6,0xf4,
+  0xc0,0xc7,0x87,0xf7,0x1,0x78,0x63,0x1e,0x66,0x11,0x80,0x31,0xbe,0xef,0xf9,0x46,
+  0x21,0x42,0x52,0x3,0xbe,0x50,0x90,0xbe,0xf,0x6e,0xd9,0x96,0x55,0xd1,0xd2,0x80,
+  0x51,0x5c,0x2c,0xc7,0x50,0x66,0x6d,0xf3,0xca,0xd6,0xd4,0x96,0xba,0x2d,0xcf,0xb8,
+  0xca,0x72,0x40,0xc4,0x60,0x71,0x42,0x2,0x39,0x35,0xd0,0x71,0xe8,0x40,0x6e,0xe0,
+  0xb7,0xe6,0xc6,0xc6,0x45,0x19,0x30,0xd7,0x7d,0xcf,0x37,0x64,0x29,0x78,0x81,0x46,
+  0xd1,0x97,0xb0,0x39,0x87,0xed,0x38,0x9c,0x88,0x6f,0x6,0xf0,0x51,0xb9,0x2c,0x94,
+  0xb1,0xd,0xe1,0xaa,0x87,0xec,0x1c,0xe2,0x50,0x26,0xc,0x18,0x20,0xc,0xd,0xdb,
+  0x2b,0x2a,0x18,0x95,0x5d,0xb8,0xf1,0x96,0x81,0x44,0x2b,0xff,0x42,0x7e,0xf6,0xba,
+  0x94,0x22,0x40,0xd1,0xf3,0x61,0x8c,0x81,0x22,0xe,0x2b,0xea,0x5a,0x76,0x28,0xf4,
+  0x6d,0x37,0x95,0x7e,0xf8,0x6e,0xca,0x6e,0x2a,0xdd,0x18,0x4f,0x26,0xbf,0xe3,0xd4,
+  0x34,0x84,0x24,0xd9,0x9f,0xad,0x5b,0xa4,0x30,0x3b,0x31,0x1a,0x68,0xe5,0xdd,0x92,
+  0xc5,0x5b,0x0,0xe4,0xec,0x99,0xa3,0xd7,0xb3,0x33,0x9e,0xf0,0x8a,0xc6,0xe8,0xd2,
+  0xb3,0x6b,0x40,0x30,0xd1,0x24,0x25,0xaa,0x2a,0xb7,0x32,0xce,0x7f,0x74,0x37,0x80,
+  0x50,0x24,0xfa,0x83,0xc6,0x75,0xeb,0x9f,0x12,0xf1,0x7a,0x46,0xec,0xa6,0xfb,0x30,
+  0x2,0x33,0x79,0xe9,0xfc,0xac,0xc8,0xf6,0x9d,0x5c,0xb8,0xff,0x96,0x12,0xf8,0xe3,
+  0x47,0x86,0xb,0x2b,0xb6,0x74,0x26,0x72,0xd9,0xaf,0x50,0xb4,0x96,0x97,0xa,0x45,
+  0x50,0x2c,0x84,0x78,0xe3,0x1a,0x47,0x29,0xfd,0xa,0x6d,0x7c,0xb5,0x4e,0x49,0xf9,
+  0x1b,0x0,0xbd,0xb9,0xd3,0x7b,0xd4,0x7c,0xd4,0x1c,0xc0,0xfa,0x70,0x2c,0xf6,0xf2,
+  0xca,0xd6,0xb6,0xef,0x45,0x5a,0xb7,0x45,0x94,0x15,0x59,0x10,0xa5,0x1,0x4d,0x5f,
+  0xe,0xb2,0x63,0x97,0xf,0x7,0x99,0x13,0x57,0x97,0x4,0x0,0x20,0xbc,0xdc,0xc4,
+  0xdb,0xd7,0xb3,0x89,0xf6,0x8a,0x68,0x65,0xb5,0x4f,0x37,0xff,0x36,0x91,0x6a,0xaa,
+  0x5c,0xe3,0x58,0xf6,0xc8,0xb9,0xef,0xe6,0xb2,0xd9,0x2d,0x5e,0xa1,0x70,0xdc,0x4d,
+  0xa5,0x2f,0x1,0xd0,0x20,0x6a,0xaa,0xa8,0xa9,0x69,0xaf,0x7d,0xb8,0x65,0x6d,0xbc,
+  0x6d,0xa7,0xa3,0x62,0x75,0xb7,0xc,0x3a,0x9,0x2a,0xe0,0xda,0xc5,0xc1,0xf1,0xe9,
+  0xcb,0xfd,0xfb,0x1,0x88,0x3b,0x1,0xc0,0x9f,0x3a,0xb9,0x77,0xc6,0x76,0x9f,0x72,
+  0xa2,0xf1,0x17,0xec,0xea,0x6,0xae,0xc8,0x29,0xb5,0x59,0xe2,0xd0,0xd1,0x6a,0x72,
+  0xd7,0xed,0x8,0x25,0x8a,0xd9,0x36,0xe9,0xcd,0x3d,0x66,0x44,0xa0,0xb9,0x1d,0x42,
+  0xb8,0x22,0xc9,0xc2,0x55,0x75,0xa4,0x2a,0x1a,0x21,0x9c,0x4,0x88,0xf1,0xcf,0xda,
+  0x57,0x4,0x45,0xa3,0xc6,0xcf,0x88,0x81,0x8e,0xff,0xbe,0xeb,0x8d,0x1f,0x3f,0xb0,
+  0x58,0xef,0x36,0x0,0x39,0x33,0x90,0xf1,0xed,0xc4,0x9f,0xb2,0xd1,0xc4,0xd3,0x49,
+  0xcb,0x5e,0xcd,0x6b,0x63,0xac,0xd4,0xf,0x4a,0x9f,0xe6,0x61,0x50,0x65,0x13,0x2c,
+  0xdb,0x26,0x4e,0xe0,0xcc,0xe,0x1,0xe1,0x18,0x2,0xcb,0x1,0xf1,0x10,0xd8,0xc2,
+  0xce,0x9,0x5,0x7b,0xea,0x82,0x3c,0xdb,0x79,0xa4,0xaf,0x38,0xd6,0xf9,0x57,0x99,
+  0x1f,0x9a,0x5a,0xac,0x57,0x6e,0x22,0xd2,0xaa,0x30,0x92,0x51,0x14,0x1d,0x15,0xda,
+  0x69,0xb,0x41,0xd6,0x84,0x22,0x31,0xc0,0x8e,0x12,0x31,0xe,0xe2,0x16,0x88,0x73,
+  0x10,0xb3,0x1,0x2b,0x4,0x58,0xe,0xc0,0xac,0x9b,0xeb,0xdc,0x2,0x11,0x83,0xa3,
+  0x72,0xc6,0x9a,0x18,0x90,0x67,0xe,0xbf,0xdf,0x35,0xd6,0xb7,0xef,0x57,0x41,0xa6,
+  0xeb,0x28,0x80,0xdb,0x6,0x93,0xa5,0x46,0x32,0xa9,0x8b,0xe3,0x19,0xad,0xd9,0x25,
+  0x5,0x77,0x23,0x89,0x62,0x95,0x13,0xb6,0x19,0x77,0xe2,0x20,0x6e,0x83,0x18,0x7,
+  0x9b,0x17,0x64,0xcc,0x2,0x31,0xb,0x8c,0x73,0x10,0xb3,0x60,0x31,0x83,0x88,0x97,
+  0x81,0x37,0xd4,0x13,0xc,0x9d,0xe8,0xe8,0x19,0xef,0x7f,0x6f,0xb7,0x98,0xec,0xee,
+  0x84,0x91,0x33,0xe5,0x84,0x96,0x1e,0x4a,0xb5,0x98,0x53,0xf9,0xe1,0xab,0xd2,0x9b,
+  0x39,0xe1,0x8b,0x70,0xd1,0xcf,0xe7,0xeb,0x4d,0x6e,0x32,0x1a,0x62,0x92,0x88,0x8,
+  0x8c,0x40,0x9c,0xc,0x38,0x29,0x30,0x15,0x80,0x8b,0x39,0xc3,0x67,0xaf,0xe8,0xe0,
+  0x52,0x8f,0x3f,0xd6,0x77,0xec,0xc2,0xf9,0xa3,0xff,0xfe,0xf3,0xcc,0xf9,0x77,0x7f,
+  0x27,0xb2,0x7d,0xdd,0xf3,0xad,0xbb,0xac,0xdd,0x75,0x2c,0x7,0x50,0x1,0xe2,0xf5,
+  0x4e,0xfd,0xae,0x6d,0x76,0xa2,0xa9,0x3d,0x1c,0xab,0xf9,0x5a,0xc8,0x89,0xac,0x74,
+  0xe2,0x2e,0xb7,0x23,0x31,0xe2,0x96,0x5,0x29,0x85,0x16,0x85,0x39,0x3f,0xf0,0xa,
+  0x97,0xa,0xd9,0xab,0xff,0x29,0x4e,0x9e,0xed,0xf1,0xc6,0x8f,0xf4,0xc2,0xe8,0x31,
+  0x0,0xb3,0x77,0x72,0xbe,0x1c,0x80,0x1b,0x56,0xd,0xa0,0xa,0xa0,0xa8,0x95,0x78,
+  0xb4,0xc6,0x4a,0x34,0xaf,0x22,0x3b,0x5e,0xd,0x18,0x63,0x44,0x3e,0x23,0x67,0x3f,
+  0x1d,0x95,0xb9,0xb,0x53,0x80,0x29,0x0,0xc8,0x2,0x58,0x32,0xea,0xfb,0x5,0x0,
+  0x4a,0x25,0x73,0x0,0x44,0x0,0x84,0x0,0xdc,0x78,0x6b,0x5,0x4a,0x17,0xac,0x88,
+  0x52,0x57,0x54,0xcb,0x75,0xf8,0x7f,0xd4,0xec,0x69,0x2,0x3f,0xb2,0xd3,0x69,0x0,
+  0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
+    // /usr/local/google/home/chudy/Ico/play.png
+  0x0,0x0,0x4,0x31,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0x73,0x7a,0x7a,0xf4,
+  0x0,0x0,0x0,0x4,0x73,0x42,0x49,0x54,0x8,0x8,0x8,0x8,0x7c,0x8,0x64,0x88,
+  0x0,0x0,0x3,0xe8,0x49,0x44,0x41,0x54,0x58,0x85,0xed,0x95,0x4d,0x68,0x5c,0x55,
+  0x18,0x86,0x9f,0xef,0xdc,0x99,0x3b,0x93,0xc4,0xc6,0x4a,0x9b,0x40,0x9b,0xc4,0xe6,
+  0xf,0x8b,0xa9,0x6d,0xa2,0x84,0xa,0x82,0xb8,0x71,0xa1,0x15,0x11,0x3,0x96,0xb4,
+  0x5a,0x14,0xbb,0xd1,0x55,0x17,0x6e,0x5d,0x9,0x9,0x8,0x52,0xa6,0x6e,0xa5,0x50,
+  0x7f,0x69,0x85,0x5a,0x6a,0x75,0x61,0xa9,0xda,0x80,0x3f,0x88,0x3f,0xd9,0x68,0x12,
+  0x25,0x20,0xc6,0xa0,0x36,0x42,0x4d,0x49,0x9a,0xcc,0xdc,0x9f,0xf3,0xb9,0x38,0xf7,
+  0xce,0x4c,0x92,0xa9,0xb1,0xed,0x6c,0xc4,0x7c,0x70,0x39,0xc3,0xe1,0x30,0xef,0xf3,
+  0xbd,0xef,0xb9,0xdf,0x85,0x8d,0xda,0xa8,0xff,0x78,0x79,0x67,0xe,0x48,0xe1,0xf8,
+  0x50,0xbe,0xf3,0x5f,0x9c,0x95,0xb,0x4f,0xf3,0xc2,0xc4,0xf3,0x9c,0xfa,0xe2,0x30,
+  0xa7,0x0,0xf,0xc0,0xdc,0x24,0x40,0x73,0x5f,0x47,0xdb,0x91,0xbd,0x3,0xfd,0xef,
+  0xbd,0x36,0xd4,0xd4,0xbd,0xce,0xd9,0xdc,0x66,0x9f,0x7,0xba,0xf6,0xdc,0xbf,0xbf,
+  0xad,0x75,0xcb,0x7e,0xa0,0xb9,0x1e,0x0,0x99,0x5c,0xe3,0x26,0x3a,0x77,0xed,0xbd,
+  0xfb,0x9e,0x9d,0x3b,0xa,0xc7,0x9f,0xd8,0xf4,0x4f,0x10,0xc6,0xa,0x6,0x2c,0x22,
+  0xa,0x90,0xa9,0x7,0x0,0x22,0x96,0x5c,0xae,0x81,0x3b,0x6,0xee,0x7b,0xf4,0xde,
+  0xdd,0xbd,0xa7,0x4f,0xc,0xdf,0xda,0x73,0xed,0xb3,0x0,0x31,0x38,0x0,0xea,0x2,
+  0x0,0x8a,0xda,0x90,0x6c,0xae,0x81,0x1d,0xbd,0xbb,0x6,0xfa,0xbb,0x5b,0x8f,0xbd,
+  0x7e,0xf0,0xb6,0x9a,0x10,0x62,0x1c,0x80,0x3,0xa9,0x13,0x80,0x11,0xb,0x36,0x40,
+  0x34,0xc0,0xcf,0xe5,0xe8,0xbd,0x73,0xcf,0x23,0x83,0x3b,0xb7,0x9d,0x7e,0xeb,0xa9,
+  0xad,0xbd,0x6b,0x0,0x4,0x84,0x18,0x31,0xb6,0xbc,0x97,0x49,0x56,0xef,0xe3,0x61,
+  0xde,0xd9,0x92,0x75,0x48,0x2,0x20,0x89,0x65,0xc9,0x2a,0xa6,0xaa,0x8b,0x64,0x2f,
+  0x52,0xf2,0x4d,0x19,0xb,0x36,0x4,0x8d,0xc0,0x46,0x64,0x7d,0x9f,0x8e,0xce,0xae,
+  0xfe,0x28,0x58,0x3a,0xf6,0xf6,0x21,0x39,0xf2,0xe4,0x9b,0x7f,0x4e,0xaf,0x76,0xa0,
+  0x3a,0x82,0x14,0xa0,0xb9,0xb3,0xbd,0x6b,0x7f,0xdb,0xc0,0x83,0x60,0x3c,0x30,0x6,
+  0x31,0x6,0x4c,0x42,0x93,0x7a,0x26,0xa,0x28,0x60,0x41,0xe3,0xca,0x6a,0x83,0x32,
+  0x80,0x68,0x48,0x2e,0x9b,0xa1,0xbb,0xa7,0x7b,0x5f,0xd6,0x9f,0xe9,0x38,0xf9,0x8c,
+  0x37,0x34,0x7c,0xe2,0x8f,0xe9,0x14,0x40,0x25,0x4a,0x2f,0xa1,0x73,0xb0,0xc,0xa2,
+  0x16,0x8d,0x3,0xf7,0x67,0x71,0x80,0xc6,0x1,0x1a,0x95,0xdc,0x1a,0x7,0x68,0xb2,
+  0x5f,0x7e,0x6c,0x8,0x71,0xe8,0xce,0xdb,0x64,0xd5,0x10,0x6c,0x4,0x1a,0x91,0xcd,
+  0x7a,0xb4,0x6f,0x6f,0xd9,0xdd,0xd7,0x9e,0x79,0xf5,0xe4,0xb3,0xdb,0x5d,0x1c,0x82,
+  0x3,0xae,0xe1,0x0,0xaa,0x16,0xa2,0x12,0xa8,0xe7,0x50,0x8d,0x1,0x23,0x60,0xcb,
+  0xe1,0xb9,0xee,0x13,0x17,0x14,0x5b,0x76,0x40,0x34,0x6,0x9c,0xb0,0xda,0xc8,0xd9,
+  0xac,0x21,0xbe,0xef,0xd1,0x75,0xfb,0xd6,0x87,0x7d,0xff,0xf2,0x99,0x91,0xc7,0x5a,
+  0xf,0x19,0x6f,0x4e,0x84,0x8,0x31,0x35,0x0,0xc0,0xba,0x8e,0x88,0x13,0xaf,0xc,
+  0xa8,0x54,0x22,0x10,0xd0,0x72,0x4,0x49,0xc,0x58,0x84,0x34,0x8a,0xd0,0x35,0x41,
+  0x8,0x36,0x6,0x8d,0x10,0x8d,0xc8,0x66,0x3c,0xda,0x5a,0x9a,0xee,0x7a,0xa8,0xaf,
+  0x74,0x34,0xef,0x35,0xb7,0x42,0x8c,0x50,0x3,0x40,0x34,0x46,0xa3,0x92,0x13,0x16,
+  0xa9,0x38,0x20,0x52,0xbe,0x80,0x88,0x26,0x4e,0x58,0x10,0x8b,0xa0,0x68,0x5c,0x4,
+  0x5b,0x72,0xab,0x86,0x10,0x2d,0x41,0x5c,0x4,0x5b,0x44,0x6d,0x98,0xc4,0x19,0x33,
+  0x7f,0x45,0x36,0x67,0x9a,0x82,0xbc,0x2e,0x2e,0x80,0xad,0xe5,0x80,0xb5,0x2e,0x5b,
+  0x35,0xe0,0x25,0xdd,0xa7,0xe,0x58,0x71,0xb7,0x45,0x14,0x34,0x46,0xa3,0xab,0x50,
+  0xba,0x82,0x46,0x8b,0x68,0x69,0xde,0x9,0x6b,0x80,0x68,0xe4,0xa2,0x48,0xdf,0x73,
+  0x3,0x81,0x85,0xa9,0x4b,0xfe,0xec,0xcb,0x17,0xcc,0xd1,0x97,0xf6,0x45,0x87,0x81,
+  0x9e,0xea,0x39,0xb0,0x22,0x2,0x89,0x8a,0xe0,0x79,0x4e,0xd8,0x88,0x13,0x26,0x46,
+  0xe3,0x12,0x5a,0x9a,0x77,0x62,0xa5,0x79,0x88,0x97,0xdd,0x4d,0x4e,0x5e,0x49,0xc,
+  0x2b,0x7f,0x27,0xec,0xa1,0x85,0x6f,0x66,0x72,0xe3,0x85,0x31,0x33,0x72,0xfe,0xa7,
+  0xe5,0xcf,0x5e,0x79,0x9c,0x3,0x92,0x9e,0x5d,0x3,0xa0,0x16,0x8d,0x43,0x97,0xa9,
+  0xa,0x1a,0x5b,0xb4,0xb4,0x0,0x4b,0x73,0x10,0x2e,0x38,0x5b,0xc5,0xba,0x39,0x91,
+  0xcc,0x87,0xea,0x47,0xaa,0xc4,0x11,0x8,0x15,0x7e,0x9c,0xf3,0x67,0xb,0x63,0x66,
+  0xe4,0xdc,0xc4,0xf2,0x45,0xa0,0xe8,0x19,0x14,0x53,0x99,0x29,0xd5,0x0,0xf6,0xab,
+  0x5f,0x2e,0x7f,0xde,0x38,0xfb,0xa5,0x2f,0xb2,0x52,0xc0,0xa4,0x83,0xa7,0x6a,0x0,
+  0xa5,0x83,0xc9,0xf3,0x90,0xc1,0x76,0x6,0x73,0x1e,0x65,0xdb,0x35,0x11,0xff,0x76,
+  0x26,0x3f,0x5e,0x18,0x33,0xa3,0xe7,0x26,0x96,0x3e,0x2,0x16,0x81,0xc6,0xd4,0xa1,
+  0x5a,0x11,0x5c,0x3d,0x78,0x96,0xe7,0x80,0x6,0xae,0xaf,0x6e,0xf9,0xed,0x45,0x3e,
+  0xc9,0xfb,0x15,0xfb,0x43,0x5b,0xee,0x7c,0xf4,0xfd,0x1f,0x96,0x3e,0x4d,0xc4,0x81,
+  0x55,0xce,0xad,0x2,0x28,0x2,0xdf,0x5f,0xa7,0x38,0x40,0x8b,0x42,0xcd,0xcc,0xab,
+  0x3a,0xaf,0x54,0x92,0x7f,0xad,0x8,0x6e,0xb8,0xd2,0xef,0x46,0x68,0x61,0x6a,0x65,
+  0xe6,0x8b,0x6b,0xce,0x56,0xc7,0x5b,0x2f,0x0,0xc5,0xbd,0x6a,0x5f,0xff,0x9a,0xfb,
+  0xae,0x30,0x26,0xa3,0x1f,0x4c,0x2e,0xaf,0xed,0xbc,0xa,0xa0,0x7c,0x61,0x93,0xba,
+  0xe9,0xcf,0x71,0x68,0x61,0xf2,0x92,0x3f,0xeb,0xc4,0x8b,0x17,0xaf,0x25,0x5e,0xa1,
+  0xa0,0xae,0xe,0xd8,0xb1,0x9f,0xbd,0xb3,0xef,0x8e,0x9b,0x37,0x3e,0x9c,0x2a,0x9e,
+  0x5f,0x47,0x5c,0x27,0x7f,0x67,0x72,0x6e,0x91,0xd6,0x38,0x26,0xc0,0xcd,0xf2,0x6a,
+  0x96,0x1b,0xaa,0x3c,0xb0,0xd,0x98,0x7,0xfe,0x5a,0xe7,0x6c,0x6,0x68,0x3,0x5a,
+  0x81,0x65,0x60,0x1a,0x77,0xf9,0x37,0xea,0x7f,0x5e,0x7f,0x3,0x6e,0xb2,0xc3,0xf0,
+  0x1c,0xaa,0x77,0x3f,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
+
+    // /usr/local/google/home/chudy/Ico/pause.png
+  0x0,0x0,0x4,0x9d,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0x73,0x7a,0x7a,0xf4,
+  0x0,0x0,0x0,0x1,0x73,0x52,0x47,0x42,0x0,0xae,0xce,0x1c,0xe9,0x0,0x0,0x0,
+  0x9,0x70,0x48,0x59,0x73,0x0,0x0,0xb,0x13,0x0,0x0,0xb,0x13,0x1,0x0,0x9a,
+  0x9c,0x18,0x0,0x0,0x0,0x7,0x74,0x49,0x4d,0x45,0x7,0xdc,0x7,0x10,0x10,0xb,
+  0x14,0xae,0x19,0x53,0xbd,0x0,0x0,0x4,0x2f,0x49,0x44,0x41,0x54,0x58,0xc3,0xed,
+  0x97,0xbb,0x8e,0x5c,0x45,0x10,0x86,0xbf,0xaa,0xea,0xee,0x33,0x63,0x7b,0x57,0x2b,
+  0x20,0xc0,0xe6,0x2a,0x4,0x42,0x46,0xf2,0x13,0xf8,0x9,0xb0,0x88,0x48,0x4c,0x48,
+  0x84,0x10,0x8f,0x40,0xee,0xa7,0x80,0xc,0x91,0x93,0x10,0x58,0x48,0x38,0x23,0xc2,
+  0x42,0x22,0xb0,0x84,0xa5,0x35,0x46,0x46,0x80,0x11,0x6b,0xbc,0xd7,0x99,0x73,0xe9,
+  0x2e,0x82,0x73,0xe6,0xb2,0xd6,0x8e,0xbd,0x3b,0x29,0xee,0xd1,0xd1,0x19,0xe9,0xd4,
+  0x54,0xff,0xf5,0xff,0x7f,0xd5,0xe9,0x81,0xe7,0xeb,0xff,0xbe,0x64,0xd5,0x83,0x77,
+  0x3f,0xf8,0x3a,0xd4,0x75,0xb3,0x95,0x4b,0x49,0x80,0x3,0xc8,0x10,0x2d,0x22,0xa2,
+  0xea,0xd3,0x6e,0xff,0xde,0xee,0x83,0x1f,0x6e,0xe4,0x77,0xae,0x7d,0x75,0xa1,0x6e,
+  0xda,0x4d,0x77,0x77,0x59,0xca,0x2a,0x80,0xa8,0x62,0x32,0xd9,0xdb,0xbe,0xf9,0xd9,
+  0xe1,0x49,0xfb,0x84,0x55,0x0,0xda,0xb6,0xbb,0xfa,0xc2,0x8b,0x9b,0xdf,0x8c,0xc6,
+  0x69,0xb,0x7,0xf7,0x1e,0x85,0xf,0xdf,0x8f,0x8e,0x8e,0x1e,0xec,0x3c,0x6a,0x3e,
+  0x4,0x7e,0x14,0x95,0x2f,0x2f,0xbd,0xf2,0xd2,0x75,0x15,0x59,0x8a,0x73,0xdc,0x21,
+  0x17,0xf8,0x77,0xe7,0xe1,0x17,0xc0,0xa7,0x43,0x21,0x7e,0x2a,0x0,0x4d,0xd7,0x5d,
+  0xb9,0x7c,0xf9,0xf5,0xad,0x37,0xde,0x7e,0x93,0x69,0x5d,0xc8,0x5,0x4a,0x71,0xda,
+  0xe,0xcc,0x2,0x77,0x7e,0xbe,0xf3,0xda,0x5f,0x77,0x77,0xaf,0x88,0xa4,0x6d,0x51,
+  0xbb,0xfe,0xfe,0xb5,0xab,0xfc,0xf3,0xb8,0xc1,0x81,0x5c,0x84,0x9c,0x9d,0x36,0x43,
+  0x32,0xe1,0xbb,0x6f,0x6f,0x7e,0x4,0x7c,0xe,0xec,0x2,0xed,0xa9,0x0,0x38,0xd2,
+  0x21,0xc2,0xbd,0xdf,0xf,0x79,0xb4,0xdf,0xc2,0x52,0x45,0x5b,0x9b,0x23,0x1c,0xa3,
+  0xe4,0xc9,0x39,0xf7,0x26,0x3b,0xf8,0xa4,0xee,0xe4,0xa7,0xbb,0xbb,0x4,0x13,0x8a,
+  0xf7,0x19,0x72,0x81,0xb7,0x2e,0x6e,0x20,0x2a,0x35,0xb0,0x1,0x1c,0x9c,0x1a,0x80,
+  0x88,0xe2,0x2e,0xb4,0xd9,0xe9,0xba,0x32,0x24,0x85,0x52,0xa0,0x69,0x9d,0x8c,0x20,
+  0xa2,0x2,0xb4,0x22,0x42,0x97,0xa1,0xe9,0xa,0xa5,0x77,0x1,0xee,0x50,0xdc,0x99,
+  0xb6,0x8e,0x98,0xad,0xf4,0xdb,0x6a,0x0,0x66,0x64,0x17,0x4a,0x61,0xbe,0x79,0xaf,
+  0xaf,0xd3,0x65,0xa7,0x14,0x41,0x55,0xfa,0x52,0x55,0xe9,0x3a,0x9f,0xc7,0xcc,0xef,
+  0xe,0x75,0xed,0xa8,0xda,0xca,0x2e,0x58,0x9,0x40,0x35,0x50,0x8a,0x90,0xcb,0x22,
+  0xf1,0x2c,0x69,0xd7,0x39,0xd9,0x15,0x54,0x7,0xb6,0x2,0x4d,0xe7,0x94,0x2,0x2a,
+  0x1c,0x8b,0x9f,0x36,0xe,0xb6,0x6,0x0,0x6,0x6,0x66,0x89,0xfa,0xcb,0xe7,0x15,
+  0x8a,0x8,0x22,0x3a,0xc4,0x2e,0x18,0x28,0x65,0xe6,0xa1,0xfe,0x3,0xa0,0xb2,0x6,
+  0x0,0x33,0xed,0xdd,0x5c,0x66,0xb4,0xe,0x4c,0xc,0xf3,0x40,0x4d,0xe7,0x83,0x41,
+  0x54,0xd9,0x3f,0x2a,0x1c,0x4d,0x33,0x22,0x8b,0x3e,0x73,0x87,0xcd,0xb1,0x23,0xa6,
+  0x67,0x7,0x20,0x6a,0xf3,0xd6,0x73,0x5f,0xc8,0xd0,0x76,0x85,0xa6,0x2d,0xb8,0x28,
+  0x32,0x97,0xc0,0xd8,0x3d,0x28,0x4c,0xea,0x82,0xd9,0xc2,0x6b,0x5e,0x6,0xcd,0x34,
+  0xac,0xe1,0x1,0x33,0xba,0x1,0x80,0x48,0xef,0xfc,0x49,0x5d,0xa8,0x9b,0x8c,0x7b,
+  0x20,0xc,0x9d,0x32,0x63,0xa0,0xe9,0x1c,0x95,0xe3,0x56,0x77,0x81,0x18,0x65,0xe,
+  0xf4,0x8c,0x5d,0xa0,0xb4,0x45,0xa0,0xc0,0xde,0x61,0x1e,0x86,0x91,0x53,0xbc,0x7,
+  0x64,0x6a,0x30,0xf3,0x80,0x2a,0xd3,0xe6,0x84,0x46,0x73,0x88,0x1,0xd0,0xb5,0x3c,
+  0x60,0x4c,0xa7,0x70,0xd0,0xb6,0x1c,0xd5,0x79,0xe9,0xe5,0x21,0x98,0xd2,0xb7,0xe0,
+  0x8c,0x1,0x51,0xea,0xc6,0x4f,0x6c,0xf4,0x10,0x7a,0x39,0xcf,0x2e,0x41,0x50,0xa6,
+  0xad,0xd3,0x66,0x3f,0x41,0x1e,0x30,0x5d,0x98,0x90,0x55,0x14,0xb,0xc4,0xb0,0xa6,
+  0x4,0x88,0x12,0x82,0x20,0x65,0xa9,0xed,0x14,0x1c,0x21,0x4,0x3d,0x26,0x81,0xa8,
+  0x92,0x62,0xef,0x7e,0x15,0xe9,0x71,0x89,0x20,0xee,0xa4,0x24,0xeb,0x49,0x80,0x18,
+  0x66,0xfd,0xb4,0xd3,0xe1,0x2e,0xa6,0x7d,0x55,0x49,0x31,0x96,0x18,0x10,0x25,0x86,
+  0x7e,0x73,0x35,0x41,0x54,0x50,0x53,0x8a,0x3b,0x29,0xd9,0x6a,0x86,0x9e,0xe,0x40,
+  0x8,0x41,0xb0,0x4e,0x30,0x6,0x30,0xa1,0xdf,0xb4,0xaa,0x2,0x9e,0xcb,0x31,0x6,
+  0x62,0xec,0x1,0x5a,0x54,0xcc,0x14,0x35,0xc5,0x81,0xaa,0x52,0x44,0x6c,0x5d,0x9,
+  0x94,0x10,0x95,0x2c,0x10,0x62,0x9f,0x5c,0x54,0xa9,0xc6,0x81,0x76,0xda,0x2d,0x6,
+  0x91,0x8,0x55,0xa5,0x58,0x50,0x42,0x34,0x42,0xd4,0x61,0x50,0x29,0xa9,0xb2,0x35,
+  0x25,0xa0,0x67,0x20,0x14,0x25,0xab,0x10,0x53,0x9f,0x5c,0x82,0x92,0xc6,0x81,0x92,
+  0x97,0xda,0x50,0x84,0x14,0x5,0x8b,0x4a,0x4c,0xd6,0x83,0x48,0x8a,0x8b,0x52,0x8d,
+  0x6d,0x3d,0x13,0x3a,0x82,0x59,0x6f,0xb8,0xac,0x4e,0x48,0x81,0x58,0xf5,0xc9,0x47,
+  0xe7,0x2,0xed,0xd4,0x96,0x1a,0x5f,0x49,0x49,0x8,0xc1,0x8,0xc9,0x88,0x95,0x11,
+  0x53,0x40,0x4c,0xa9,0x46,0x1,0xd6,0x91,0xc0,0x51,0xcc,0x4,0xf3,0xfe,0xa,0xc9,
+  0x48,0xa3,0x40,0xa8,0x2,0xa3,0x71,0x64,0x12,0x8f,0x33,0x10,0x82,0xa2,0x41,0xb0,
+  0x68,0x84,0x2a,0x90,0x46,0x1,0x8d,0x46,0x35,0x5a,0xd3,0x84,0x22,0x3d,0x3,0x95,
+  0x29,0xe2,0x42,0x35,0xee,0x2b,0x4f,0xa3,0xc8,0xf9,0x8d,0xc8,0xde,0x23,0x43,0x16,
+  0x1e,0x90,0x14,0x85,0x51,0x65,0x8c,0x47,0x46,0x35,0xee,0x41,0x86,0x51,0xa0,0x1a,
+  0x47,0x44,0x4c,0xd6,0xf0,0x40,0x39,0x98,0x4c,0x5b,0x2e,0xbe,0x7c,0x8e,0x54,0x19,
+  0x31,0x19,0xb1,0x12,0x52,0xe5,0xc4,0x92,0x39,0x9c,0xb4,0x78,0x9e,0xd,0xe0,0xf2,
+  0xc7,0x64,0x5a,0x5f,0xba,0xfc,0x6a,0xd5,0xc7,0x25,0x25,0x56,0x90,0x2a,0xe7,0xef,
+  0x9d,0x1a,0x2f,0xcd,0xce,0x99,0x4f,0x44,0xda,0xec,0x7c,0xff,0xdb,0xfd,0xc7,0x37,
+  0x7e,0xdd,0xee,0xde,0x73,0x2f,0xa,0xc3,0x1,0x44,0x14,0x2c,0x9a,0x1f,0xfd,0xb9,
+  0x7d,0x78,0xff,0xd6,0x2f,0x40,0x68,0xe,0x1e,0x7e,0x72,0xfb,0xf6,0xce,0xc7,0x25,
+  0x37,0xa1,0x9f,0x40,0x43,0x9c,0xf6,0x6,0xac,0x1f,0xdc,0xba,0x9,0x94,0xe1,0x3a,
+  0xdd,0xff,0x82,0xd9,0x2b,0x1,0xd8,0x2,0xd2,0x9,0xb1,0x2,0xb4,0x56,0x6d,0x3c,
+  0xce,0xf5,0x7e,0x3,0x9c,0x7,0x2e,0xc,0xbf,0x39,0x61,0xb4,0x57,0x87,0xa5,0xab,
+  0xf7,0x9e,0x3c,0x96,0x3f,0xb,0x80,0x0,0xfa,0x94,0xe7,0xbe,0x54,0x95,0x3e,0x23,
+  0x5f,0x79,0x72,0xf3,0xe7,0xb,0xe0,0x3f,0x84,0x11,0xd1,0x4,0x27,0x48,0x17,0xe1,
+  0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
+    // /usr/local/google/home/chudy/Ico/rewind.png
+  0x0,0x0,0x4,0x51,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0x73,0x7a,0x7a,0xf4,
+  0x0,0x0,0x0,0x4,0x73,0x42,0x49,0x54,0x8,0x8,0x8,0x8,0x7c,0x8,0x64,0x88,
+  0x0,0x0,0x4,0x8,0x49,0x44,0x41,0x54,0x58,0x85,0xed,0x96,0x4d,0x68,0x5c,0x55,
+  0x14,0xc7,0x7f,0xf7,0x65,0x26,0xcd,0x7b,0x93,0xce,0x47,0x53,0x93,0x26,0x93,0xa6,
+  0x1,0xd,0x36,0x9,0xc1,0xa6,0x5,0xdb,0xe0,0x42,0x4,0xd7,0x8d,0x60,0xb1,0xb8,
+  0xaa,0x60,0x85,0x6c,0x4,0x15,0x82,0x9d,0x45,0x44,0xac,0xb8,0x55,0x4b,0x10,0x84,
+  0x6a,0x77,0x4a,0x3f,0x4,0x5d,0xb8,0x50,0x2c,0xba,0xd1,0x22,0x5,0x37,0xea,0xca,
+  0x2e,0x14,0x9d,0x24,0x82,0xd6,0x4c,0x4d,0x66,0xe6,0xcd,0xbb,0xef,0x1e,0x17,0xf7,
+  0xbd,0x99,0x37,0xf9,0x68,0x48,0xd2,0x9d,0x3d,0xf0,0x38,0xef,0x1d,0xde,0xbd,0xff,
+  0xff,0x3d,0xe7,0x7f,0xee,0xbd,0x70,0xdf,0xee,0xdb,0xff,0xdd,0x54,0xec,0xbf,0x3a,
+  0xcd,0xd3,0x3d,0x69,0xf8,0xbd,0x4e,0xf9,0xe4,0x35,0x6e,0x6c,0x35,0xf0,0xc3,0x53,
+  0x5e,0x7e,0x5f,0x67,0xfd,0xc4,0x53,0x1f,0x99,0x2f,0x0,0xd9,0x29,0x1,0x27,0xf2,
+  0x99,0xde,0xde,0xe2,0xd5,0xe2,0xd4,0xa9,0xab,0x7b,0x3b,0x78,0x15,0xe8,0xdc,0x8a,
+  0xf8,0x70,0x7f,0x5f,0xa9,0x58,0xc8,0x5e,0x1,0x32,0x3b,0x5,0x7,0x48,0x45,0xde,
+  0xf5,0xbc,0x6e,0x72,0x3,0x23,0xa4,0x6c,0x2c,0x5,0x34,0x36,0x1a,0x30,0x3f,0xed,
+  0x16,0xc6,0x86,0xe,0x94,0x26,0xa7,0x9e,0x9c,0xad,0xdc,0xfa,0xe,0x58,0x76,0x81,
+  0x95,0x9d,0x12,0x88,0x33,0x80,0x42,0x10,0xa3,0x13,0x91,0xd,0x4d,0x8d,0xd,0xf5,
+  0x95,0xc6,0x27,0xa7,0x66,0xbc,0x42,0x3f,0x4a,0x99,0x9d,0xe2,0x36,0x2d,0xd5,0x7c,
+  0x13,0x3,0x61,0xa3,0x29,0x8a,0xb5,0x36,0x3f,0xed,0x16,0x46,0x87,0x7a,0x4b,0x93,
+  0x8f,0x3e,0x3e,0xeb,0xf5,0x14,0x51,0xc6,0x47,0xc9,0xbd,0x24,0x80,0x25,0xb0,0x9,
+  0x3,0x35,0x7a,0xf0,0x81,0xd2,0xf8,0xc4,0xb1,0x19,0x2f,0xdf,0xb,0x61,0x3,0xc1,
+  0xd8,0x31,0xf7,0x8c,0x80,0x18,0x24,0x5c,0x5f,0xf6,0xf9,0x69,0xb7,0x30,0x3a,0xd8,
+  0x53,0x9a,0x3c,0x7a,0x62,0xd6,0x2b,0x1c,0x80,0xd0,0xb7,0xc0,0x62,0x70,0x53,0x86,
+  0xeb,0x67,0x38,0x39,0x98,0xa3,0x82,0x2,0xa5,0xb0,0xb,0x50,0xd1,0x3a,0x92,0x8b,
+  0x49,0xbc,0xdf,0x69,0xc0,0xb1,0xb,0x7c,0x2,0xc8,0xfa,0x12,0xb4,0x67,0x40,0x8d,
+  0xe,0xee,0x2b,0x8d,0x8d,0x4d,0xcc,0x78,0xd9,0x1e,0x94,0x69,0x60,0x3b,0x4e,0x0,
+  0x43,0x66,0x60,0x94,0xa3,0x85,0xe2,0x45,0x41,0x40,0x42,0x3b,0x7,0x91,0x37,0xda,
+  0x12,0x35,0xda,0xc6,0x4c,0x68,0xff,0x31,0x1a,0xe7,0x9f,0xbf,0x81,0xda,0x5e,0x60,
+  0xa5,0xad,0x4,0x4a,0xb7,0x32,0x30,0x3f,0xed,0x16,0xe,0xf,0xe4,0x4b,0x47,0x26,
+  0x1e,0x99,0xcd,0xe4,0xfb,0x50,0x61,0x1d,0x54,0xdc,0xee,0x96,0x40,0x87,0x97,0xc3,
+  0xf3,0xba,0x2d,0xa0,0x84,0x20,0x1a,0x8c,0x46,0x22,0x8f,0x68,0x8,0x83,0x28,0x1e,
+  0x40,0x18,0x20,0x26,0xa0,0xb6,0xba,0xc,0xe0,0xb6,0x13,0x10,0x83,0x98,0xa6,0x6,
+  0x9c,0xc3,0x7,0xb2,0xe7,0xc6,0x46,0x46,0x66,0xbc,0xee,0x7c,0x53,0x1b,0x92,0x24,
+  0xa0,0xc,0x2a,0x2a,0x85,0x2d,0x89,0x6,0x9,0x2d,0xb8,0x84,0x20,0x81,0xf5,0x68,
+  0x14,0x11,0x29,0x15,0xa2,0x8,0x51,0x4e,0x6b,0xdf,0x6a,0x2f,0x81,0xf6,0xd9,0xe3,
+  0x65,0x72,0x9f,0x3d,0xd7,0xf5,0xd6,0x91,0xd1,0x87,0x5f,0xcc,0xe4,0xf6,0xa3,0x74,
+  0x1d,0x1c,0x85,0xc4,0xf5,0x45,0x40,0x89,0x5,0x47,0x10,0xb4,0xd5,0x45,0x58,0xb7,
+  0x1a,0xa,0x7d,0x54,0x58,0x43,0x74,0xd,0x8c,0xfd,0x6e,0x65,0x24,0xca,0x94,0x36,
+  0xeb,0x9,0xa8,0x48,0x3,0x69,0x2f,0x7f,0xa8,0x98,0x71,0xe,0xb9,0x5e,0xb7,0x9d,
+  0xd8,0x51,0x20,0x91,0xaa,0x94,0x58,0x6f,0x2,0xa4,0x51,0x41,0x82,0x15,0xc4,0x5f,
+  0x6,0x5d,0x5,0xe3,0xa3,0x88,0x4a,0x40,0xd8,0x2e,0xbc,0x35,0x9d,0xa5,0x12,0x7b,
+  0x4d,0x7b,0x1b,0x6a,0x9f,0x3f,0x97,0xca,0xbf,0x5e,0xfa,0x39,0xf5,0xf9,0x9c,0xdb,
+  0x75,0xee,0x60,0xb1,0x98,0x4f,0xa5,0x53,0x36,0x9d,0x41,0xd5,0x82,0xd5,0x6f,0x83,
+  0xfe,0xd7,0x96,0xc5,0xa1,0xb9,0x71,0x29,0x87,0x66,0x7,0xac,0x7b,0x9a,0xc8,0x91,
+  0x7c,0x12,0xb1,0x75,0x1a,0x70,0xd3,0x54,0x3f,0xfe,0x51,0x7f,0xfa,0xe0,0xfe,0x72,
+  0xcf,0xb3,0xc7,0x1b,0x2f,0xc,0x17,0xd2,0x79,0xb,0xba,0xa,0xd2,0x40,0x21,0x16,
+  0x34,0x39,0x79,0xfc,0xbd,0x51,0x3c,0x9,0x1e,0xf9,0x8d,0x33,0x20,0x21,0xe8,0x6,
+  0x29,0x7,0xd,0x94,0xdf,0xfc,0xba,0x7a,0xa1,0x5c,0x59,0x58,0x7c,0xe5,0xb8,0x7e,
+  0xed,0x60,0x56,0xf2,0xe9,0x8e,0x68,0x92,0xd8,0x3b,0x89,0xc9,0xd6,0x82,0xc7,0xdf,
+  0xd0,0x6a,0xeb,0xc4,0xaa,0x37,0x21,0x20,0x56,0x34,0xd6,0xc,0xb0,0x74,0xe9,0x87,
+  0xe0,0xea,0x60,0x36,0x3d,0x70,0x7a,0x5c,0x9f,0x1d,0xce,0x49,0xde,0x89,0x7,0x46,
+  0x20,0x21,0x50,0x6d,0x58,0x1f,0x6f,0x42,0x4d,0x42,0xc9,0xd8,0x1a,0xd,0xac,0xfa,
+  0x1b,0x11,0x30,0x1,0x52,0x59,0x48,0xfe,0xa7,0x81,0xf2,0xf9,0x6f,0x82,0x77,0x17,
+  0x57,0xbb,0x16,0x5f,0x9e,0xf2,0xe7,0x6,0xb3,0x92,0x4f,0x27,0x56,0x5a,0xd,0xa0,
+  0xef,0xd,0x4e,0x1,0x35,0xb6,0x67,0x2b,0xf1,0x98,0x16,0x81,0xd0,0x47,0xea,0xfe,
+  0x5a,0xb6,0x2,0x2c,0x5d,0xbc,0x59,0xbf,0x32,0x98,0xed,0x1c,0x78,0x66,0x42,0x9f,
+  0x1d,0xca,0x99,0x5c,0x9c,0x88,0xd0,0x76,0xd3,0x4d,0xe0,0xaf,0x6d,0x12,0x30,0x80,
+  0xdf,0x46,0x40,0x8c,0xd9,0xec,0x5e,0xa3,0x81,0xf2,0xeb,0xd7,0x1b,0xef,0x2c,0xae,
+  0xb8,0x8b,0x2f,0x3d,0xe6,0xcf,0x15,0xb3,0x26,0x97,0x6e,0x89,0xad,0x6,0x54,0xb7,
+  0x49,0xa0,0x69,0x77,0x3f,0xfd,0x5b,0x26,0xc0,0xd2,0xfb,0xdf,0xd7,0x2e,0x5f,0xfb,
+  0xa9,0xf3,0x83,0x85,0x15,0xa7,0x62,0xa0,0x4d,0x58,0xbb,0x26,0x90,0x3c,0xc9,0x36,
+  0x31,0xd,0x94,0xe7,0xbe,0xac,0xbf,0x7d,0xe1,0xdb,0xae,0xf3,0x7f,0xdc,0x71,0x2a,
+  0xe1,0xee,0x4f,0xe3,0x56,0x9,0x6e,0x37,0x2c,0x78,0x25,0xb8,0xeb,0xff,0x2,0x2c,
+  0xbd,0x77,0xa3,0x7a,0xb9,0x98,0x77,0x7,0x9e,0x78,0xc8,0x7f,0x7e,0xb7,0x77,0x82,
+  0x78,0xbd,0xdd,0xc0,0xd1,0xc8,0xff,0x6,0xfc,0xc2,0x26,0x77,0xc2,0xc4,0xb8,0x7e,
+  0xa0,0x17,0xb8,0xc5,0x2e,0xee,0x84,0xc9,0x6d,0x62,0xf,0xb6,0x24,0x7a,0xb,0xf0,
+  0xd8,0x1c,0x6c,0x6,0x3,0x76,0x71,0x2d,0xbf,0x6f,0xff,0x1,0x15,0x13,0xb7,0x7f,
+  0x67,0x24,0x89,0x36,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
+
+    // /usr/local/google/home/chudy/Ico/previous.png
+  0x0,0x0,0x3,0xaf,
+  0x89,
+  0x50,0x4e,0x47,0xd,0xa,0x1a,0xa,0x0,0x0,0x0,0xd,0x49,0x48,0x44,0x52,0x0,
+  0x0,0x0,0x20,0x0,0x0,0x0,0x20,0x8,0x6,0x0,0x0,0x0,0x73,0x7a,0x7a,0xf4,
+  0x0,0x0,0x0,0x4,0x73,0x42,0x49,0x54,0x8,0x8,0x8,0x8,0x7c,0x8,0x64,0x88,
+  0x0,0x0,0x3,0x66,0x49,0x44,0x41,0x54,0x58,0x85,0xed,0x95,0xdd,0x6f,0x14,0x55,
+  0x18,0xc6,0x7f,0x67,0xf6,0x83,0x26,0x48,0x65,0x15,0xdd,0x32,0x45,0x97,0x42,0x5a,
+  0x2,0x9b,0x2e,0x42,0x9,0xc1,0x18,0xb3,0xea,0x85,0x89,0x36,0xe1,0xc6,0xc4,0x18,
+  0xb2,0x44,0x6f,0xe0,0x1f,0xe8,0x15,0x37,0x78,0xa7,0x19,0x2f,0xb8,0xf0,0x2b,0x59,
+  0x25,0x92,0x68,0x62,0xc,0xa8,0x34,0xf1,0x1f,0x40,0x89,0x89,0x72,0x1,0x26,0x2,
+  0x4d,0x14,0x89,0x62,0x61,0x81,0xa0,0x5d,0x5b,0xba,0xee,0xce,0xcc,0x39,0xaf,0x17,
+  0x67,0x76,0x77,0xba,0x76,0xb,0x6d,0x63,0x13,0x93,0x3e,0xc9,0xc9,0xb9,0x98,0x3d,
+  0xef,0xfb,0xdb,0xe7,0x3c,0xef,0xc,0xac,0x6a,0x55,0xff,0x47,0x9d,0xd8,0xaf,0x9e,
+  0x3e,0xf9,0x12,0x63,0x80,0x5a,0x6e,0x2d,0x67,0xb1,0x7,0xc6,0x5f,0xcb,0x96,0x9e,
+  0x79,0x6e,0xff,0x67,0x1b,0xd3,0x14,0x81,0x35,0x2b,0xa,0x50,0x1e,0x55,0xc5,0xc2,
+  0xce,0xbd,0x5e,0x76,0x70,0xc4,0x55,0xa,0x67,0xb1,0xe7,0xe7,0x53,0xf2,0x7e,0x7f,
+  0x38,0xfe,0xea,0x23,0xa5,0xc2,0xce,0xbd,0x5e,0xff,0xf6,0x27,0x5d,0x25,0x6,0xb5,
+  0x6c,0xf3,0x17,0x1,0x50,0x1e,0x55,0xc5,0xc2,0xf0,0x88,0xd7,0xb7,0xf5,0x9,0x17,
+  0x13,0x20,0x2b,0x9,0x70,0xfa,0xe0,0xc3,0xa5,0xc2,0xf0,0x2e,0xcf,0x1d,0xda,0xe3,
+  0x2a,0x25,0xa0,0x1b,0x20,0x86,0x44,0x62,0x5,0x0,0xca,0xa3,0xaa,0x58,0xc8,0x17,
+  0xbc,0xbe,0xcd,0x79,0x57,0x19,0x1f,0x90,0x68,0x19,0xb2,0x6b,0xd9,0xfd,0xe3,0x61,
+  0x3e,0x49,0x24,0x8,0x51,0x58,0x47,0xe2,0x3b,0xcc,0x3b,0x23,0x37,0x66,0x39,0xf7,
+  0xec,0x71,0x8e,0x45,0x85,0xba,0x8f,0xd1,0xe9,0x52,0xa6,0x54,0xd8,0x9e,0xf7,0xdc,
+  0xa1,0xdd,0xae,0xa3,0x9a,0x15,0xdb,0x0,0xa0,0x41,0xc,0x88,0x6,0xd1,0x88,0x84,
+  0x20,0x21,0x98,0xa0,0xbd,0xeb,0xc0,0xee,0x26,0x4,0xed,0x83,0xe,0x98,0xb8,0x74,
+  0xe1,0xab,0x5d,0xef,0xf1,0x32,0x50,0xef,0xea,0x40,0x79,0x54,0x15,0x87,0x87,0xb6,
+  0x79,0xd9,0xc7,0x6,0x5d,0xa5,0x7d,0xa4,0x3,0x40,0x29,0x63,0x9b,0x63,0x40,0x42,
+  0x44,0xc,0x10,0x80,0xd1,0xa8,0x8,0x44,0x9a,0x20,0xf1,0x45,0x88,0x61,0xee,0xf4,
+  0xfc,0xb,0xe0,0xcb,0x3,0xbd,0xa5,0xe1,0xc1,0x41,0xcf,0xcd,0x6d,0x73,0x1d,0x4,
+  0xc2,0xba,0xf5,0x35,0x2,0x50,0x8e,0x80,0x8,0xd2,0x82,0xd0,0x40,0xac,0xb1,0x84,
+  0x60,0xb4,0x5,0x22,0x6c,0xed,0xa2,0xec,0xee,0x74,0x64,0x67,0xe,0xc0,0xfb,0x2f,
+  0x50,0xcc,0xf,0xc,0x78,0x7d,0x1b,0x73,0xae,0xd2,0xbe,0x6d,0x2c,0xb4,0x1,0x94,
+  0x20,0x12,0xbf,0x6,0x3,0x4a,0xa3,0x22,0x7,0x44,0xc,0x48,0x10,0x5d,0x4f,0x18,
+  0x1,0x34,0x97,0x5,0xed,0x7c,0x73,0xc4,0x1,0x12,0x57,0xa6,0xd8,0xf0,0x94,0x1f,
+  0xf4,0x48,0xd8,0x0,0x47,0xac,0xf5,0x8e,0xb2,0x87,0x14,0x28,0x25,0x91,0x13,0x6,
+  0x94,0xb1,0x20,0xa6,0x8e,0x98,0x6,0xa2,0xeb,0xb6,0x79,0x58,0x3,0x5d,0xb7,0xcb,
+  0x4,0x60,0x7c,0x9b,0x13,0x13,0x82,0x18,0x9c,0x5,0x0,0xe4,0xd8,0x77,0x7c,0x9b,
+  0x4c,0x5d,0xfd,0xf0,0x20,0xe6,0xf0,0x96,0x2d,0x5b,0x33,0x4e,0xd3,0x1,0x69,0x3b,
+  0x0,0x1a,0x9,0x67,0xc1,0xff,0xb,0x82,0xbb,0x88,0x5f,0xb5,0x8d,0xc5,0x47,0x11,
+  0xdd,0x75,0x34,0x9,0xf3,0x45,0x5c,0x2d,0x0,0x60,0x80,0x5b,0x6f,0x9d,0xad,0xbf,
+  0x3b,0x39,0xf5,0x53,0xe5,0xc8,0x8b,0xfa,0xf5,0x5c,0x7f,0x7f,0x26,0x99,0x54,0x76,
+  0xf6,0x1b,0x55,0x4c,0xa3,0xa,0x7e,0x15,0xf4,0xdf,0x36,0xb,0xcd,0x38,0x39,0x51,
+  0x61,0x45,0xcb,0xad,0x6e,0x10,0xb,0x1,0x60,0x3d,0xe5,0xe6,0xa7,0x17,0xcd,0x17,
+  0xb9,0x87,0x7e,0x77,0xf,0x8c,0xcc,0x1e,0xda,0xfc,0x80,0xce,0x38,0xe1,0x8c,0xb5,
+  0x54,0xc5,0x32,0xdc,0xd9,0xa4,0xd9,0x3c,0xf6,0x4c,0x75,0x2,0xc8,0xbd,0x1,0xc0,
+  0x26,0xe6,0xfa,0x9b,0xdf,0xd4,0xdf,0xb9,0x3e,0x7d,0xbb,0x32,0xb6,0x47,0x1f,0xcd,
+  0xf5,0x4a,0x26,0x95,0x8c,0x15,0x76,0xa0,0x52,0x63,0xea,0x97,0x3f,0xb9,0xd2,0xca,
+  0x87,0x13,0x65,0xb5,0x95,0x17,0xda,0x2f,0xa4,0x98,0xee,0xcc,0x30,0x11,0xfd,0xd1,
+  0xae,0x0,0x2d,0x27,0x3e,0xfe,0x21,0xfc,0x7c,0xd3,0xba,0x84,0xfb,0xca,0xe,0x73,
+  0x68,0xe0,0x41,0x59,0xef,0xc4,0x2c,0x9e,0x9c,0xe6,0xe2,0xf3,0x1f,0x71,0x4,0xf0,
+  0xbb,0xd4,0xe8,0xa6,0xdb,0xd8,0xd9,0x5c,0x10,0xa0,0xe5,0xc4,0x1b,0x67,0xf5,0xdb,
+  0x95,0xd9,0x54,0x65,0x6c,0x5f,0x78,0x74,0x53,0xaf,0xac,0x4f,0x47,0xf,0x7b,0xd7,
+  0x30,0xd,0x5c,0x0,0x6a,0x8b,0x4,0x98,0xa3,0x7b,0x7d,0xcf,0x5,0xb8,0x79,0xe2,
+  0x7c,0x70,0xea,0xe4,0x44,0xf2,0xf8,0xe4,0x5d,0x55,0x35,0xd0,0x35,0xe1,0x4b,0xd1,
+  0xfd,0x7c,0xd3,0xc,0x30,0xf3,0xf5,0xaf,0xe6,0xb2,0x2f,0x3d,0x7f,0xec,0xc8,0x9a,
+  0x7d,0x6b,0xd3,0xd2,0x33,0xd5,0xe0,0xe7,0xf2,0xf7,0x9c,0x22,0x66,0xe7,0x7f,0x5,
+  0xd0,0x54,0xed,0xfc,0x8d,0xf0,0xb7,0x47,0xd7,0xa5,0xd3,0x8f,0x67,0x24,0xef,0x1b,
+  0xb9,0xf6,0xc1,0xb9,0x95,0x5,0x30,0xc0,0xcc,0x99,0xab,0xfa,0xb2,0xa1,0xe7,0x4e,
+  0x2a,0x15,0xd6,0xc6,0x2f,0x71,0x6,0x9b,0x95,0x25,0x6b,0x29,0x37,0x99,0x4,0x36,
+  0x60,0xe1,0x6f,0x2d,0x17,0x60,0x55,0xab,0xfa,0x7,0x42,0xae,0x87,0xb8,0x50,0x72,
+  0xc9,0x52,0x0,0x0,0x0,0x0,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82,
+
+};
+
+static const unsigned char qt_resource_name[] = {
+  // images
+  0x0,0x6,
+  0x7,0x3,0x7d,0xc3,
+  0x0,0x69,
+  0x0,0x6d,0x0,0x61,0x0,0x67,0x0,0x65,0x0,0x73,
+    // Ico
+  0x0,0x3,
+  0x0,0x0,0x4f,0x9f,
+  0x0,0x49,
+  0x0,0x63,0x0,0x6f,
+    // Icons
+  0x0,0x5,
+  0x0,0x4f,0xa6,0x53,
+  0x0,0x49,
+  0x0,0x63,0x0,0x6f,0x0,0x6e,0x0,0x73,
+    // skia.png
+  0x0,0x8,
+  0x1,0xf4,0x58,0x7,
+  0x0,0x73,
+  0x0,0x6b,0x0,0x69,0x0,0x61,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+    // delete.png
+  0x0,0xa,
+  0xc,0xad,0xf,0x7,
+  0x0,0x64,
+  0x0,0x65,0x0,0x6c,0x0,0x65,0x0,0x74,0x0,0x65,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+    // breakpoint_16x16.png
+  0x0,0x14,
+  0x9,0x1d,0xb7,0x27,
+  0x0,0x62,
+  0x0,0x72,0x0,0x65,0x0,0x61,0x0,0x6b,0x0,0x70,0x0,0x6f,0x0,0x69,0x0,0x6e,0x0,0x74,0x0,0x5f,0x0,0x31,0x0,0x36,0x0,0x78,0x0,0x31,0x0,0x36,0x0,0x2e,
+  0x0,0x70,0x0,0x6e,0x0,0x67,
+    // blank.png
+  0x0,0x9,
+  0x8,0x4e,0x85,0x7,
+  0x0,0x62,
+  0x0,0x6c,0x0,0x61,0x0,0x6e,0x0,0x6b,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+    // breakpoint.png
+  0x0,0xe,
+  0x1,0x27,0x3,0xa7,
+  0x0,0x62,
+  0x0,0x72,0x0,0x65,0x0,0x61,0x0,0x6b,0x0,0x70,0x0,0x6f,0x0,0x69,0x0,0x6e,0x0,0x74,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+    // next.png
+  0x0,0x8,
+  0xc,0xf7,0x59,0xc7,
+  0x0,0x6e,
+  0x0,0x65,0x0,0x78,0x0,0x74,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+    // inspector.png
+  0x0,0xd,
+  0xb,0xd1,0x81,0xc7,
+  0x0,0x69,
+  0x0,0x6e,0x0,0x73,0x0,0x70,0x0,0x65,0x0,0x63,0x0,0x74,0x0,0x6f,0x0,0x72,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+    // reload.png
+  0x0,0xa,
+  0x5,0x78,0x4f,0x27,
+  0x0,0x72,
+  0x0,0x65,0x0,0x6c,0x0,0x6f,0x0,0x61,0x0,0x64,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+    // play.png
+  0x0,0x8,
+  0x2,0x8c,0x59,0xa7,
+  0x0,0x70,
+  0x0,0x6c,0x0,0x61,0x0,0x79,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+    // pause.png
+  0x0,0x9,
+  0xc,0x98,0xba,0x47,
+  0x0,0x70,
+  0x0,0x61,0x0,0x75,0x0,0x73,0x0,0x65,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+    // rewind.png
+  0x0,0xa,
+  0x0,0x48,0x4e,0x87,
+  0x0,0x72,
+  0x0,0x65,0x0,0x77,0x0,0x69,0x0,0x6e,0x0,0x64,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+    // previous.png
+  0x0,0xc,
+  0x8,0x37,0xcd,0x47,
+  0x0,0x70,
+  0x0,0x72,0x0,0x65,0x0,0x76,0x0,0x69,0x0,0x6f,0x0,0x75,0x0,0x73,0x0,0x2e,0x0,0x70,0x0,0x6e,0x0,0x67,
+
+};
+
+static const unsigned char qt_resource_struct[] = {
+  // :
+  0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
+  // :/images
+  0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x2,
+  // :/images/Ico
+  0x0,0x0,0x0,0x12,0x0,0x2,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x9,
+  // :/images/Icons
+  0x0,0x0,0x0,0x1e,0x0,0x2,0x0,0x0,0x0,0x5,0x0,0x0,0x0,0x4,
+  // :/images/Icons/breakpoint.png
+  0x0,0x0,0x0,0xa4,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xe,0x9b,
+  // :/images/Icons/skia.png
+  0x0,0x0,0x0,0x2e,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,
+  // :/images/Icons/blank.png
+  0x0,0x0,0x0,0x8c,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0xe,0x5,
+  // :/images/Icons/breakpoint_16x16.png
+  0x0,0x0,0x0,0x5e,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x9,0xad,
+  // :/images/Icons/delete.png
+  0x0,0x0,0x0,0x44,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x4,0x22,
+  // :/images/Ico/rewind.png
+  0x0,0x0,0x1,0x44,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x33,0x26,
+  // :/images/Ico/play.png
+  0x0,0x0,0x1,0x16,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x2a,0x50,
+  // :/images/Ico/reload.png
+  0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x22,0xc0,
+  // :/images/Ico/previous.png
+  0x0,0x0,0x1,0x5e,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x37,0x7b,
+  // :/images/Ico/inspector.png
+  0x0,0x0,0x0,0xdc,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1b,0x14,
+  // :/images/Ico/pause.png
+  0x0,0x0,0x1,0x2c,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x2e,0x85,
+  // :/images/Ico/next.png
+  0x0,0x0,0x0,0xc6,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x17,0x66,
+
+};
+
+QT_BEGIN_NAMESPACE
+
+extern Q_CORE_EXPORT bool qRegisterResourceData
+    (int, const unsigned char *, const unsigned char *, const unsigned char *);
+
+extern Q_CORE_EXPORT bool qUnregisterResourceData
+    (int, const unsigned char *, const unsigned char *, const unsigned char *);
+
+QT_END_NAMESPACE
+
+
+int QT_MANGLE_NAMESPACE(qInitResources_SkIcons)()
+{
+    QT_PREPEND_NAMESPACE(qRegisterResourceData)
+        (0x01, qt_resource_struct, qt_resource_name, qt_resource_data);
+    return 1;
+}
+
+Q_CONSTRUCTOR_FUNCTION(QT_MANGLE_NAMESPACE(qInitResources_SkIcons))
+
+int QT_MANGLE_NAMESPACE(qCleanupResources_SkIcons)()
+{
+    QT_PREPEND_NAMESPACE(qUnregisterResourceData)
+       (0x01, qt_resource_struct, qt_resource_name, qt_resource_data);
+    return 1;
+}
+
+Q_DESTRUCTOR_FUNCTION(QT_MANGLE_NAMESPACE(qCleanupResources_SkIcons))
+
diff --git a/debugger/SkDebugCanvas.cpp b/debugger/SkDebugCanvas.cpp
new file mode 100644
index 0000000..b970bb9
--- /dev/null
+++ b/debugger/SkDebugCanvas.cpp
@@ -0,0 +1,309 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include <iostream>
+#include "SkDebugCanvas.h"
+#include "SkDrawCommand.h"
+
+static SkBitmap make_noconfig_bm(int width, int height) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kNo_Config, width, height);
+    return bm;
+}
+
+SkDebugCanvas::SkDebugCanvas(int width, int height)
+        : INHERITED(make_noconfig_bm(width, height)) {
+    // TODO(chudy): Free up memory from all draw commands in destructor.
+    fWidth = width;
+    fHeight = height;
+    // do we need fBm anywhere?
+    fBm.setConfig(SkBitmap::kNo_Config, fWidth, fHeight);
+    fFilter = false;
+    fIndex = 0;
+    fUserOffset.set(0,0);
+    fUserScale = 1.0;
+}
+
+SkDebugCanvas::~SkDebugCanvas() {
+    commandVector.deleteAll();
+}
+
+void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
+    commandVector.push(command);
+}
+
+void SkDebugCanvas::draw(SkCanvas* canvas) {
+    if(!commandVector.isEmpty()) {
+        for (int i = 0; i < commandVector.count(); i++) {
+            if (commandVector[i]->isVisible()) {
+                commandVector[i]->execute(canvas);
+            }
+        }
+    }
+    fIndex = commandVector.count() - 1;
+}
+
+void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
+    canvas->translate(SkIntToScalar(fUserOffset.fX),
+                      SkIntToScalar(fUserOffset.fY));
+    if (fUserScale < 0) {
+        canvas->scale((1.0f / -fUserScale), (1.0f / -fUserScale));
+    } else if (fUserScale > 0) {
+        canvas->scale(fUserScale, fUserScale);
+    }
+}
+
+int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
+    bitmap.allocPixels();
+
+    SkCanvas canvas(bitmap);
+    canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y));
+    applyUserTransform(&canvas);
+
+    int layer = 0;
+    SkColor prev = bitmap.getColor(0,0);
+    for (int i = 0; i < index; i++) {
+        if (commandVector[i]->isVisible()) {
+            commandVector[i]->execute(&canvas);
+        }
+        if (prev != bitmap.getColor(0,0)) {
+            layer = i;
+        }
+        prev = bitmap.getColor(0,0);
+    }
+    return layer;
+}
+
+void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) {
+    int counter = 0;
+    SkASSERT(!commandVector.isEmpty());
+    SkASSERT(index < commandVector.count());
+    int i;
+
+    // This only works assuming the canvas and device are the same ones that
+    // were previously drawn into because they need to preserve all saves
+    // and restores.
+    if (fIndex < index) {
+        i = fIndex + 1;
+    } else {
+        i = 0;
+        canvas->clear(0);
+        canvas->resetMatrix();
+        SkRect rect = SkRect::MakeWH(SkIntToScalar(fWidth),
+                                     SkIntToScalar(fHeight));
+        canvas->clipRect(rect, SkRegion::kReplace_Op );
+        applyUserTransform(canvas);
+    }
+
+    for (; i <= index; i++) {
+        if (i == index && fFilter) {
+            SkPaint p;
+            p.setColor(0xAAFFFFFF);
+            canvas->save();
+            canvas->resetMatrix();
+            SkRect mask;
+            mask.set(SkIntToScalar(0), SkIntToScalar(0),
+                    SkIntToScalar(fWidth), SkIntToScalar(fHeight));
+            canvas->clipRect(mask, SkRegion::kReplace_Op, false);
+            canvas->drawRectCoords(SkIntToScalar(0), SkIntToScalar(0),
+                    SkIntToScalar(fWidth), SkIntToScalar(fHeight), p);
+            canvas->restore();
+        }
+
+        if (commandVector[i]->isVisible()) {
+            commandVector[i]->execute(canvas);
+        }
+    }
+    fMatrix = canvas->getTotalMatrix();
+    fClip = canvas->getTotalClip().getBounds();
+    fIndex = index;
+}
+
+SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
+    SkASSERT(index < commandVector.count());
+    return commandVector[index];
+}
+
+SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) {
+    SkASSERT(index < commandVector.count());
+    return commandVector[index]->Info();
+}
+
+bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
+    SkASSERT(index < commandVector.count());
+    return commandVector[index]->isVisible();
+}
+
+SkTDArray <SkDrawCommand*> SkDebugCanvas::getDrawCommands() {
+    return commandVector;
+}
+
+// TODO(chudy): Free command string memory.
+SkTDArray<SkString*>* SkDebugCanvas::getDrawCommandsAsStrings() {
+    SkTDArray<SkString*>* commandString = new SkTDArray<SkString*>();
+    if (!commandVector.isEmpty()) {
+        for (int i = 0; i < commandVector.count(); i ++) {
+            commandString->push(new SkString(commandVector[i]->toString()));
+        }
+    }
+    return commandString;
+}
+
+void SkDebugCanvas::toggleFilter(bool toggle) {
+    fFilter = toggle;
+}
+
+void SkDebugCanvas::clear(SkColor color) {
+    addDrawCommand(new Clear(color));
+}
+
+bool SkDebugCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+    addDrawCommand(new ClipPath(path, op, doAA));
+    return true;
+}
+
+bool SkDebugCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+    addDrawCommand(new ClipRect(rect, op, doAA));
+    return true;
+}
+
+bool SkDebugCanvas::clipRegion(const SkRegion& region, SkRegion::Op op) {
+    addDrawCommand(new ClipRegion(region, op));
+    return true;
+}
+
+bool SkDebugCanvas::concat(const SkMatrix& matrix) {
+    addDrawCommand(new Concat(matrix));
+    return true;
+}
+
+void SkDebugCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
+        SkScalar top, const SkPaint* paint = NULL) {
+    addDrawCommand(new DrawBitmap(bitmap, left, top, paint));
+}
+
+void SkDebugCanvas::drawBitmapRectToRect(const SkBitmap& bitmap,
+        const SkRect* src, const SkRect& dst, const SkPaint* paint) {
+    addDrawCommand(new DrawBitmapRect(bitmap, src, dst, paint));
+}
+
+void SkDebugCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
+        const SkMatrix& matrix, const SkPaint* paint) {
+    addDrawCommand(new DrawBitmapMatrix(bitmap, matrix, paint));
+}
+
+void SkDebugCanvas::drawBitmapNine(const SkBitmap& bitmap,
+        const SkIRect& center, const SkRect& dst, const SkPaint* paint) {
+    addDrawCommand(new DrawBitmapNine(bitmap, center, dst, paint));
+}
+
+void SkDebugCanvas::drawData(const void* data, size_t length) {
+    addDrawCommand(new DrawData(data, length));
+}
+
+void SkDebugCanvas::drawPaint(const SkPaint& paint) {
+    addDrawCommand(new DrawPaint(paint));
+}
+
+void SkDebugCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    addDrawCommand(new DrawPath(path, paint));
+}
+
+void SkDebugCanvas::drawPicture(SkPicture& picture) {
+    addDrawCommand(new DrawPicture(picture));
+}
+
+void SkDebugCanvas::drawPoints(PointMode mode, size_t count,
+        const SkPoint pts[], const SkPaint& paint) {
+    addDrawCommand(new DrawPoints(mode, count, pts, paint));
+}
+
+void SkDebugCanvas::drawPosText(const void* text, size_t byteLength,
+        const SkPoint pos[], const SkPaint& paint) {
+    addDrawCommand(new DrawPosText(text, byteLength, pos, paint));
+}
+
+void SkDebugCanvas::drawPosTextH(const void* text, size_t byteLength,
+        const SkScalar xpos[], SkScalar constY, const SkPaint& paint) {
+    addDrawCommand(new DrawPosTextH(text, byteLength, xpos, constY, paint));
+}
+
+void SkDebugCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+    // NOTE(chudy): Messing up when renamed to DrawRect... Why?
+    addDrawCommand(new DrawRectC(rect, paint));
+}
+
+void SkDebugCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
+        const SkPaint* paint = NULL) {
+    addDrawCommand(new DrawSprite(bitmap, left, top, paint));
+}
+
+void SkDebugCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
+        SkScalar y, const SkPaint& paint) {
+    addDrawCommand(new DrawTextC(text, byteLength, x, y, paint));
+}
+
+void SkDebugCanvas::drawTextOnPath(const void* text, size_t byteLength,
+        const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) {
+    addDrawCommand(new DrawTextOnPath(text, byteLength, path, matrix, paint));
+}
+
+void SkDebugCanvas::drawVertices(VertexMode vmode, int vertexCount,
+        const SkPoint vertices[], const SkPoint texs[], const SkColor colors[],
+        SkXfermode*, const uint16_t indices[], int indexCount,
+        const SkPaint& paint) {
+    addDrawCommand(new DrawVertices(vmode, vertexCount, vertices, texs, colors,
+            NULL, indices, indexCount, paint));
+}
+
+void SkDebugCanvas::restore() {
+    addDrawCommand(new Restore());
+}
+
+bool SkDebugCanvas::rotate(SkScalar degrees) {
+    addDrawCommand(new Rotate(degrees));
+    return true;
+}
+
+int SkDebugCanvas::save(SaveFlags flags) {
+    addDrawCommand(new Save(flags));
+    return true;
+}
+
+int SkDebugCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+        SaveFlags flags) {
+    addDrawCommand(new SaveLayer(bounds, paint, flags));
+    return true;
+}
+
+bool SkDebugCanvas::scale(SkScalar sx, SkScalar sy) {
+    addDrawCommand(new Scale(sx, sy));
+    return true;
+}
+
+void SkDebugCanvas::setMatrix(const SkMatrix& matrix) {
+    addDrawCommand(new SetMatrix(matrix));
+}
+
+bool SkDebugCanvas::skew(SkScalar sx, SkScalar sy) {
+    addDrawCommand(new Skew(sx, sy));
+    return true;
+}
+
+bool SkDebugCanvas::translate(SkScalar dx, SkScalar dy) {
+    addDrawCommand(new Translate(dx, dy));
+    return true;
+}
+
+void SkDebugCanvas::toggleCommand(int index, bool toggle) {
+    SkASSERT(index < commandVector.count());
+    commandVector[index]->setVisible(toggle);
+}
diff --git a/debugger/SkDebugCanvas.h b/debugger/SkDebugCanvas.h
new file mode 100644
index 0000000..5172bc2
--- /dev/null
+++ b/debugger/SkDebugCanvas.h
@@ -0,0 +1,224 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SKDEBUGCANVAS_H_
+#define SKDEBUGCANVAS_H_
+
+#include "SkCanvas.h"
+#include "SkDrawCommand.h"
+#include "SkPicture.h"
+#include "SkTDArray.h"
+#include "SkString.h"
+
+class SkDebugCanvas : public SkCanvas {
+public:
+    SkDebugCanvas(int width, int height);
+    ~SkDebugCanvas();
+
+    void toggleFilter(bool toggle);
+
+    /**
+        Executes all draw calls to the canvas.
+        @param canvas  The canvas being drawn to
+     */
+    void draw(SkCanvas* canvas);
+
+    /**
+        Executes the draw calls in the specified range.
+        @param canvas  The canvas being drawn to
+        @param i  The beginning of the range
+        @param j  The end of the range
+        TODO(chudy): Implement
+     */
+    void drawRange(SkCanvas* canvas, int i, int j);
+
+    /**
+        Executes the draw calls up to the specified index.
+        @param canvas  The canvas being drawn to
+        @param index  The index of the final command being executed
+     */
+    void drawTo(SkCanvas* canvas, int index);
+
+    /**
+        Returns the most recently calculated transformation matrix
+     */
+    const SkMatrix& getCurrentMatrix() {
+        return fMatrix;
+    }
+
+    /**
+        Returns the most recently calculated clip
+     */
+    const SkIRect& getCurrentClip() {
+        return fClip;
+    }
+
+    /**
+        Returns the index of the last draw command to write to the pixel at (x,y)
+     */
+    int getCommandAtPoint(int x, int y, int index);
+
+    /**
+        Returns the draw command at the given index.
+        @param index  The index of the command
+     */
+    SkDrawCommand* getDrawCommandAt(int index);
+
+    /**
+        Returns information about the command at the given index.
+        @param index  The index of the command
+     */
+    SkTDArray<SkString*>* getCommandInfo(int index);
+
+    /**
+        Returns the visibility of the command at the given index.
+        @param index  The index of the command
+     */
+    bool getDrawCommandVisibilityAt(int index);
+
+    /**
+        Returns the vector of draw commands
+     */
+    SkTDArray<SkDrawCommand*> getDrawCommands();
+
+    /**
+     * Returns the string vector of draw commands
+     */
+    SkTDArray<SkString*>* getDrawCommandsAsStrings();
+
+    /**
+        Returns length of draw command vector.
+     */
+    int getSize() {
+        return commandVector.count();
+    }
+
+    /**
+        Toggles the visibility / execution of the draw command at index i with
+        the value of toggle.
+     */
+    void toggleCommand(int index, bool toggle);
+
+    void setBounds(int width, int height) {
+        fWidth = width;
+        fHeight = height;
+    }
+
+    void setUserOffset(SkIPoint offset) {
+        fUserOffset = offset;
+    }
+
+    void setUserScale(float scale) {
+        fUserScale = scale;
+    }
+
+////////////////////////////////////////////////////////////////////////////////
+// Inherited from SkCanvas
+////////////////////////////////////////////////////////////////////////////////
+
+    virtual void clear(SkColor) SK_OVERRIDE;
+
+    virtual bool clipPath(const SkPath&, SkRegion::Op, bool) SK_OVERRIDE;
+
+    virtual bool clipRect(const SkRect&, SkRegion::Op, bool) SK_OVERRIDE;
+
+    virtual bool clipRegion(const SkRegion& region, SkRegion::Op op) SK_OVERRIDE;
+
+    virtual bool concat(const SkMatrix& matrix) SK_OVERRIDE;
+
+    virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+                            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,
+                                const SkRect& dst, const SkPaint*) SK_OVERRIDE;
+
+    virtual void drawData(const void*, size_t) SK_OVERRIDE;
+
+    virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
+
+    virtual void drawPath(const SkPath& path, const SkPaint&) SK_OVERRIDE;
+
+    virtual void drawPicture(SkPicture& picture) SK_OVERRIDE;
+
+    virtual void drawPoints(PointMode, size_t count, const SkPoint pts[],
+                            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 drawRect(const SkRect& rect, 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 drawTextOnPath(const void* text, size_t byteLength,
+                            const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint&) SK_OVERRIDE;
+
+    virtual void drawVertices(VertexMode, int vertexCount,
+                          const SkPoint vertices[], const SkPoint texs[],
+                          const SkColor colors[], SkXfermode*,
+                          const uint16_t indices[], int indexCount,
+                              const SkPaint&) SK_OVERRIDE;
+
+    virtual void restore() SK_OVERRIDE;
+
+    virtual bool rotate(SkScalar degrees) SK_OVERRIDE;
+
+    virtual int save(SaveFlags) SK_OVERRIDE;
+
+    virtual int saveLayer(const SkRect* bounds, const SkPaint*, SaveFlags) SK_OVERRIDE;
+
+    virtual bool scale(SkScalar sx, SkScalar sy) SK_OVERRIDE;
+
+    virtual void setMatrix(const SkMatrix& matrix) SK_OVERRIDE;
+
+    virtual bool skew(SkScalar sx, SkScalar sy) SK_OVERRIDE;
+
+    virtual bool translate(SkScalar dx, SkScalar dy) SK_OVERRIDE;
+
+private:
+    typedef SkCanvas INHERITED;
+    SkTDArray<SkDrawCommand*> commandVector;
+    int fHeight;
+    int fWidth;
+    SkBitmap fBm;
+    bool fFilter;
+    int fIndex;
+    SkIPoint fUserOffset;
+    float fUserScale;
+    SkMatrix fMatrix;
+    SkIRect fClip;
+
+    /**
+        Adds the command to the classes vector of commands.
+        @param command  The draw command for execution
+     */
+    void addDrawCommand(SkDrawCommand* command);
+
+    /**
+        Applies any panning and zooming the user has specified before
+        drawing anything else into the canvas.
+     */
+    void applyUserTransform(SkCanvas* canvas);
+};
+
+#endif
diff --git a/debugger/SkDebugger.cpp b/debugger/SkDebugger.cpp
new file mode 100644
index 0000000..7934c73
--- /dev/null
+++ b/debugger/SkDebugger.cpp
@@ -0,0 +1,44 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDebugger.h"
+
+SkDebugger::SkDebugger() {
+    // Create this some other dynamic way?
+    fDebugCanvas = new SkDebugCanvas(100, 100);
+    fPicture = NULL;
+    fPictureWidth = 0;
+    fPictureHeight = 0;
+    fIndex = 0;
+}
+
+SkDebugger::~SkDebugger() {
+    // Need to inherit from SkRef object in order for following to work
+    SkSafeUnref(fDebugCanvas);
+    SkSafeUnref(fPicture);
+}
+
+void SkDebugger::loadPicture(SkPicture* picture) {
+    fPictureWidth = picture->width();
+    fPictureHeight = picture->height();
+    delete fDebugCanvas;
+    fDebugCanvas = new SkDebugCanvas(fPictureWidth, fPictureHeight);
+    fDebugCanvas->setBounds(fPictureWidth, fPictureHeight);
+    picture->draw(fDebugCanvas);
+    fIndex = fDebugCanvas->getSize() - 1;
+    SkRefCnt_SafeAssign(fPicture, picture);
+}
+
+SkPicture* SkDebugger::makePicture() {
+    SkSafeUnref(fPicture);
+    fPicture = new SkPicture();
+    SkCanvas* canvas = fPicture->beginRecording(fPictureWidth, fPictureHeight);
+    fDebugCanvas->draw(canvas);
+    fPicture->endRecording();
+    return fPicture;
+}
diff --git a/debugger/SkDebugger.h b/debugger/SkDebugger.h
new file mode 100644
index 0000000..67fa6a0
--- /dev/null
+++ b/debugger/SkDebugger.h
@@ -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.
+ */
+
+
+#ifndef SKDEBUGGER_H_
+#define SKDEBUGGER_H_
+
+#include "SkDebugCanvas.h"
+#include "SkPicture.h"
+
+class SkDebugger {
+public:
+    SkDebugger();
+
+    ~SkDebugger();
+
+    void setIndex(int index) {
+        fIndex = index;
+    }
+    void draw(SkCanvas* canvas) {
+        if (fIndex > 0) {
+            fDebugCanvas->drawTo(canvas, fIndex);
+        }
+    }
+
+    void step();
+    void stepBack();
+    void play();
+    void rewind();
+
+    bool isCommandVisible(int index) {
+        return fDebugCanvas->getDrawCommandVisibilityAt(index);
+    }
+
+    void setCommandVisible(int index, bool isVisible) {
+        fDebugCanvas->toggleCommand(index, isVisible);
+    }
+
+    SkTDArray<SkString*>* getDrawCommands() {
+        return fDebugCanvas->getDrawCommandsAsStrings();
+    }
+
+    void highlightCurrentCommand(bool on) {
+        fDebugCanvas->toggleFilter(on);
+    }
+
+    void resize(int width, int height) {
+        fDebugCanvas->setBounds(width, height);
+    }
+
+    void loadPicture(SkPicture* picture);
+
+    SkPicture* makePicture();
+
+    int getSize() {
+        return fDebugCanvas->getSize();
+    }
+
+    void setUserOffset(SkIPoint userOffset) {
+        // Should this live in debugger instead?
+        fDebugCanvas->setUserOffset(userOffset);
+    }
+
+    void setUserScale(float userScale) {
+        fDebugCanvas->setUserScale(userScale);
+    }
+
+    int getCommandAtPoint(int x, int y, int index) {
+        return fDebugCanvas->getCommandAtPoint(x, y, index);
+    }
+
+    SkTDArray<SkString*>* getCommandInfo(int index) {
+        return fDebugCanvas->getCommandInfo(index);
+    }
+
+    const SkMatrix& getCurrentMatrix() {
+        return fDebugCanvas->getCurrentMatrix();
+    }
+
+    const SkIRect& getCurrentClip() {
+        return fDebugCanvas->getCurrentClip();
+    }
+
+    int pictureHeight() {
+        return fPictureHeight;
+    }
+
+    int pictureWidth() {
+        return fPictureWidth;
+    }
+
+    int index() {
+        return fIndex;
+    }
+
+private:
+    SkDebugCanvas* fDebugCanvas;
+    SkPicture* fPicture;
+
+    int fPictureWidth;
+    int fPictureHeight;
+    int fIndex;
+};
+
+
+#endif /* SKDEBUGGER_H_ */
diff --git a/debugger/SkDrawCommand.cpp b/debugger/SkDrawCommand.cpp
new file mode 100644
index 0000000..2c4847a
--- /dev/null
+++ b/debugger/SkDrawCommand.cpp
@@ -0,0 +1,489 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkDrawCommand.h"
+#include "SkObjectParser.h"
+
+// TODO(chudy): Refactor into non subclass model.
+
+SkDrawCommand::SkDrawCommand() {
+    fVisible = true;
+}
+
+SkDrawCommand::~SkDrawCommand() {
+    fInfo.deleteAll();
+}
+
+const char* SkDrawCommand::GetCommandString(DrawType type) {
+    switch (type) {
+        case UNUSED: SkDEBUGFAIL("DrawType UNUSED\n"); break;
+        case DRAW_CLEAR: return "Clear";
+        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_NINE: return "Draw Bitmap Nine";
+        case DRAW_BITMAP_RECT_TO_RECT: return "Draw Bitmap Rect";
+        case DRAW_DATA: return "Draw Data";
+        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: return "Draw Rect";
+        case DRAW_SPRITE: return "Draw Sprite";
+        case DRAW_TEXT: return "Draw Text";
+        case DRAW_TEXT_ON_PATH: return "Draw Text On Path";
+        case DRAW_VERTICES: return "Draw Vertices";
+        case RESTORE: return "Restore";
+        case ROTATE: return "Rotate";
+        case SAVE: return "Save";
+        case SAVE_LAYER: return "Save Layer";
+        case SCALE: return "Scale";
+        case SET_MATRIX: return "Set Matrix";
+        case SKEW: return "Skew";
+        case TRANSLATE: return "Translate";
+        default:
+            SkDebugf("DrawType error 0x%08x\n", type);
+            SkASSERT(0);
+            break;
+    }
+    SkDEBUGFAIL("DrawType UNUSED\n");
+    return NULL;
+}
+
+SkString SkDrawCommand::toString() {
+    return SkString(GetCommandString(fDrawType));
+}
+
+Clear::Clear(SkColor color) {
+    this->fColor = color;
+    this->fDrawType = DRAW_CLEAR;
+    this->fInfo.push(SkObjectParser::CustomTextToString("No Parameters"));
+}
+
+void Clear::execute(SkCanvas* canvas) {
+    canvas->clear(this->fColor);
+}
+
+ClipPath::ClipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+    this->fPath = &path;
+    this->fOp = op;
+    this->fDoAA = doAA;
+    this->fDrawType = CLIP_PATH;
+
+    this->fInfo.push(SkObjectParser::PathToString(path));
+    this->fInfo.push(SkObjectParser::RegionOpToString(op));
+    this->fInfo.push(SkObjectParser::BoolToString(doAA));
+}
+
+void ClipPath::execute(SkCanvas* canvas) {
+    canvas->clipPath(*this->fPath, this->fOp, this->fDoAA);
+}
+
+ClipRegion::ClipRegion(const SkRegion& region, SkRegion::Op op) {
+    this->fRegion = &region;
+    this->fOp = op;
+    this->fDrawType = CLIP_REGION;
+
+    this->fInfo.push(SkObjectParser::RegionToString(region));
+    this->fInfo.push(SkObjectParser::RegionOpToString(op));
+}
+
+void ClipRegion::execute(SkCanvas* canvas) {
+    canvas->clipRegion(*this->fRegion, this->fOp);
+}
+
+ClipRect::ClipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+    this->fRect = &rect;
+    this->fOp = op;
+    this->fDoAA = doAA;
+    this->fDrawType = CLIP_RECT;
+
+    this->fInfo.push(SkObjectParser::RectToString(rect));
+    this->fInfo.push(SkObjectParser::RegionOpToString(op));
+    this->fInfo.push(SkObjectParser::BoolToString(doAA));
+}
+
+void ClipRect::execute(SkCanvas* canvas) {
+    canvas->clipRect(*this->fRect, this->fOp, this->fDoAA);
+}
+
+Concat::Concat(const SkMatrix& matrix) {
+    this->fMatrix = &matrix;
+    this->fDrawType = CONCAT;
+
+    this->fInfo.push(SkObjectParser::MatrixToString(matrix));
+}
+
+void Concat::execute(SkCanvas* canvas) {
+    canvas->concat(*this->fMatrix);
+}
+
+DrawBitmap::DrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+        const SkPaint* paint) {
+    this->fBitmap = &bitmap;
+    this->fLeft = left;
+    this->fTop = top;
+    this->fPaint = paint;
+    this->fDrawType = DRAW_BITMAP;
+
+    this->fInfo.push(SkObjectParser::BitmapToString(bitmap));
+    this->fInfo.push(SkObjectParser::ScalarToString(left, "SkScalar left: "));
+    this->fInfo.push(SkObjectParser::ScalarToString(top, "SkScalar top: "));
+}
+
+void DrawBitmap::execute(SkCanvas* canvas) {
+    canvas->drawBitmap(*this->fBitmap, this->fLeft, this->fTop, this->fPaint);
+}
+
+DrawBitmapMatrix::DrawBitmapMatrix(const SkBitmap& bitmap,
+        const SkMatrix& matrix, const SkPaint* paint) {
+    this->fBitmap = &bitmap;
+    this->fMatrix = &matrix;
+    this->fPaint = paint;
+    this->fDrawType = DRAW_BITMAP_MATRIX;
+
+    this->fInfo.push(SkObjectParser::BitmapToString(bitmap));
+    this->fInfo.push(SkObjectParser::MatrixToString(matrix));
+    if (paint) this->fInfo.push(SkObjectParser::PaintToString(*paint));
+}
+
+void DrawBitmapMatrix::execute(SkCanvas* canvas) {
+    canvas->drawBitmapMatrix(*this->fBitmap, *this->fMatrix, this->fPaint);
+}
+
+DrawBitmapNine::DrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+        const SkRect& dst, const SkPaint* paint) {
+    this->fBitmap = &bitmap;
+    this->fCenter = &center;
+    this->fDst = &dst;
+    this->fPaint = paint;
+    this->fDrawType = DRAW_BITMAP_NINE;
+
+    this->fInfo.push(SkObjectParser::BitmapToString(bitmap));
+    this->fInfo.push(SkObjectParser::IRectToString(center));
+    this->fInfo.push(SkObjectParser::RectToString(dst));
+    if (paint) this->fInfo.push(SkObjectParser::PaintToString(*paint));
+}
+
+void DrawBitmapNine::execute(SkCanvas* canvas) {
+    canvas->drawBitmapNine(*this->fBitmap, *this->fCenter, *this->fDst, this->fPaint);
+}
+
+DrawBitmapRect::DrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
+        const SkRect& dst, const SkPaint* paint) {
+    this->fBitmap = &bitmap;
+    this->fSrc = src;
+    this->fDst = &dst;
+    this->fPaint = paint;
+    this->fDrawType = DRAW_BITMAP_RECT_TO_RECT;
+
+    this->fInfo.push(SkObjectParser::BitmapToString(bitmap));
+    if (src) this->fInfo.push(SkObjectParser::RectToString(*src));
+    this->fInfo.push(SkObjectParser::RectToString(dst));
+    if (paint) this->fInfo.push(SkObjectParser::PaintToString(*paint));
+}
+
+void DrawBitmapRect::execute(SkCanvas* canvas) {
+    canvas->drawBitmapRectToRect(*this->fBitmap, this->fSrc, *this->fDst, this->fPaint);
+}
+
+DrawData::DrawData(const void* data, size_t length) {
+    this->fData = data;
+    this->fLength = length;
+    this->fDrawType = DRAW_DATA;
+    // TODO(chudy): See if we can't display data and length.
+}
+
+void DrawData::execute(SkCanvas* canvas) {
+    canvas->drawData(this->fData, this->fLength);
+}
+
+DrawPaint::DrawPaint(const SkPaint& paint) {
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_PAINT;
+
+    this->fInfo.push(SkObjectParser::PaintToString(paint));
+}
+
+void DrawPaint::execute(SkCanvas* canvas) {
+    canvas->drawPaint(*this->fPaint);
+}
+
+DrawPath::DrawPath(const SkPath& path, const SkPaint& paint) {
+    this->fPath = &path;
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_PATH;
+
+    this->fInfo.push(SkObjectParser::PathToString(path));
+    this->fInfo.push(SkObjectParser::PaintToString(paint));
+}
+
+void DrawPath::execute(SkCanvas* canvas) {
+    canvas->drawPath(*this->fPath, *this->fPaint);
+}
+
+DrawPicture::DrawPicture(SkPicture& picture) {
+    this->fPicture = &picture;
+    this->fDrawType = DRAW_PICTURE;
+    this->fInfo.push(SkObjectParser::CustomTextToString("To be implemented."));
+}
+
+void DrawPicture::execute(SkCanvas* canvas) {
+    canvas->drawPicture(*this->fPicture);
+}
+
+DrawPoints::DrawPoints(SkCanvas::PointMode mode, size_t count,
+        const SkPoint pts[], const SkPaint& paint) {
+    this->fMode = mode;
+    this->fCount = count;
+    this->fPts = pts;
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_POINTS;
+
+    this->fInfo.push(SkObjectParser::PointsToString(pts, count));
+    this->fInfo.push(SkObjectParser::ScalarToString(SkIntToScalar(count),
+                                                    "Points: "));
+    this->fInfo.push(SkObjectParser::PointModeToString(mode));
+}
+
+void DrawPoints::execute(SkCanvas* canvas) {
+    canvas->drawPoints(this->fMode, this->fCount, this->fPts, *this->fPaint);
+}
+
+DrawPosText::DrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+        const SkPaint& paint) {
+    this->fText = text;
+    this->fByteLength = byteLength;
+    this->fPos = pos;
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_POS_TEXT;
+
+    this->fInfo.push(SkObjectParser::TextToString(text, byteLength));
+    // TODO(chudy): Test that this works.
+    this->fInfo.push(SkObjectParser::PointsToString(pos, 1));
+    this->fInfo.push(SkObjectParser::PaintToString(paint));
+}
+
+void DrawPosText::execute(SkCanvas* canvas) {
+    canvas->drawPosText(this->fText, this->fByteLength, this->fPos, *this->fPaint);
+}
+
+
+DrawPosTextH::DrawPosTextH(const void* text, size_t byteLength,
+        const SkScalar xpos[], SkScalar constY, const SkPaint& paint) {
+    this->fText = text;
+    this->fByteLength = byteLength;
+    this->fXpos = xpos;
+    this->fConstY = constY;
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_POS_TEXT_H;
+
+    this->fInfo.push(SkObjectParser::TextToString(text, byteLength));
+    this->fInfo.push(SkObjectParser::ScalarToString(xpos[0], "XPOS: "));
+    this->fInfo.push(SkObjectParser::ScalarToString(constY, "SkScalar constY: "));
+    this->fInfo.push(SkObjectParser::PaintToString(paint));
+}
+
+void DrawPosTextH::execute(SkCanvas* canvas) {
+    canvas->drawPosTextH(this->fText, this->fByteLength, this->fXpos, this->fConstY,
+            *this->fPaint);
+}
+
+DrawRectC::DrawRectC(const SkRect& rect, const SkPaint& paint) {
+    this->fRect = &rect;
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_RECT;
+
+    this->fInfo.push(SkObjectParser::RectToString(rect));
+    this->fInfo.push(SkObjectParser::PaintToString(paint));
+}
+
+void DrawRectC::execute(SkCanvas* canvas) {
+    canvas->drawRect(*this->fRect, *this->fPaint);
+}
+
+DrawSprite::DrawSprite(const SkBitmap& bitmap, int left, int top,
+        const SkPaint* paint) {
+    this->fBitmap = &bitmap;
+    this->fLeft = left;
+    this->fTop = top;
+    this->fPaint = paint;
+    this->fDrawType = DRAW_SPRITE;
+
+    this->fInfo.push(SkObjectParser::BitmapToString(bitmap));
+    this->fInfo.push(SkObjectParser::IntToString(left, "Left: "));
+    this->fInfo.push(SkObjectParser::IntToString(top, "Top: "));
+}
+
+void DrawSprite::execute(SkCanvas* canvas) {
+    canvas->drawSprite(*this->fBitmap, this->fLeft, this->fTop, this->fPaint);
+}
+
+DrawTextC::DrawTextC(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+        const SkPaint& paint) {
+    this->fText = text;
+    this->fByteLength = byteLength;
+    this->fX = x;
+    this->fY = y;
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_TEXT;
+
+    this->fInfo.push(SkObjectParser::TextToString(text, byteLength));
+    this->fInfo.push(SkObjectParser::ScalarToString(x, "SkScalar x: "));
+    this->fInfo.push(SkObjectParser::ScalarToString(y, "SkScalar y: "));
+    this->fInfo.push(SkObjectParser::PaintToString(paint));
+}
+
+void DrawTextC::execute(SkCanvas* canvas) {
+    canvas->drawText(this->fText, this->fByteLength, this->fX, this->fY, *this->fPaint);
+}
+
+DrawTextOnPath::DrawTextOnPath(const void* text, size_t byteLength,
+        const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) {
+    this->fText = text;
+    this->fByteLength = byteLength;
+    this->fPath = &path;
+    this->fMatrix = matrix;
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_TEXT_ON_PATH;
+
+    this->fInfo.push(SkObjectParser::TextToString(text, byteLength));
+    this->fInfo.push(SkObjectParser::PathToString(path));
+    if (matrix) this->fInfo.push(SkObjectParser::MatrixToString(*matrix));
+    this->fInfo.push(SkObjectParser::PaintToString(paint));
+}
+
+void DrawTextOnPath::execute(SkCanvas* canvas) {
+    canvas->drawTextOnPath(this->fText, this->fByteLength, *this->fPath,
+            this->fMatrix, *this->fPaint);
+}
+
+DrawVertices::DrawVertices(SkCanvas::VertexMode vmode, int vertexCount,
+        const SkPoint vertices[], const SkPoint texs[], const SkColor colors[],
+        SkXfermode* xfermode, const uint16_t indices[], int indexCount,
+        const SkPaint& paint) {
+    this->fVmode = vmode;
+    this->fVertexCount = vertexCount;
+    this->fTexs = texs;
+    this->fColors = colors;
+    this->fXfermode = xfermode;
+    this->fIndices = indices;
+    this->fIndexCount = indexCount;
+    this->fPaint = &paint;
+    this->fDrawType = DRAW_VERTICES;
+    // TODO(chudy)
+    this->fInfo.push(SkObjectParser::CustomTextToString("To be implemented."));
+}
+
+void DrawVertices::execute(SkCanvas* canvas) {
+    canvas->drawVertices(this->fVmode, this->fVertexCount, this->fVertices,
+            this->fTexs, this->fColors, this->fXfermode, this->fIndices,
+            this->fIndexCount, *this->fPaint);
+}
+
+Restore::Restore() {
+    this->fDrawType = RESTORE;
+    this->fInfo.push(SkObjectParser::CustomTextToString("No Parameters"));
+}
+
+void Restore::execute(SkCanvas* canvas) {
+    canvas->restore();
+}
+
+Rotate::Rotate(SkScalar degrees) {
+    this->fDegrees = degrees;
+    this->fDrawType = ROTATE;
+
+    this->fInfo.push(SkObjectParser::ScalarToString(degrees, "SkScalar degrees: "));
+}
+
+void Rotate::execute(SkCanvas* canvas) {
+    canvas->rotate(this->fDegrees);
+}
+
+Save::Save(SkCanvas::SaveFlags flags) {
+    this->fFlags = flags;
+    this->fDrawType = SAVE;
+    this->fInfo.push(SkObjectParser::SaveFlagsToString(flags));
+}
+
+void Save::execute(SkCanvas* canvas) {
+    canvas->save(this->fFlags);
+}
+
+SaveLayer::SaveLayer(const SkRect* bounds, const SkPaint* paint,
+        SkCanvas::SaveFlags flags) {
+    this->fBounds = bounds;
+    this->fPaint = paint;
+    this->fFlags = flags;
+    this->fDrawType = SAVE_LAYER;
+
+    if (bounds) this->fInfo.push(SkObjectParser::RectToString(*bounds));
+    if (paint) this->fInfo.push(SkObjectParser::PaintToString(*paint));
+    this->fInfo.push(SkObjectParser::SaveFlagsToString(flags));
+}
+
+void SaveLayer::execute(SkCanvas* canvas) {
+    canvas->saveLayer(this->fBounds, this->fPaint, this->fFlags);
+}
+
+Scale::Scale(SkScalar sx, SkScalar sy) {
+    this->fSx = sx;
+    this->fSy = sy;
+    this->fDrawType = SCALE;
+
+    this->fInfo.push(SkObjectParser::ScalarToString(sx, "SkScalar sx: "));
+    this->fInfo.push(SkObjectParser::ScalarToString(sy, "SkScalar sy: "));
+}
+
+void Scale::execute(SkCanvas* canvas) {
+    canvas->scale(this->fSx, this->fSy);
+}
+
+SetMatrix::SetMatrix(const SkMatrix& matrix) {
+    this->fMatrix = &matrix;
+    this->fDrawType = SET_MATRIX;
+
+    this->fInfo.push(SkObjectParser::MatrixToString(matrix));
+}
+
+void SetMatrix::execute(SkCanvas* canvas) {
+    canvas->setMatrix(*this->fMatrix);
+}
+
+Skew::Skew(SkScalar sx, SkScalar sy) {
+    this->fSx = sx;
+    this->fSy = sy;
+    this->fDrawType = SKEW;
+
+    this->fInfo.push(SkObjectParser::ScalarToString(sx, "SkScalar sx: "));
+    this->fInfo.push(SkObjectParser::ScalarToString(sy, "SkScalar sy: "));
+}
+
+void Skew::execute(SkCanvas* canvas) {
+    canvas->skew(this->fSx, this->fSy);
+}
+
+Translate::Translate(SkScalar dx, SkScalar dy) {
+    this->fDx = dx;
+    this->fDy = dy;
+    this->fDrawType = TRANSLATE;
+
+    this->fInfo.push(SkObjectParser::ScalarToString(dx, "SkScalar dx: "));
+    this->fInfo.push(SkObjectParser::ScalarToString(dy, "SkScalar dy: "));
+}
+
+void Translate::execute(SkCanvas* canvas) {
+    canvas->translate(this->fDx, this->fDy);
+}
diff --git a/debugger/SkDrawCommand.h b/debugger/SkDrawCommand.h
new file mode 100644
index 0000000..3334209
--- /dev/null
+++ b/debugger/SkDrawCommand.h
@@ -0,0 +1,348 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKDRAWCOMMAND_H_
+#define SKDRAWCOMMAND_H_
+
+#include "SkPictureFlat.h"
+#include "SkCanvas.h"
+
+class SkDrawCommand {
+public:
+    /* TODO(chudy): Remove subclasses. */
+    SkDrawCommand();
+
+    virtual ~SkDrawCommand();
+
+    virtual SkString toString();
+
+    virtual const char* toCString() {
+        return GetCommandString(fDrawType);
+    }
+
+    bool isVisible() const {
+        return fVisible;
+    }
+
+    void setVisible(bool toggle) {
+        fVisible = toggle;
+    }
+
+    SkTDArray<SkString*>* Info() {return &fInfo; };
+    virtual void execute(SkCanvas* canvas)=0;
+    DrawType getType() { return fDrawType; };
+
+protected:
+    DrawType fDrawType;
+    SkTDArray<SkString*> fInfo;
+
+private:
+    bool fVisible;
+    static const char* GetCommandString(DrawType type);
+};
+
+class Restore : public SkDrawCommand {
+public:
+    Restore();
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+};
+
+class Clear : public SkDrawCommand {
+public:
+    Clear(SkColor color);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    SkColor fColor;
+};
+
+class ClipPath : public SkDrawCommand {
+public:
+    ClipPath(const SkPath& path, SkRegion::Op op, bool doAA);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkPath* fPath;
+    SkRegion::Op fOp;
+    bool fDoAA;
+};
+
+class ClipRegion : public SkDrawCommand {
+public:
+    ClipRegion(const SkRegion& region, SkRegion::Op op);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkRegion* fRegion;
+    SkRegion::Op fOp;
+};
+
+class ClipRect : public SkDrawCommand {
+public:
+    ClipRect(const SkRect& rect, SkRegion::Op op, bool doAA);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkRect* fRect;
+    SkRegion::Op fOp;
+    bool fDoAA;
+};
+
+class Concat : public SkDrawCommand {
+public:
+    Concat(const SkMatrix& matrix);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkMatrix* fMatrix;
+};
+
+class DrawBitmap : public SkDrawCommand {
+public:
+    DrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+            const SkPaint* paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkPaint* fPaint;
+    const SkBitmap* fBitmap;
+    SkScalar fLeft;
+    SkScalar fTop;
+};
+
+class DrawBitmapMatrix : public SkDrawCommand {
+public:
+    DrawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
+            const SkPaint* paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkPaint* fPaint;
+    const SkBitmap* fBitmap;
+    const SkMatrix* fMatrix;
+};
+
+class DrawBitmapNine : public SkDrawCommand {
+public:
+    DrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+            const SkRect& dst, const SkPaint* paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkBitmap* fBitmap;
+    const SkIRect* fCenter;
+    const SkRect* fDst;
+    const SkPaint* fPaint;
+};
+
+class DrawBitmapRect : public SkDrawCommand {
+public:
+    DrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
+            const SkRect& dst, const SkPaint* paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkRect* fSrc;
+    const SkPaint* fPaint;
+    const SkBitmap* fBitmap;
+    const SkRect* fDst;
+};
+
+class DrawData : public SkDrawCommand {
+public:
+    DrawData(const void* data, size_t length);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const void* fData;
+    size_t fLength;
+};
+
+class DrawPaint : public SkDrawCommand {
+public:
+    DrawPaint(const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkPaint* fPaint;
+};
+
+class DrawPath : public SkDrawCommand {
+public:
+    DrawPath(const SkPath& path, const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkPath* fPath;
+    const SkPaint* fPaint;
+};
+
+class DrawPicture : public SkDrawCommand {
+public:
+    DrawPicture(SkPicture& picture);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    SkPicture* fPicture;
+};
+
+class DrawPoints : public SkDrawCommand {
+public:
+    DrawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[],
+            const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkPoint* fPts;
+    SkCanvas::PointMode fMode;
+    size_t fCount;
+    const SkPaint* fPaint;
+};
+
+/* TODO(chudy): DrawText is a predefined macro and was breaking something
+ * in the windows build of the debugger.
+ */
+class DrawTextC : public SkDrawCommand {
+public:
+    DrawTextC(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+            const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const void* fText;
+    size_t fByteLength;
+    SkScalar fX;
+    SkScalar fY;
+    const SkPaint* fPaint;
+};
+
+class DrawPosText : public SkDrawCommand {
+public:
+    DrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+            const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkPoint* fPos;
+    const void* fText;
+    size_t fByteLength;
+    const SkPaint* fPaint;
+};
+
+class DrawTextOnPath : public SkDrawCommand {
+public:
+    DrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+            const SkMatrix* matrix, const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkMatrix* fMatrix;
+    const void* fText;
+    size_t fByteLength;
+    const SkPath* fPath;
+    const SkPaint* fPaint;
+};
+
+class DrawPosTextH : public SkDrawCommand {
+public:
+    DrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+            SkScalar constY, const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkScalar* fXpos;
+    const void* fText;
+    size_t fByteLength;
+    SkScalar fConstY;
+    const SkPaint* fPaint;
+};
+
+class DrawRectC : public SkDrawCommand {
+public:
+    DrawRectC(const SkRect& rect, const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkRect* fRect;
+    const SkPaint* fPaint;
+};
+
+class DrawSprite : public SkDrawCommand {
+public:
+    DrawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkPaint* fPaint;
+    int fLeft;
+    int fTop;
+    const SkBitmap* fBitmap;
+};
+
+class DrawVertices : public SkDrawCommand {
+public:
+    DrawVertices(SkCanvas::VertexMode vmode, int vertexCount,
+            const SkPoint vertices[], const SkPoint texs[], const SkColor colors[],
+            SkXfermode* xfermode, const uint16_t indices[], int indexCount,
+            const SkPaint& paint);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    SkCanvas::VertexMode fVmode;
+    int fVertexCount;
+    int fIndexCount;
+    const SkPoint* fVertices;
+    const SkPoint* fTexs;
+    const SkColor* fColors;
+    const uint16_t* fIndices;
+    SkXfermode* fXfermode;
+    const SkPaint* fPaint;
+};
+
+class Rotate : public SkDrawCommand {
+public:
+    Rotate(SkScalar degrees);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    SkScalar fDegrees;
+};
+
+class Save : public SkDrawCommand {
+public:
+    Save(SkCanvas::SaveFlags flags);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    SkCanvas::SaveFlags fFlags;
+};
+
+class SaveLayer : public SkDrawCommand {
+public:
+    SaveLayer(const SkRect* bounds, const SkPaint* paint,
+            SkCanvas::SaveFlags flags);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkRect* fBounds;
+    const SkPaint* fPaint;
+    SkCanvas::SaveFlags fFlags;
+};
+
+class Scale : public SkDrawCommand {
+public:
+    Scale(SkScalar sx, SkScalar sy);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    SkScalar fSx;
+    SkScalar fSy;
+};
+
+class SetMatrix : public SkDrawCommand {
+public:
+    SetMatrix(const SkMatrix& matrix);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    const SkMatrix* fMatrix;
+};
+
+class Skew : public SkDrawCommand {
+public:
+    Skew(SkScalar sx, SkScalar sy);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    SkScalar fSx;
+    SkScalar fSy;
+};
+
+class Translate : public SkDrawCommand {
+public:
+    Translate(SkScalar dx, SkScalar dy);
+    virtual void execute(SkCanvas* canvas) SK_OVERRIDE;
+private:
+    SkScalar fDx;
+    SkScalar fDy;
+};
+
+#endif
diff --git a/debugger/SkObjectParser.cpp b/debugger/SkObjectParser.cpp
new file mode 100644
index 0000000..bf6a053
--- /dev/null
+++ b/debugger/SkObjectParser.cpp
@@ -0,0 +1,182 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkObjectParser.h"
+
+/* TODO(chudy): Replace all std::strings with char */
+
+SkString* SkObjectParser::BitmapToString(const SkBitmap& bitmap) {
+    SkString* mBitmap = new SkString("SkBitmap: Data unavailable");
+    return mBitmap;
+}
+
+SkString* SkObjectParser::BoolToString(bool doAA) {
+    SkString* mBool = new SkString("Bool doAA: ");
+    if (doAA) {
+        mBool->append("True");
+    } else {
+        mBool->append("False");
+    }
+    return mBool;
+}
+
+SkString* SkObjectParser::CustomTextToString(const char* text) {
+    SkString* mText = new SkString(text);
+    return mText;
+}
+
+SkString* SkObjectParser::IntToString(int x, const char* text) {
+    SkString* mInt = new SkString(text);
+    mInt->append(" ");
+    mInt->appendScalar(SkIntToScalar(x));
+    return mInt;
+}
+
+SkString* SkObjectParser::IRectToString(const SkIRect& rect) {
+    SkString* mRect = new SkString("SkIRect: ");
+    mRect->append("L: ");
+    mRect->appendScalar(SkIntToScalar(rect.left()));
+    mRect->append(", T: ");
+    mRect->appendScalar(SkIntToScalar(rect.top()));
+    mRect->append(", R: ");
+    mRect->appendScalar(SkIntToScalar(rect.right()));
+    mRect->append(", B: ");
+    mRect->appendScalar(SkIntToScalar(rect.bottom()));
+    return mRect;
+}
+
+SkString* SkObjectParser::MatrixToString(const SkMatrix& matrix) {
+    SkString* mMatrix = new SkString("SkMatrix: (");
+    for (int i = 0; i < 8; i++) {
+        mMatrix->appendScalar(matrix.get(i));
+        mMatrix->append("), (");
+    }
+    mMatrix->appendScalar(matrix.get(8));
+    mMatrix->append(")");
+    return mMatrix;
+}
+
+SkString* SkObjectParser::PaintToString(const SkPaint& paint) {
+    SkColor color = paint.getColor();
+    SkString* mPaint = new SkString("SkPaint: 0x");
+    mPaint->appendHex(color);
+    return mPaint;
+}
+
+SkString* SkObjectParser::PathToString(const SkPath& path) {
+    SkString* mPath = new SkString("SkPath: ");
+    for (int i = 0; i < path.countPoints(); i++) {
+        mPath->append("(");
+        mPath->appendScalar(path.getPoint(i).fX);
+        mPath->append(", ");
+        mPath->appendScalar(path.getPoint(i).fY);
+        mPath->append(") ");
+    }
+    return mPath;
+}
+
+SkString* SkObjectParser::PointsToString(const SkPoint pts[], size_t count) {
+    SkString* mPoints = new SkString("SkPoints pts[]: ");
+    for (unsigned int i = 0; i < count; i++) {
+        mPoints->append("(");
+        mPoints->appendScalar(pts[i].fX);
+        mPoints->append(",");
+        mPoints->appendScalar(pts[i].fY);
+        mPoints->append(")");
+    }
+    return mPoints;
+}
+
+SkString* SkObjectParser::PointModeToString(SkCanvas::PointMode mode) {
+    SkString* mMode = new SkString("SkCanvas::PointMode: ");
+    if (mode == SkCanvas::kPoints_PointMode) {
+        mMode->append("kPoints_PointMode");
+    } else if (mode == SkCanvas::kLines_PointMode) {
+        mMode->append("kLines_Mode");
+    } else if (mode == SkCanvas::kPolygon_PointMode) {
+        mMode->append("kPolygon_PointMode");
+    }
+    return mMode;
+}
+
+SkString* SkObjectParser::RectToString(const SkRect& rect) {
+    SkString* mRect = new SkString("SkRect: ");
+    mRect->append("(");
+    mRect->appendScalar(rect.left());
+    mRect->append(", ");
+    mRect->appendScalar(rect.top());
+    mRect->append(", ");
+    mRect->appendScalar(rect.right());
+    mRect->append(", ");
+    mRect->appendScalar(rect.bottom());
+    mRect->append(")");
+    return mRect;
+}
+
+SkString* SkObjectParser::RegionOpToString(SkRegion::Op op) {
+    SkString* mOp = new SkString("SkRegion::Op: ");
+    if (op == SkRegion::kDifference_Op) {
+        mOp->append("kDifference_Op");
+    } else if (op == SkRegion::kIntersect_Op) {
+        mOp->append("kIntersect_Op");
+    } else if (op == SkRegion::kUnion_Op) {
+        mOp->append("kUnion_Op");
+    } else if (op == SkRegion::kXOR_Op) {
+        mOp->append("kXOR_Op");
+    } else if (op == SkRegion::kReverseDifference_Op) {
+        mOp->append("kReverseDifference_Op");
+    } else if (op == SkRegion::kReplace_Op) {
+        mOp->append("kReplace_Op");
+    } else {
+        mOp->append("Unknown Type");
+    }
+    return mOp;
+}
+
+SkString* SkObjectParser::RegionToString(const SkRegion& region) {
+    SkString* mRegion = new SkString("SkRegion: Data unavailable.");
+    return mRegion;
+}
+
+SkString* SkObjectParser::SaveFlagsToString(SkCanvas::SaveFlags flags) {
+    SkString* mFlags = new SkString("SkCanvas::SaveFlags: ");
+    if(flags == SkCanvas::kMatrixClip_SaveFlag) {
+        mFlags->append("kMatrixClip_SaveFlag");
+    } else if (flags == SkCanvas::kClip_SaveFlag) {
+        mFlags->append("kClip_SaveFlag");
+    } else if (flags == SkCanvas::kHasAlphaLayer_SaveFlag) {
+        mFlags->append("kHasAlphaLayer_SaveFlag");
+    } else if (flags == SkCanvas::kFullColorLayer_SaveFlag) {
+        mFlags->append("kFullColorLayer_SaveFlag");
+    } else if (flags == SkCanvas::kClipToLayer_SaveFlag) {
+        mFlags->append("kClipToLayer_SaveFlag");
+    } else if (flags == SkCanvas::kMatrixClip_SaveFlag) {
+        mFlags->append("kMatrixClip_SaveFlag");
+    } else if (flags == SkCanvas::kARGB_NoClipLayer_SaveFlag) {
+        mFlags->append("kARGB_NoClipLayer_SaveFlag");
+    } else if (flags == SkCanvas::kARGB_ClipLayer_SaveFlag) {
+        mFlags->append("kARGB_ClipLayer_SaveFlag");
+    } else {
+        mFlags->append("Data Unavailable");
+    }
+    return mFlags;
+}
+
+SkString* SkObjectParser::ScalarToString(SkScalar x, const char* text) {
+    SkString* mScalar = new SkString(text);
+    mScalar->append(" ");
+    mScalar->appendScalar(x);
+    return mScalar;
+}
+
+SkString* SkObjectParser::TextToString(const void* text, size_t byteLength) {
+    SkString* mText = new SkString(6+byteLength+1);
+    mText->append("Text: ");
+    mText->append((char*) text, byteLength);
+    return mText;
+}
diff --git a/debugger/SkObjectParser.h b/debugger/SkObjectParser.h
new file mode 100644
index 0000000..fc6fb1a
--- /dev/null
+++ b/debugger/SkObjectParser.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 SKOBJECTPARSER_H_
+#define SKOBJECTPARSER_H_
+
+#include "SkCanvas.h"
+#include "SkString.h"
+
+/** \class SkObjectParser
+
+    The ObjectParser is used to return string information about parameters
+    in each draw command.
+ */
+class SkObjectParser {
+public:
+
+    /**
+        Returns a string about a bitmaps bounds and config.
+        @param bitmap  SkBitmap
+    */
+    static SkString* BitmapToString(const SkBitmap& bitmap);
+
+    /**
+        Returns a string representation of a boolean.
+        @param doAA  boolean
+     */
+    static SkString* BoolToString(bool doAA);
+
+    /**
+        Returns a string representation of the text pointer passed in.
+     */
+    static SkString* CustomTextToString(const char* text);
+
+    /**
+        Returns a string representation of an integer with the text parameter
+        at the front of the string.
+        @param x  integer
+        @param text
+     */
+    static SkString* IntToString(int x, const char* text);
+    /**
+        Returns a string representation of the SkIRects coordinates.
+        @param rect  SkIRect
+     */
+    static SkString* IRectToString(const SkIRect& rect);
+
+    /**
+        Returns a string representation of an SkMatrix's contents
+        @param matrix  SkMatrix
+     */
+    static SkString* MatrixToString(const SkMatrix& matrix);
+
+    /**
+        Returns a string representation of an SkPaint's color
+        @param paint  SkPaint
+     */
+    static SkString* PaintToString(const SkPaint& paint);
+
+    /**
+        Returns a string representation of a SkPath's points.
+        @param path  SkPath
+     */
+    static SkString* PathToString(const SkPath& path);
+
+    /**
+        Returns a string representation of the points in the point array.
+        @param pts[]  Array of SkPoints
+        @param count
+     */
+    static SkString* PointsToString(const SkPoint pts[], size_t count);
+
+    /**
+        Returns a string representation of the SkCanvas PointMode enum.
+     */
+    static SkString* PointModeToString(SkCanvas::PointMode mode);
+
+    /**
+        Returns a string representation of the SkRects coordinates.
+        @param rect  SkRect
+     */
+    static SkString* RectToString(const SkRect& rect);
+
+    /**
+        Returns a string representation of the SkRegion enum.
+        @param op  SkRegion::op enum
+     */
+    static SkString* RegionOpToString(SkRegion::Op op);
+
+    /**
+        Returns a string representation of the SkRegion.
+        @param region  SkRegion
+     */
+    static SkString* RegionToString(const SkRegion& region);
+
+    /**
+        Returns a string representation of the SkCanvas::SaveFlags enum.
+        @param flags  SkCanvas::SaveFlags enum
+     */
+    static SkString* SaveFlagsToString(SkCanvas::SaveFlags flags);
+
+    /**
+        Returns a string representation of an SkScalar with the text parameter
+        at the front of the string.
+        @param x  SkScalar
+        @param text
+     */
+    static SkString* ScalarToString(SkScalar x, const char* text);
+
+    /**
+        Returns a string representation of the char pointer passed in.
+        @param text  const void* that will be cast to a char*
+     */
+    static SkString* TextToString(const void* text, size_t byteLength);
+};
+
+#endif
diff --git a/debugger/debuggermain.cpp b/debugger/debuggermain.cpp
new file mode 100644
index 0000000..86a4574
--- /dev/null
+++ b/debugger/debuggermain.cpp
@@ -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.
+ */
+
+#include "SkDebuggerGUI.h"
+#include <QApplication>
+
+int main(int argc, char *argv[]) {
+    QApplication a(argc, argv);
+    SkDebuggerGUI w;
+    w.show();
+    return a.exec();
+}
diff --git a/experimental/Debugger/DebuggerCommandsView.cpp b/experimental/Debugger/DebuggerCommandsView.cpp
new file mode 100644
index 0000000..735c808
--- /dev/null
+++ b/experimental/Debugger/DebuggerCommandsView.cpp
@@ -0,0 +1,145 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "DebuggerViews.h"
+
+DebuggerCommandsView::DebuggerCommandsView() {
+    fBGColor = 0xFFBBBBBB;
+    fTopIndex = 0;
+    fHighlight = 0;
+    fResizing = false;
+
+    SkPaint p;
+    p.setTextSize(SkIntToScalar(SKDEBUGGER_TEXTSIZE));
+    fSpacing = p.getFontSpacing();
+    fCentered = false;
+    fRange = (int)(this->height()/fSpacing) - 1;
+}
+
+DebuggerCommandsView::~DebuggerCommandsView() {
+    fList.deleteAll();
+}
+
+bool DebuggerCommandsView::onEvent(const SkEvent& evt) {
+    if (evt.isType(SKDEBUGGER_COMMANDTYPE)) {
+        *fList.append() = new SkString(evt.findString(SKDEBUGGER_ATOM));
+        this->inval(NULL);
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
+}
+
+void DebuggerCommandsView::onSizeChange() {
+    fRange = (int)(this->height()/fSpacing);
+    this->INHERITED::onSizeChange();
+}
+
+void DebuggerCommandsView::alignCenter() {
+    if (!fCentered || fHighlight < fRange/2 || fHighlight > (fList.count() - fRange/2)) {
+        return;
+    } else {
+        if (fHighlight > (fTopIndex + fRange/2))
+            fTopIndex += fHighlight - (fTopIndex + fRange/2);
+        if (fHighlight < (fTopIndex + fRange/2))
+            fTopIndex -= (fTopIndex + fRange/2) - fHighlight;
+    }
+}
+
+int DebuggerCommandsView::nextItem() {
+    if (fHighlight < fList.count() - 1)
+        ++fHighlight;
+    if (fHighlight < fTopIndex || fHighlight > (fTopIndex + fRange))
+        fTopIndex = fHighlight;
+    if (fHighlight == (fTopIndex + fRange))
+        ++fTopIndex;
+    this->alignCenter();
+    this->inval(NULL);
+    return fHighlight;
+}
+
+int DebuggerCommandsView::prevItem() {
+    if (fHighlight > 0)
+        --fHighlight;
+    if (fHighlight < fTopIndex || fHighlight > (fTopIndex + fRange))
+        fTopIndex = fHighlight;
+    this->alignCenter();
+    this->inval(NULL);
+    return fHighlight;
+}
+
+int DebuggerCommandsView::scrollUp() {
+    if (fTopIndex > 0)
+        --fTopIndex;
+    this->inval(NULL);
+    return fHighlight;
+}
+
+int DebuggerCommandsView::scrollDown() {
+    if (fTopIndex < (fList.count() - 1))
+        ++fTopIndex;
+    this->inval(NULL);
+    return fHighlight;
+}
+
+void DebuggerCommandsView::highlight(int index) {
+    SkASSERT(index >= 0 && index < fList.count());
+    if (fHighlight != index) {
+        fHighlight = index;
+        this->alignCenter();
+        this->inval(NULL);
+    }
+}
+
+int DebuggerCommandsView::selectHighlight(int ypos) {
+    int i = (int)(ypos/fSpacing) + fTopIndex;
+    if (i >= fList.count()) {
+        i = fList.count() - 1;
+    }
+    if (fHighlight != i) {
+        fHighlight = i;
+        this->alignCenter();
+        this->inval(NULL);
+    }
+    return fHighlight;
+}
+
+void DebuggerCommandsView::toggleCentered() {
+    fCentered = !fCentered;
+    this->alignCenter();
+    this->inval(NULL);
+}
+
+void DebuggerCommandsView::onDraw(SkCanvas* canvas) {
+    canvas->drawColor(fBGColor);
+
+    SkPaint p;
+    p.setTextSize(SkIntToScalar(SKDEBUGGER_TEXTSIZE));
+    p.setAntiAlias(true);
+
+    //draw highlight
+    int selected = fHighlight - fTopIndex;
+    SkRect r = {0, fSpacing * selected, this->width(), fSpacing * (selected+1)};
+    p.setColor(SKDEBUGGER_HIGHLIGHTCOLOR);
+    canvas->drawRect(r, p);
+
+    int endIndex = fTopIndex + fRange;
+    if (endIndex > fList.count())
+        endIndex = fList.count();
+
+    p.setColor(SKDEBUGGER_TEXTCOLOR);
+    int pos;
+    for (int i = fTopIndex; i < endIndex; ++i) {
+        pos = i - fTopIndex;
+        canvas->drawText(fList[i]->c_str(), fList[i]->size(),
+                         0, fSpacing - 2 + fSpacing * pos, p);
+    }
+    p.setColor(SKDEBUGGER_RESIZEBARCOLOR);
+    r = SkRect::MakeXYWH(this->width() - SKDEBUGGER_RESIZEBARSIZE, 0,
+                         SKDEBUGGER_RESIZEBARSIZE, this->height());
+    canvas->drawRect(r, p);
+}
+
diff --git a/experimental/Debugger/DebuggerContentView.cpp b/experimental/Debugger/DebuggerContentView.cpp
new file mode 100644
index 0000000..f079508
--- /dev/null
+++ b/experimental/Debugger/DebuggerContentView.cpp
@@ -0,0 +1,273 @@
+#include "SampleCode.h"
+#include "SkOSMenu.h"
+
+#include "DebuggerViews.h"
+static const char gIsDebuggerQuery[] = "is-debugger";
+class DebuggerView : public SampleView {
+public:
+        DebuggerView(const char* data, size_t size) {
+        fData.append(size, data);
+        fCommandsVisible = true;
+        fCommandsResizing = false;
+        fStateVisible = true;
+        fStateResizing = false;
+
+        fCommands = new DebuggerCommandsView;
+        fCommands->setVisibleP(fCommandsVisible);
+        this->attachChildToFront(fCommands)->unref();
+
+
+        fState = new DebuggerStateView;
+        fState->setVisibleP(fStateVisible);
+        this->attachChildToFront(fState)->unref();
+
+        fAtomsToRead = 0;
+        fDisplayClip = false;
+
+        fDumper = new SkDebugDumper(this->getSinkID(), fCommands->getSinkID(),
+                                    fState->getSinkID());
+
+        fDumper->unload();
+        fAtomBounds.reset();
+        fFrameBounds.reset();
+
+        SkDumpCanvas* dumpCanvas = new SkDumpCanvas(fDumper);
+        SkGPipeReader* dumpReader = new SkGPipeReader(dumpCanvas);
+
+
+        if (size > 0) {
+            int offset = 0;
+            int frameBound = 0;
+            size_t bytesRead;
+            while (static_cast<unsigned>(offset) < size) {
+                SkGPipeReader::Status s =
+                    dumpReader->playback(data + offset, size - offset,
+                                         SkGPipeReader::kReadAtom_PlaybackFlag, &bytesRead);
+                SkASSERT(SkGPipeReader::kError_Status != s);
+                offset += bytesRead;
+
+                if (SkGPipeReader::kDone_Status == s) {
+                    fDumper->dump(dumpCanvas, SkDumpCanvas::kNULL_Verb,
+                                 "End of Frame", NULL);
+                    delete dumpReader;
+                    delete dumpCanvas;
+                    dumpCanvas = new SkDumpCanvas(fDumper);
+                    dumpReader = new SkGPipeReader(dumpCanvas);
+                    frameBound = offset;
+                }
+                fAtomBounds.append(1, &offset);
+                fFrameBounds.append(1, &frameBound);
+            }
+        }
+
+        delete dumpReader;
+        delete dumpCanvas;
+
+        fDumper->load();
+    }
+
+    ~DebuggerView() {
+        fAtomBounds.reset();
+        fFrameBounds.reset();
+        delete fDumper;
+    }
+
+    virtual void requestMenu(SkOSMenu* menu) {
+        menu->setTitle("Debugger");
+        menu->appendSwitch("Show Commands", "Commands", this->getSinkID(), fCommandsVisible);
+        menu->appendSwitch("Show State", "State", this->getSinkID(), fStateVisible);
+        menu->appendSwitch("Display Clip", "Clip", this->getSinkID(), fDisplayClip);
+    }
+
+
+    void goToAtom(int atom) {
+        if (atom != fAtomsToRead) {
+            fAtomsToRead = atom;
+            this->inval(NULL);
+        }
+    }
+
+protected:
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Debugger");
+            return true;
+        }
+        if (evt->isType(gIsDebuggerQuery)) {
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual bool onEvent(const SkEvent& evt) {
+        if (SkOSMenu::FindSwitchState(evt, "Commands", &fCommandsVisible) ||
+            SkOSMenu::FindSwitchState(evt, "State", &fStateVisible)) {
+            fCommands->setVisibleP(fCommandsVisible);
+            fState->setVisibleP(fStateVisible);
+            fStateOffset = (fCommandsVisible) ? fCommands->width() : 0;
+            fState->setSize(this->width() - fStateOffset, fState->height());
+            fState->setLoc(fStateOffset, this->height() - fState->height());
+            this->inval(NULL);
+            return true;
+        }
+        if (SkOSMenu::FindSwitchState(evt, "Clip", &fDisplayClip)) {
+            this->inval(NULL);
+            return true;
+        }
+        return this->INHERITED::onEvent(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        if (fData.count() <= 0)
+            return;
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->translate(fStateOffset, 0);
+
+        int lastFrameBound = fFrameBounds[fAtomsToRead];
+        int toBeRead = fAtomBounds[fAtomsToRead] - lastFrameBound;
+        int firstChunk = (fAtomsToRead > 0) ? fAtomBounds[fAtomsToRead - 1] - lastFrameBound: 0;
+        if (toBeRead > 0) {
+            SkDumpCanvas* dumpCanvas = new SkDumpCanvas(fDumper);
+            SkGPipeReader* dumpReader = new SkGPipeReader(dumpCanvas);
+            SkGPipeReader* reader = new SkGPipeReader(canvas);
+            fDumper->disable();
+
+            int offset = 0;
+            size_t bytesRead;
+            SkGPipeReader::Status s;
+            //Read the first chunk
+            if (offset < firstChunk && firstChunk < toBeRead) {
+                s = dumpReader->playback(fData.begin() + offset, firstChunk - offset);
+                SkASSERT(SkGPipeReader::kError_Status != s);
+                s = reader->playback(fData.begin() + offset, firstChunk - offset, 0, &bytesRead);
+                SkASSERT(SkGPipeReader::kError_Status != s);
+                if (SkGPipeReader::kDone_Status == s){
+                    delete dumpReader;
+                    delete dumpCanvas;
+                    dumpCanvas = new SkDumpCanvas(fDumper);
+                    dumpReader = new SkGPipeReader(dumpCanvas);
+                    delete reader;
+                    reader = new SkGPipeReader(canvas);
+                }
+                offset += bytesRead;
+            }
+            SkASSERT(offset == firstChunk);
+            //Then read the current atom
+            fDumper->enable();
+            s = dumpReader->playback(fData.begin() + offset, toBeRead - offset,
+                                     SkGPipeReader::kReadAtom_PlaybackFlag);
+            SkASSERT(SkGPipeReader::kError_Status != s);
+            s = reader->playback(fData.begin() + offset, toBeRead - offset,
+                                 SkGPipeReader::kReadAtom_PlaybackFlag, &bytesRead);
+            SkASSERT(SkGPipeReader::kError_Status != s);
+
+            delete reader;
+            delete dumpReader;
+            delete dumpCanvas;
+
+            if (fDisplayClip) {
+                SkPaint p;
+                p.setColor(0x440000AA);
+                SkPath path;
+                canvas->getTotalClip().getBoundaryPath(&path);
+                canvas->drawPath(path, p);
+            }
+        }
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        return new Click(this);
+    }
+
+    virtual bool onClick(SkView::Click* click) {
+        SkPoint prev = click->fPrev;
+        SkPoint curr = click->fCurr;
+        bool handled = true;
+        switch (click->fState) {
+            case SkView::Click::kDown_State:
+                if (SkScalarAbs(curr.fX - fCommands->width()) <= SKDEBUGGER_RESIZEBARSIZE) {
+                    fCommandsResizing = true;
+                }
+                else if (SkScalarAbs(curr.fY - (this->height() - fState->height())) <= SKDEBUGGER_RESIZEBARSIZE &&
+                         curr.fX > fCommands->width()) {
+                    fStateResizing = true;
+                }
+                else if (curr.fX < fCommands->width()) {
+                    fAtomsToRead = fCommands->selectHighlight(
+                                                  SkScalarFloorToInt(curr.fY));
+                }
+                else
+                    handled = false;
+                break;
+            case SkView::Click::kMoved_State:
+                if (fCommandsResizing)
+                    fCommands->setSize(curr.fX, this->height());
+                else if (fStateResizing)
+                    fState->setSize(this->width(), this->height() - curr.fY);
+                else if (curr.fX < fCommands->width()) {
+                    if (curr.fY - prev.fY < 0) {
+                        fCommands->scrollDown();
+                    }
+                    if (curr.fY - prev.fY > 0) {
+                        fCommands->scrollUp();
+                    }
+                }
+                else
+                    handled = false;
+                break;
+            case SkView::Click::kUp_State:
+                fStateResizing = fCommandsResizing = false;
+                break;
+            default:
+                break;
+        }
+
+        fStateOffset = fCommands->width();
+        fState->setSize(this->width() - fStateOffset, fState->height());
+        fState->setLoc(fStateOffset, this->height() - fState->height());
+        if (handled)
+            this->inval(NULL);
+        return handled;
+    }
+
+    virtual void onSizeChange() {
+        this->INHERITED::onSizeChange();
+        fCommands->setSize(CMD_WIDTH, this->height());
+        fCommands->setLoc(0, 0);
+        fState->setSize(this->width() - CMD_WIDTH, SkFloatToScalar(INFO_HEIGHT));
+        fState->setLoc(CMD_WIDTH, this->height() - SkFloatToScalar(INFO_HEIGHT));
+    }
+
+private:
+    DebuggerCommandsView*   fCommands;
+    DebuggerStateView*      fState;
+    bool                    fCommandsResizing;
+    bool                    fCommandsVisible;
+    bool                    fStateResizing;
+    bool                    fStateVisible;
+    float                   fStateOffset;
+    bool                    fDisplayClip;
+    int                     fAtomsToRead;
+    SkTDArray<int>          fAtomBounds;
+    SkTDArray<int>          fFrameBounds;
+    SkTDArray<char>         fData;
+    SkDebugDumper*          fDumper;
+
+    typedef SampleView INHERITED;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkView* create_debugger(const char* data, size_t size);
+
+SkView* create_debugger(const char* data, size_t size) {
+    return SkNEW_ARGS(DebuggerView, (data, size));
+};
+
+bool is_debugger(SkView* view);
+
+bool is_debugger(SkView* view) {
+    SkEvent isDebugger(gIsDebuggerQuery);
+    return view->doQuery(&isDebugger);
+}
diff --git a/experimental/Debugger/DebuggerStateView.cpp b/experimental/Debugger/DebuggerStateView.cpp
new file mode 100644
index 0000000..27befcb
--- /dev/null
+++ b/experimental/Debugger/DebuggerStateView.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 "DebuggerViews.h"
+#include "SkRect.h"
+
+DebuggerStateView::DebuggerStateView() {
+    fBGColor = 0xFF999999;
+    fPaint.setColor(fBGColor);
+    fResizing = false;
+}
+
+bool DebuggerStateView::onEvent(const SkEvent& evt) {
+    if (evt.isType(SKDEBUGGER_STATETYPE)) {
+        fMatrix = evt.findString(SKDEBUGGER_MATRIX);
+        fClip = evt.findString(SKDEBUGGER_CLIP);
+
+        SkPaint* ptr;
+        if (evt.getMetaData().findPtr(SKDEBUGGER_PAINT, (void**)&ptr)) {
+            fPaint = *ptr;
+            fPaintInfo = evt.findString(SKDEBUGGER_PAINTINFO);
+        }
+        this->inval(NULL);
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
+}
+
+void DebuggerStateView::onDraw(SkCanvas* canvas) {
+    canvas->drawColor(fBGColor);
+
+    //Display Current Paint
+    SkRect r = {10, 20, 40, 50};
+    canvas->drawRect(r, fPaint);
+    //Display Information
+    SkPaint p;
+    p.setTextSize(SKDEBUGGER_TEXTSIZE);
+    p.setAntiAlias(true);
+    SkScalar x = 50 * SK_Scalar1;
+    canvas->drawText(fPaintInfo.c_str(), fPaintInfo.size(), x, 30, p);
+    canvas->drawText(fMatrix.c_str(), fMatrix.size(), x, 60, p);
+    canvas->drawText(fClip.c_str(), fClip.size(), x, 90, p);
+
+    p.setColor(SKDEBUGGER_RESIZEBARCOLOR);
+    r = SkRect::MakeXYWH(0, 0, this->width(), SKDEBUGGER_RESIZEBARSIZE);
+    canvas->drawRect(r, p);
+}
+
diff --git a/experimental/Debugger/DebuggerViews.h b/experimental/Debugger/DebuggerViews.h
new file mode 100644
index 0000000..c63283f
--- /dev/null
+++ b/experimental/Debugger/DebuggerViews.h
@@ -0,0 +1,100 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkView.h"
+#include "SkColor.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkGPipe.h"
+#include "SkPaint.h"
+
+#include "SkDebugDumper.h"
+
+#define SKDEBUGGER_COMMANDTYPE  "SKDEBUGGER_COMMAND"
+#define SKDEBUGGER_STATETYPE    "SKDEBUGGER_STATE"
+
+#define SKDEBUGGER_ATOM         "SKDEBUGGER_ATOM"
+#define SKDEBUGGER_MATRIX       "SKDEBUGGER_MATRIX"
+#define SKDEBUGGER_CLIP         "SKDEBUGGER_CLIP"
+#define SKDEBUGGER_PAINTINFO    "SKDEBUGGER_PAINTINFO"
+#define SKDEBUGGER_PAINT        "SKDEBUGGER_PAINT"
+
+#define SKDEBUGGER_TEXTSIZE         14
+#define CMD_WIDTH                   200
+#define INFO_HEIGHT                 150.0f
+#define SKDEBUGGER_HIGHLIGHTCOLOR   0xFF113399
+#define SKDEBUGGER_TEXTCOLOR        0xFF000000
+#define SKDEBUGGER_RESIZEBARCOLOR   0xFF333333
+#define SKDEBUGGER_RESIZEBARSIZE    5
+
+/*
+ * Debugger - Info Panel
+ */
+class DebuggerStateView : public SkView {
+public:
+    DebuggerStateView();
+
+protected:
+    virtual bool onEvent(const SkEvent& evt);
+    virtual void onDraw(SkCanvas* canvas);
+private:
+    SkColor     fBGColor;
+    SkPaint     fPaint;
+    SkString    fMatrix;
+    SkString    fPaintInfo;
+    SkString    fClip;
+    bool        fResizing;
+    typedef SkView INHERITED;
+};
+
+/*
+ * Debugger - Commands List
+ */
+class DebuggerCommandsView : public SkView {
+public:
+    DebuggerCommandsView();
+    ~DebuggerCommandsView();
+    int nextItem();
+    int prevItem();
+    int scrollUp();
+    int scrollDown();
+    void highlight(int index);
+    int  selectHighlight(int ypos);
+    void toggleCentered();
+
+protected:
+    virtual bool onEvent(const SkEvent& evt);
+    virtual void onSizeChange();
+    virtual void onDraw(SkCanvas* canvas);
+private:
+    void        init();
+    void        alignCenter();
+    SkColor     fBGColor;
+    int         fTopIndex;
+    int         fHighlight;
+    SkScalar    fSpacing;
+    int         fRange;
+    bool        fResizing;
+    bool        fCentered;
+    SkTDArray<SkString*> fList;
+    typedef SkView INHERITED;
+};
+
+
+static void* PaintProc(void* ptr, bool doRef) {
+    SkPaint* p = (SkPaint*) ptr;
+
+    if (doRef) {
+        return new SkPaint(*p);
+    }
+    else {
+        delete p;
+        return NULL;
+    }
+
+}
+
diff --git a/experimental/Debugger/SkDebugDumper.cpp b/experimental/Debugger/SkDebugDumper.cpp
new file mode 100644
index 0000000..cfda184
--- /dev/null
+++ b/experimental/Debugger/SkDebugDumper.cpp
@@ -0,0 +1,148 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkDebugDumper.h"
+#include "SkString.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+#include "SkPathEffect.h"
+#include "SkXfermode.h"
+#include "SkColorFilter.h"
+#include "SkPathEffect.h"
+#include "SkMaskFilter.h"
+#include "DebuggerViews.h"
+
+SkDebugDumper::SkDebugDumper(SkEventSinkID cID, SkEventSinkID clID,
+                             SkEventSinkID ipID) {
+    fContentID = cID;
+    fCommandsID = clID;
+    fStateID = ipID;
+    fInit = false;
+    fDisabled = false;
+    fCount = 0;
+}
+
+static void appendPtr(SkString* str, const void* ptr, const char name[]) {
+    if (ptr) {
+        str->appendf("%s: %p\t", name, ptr);
+    }
+}
+
+static void appendFlattenable(SkString* str, const SkFlattenable* ptr,
+                              const char name[]) {
+    if (ptr) {
+        str->appendf("%s: %p\n", name, ptr);
+    }
+}
+
+static SkString dumpMatrix(SkDumpCanvas* canvas) {
+    SkString str;
+    SkMatrix m = canvas->getTotalMatrix();
+    str.append("Matrix:");
+    str.appendf("Translate (%0.4g, %0.4g) ",
+                 SkScalarToFloat(m.get(SkMatrix::kMTransX)),
+                 SkScalarToFloat(m.get(SkMatrix::kMTransY)));
+    str.appendf("Scale (%0.4g, %0.4g) ",
+                 SkScalarToFloat(m.get(SkMatrix::kMScaleX)),
+                 SkScalarToFloat(m.get(SkMatrix::kMScaleY)));
+    str.appendf("Skew (%0.4g, %0.4g) ",
+                 SkScalarToFloat(m.get(SkMatrix::kMSkewX)),
+                 SkScalarToFloat(m.get(SkMatrix::kMSkewY)));
+    str.appendf("Perspective (%0.4g, %0.4g, %0.4g) ",
+                 SkScalarToFloat(SkPerspToScalar(m.get(SkMatrix::kMPersp0))),
+                 SkScalarToFloat(SkPerspToScalar(m.get(SkMatrix::kMPersp1))),
+                 SkScalarToFloat(SkPerspToScalar(m.get(SkMatrix::kMPersp2))));
+    return str;
+}
+
+
+static const int maxPts = 50;
+static SkString dumpClip(SkDumpCanvas* canvas) {
+    SkString str;
+    SkPath p;
+    if (canvas->getTotalClip().getBoundaryPath(&p)) {
+        SkPoint pts[maxPts];
+        int numPts = p.getPoints(pts, maxPts);
+
+        str.append("Clip: [ ");
+        for (int i = 0; i < numPts; ++i) {
+            str.appendf("(%0.4g, %0.4g)", pts[i].x(), pts[i].y());
+            if (i < numPts-1)
+                str.append(" , ");
+        }
+        str.append(" ]");
+    }
+    return str;
+}
+
+static const char* gPaintFlags[] = {
+    "AntiAliasing",
+    "Bitmap Filtering",
+    "Dithering",
+    "Underline Text",
+    "Strike-Through Text",
+    "Fake Bold Text",
+    "Linear Text",
+    "Subpixel Positioned Text",
+    "Device Kerning Text",
+    "LCD/Subpixel Glyph Rendering",
+    "Embedded Bitmap Text",
+    "Freetype Autohinting",
+    "ALL"
+};
+
+
+static SkString dumpPaint(SkDumpCanvas* canvas, const SkPaint* p,
+                      SkDumpCanvas::Verb verb) {
+    SkString str;
+    str.appendf("Color: #%08X\n", p->getColor());
+    str.appendf("Flags: %s\n", gPaintFlags[p->getFlags()]);
+    appendFlattenable(&str, p->getShader(), "shader");
+    appendFlattenable(&str, p->getXfermode(), "xfermode");
+    appendFlattenable(&str, p->getPathEffect(), "pathEffect");
+    appendFlattenable(&str, p->getMaskFilter(), "maskFilter");
+    appendFlattenable(&str, p->getPathEffect(), "pathEffect");
+    appendFlattenable(&str, p->getColorFilter(), "filter");
+
+    if (SkDumpCanvas::kDrawText_Verb == verb) {
+        str.appendf("Text Size:%0.4g\n", SkScalarToFloat(p->getTextSize()));
+        appendPtr(&str, p->getTypeface(), "typeface");
+    }
+
+    return str;
+}
+
+void SkDebugDumper::dump(SkDumpCanvas* canvas, SkDumpCanvas::Verb verb,
+                          const char str[], const SkPaint* p) {
+    if (!fDisabled) {
+        SkString msg, tab;
+
+        const int level = canvas->getNestLevel() + canvas->getSaveCount() - 1;
+        SkASSERT(level >= 0);
+        for (int i = 0; i < level; i++) {
+            tab.append("| ");
+        }
+
+        msg.appendf("%03d: %s%s\n", fCount, tab.c_str(), str);
+        ++fCount;
+        if (!fInit) {
+            SkEvent* cmd = new SkEvent(SKDEBUGGER_COMMANDTYPE, fCommandsID);
+            cmd->setString(SKDEBUGGER_ATOM, msg);
+            cmd->postDelay(100);
+        }
+        else {
+            SkEvent* state = new SkEvent(SKDEBUGGER_STATETYPE, fStateID);
+            state->setString(SKDEBUGGER_MATRIX, dumpMatrix(canvas));
+            state->setString(SKDEBUGGER_CLIP, dumpClip(canvas));
+            if (p) {
+                state->setString(SKDEBUGGER_PAINTINFO, dumpPaint(canvas, p, verb));
+                state->getMetaData().setPtr(SKDEBUGGER_PAINT, (void*)p, PaintProc);
+            }
+            state->post();
+        }
+    }
+}
diff --git a/experimental/Debugger/SkDebugDumper.h b/experimental/Debugger/SkDebugDumper.h
new file mode 100644
index 0000000..e7d6956
--- /dev/null
+++ b/experimental/Debugger/SkDebugDumper.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 SkDebugDumper_DEFINED
+#define SkDebugDumper_DEFINED
+#include "SkDumpCanvas.h"
+#include "SkEvent.h"
+
+/** Formats the draw commands, and send them to a function-pointer provided
+ by the caller.
+ */
+class SkDebugDumper : public SkDumpCanvas::Dumper {
+public:
+    SkDebugDumper(SkEventSinkID cID, SkEventSinkID clID, SkEventSinkID ipID);
+    // override from baseclass that does the formatting, and in turn calls
+    // the function pointer that was passed to the constructor
+    virtual void dump(SkDumpCanvas*, SkDumpCanvas::Verb, const char str[],
+                      const SkPaint*);
+
+    void load() { fInit = true; };
+    void unload() { fInit = false; fCount = 0;};
+    void disable() { fDisabled = true; };
+    void enable() { fDisabled = false; };
+private:
+    int             fCount;
+    bool            fInit;
+    bool            fDisabled;
+    SkEventSinkID   fContentID;
+    SkEventSinkID   fCommandsID;
+    SkEventSinkID   fStateID;
+
+    typedef SkDumpCanvas::Dumper INHERITED;
+};
+#endif
diff --git a/experimental/DrawingBoard/SampleDrawingClient.cpp b/experimental/DrawingBoard/SampleDrawingClient.cpp
new file mode 100644
index 0000000..fbefbc8
--- /dev/null
+++ b/experimental/DrawingBoard/SampleDrawingClient.cpp
@@ -0,0 +1,279 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGPipe.h"
+#include "SkSockets.h"
+#include "SkNetPipeController.h"
+#include "SkCornerPathEffect.h"
+#include "SkColorPalette.h"
+#include "SkOSMenu.h"
+
+
+/**
+ * Drawing Client
+ *
+ * A drawing client that allows a user to perform simple brush stokes with
+ * a selected color and brush size. The drawing client communicates with a
+ * drawing server to send/receive data to/from other clients connected to the
+ * same server. The drawing client stores data in fData and fBuffer depending on
+ * the data type. Append type means that the drawing data is a completed stroke
+ * and Replace type means that the drawing data is in progress and will be
+ * replaced by subsequent data. fData and fBuffer are read by a pipe reader and
+ * reproduce the drawing. When the client is in a normal state, the data stored
+ * on the client and the server should be identical.
+ * The drawing client is also able to switch between vector and bitmap drawing.
+ * The drawing client also renders the latest drawing stroke locally in order to
+ * produce better reponses. This can be disabled by calling
+ * controller.disablePlayBack(), which will introduce a lag between the input
+ * and the drawing.
+ * Note: in order to keep up with the drawing data, the client will try to read
+ * a few times each frame in case more than one frame worth of data has been
+ * received and render them together. This behavior can be adjusted by tweaking
+ * MAX_READ_PER_FRAME or disabled by turning fSync to false
+ */
+
+#define MAX_READ_PER_FRAME 5
+
+class DrawingClientView : public SampleView {
+public:
+    DrawingClientView() {
+        fSocket = NULL;
+        fTotalBytesRead = 0;
+        fPalette = new SkColorPalette;
+        fPalette->setSize(100, 300);
+        fPalette->setVisibleP(true);
+        this->attachChildToFront(fPalette);
+        fPalette->unref();
+        fBrushSize = SkFloatToScalar(2.5);
+        fAA = false;
+        fPaletteVisible = true;
+        fSync = true;
+        fVector = true;
+    }
+    ~DrawingClientView() {
+        if (fSocket) {
+            delete fSocket;
+        }
+        fData.reset();
+        fBuffer.reset();
+    }
+
+    virtual void requestMenu(SkOSMenu* menu) {
+        menu->setTitle("Drawing Client");
+        menu->appendTextField("Server IP", "Server IP", this->getSinkID(),
+                              "IP address or hostname");
+        menu->appendSwitch("Vector", "Vector", this->getSinkID(), fVector);
+        menu->appendSlider("Brush Size", "Brush Size", this->getSinkID(), 1.0,
+                           100.0, fBrushSize);
+        menu->appendSwitch("Anti-Aliasing", "AA", this->getSinkID(), fAA);
+        menu->appendSwitch("Show Color Palette", "Palette", this->getSinkID(),
+                           fPaletteVisible);
+        menu->appendSwitch("Sync", "Sync", this->getSinkID(), fSync);
+        menu->appendAction("Clear", this->getSinkID());
+    }
+
+protected:
+
+    static void readData(int cid, const void* data, size_t size,
+                         SkSocket::DataType type, void* context) {
+        DrawingClientView* view = (DrawingClientView*)context;
+        view->onRead(cid, data, size, type);
+    }
+
+    void onRead(int cid, const void* data, size_t size, SkSocket::DataType type) {
+        if (size > 0) {
+            fBuffer.reset();
+            if (type == SkSocket::kPipeReplace_type)
+                fBuffer.append(size, (const char*)data);
+            else if (type == SkSocket::kPipeAppend_type)
+                fData.append(size, (const char*)data);
+            else {
+                //other types of data
+            }
+        }
+    }
+
+    bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Drawing Client");
+            return true;
+        }
+
+        return this->INHERITED::onQuery(evt);
+    }
+
+    bool onEvent(const SkEvent& evt) {;
+        if (SkOSMenu::FindSliderValue(evt, "Brush Size", &fBrushSize))
+            return true;
+
+        SkString s;
+        if (SkOSMenu::FindText(evt, "Server IP", &s)) {
+            if (NULL != fSocket) {
+                delete fSocket;
+            }
+            fSocket = new SkTCPClient(s.c_str(), 40000);
+            fSocket->connectToServer();
+            fSocket->suspendWrite();
+            SkDebugf("Connecting to %s\n", s.c_str());
+            fData.reset();
+            fBuffer.reset();
+            this->inval(NULL);
+            return true;
+        }
+        if (SkOSMenu::FindSwitchState(evt, "AA", &fAA) ||
+            SkOSMenu::FindSwitchState(evt, "Sync", &fSync))
+            return true;
+        if (SkOSMenu::FindSwitchState(evt, "Vector", &fVector)) {
+            this->clearBitmap();
+            return true;
+        }
+        if (SkOSMenu::FindAction(evt, "Clear")) {
+            this->clear();
+            return true;
+        }
+        if (SkOSMenu::FindSwitchState(evt, "Palette", &fPaletteVisible)) {
+            fPalette->setVisibleP(fPaletteVisible);
+            return true;
+        }
+        return this->INHERITED::onEvent(evt);
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        return new Click(this);
+    }
+
+    virtual bool onClick(SkView::Click* click) {
+        switch (click->fState) {
+            case SkView::Click::kDown_State:
+                fCurrLine.moveTo(click->fCurr);
+                fType = SkSocket::kPipeReplace_type;
+                if (fSocket)
+                    fSocket->resumeWrite();
+                break;
+            case SkView::Click::kMoved_State:
+                fCurrLine.lineTo(click->fCurr);
+                break;
+            case SkView::Click::kUp_State:
+                fType = SkSocket::kPipeAppend_type;
+                break;
+            default:
+                break;
+        }
+        return true;
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        if (fSocket) {
+            if (fSocket->isConnected()) {
+                if (fSync) {
+                    int count = 0;
+                    while (fSocket->readPacket(readData, this) > 0 &&
+                           count < MAX_READ_PER_FRAME)
+                        ++count;
+                }
+                else
+                    fSocket->readPacket(readData, this);
+            }
+            else
+                fSocket->connectToServer();
+        }
+        size_t bytesRead = 0;
+        SkGPipeReader::Status status;
+        SkCanvas bufferCanvas(fBase);
+        SkCanvas* tempCanvas;
+        while (fTotalBytesRead < fData.count()) {
+            if (fVector)
+                tempCanvas = canvas;
+            else
+                tempCanvas = &bufferCanvas;
+            SkGPipeReader reader(tempCanvas);
+            status = reader.playback(fData.begin() + fTotalBytesRead,
+                                     fData.count() - fTotalBytesRead,
+                                     &bytesRead);
+            SkASSERT(SkGPipeReader::kError_Status != status);
+            fTotalBytesRead += bytesRead;
+        }
+        if (fVector)
+            fTotalBytesRead = 0;
+        else
+            canvas->drawBitmap(fBase, 0, 0, NULL);
+
+        size_t totalBytesRead = 0;
+        while (totalBytesRead < fBuffer.count()) {
+            SkGPipeReader reader(canvas);
+            status = reader.playback(fBuffer.begin() + totalBytesRead,
+                                     fBuffer.count() - totalBytesRead,
+                                     &bytesRead);
+            SkASSERT(SkGPipeReader::kError_Status != status);
+            totalBytesRead += bytesRead;
+        }
+
+        SkNetPipeController controller(canvas);
+        SkGPipeWriter writer;
+        SkCanvas* writerCanvas = writer.startRecording(&controller,
+                                                       SkGPipeWriter::kCrossProcess_Flag);
+
+        //controller.disablePlayback();
+        SkPaint p;
+        p.setColor(fPalette->getColor());
+        p.setStyle(SkPaint::kStroke_Style);
+        p.setStrokeWidth(fBrushSize);
+        p.setStrokeCap(SkPaint::kRound_Cap);
+        p.setStrokeJoin(SkPaint::kRound_Join);
+        p.setAntiAlias(fAA);
+        p.setPathEffect(new SkCornerPathEffect(55))->unref();
+        writerCanvas->drawPath(fCurrLine, p);
+        writer.endRecording();
+
+        controller.writeToSocket(fSocket, fType);
+        if (fType == SkSocket::kPipeAppend_type && fSocket) {
+            fSocket->suspendWrite();
+            fCurrLine.reset();
+        }
+
+        this->inval(NULL);
+    }
+
+    virtual void onSizeChange() {
+        this->INHERITED::onSizeChange();
+        fPalette->setLoc(this->width()-100, 0);
+        fBase.setConfig(SkBitmap::kARGB_8888_Config, this->width(), this->height());
+        fBase.allocPixels(NULL);
+        this->clearBitmap();
+    }
+
+private:
+    void clear() {
+        fData.reset();
+        fBuffer.reset();
+        fCurrLine.reset();
+        fTotalBytesRead = 0;
+        this->clearBitmap();
+    }
+    void clearBitmap() {
+        fTotalBytesRead = 0;
+        fBase.eraseColor(fBGColor);
+    }
+    SkTDArray<char>     fData;
+    SkTDArray<char>     fBuffer;
+    SkBitmap            fBase;
+    SkPath              fCurrLine;
+    SkTCPClient*        fSocket;
+    SkSocket::DataType  fType;
+    SkColorPalette*     fPalette;
+    bool                fPaletteVisible;
+    size_t              fTotalBytesRead;
+    SkScalar            fBrushSize;
+    bool                fAA;
+    bool                fSync;
+    bool                fVector;
+
+    typedef SampleView INHERITED;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DrawingClientView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/experimental/DrawingBoard/SampleDrawingServer.cpp b/experimental/DrawingBoard/SampleDrawingServer.cpp
new file mode 100644
index 0000000..fa059fa
--- /dev/null
+++ b/experimental/DrawingBoard/SampleDrawingServer.cpp
@@ -0,0 +1,222 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGPipe.h"
+#include "SkSockets.h"
+#include "SkNetPipeController.h"
+#include "SkCornerPathEffect.h"
+#include "SkOSMenu.h"
+#include <map>
+
+/**
+ * Drawing Server
+ *
+ * This simple drawing server can accept connections from multiple drawing
+ * clients simultaneously. It accumulates drawing data from each client each
+ * frame, stores it in the appropriate place, and then broadcasts incremental
+ * changes back to all the clients. Each logical packet, meaning one brush
+ * stoke in this case can be of two types, append and replace. Append types are
+ * completed strokes ready to be stored in the fData queue and will no longer be
+ * modified. Replace types are drawing operations that are still in progress on
+ * the client side, so they are appended to fBuffer. The location and size of
+ * the buffered data for each client is stored in a map and updated properly.
+ * Each time a new replace drawing call is received from a client, its previous
+ * buffered data is discarded.
+ * Since the Server keeps all the complete drawing data and the latest buffered
+ * data, it's able to switch between vector and bitmap drawing
+ */
+
+class DrawingServerView : public SampleView {
+public:
+    DrawingServerView(){
+        fServer = new SkTCPServer(40000);
+        fServer->suspendWrite();
+        fTotalBytesRead = fTotalBytesWritten = 0;
+        fVector = true;
+    }
+    ~DrawingServerView() {
+        delete fServer;
+        fData.reset();
+        fBuffer.reset();
+        fClientMap.clear();
+    }
+
+    virtual void requestMenu(SkOSMenu* menu) {
+        menu->setTitle("Drawing Server");
+        menu->appendAction("Clear", this->getSinkID());
+        menu->appendSwitch("Vector", "Vector", this->getSinkID(), fVector);
+    }
+
+protected:
+    static void readData(int cid, const void* data, size_t size,
+                         SkSocket::DataType type, void* context) {
+        DrawingServerView* view = (DrawingServerView*)context;
+        view->onRead(cid, data, size, type);
+    }
+
+    void onRead(int cid, const void* data, size_t size, SkSocket::DataType type) {
+        if (NULL == data && size <= 0)
+            return;
+
+        ClientState* cs;
+        std::map<int, ClientState*>::iterator it = fClientMap.find(cid);
+        if (it == fClientMap.end()) { //New client
+            cs = new ClientState;
+            cs->bufferBase = 0;
+            cs->bufferSize = 0;
+            fClientMap[cid] = cs;
+        }
+        else {
+            cs = it->second;
+        }
+
+        if (type == SkSocket::kPipeReplace_type) {
+            fBuffer.remove(cs->bufferBase, cs->bufferSize);
+
+            for (it = fClientMap.begin(); it != fClientMap.end(); ++it) {
+                if (cid == it->first)
+                    continue;
+                else {
+                    if (it->second->bufferBase > cs->bufferBase) {
+                        it->second->bufferBase -= cs->bufferSize;
+                        SkASSERT(it->second->bufferBase >= 0);
+                    }
+                }
+            }
+
+            cs->bufferBase = fBuffer.count();
+            cs->bufferSize = size;
+            fBuffer.append(size, (const char*)data);
+        }
+        else if (type == SkSocket::kPipeAppend_type) {
+            fData.append(size, (const char*)data);
+            fServer->resumeWrite();
+            fServer->writePacket(fData.begin() + fTotalBytesWritten,
+                                 fData.count() - fTotalBytesWritten,
+                                 SkSocket::kPipeAppend_type);
+            fTotalBytesWritten = fData.count();
+            fServer->suspendWrite();
+        }
+        else {
+            //other types of data
+        }
+    }
+
+    bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Drawing Server");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    bool onEvent(const SkEvent& evt) {
+        if (SkOSMenu::FindAction(evt, "Clear")) {
+            this->clear();
+            return true;
+        }
+        if (SkOSMenu::FindSwitchState(evt, "Vector", &fVector)) {
+            this->clearBitmap();
+            return true;
+        }
+        return this->INHERITED::onEvent(evt);
+    }
+
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        if (fCurrMatrix != canvas->getTotalMatrix()) {
+            fTotalBytesRead = 0;
+            fCurrMatrix = canvas->getTotalMatrix();
+        }
+
+        fServer->acceptConnections();
+        if (fServer->readPacket(readData, this) > 0) {
+            fServer->resumeWrite();
+        }
+        else {
+            fServer->suspendWrite();
+        }
+
+        size_t bytesRead;
+        SkGPipeReader::Status stat;
+        SkCanvas bufferCanvas(fBase);
+        SkCanvas* tempCanvas;
+        while (fTotalBytesRead < fData.count()) {
+            if (fVector) {
+                tempCanvas = canvas;
+            } else {
+                tempCanvas = &bufferCanvas;
+            }
+            SkGPipeReader reader(tempCanvas);
+            stat = reader.playback(fData.begin() + fTotalBytesRead,
+                                   fData.count() - fTotalBytesRead,
+                                   &bytesRead);
+            SkASSERT(SkGPipeReader::kError_Status != stat);
+            fTotalBytesRead += bytesRead;
+        }
+        if (fVector) {
+            fTotalBytesRead = 0;
+        } else {
+            canvas->drawBitmap(fBase, 0, 0, NULL);
+        }
+
+        size_t totalBytesRead = 0;
+        while (totalBytesRead < fBuffer.count()) {
+            SkGPipeReader reader(canvas);
+            stat = reader.playback(fBuffer.begin() + totalBytesRead,
+                                   fBuffer.count() - totalBytesRead,
+                                   &bytesRead);
+            SkASSERT(SkGPipeReader::kError_Status != stat);
+            totalBytesRead += bytesRead;
+        }
+
+        fServer->writePacket(fBuffer.begin(), fBuffer.count(),
+                             SkSocket::kPipeReplace_type);
+
+        this->inval(NULL);
+    }
+
+    virtual void onSizeChange() {
+        this->INHERITED::onSizeChange();
+        fBase.setConfig(SkBitmap::kARGB_8888_Config,
+                        this->width(),
+                        this->height());
+        fBase.allocPixels(NULL);
+        this->clearBitmap();
+    }
+
+private:
+    void clear() {
+        fData.reset();
+        fBuffer.reset();
+        fTotalBytesRead = fTotalBytesWritten = 0;
+        fClientMap.clear();
+        this->clearBitmap();
+    }
+    void clearBitmap() {
+        fTotalBytesRead = 0;
+        fBase.eraseColor(fBGColor);
+    }
+
+    struct ClientState {
+        int bufferBase;
+        int bufferSize;
+    };
+
+    std::map<int, ClientState*> fClientMap;
+    SkTDArray<char>             fData;
+    SkTDArray<char>             fBuffer;
+    size_t                      fTotalBytesRead;
+    size_t                      fTotalBytesWritten;
+    SkMatrix                    fCurrMatrix;
+    SkBitmap                    fBase;
+    bool                        fVector;
+    SkTCPServer*                fServer;
+    typedef SampleView INHERITED;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new DrawingServerView; }
+static SkViewRegister reg(MyFactory);
diff --git a/experimental/DrawingBoard/SkColorPalette.cpp b/experimental/DrawingBoard/SkColorPalette.cpp
new file mode 100644
index 0000000..566c134
--- /dev/null
+++ b/experimental/DrawingBoard/SkColorPalette.cpp
@@ -0,0 +1,196 @@
+
+/*
+ * Copyright 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"
+#include "SkPaint.h"
+#include "SkGradientShader.h"
+#include "SkColorPalette.h"
+
+SkColorPalette::SkColorPalette() {
+    fSlotRect = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(20));
+    fGradientRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
+    fSelected = 0;
+    fCurrColor = 0xFF000000;
+
+    fColors[0] = SK_ColorWHITE;
+    fColors[1] = SK_ColorBLACK;
+    fColors[2] = SK_ColorRED;
+    fColors[3] = SK_ColorGREEN;
+    fColors[4] = SK_ColorBLUE;
+}
+
+bool SkColorPalette::onEvent(const SkEvent& evt) {
+    return this->INHERITED::onEvent(evt);
+}
+
+void SkColorPalette::onDraw(SkCanvas* canvas) {
+    canvas->drawColor(SK_ColorWHITE);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    canvas->translate(PalettePadding, PalettePadding);
+
+    for (int i = 0; i < PaletteSlots; ++i) {
+        if (fSelected == i) {
+            paint.setStrokeWidth(SkIntToScalar(3));
+        }
+        else {
+            paint.setStrokeWidth(1);
+        }
+
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(SK_ColorBLACK);
+        canvas->drawRect(fSlotRect, paint);
+        paint.setStyle(SkPaint::kFill_Style);
+        paint.setColor(fColors[i]);
+        canvas->drawRect(fSlotRect, paint);
+        canvas->translate(0, fSlotRect.height() + PalettePadding);
+    }
+    paint.setStrokeWidth(0);
+    canvas->translate(0, PalettePadding);
+    SkPoint p = SkPoint::Make(0,0);
+    SkPoint q = SkPoint::Make(this->width(), 0);
+    SkPoint pts[] = {p, q};
+
+    SkColor colors[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorGREEN,
+        SK_ColorCYAN, SK_ColorBLUE, SK_ColorMAGENTA,SK_ColorRED};
+    SkScalar colorPositions[] = { 0, 0.2, 0.4, 0.5, 0.6, 0.8, 1.0};
+
+
+    SkShader* shader1 = SkGradientShader::CreateLinear(pts, colors, colorPositions,7,
+                                                       SkShader::kMirror_TileMode);
+    paint.setShader(shader1)->unref();
+
+    canvas->drawRect(fGradientRect, paint);
+
+    //this->INHERITED::onDraw(canvas);
+}
+
+SkView::Click* SkColorPalette::onFindClickHandler(SkScalar x, SkScalar y) {
+    return new Click(this);
+}
+
+bool SkColorPalette::onClick(SkView::Click* click) {
+    SkPoint curr = click->fCurr;
+    //SkDebugf("click %f %f \n", curr.fX, curr.fY);
+    int selected = selectSlot(curr);
+    if (selected >= 0) {
+        switch (click->fState) {
+            case SkView::Click::kDown_State:
+            case SkView::Click::kMoved_State:
+            case SkView::Click::kUp_State:
+                fSelected = selected;
+                fCurrColor = fColors[fSelected];
+                break;
+            default:
+                break;
+        }
+        return true;
+    }
+    else{
+        //account for padding
+        curr.fX -= PalettePadding;
+        curr.fY -= 2 * PalettePadding + (fSlotRect.height() + PalettePadding) * PaletteSlots;
+        if (curr.fX < 0 || curr.fX > fGradientRect.width() ||
+            curr.fY < 0 || curr.fY > fGradientRect.height()) {
+            return false;
+        }
+        else {
+            switch (click->fState) {
+                case SkView::Click::kDown_State:
+                case SkView::Click::kMoved_State:
+                case SkView::Click::kUp_State:
+                    fColors[fSelected] = selectColorFromGradient(curr);
+                    fCurrColor = fColors[fSelected];
+                    break;
+                default:
+                    break;
+            }
+            return true;
+        }
+    }
+}
+
+void SkColorPalette::onSizeChange() {
+    fGradientRect = SkRect::MakeWH(this->width() - 2*PalettePadding,
+                                   this->width() - 2*PalettePadding);
+    this->INHERITED::onSizeChange();
+}
+
+int SkColorPalette::selectSlot(SkPoint& cursorPosition) {
+    //account for padding
+    cursorPosition.fX -= PalettePadding;
+    cursorPosition.fY -= PalettePadding;
+
+    if (cursorPosition.fX < 0 || cursorPosition.fX > fSlotRect.width() ||
+        cursorPosition.fY < 0 || cursorPosition.fY > (fSlotRect.height() + PalettePadding) * PaletteSlots) {
+        return -1;
+    }
+    int index = cursorPosition.fY/(fSlotRect.height() + PalettePadding);
+    int offset = (int)cursorPosition.fY%((int)fSlotRect.height() + PalettePadding);
+    if (offset <= fSlotRect.height()) {
+        return index;
+    }
+    else {
+        return -1;
+    }
+}
+
+SkColor SkColorPalette::selectColorFromGradient(SkPoint& cursorPosition) {
+    float h = cursorPosition.fX/fGradientRect.width();
+    float s = 1.0 - cursorPosition.fY/fGradientRect.height();
+    float v = 1.0;
+    float _h,r,g,b;
+    float _1, _2, _3;
+    int _i;
+
+    _h = h * 6;
+    _i = (int)_h;
+    _1 = v * (1 - s);
+    _2 = v * (1 - s * (_h - _i));
+    _3 = v * (1 - s * (1 - (_h - _i)));
+
+    if (_i == 0) {
+        r = v;
+        g = _3;
+        b = _1;
+    }
+    else if (_i == 1) {
+        r = _2;
+        g = v;
+        b = _1;
+    }
+    else if (_i == 2) {
+        r = _1;
+        g = v;
+        b = _3;
+    }
+    else if (_i == 3) {
+        r = _1;
+        g = _2;
+        b = v;
+    }
+    else if (_i == 4) {
+        r = _3;
+        g = _1;
+        b = v;
+    }
+    else {
+        r = v;
+        g = _1;
+        b = _2;
+    };
+
+    SkColor retval = 0xFF000000;
+    retval += ((int)(r * 255) << 16);
+    retval += ((int)(g * 255) << 8);
+    retval += (int)(b * 255);
+    return retval;
+}
+
diff --git a/experimental/DrawingBoard/SkColorPalette.h b/experimental/DrawingBoard/SkColorPalette.h
new file mode 100644
index 0000000..b8e4c9f
--- /dev/null
+++ b/experimental/DrawingBoard/SkColorPalette.h
@@ -0,0 +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.
+ */
+#ifndef SkColorPalette_DEFINED
+#define SkColorPalette_DEFINED
+
+#define PaletteSlots 5
+#define PalettePadding 5
+class SkColorPalette : public SkView {
+public:
+    SkColorPalette();
+    SkColor getColor() { return fCurrColor; }
+protected:
+    virtual bool onEvent(const SkEvent& evt);
+    virtual void onDraw(SkCanvas* canvas);
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y);
+    virtual bool onClick(SkView::Click* click);
+    virtual void onSizeChange();
+private:
+    int selectSlot(SkPoint& cursorPosition);
+    SkColor selectColorFromGradient(SkPoint& cursorPosition);
+    int     fSelected;
+    SkRect  fGradientRect;
+    SkRect  fSlotRect;
+    SkColor fCurrColor;
+    SkColor fColors[PaletteSlots];
+    typedef SkView INHERITED;
+};
+
+#endif
+
diff --git a/experimental/DrawingBoard/SkNetPipeController.cpp b/experimental/DrawingBoard/SkNetPipeController.cpp
new file mode 100644
index 0000000..a61ca64
--- /dev/null
+++ b/experimental/DrawingBoard/SkNetPipeController.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkNetPipeController.h"
+
+SkNetPipeController::SkNetPipeController(SkCanvas* target) : fReader(target) {
+    fBlock = NULL;
+    fBlockSize = fBytesWritten = 0;
+    fPlayback = true;
+    fStatus = SkGPipeReader::kDone_Status;
+    fTotalWritten = 0;
+    fAtomsWritten = 0;
+}
+SkNetPipeController::~SkNetPipeController() {
+    sk_free(fBlock);
+}
+
+int SkNetPipeController::writeToSocket(SkSocket* sockfd, SkSocket::DataType type) {
+    if (NULL != sockfd && fTotalWritten > 4)
+        return sockfd->writePacket(fBlock, fBytesWritten, type);
+    else
+        return -1;
+}
+
+void* SkNetPipeController::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 SkNetPipeController::notifyWritten(size_t bytes) {
+    SkASSERT(fBytesWritten + bytes <= fBlockSize);
+
+    if (fPlayback) {
+        fStatus = fReader.playback((const char*)fBlock + fBytesWritten, bytes);
+    }
+
+    SkASSERT(SkGPipeReader::kError_Status != fStatus);
+    fBytesWritten += bytes;
+    fTotalWritten += bytes;
+
+    fAtomsWritten += 1;
+}
+
diff --git a/experimental/DrawingBoard/SkNetPipeController.h b/experimental/DrawingBoard/SkNetPipeController.h
new file mode 100644
index 0000000..081239b
--- /dev/null
+++ b/experimental/DrawingBoard/SkNetPipeController.h
@@ -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.
+ */
+#ifndef SkNetPipeController_DEFINED
+#define SkNetPipeController_DEFINED
+#include "SkTypes.h"
+#include "SkCanvas.h"
+#include "SkGPipe.h"
+#include "SkSockets.h"
+class SkNetPipeController : public SkGPipeController {
+public:
+    SkNetPipeController(SkCanvas* target);
+    ~SkNetPipeController();
+
+    virtual void* requestBlock(size_t minRequest, size_t* actual);
+    virtual void notifyWritten(size_t bytes);
+
+    int writeToSocket(SkSocket* sockfd, SkSocket::DataType type);
+    void enablePlayback() { fPlayback = true; }
+    void disablePlayback() { fPlayback = false; }
+
+private:
+    SkGPipeReader   fReader;
+    bool            fPlayback;
+    void*           fBlock;
+    size_t          fBlockSize;
+    size_t          fBytesWritten;
+    int             fAtomsWritten;
+    size_t          fTotalWritten;
+
+    SkGPipeReader::Status   fStatus;
+};
+#endif
+
diff --git a/experimental/FileReaderApp/English.lproj/InfoPlist.strings b/experimental/FileReaderApp/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/experimental/FileReaderApp/English.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/experimental/FileReaderApp/English.lproj/MainMenu.xib b/experimental/FileReaderApp/English.lproj/MainMenu.xib
new file mode 100644
index 0000000..53e910b
--- /dev/null
+++ b/experimental/FileReaderApp/English.lproj/MainMenu.xib
@@ -0,0 +1,4272 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+	<data>
+		<int key="IBDocument.SystemTarget">1060</int>
+		<string key="IBDocument.SystemVersion">10J3250</string>
+		<string key="IBDocument.InterfaceBuilderVersion">851</string>
+		<string key="IBDocument.AppKitVersion">1038.35</string>
+		<string key="IBDocument.HIToolboxVersion">461.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+			<string key="NS.object.0">851</string>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+		</object>
+		<object class="NSArray" key="IBDocument.PluginDependencies">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+			<integer value="1" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSCustomObject" id="1021">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSCustomObject" id="1014">
+				<string key="NSClassName">FirstResponder</string>
+			</object>
+			<object class="NSCustomObject" id="1050">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSMenu" id="649796088">
+				<string key="NSTitle">AMainMenu</string>
+				<object class="NSMutableArray" key="NSMenuItems">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="NSMenuItem" id="694149608">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">FileReaderApp</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<object class="NSCustomResource" key="NSOnImage" id="35465992">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuCheckmark</string>
+						</object>
+						<object class="NSCustomResource" key="NSMixedImage" id="502551668">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuMixedState</string>
+						</object>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="110575045">
+							<string key="NSTitle">FileReaderApp</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="238522557">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">About FileReaderApp</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="304266470">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="609285721">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Preferences…</string>
+									<string key="NSKeyEquiv">,</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="481834944">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1046388886">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Services</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="752062318">
+										<string key="NSTitle">Services</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+										</object>
+										<string key="NSName">_NSServicesMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="646227648">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="755159360">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Hide FileReaderApp</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="342932134">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Hide Others</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="908899353">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Show All</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1056857174">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="632727374">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Quit FileReaderApp</string>
+									<string key="NSKeyEquiv">q</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+							<string key="NSName">_NSAppleMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="379814623">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">File</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="720053764">
+							<string key="NSTitle">File</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="705341025">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">New</string>
+									<string key="NSKeyEquiv">n</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="722745758">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Open…</string>
+									<string key="NSKeyEquiv">o</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1025936716">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Open Recent</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="1065607017">
+										<string key="NSTitle">Open Recent</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="759406840">
+												<reference key="NSMenu" ref="1065607017"/>
+												<string key="NSTitle">Clear Menu</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+										<string key="NSName">_NSRecentDocumentsMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="425164168">
+									<reference key="NSMenu" ref="720053764"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="776162233">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Close</string>
+									<string key="NSKeyEquiv">w</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1023925487">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Save</string>
+									<string key="NSKeyEquiv">s</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="117038363">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Save As…</string>
+									<string key="NSKeyEquiv">S</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="579971712">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Revert to Saved</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1010469920">
+									<reference key="NSMenu" ref="720053764"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="294629803">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Page Setup...</string>
+									<string key="NSKeyEquiv">P</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSToolTip"/>
+								</object>
+								<object class="NSMenuItem" id="49223823">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Print…</string>
+									<string key="NSKeyEquiv">p</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="952259628">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Edit</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="789758025">
+							<string key="NSTitle">Edit</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="1058277027">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Undo</string>
+									<string key="NSKeyEquiv">z</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="790794224">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Redo</string>
+									<string key="NSKeyEquiv">Z</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1040322652">
+									<reference key="NSMenu" ref="789758025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="296257095">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Cut</string>
+									<string key="NSKeyEquiv">x</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="860595796">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Copy</string>
+									<string key="NSKeyEquiv">c</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="29853731">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Paste</string>
+									<string key="NSKeyEquiv">v</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="82994268">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Paste and Match Style</string>
+									<string key="NSKeyEquiv">V</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="437104165">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Delete</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="583158037">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Select All</string>
+									<string key="NSKeyEquiv">a</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="212016141">
+									<reference key="NSMenu" ref="789758025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="892235320">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Find</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="963351320">
+										<string key="NSTitle">Find</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="447796847">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find…</string>
+												<string key="NSKeyEquiv">f</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="326711663">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find Next</string>
+												<string key="NSKeyEquiv">g</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="270902937">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find Previous</string>
+												<string key="NSKeyEquiv">G</string>
+												<int key="NSKeyEquivModMask">1179648</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="159080638">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Use Selection for Find</string>
+												<string key="NSKeyEquiv">e</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">7</int>
+											</object>
+											<object class="NSMenuItem" id="88285865">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Jump to Selection</string>
+												<string key="NSKeyEquiv">j</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="972420730">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Spelling and Grammar</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="769623530">
+										<string key="NSTitle">Spelling and Grammar</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="679648819">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Show Spelling and Grammar</string>
+												<string key="NSKeyEquiv">:</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="96193923">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Document Now</string>
+												<string key="NSKeyEquiv">;</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="859480356">
+												<reference key="NSMenu" ref="769623530"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="948374510">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Spelling While Typing</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="967646866">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Grammar With Spelling</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="795346622">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Correct Spelling Automatically</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="507821607">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Substitutions</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="698887838">
+										<string key="NSTitle">Substitutions</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="65139061">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Show Substitutions</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="19036812">
+												<reference key="NSMenu" ref="698887838"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="605118523">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Copy/Paste</string>
+												<string key="NSKeyEquiv">f</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="197661976">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Quotes</string>
+												<string key="NSKeyEquiv">g</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="672708820">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Dashes</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="708854459">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Links</string>
+												<string key="NSKeyEquiv">G</string>
+												<int key="NSKeyEquivModMask">1179648</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="537092702">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Text Replacement</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="288088188">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Transformations</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="579392910">
+										<string key="NSTitle">Transformations</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="1060694897">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Make Upper Case</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="879586729">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Make Lower Case</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="56570060">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Capitalize</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="676164635">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Speech</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="785027613">
+										<string key="NSTitle">Speech</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="731782645">
+												<reference key="NSMenu" ref="785027613"/>
+												<string key="NSTitle">Start Speaking</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="680220178">
+												<reference key="NSMenu" ref="785027613"/>
+												<string key="NSTitle">Stop Speaking</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="302598603">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Format</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="941447902">
+							<string key="NSTitle">Format</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="792887677">
+									<reference key="NSMenu" ref="941447902"/>
+									<string key="NSTitle">Font</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="786677654">
+										<string key="NSTitle">Font</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="159677712">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Show Fonts</string>
+												<string key="NSKeyEquiv">t</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="305399458">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Bold</string>
+												<string key="NSKeyEquiv">b</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="814362025">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Italic</string>
+												<string key="NSKeyEquiv">i</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="330926929">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Underline</string>
+												<string key="NSKeyEquiv">u</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="533507878">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="158063935">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Bigger</string>
+												<string key="NSKeyEquiv">+</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="885547335">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Smaller</string>
+												<string key="NSKeyEquiv">-</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">4</int>
+											</object>
+											<object class="NSMenuItem" id="901062459">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="767671776">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Kern</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="175441468">
+													<string key="NSTitle">Kern</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="252969304">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="766922938">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Use None</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="677519740">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Tighten</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="238351151">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Loosen</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="691570813">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Ligature</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="1058217995">
+													<string key="NSTitle">Ligature</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="706297211">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="568384683">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use None</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="663508465">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use All</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="769124883">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Baseline</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="18263474">
+													<string key="NSTitle">Baseline</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="257962622">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="644725453">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Superscript</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="1037576581">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Subscript</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="941806246">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Raise</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="1045724900">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Lower</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="739652853">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="1012600125">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Show Colors</string>
+												<string key="NSKeyEquiv">C</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="214559597">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="596732606">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Copy Style</string>
+												<string key="NSKeyEquiv">c</string>
+												<int key="NSKeyEquivModMask">1572864</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="393423671">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Paste Style</string>
+												<string key="NSKeyEquiv">v</string>
+												<int key="NSKeyEquivModMask">1572864</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+										<string key="NSName">_NSFontMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="215659978">
+									<reference key="NSMenu" ref="941447902"/>
+									<string key="NSTitle">Text</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="446991534">
+										<string key="NSTitle">Text</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="875092757">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Align Left</string>
+												<string key="NSKeyEquiv">{</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="630155264">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Center</string>
+												<string key="NSKeyEquiv">|</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="945678886">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Justify</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="512868991">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Align Right</string>
+												<string key="NSKeyEquiv">}</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="163117631">
+												<reference key="NSMenu" ref="446991534"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="31516759">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Writing Direction</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="956096989">
+													<string key="NSTitle">Writing Direction</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="257099033">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<string key="NSTitle">Paragraph</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="551969625">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="249532473">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="607364498">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="508151438">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<bool key="NSIsSeparator">YES</bool>
+															<string key="NSTitle"/>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="981751889">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<string key="NSTitle">Selection</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="380031999">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="825984362">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="560145579">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="908105787">
+												<reference key="NSMenu" ref="446991534"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="644046920">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Show Ruler</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="231811626">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Copy Ruler</string>
+												<string key="NSKeyEquiv">c</string>
+												<int key="NSKeyEquivModMask">1310720</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="883618387">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Paste Ruler</string>
+												<string key="NSKeyEquiv">v</string>
+												<int key="NSKeyEquivModMask">1310720</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="586577488">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">View</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="466310130">
+							<string key="NSTitle">View</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="102151532">
+									<reference key="NSMenu" ref="466310130"/>
+									<string key="NSTitle">Show Toolbar</string>
+									<string key="NSKeyEquiv">t</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="237841660">
+									<reference key="NSMenu" ref="466310130"/>
+									<string key="NSTitle">Customize Toolbar…</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="713487014">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Window</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="835318025">
+							<string key="NSTitle">Window</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="1011231497">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Minimize</string>
+									<string key="NSKeyEquiv">m</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="575023229">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Zoom</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="299356726">
+									<reference key="NSMenu" ref="835318025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="625202149">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Bring All to Front</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+							<string key="NSName">_NSWindowsMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="448692316">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Help</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="992780483">
+							<string key="NSTitle">Help</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="105068016">
+									<reference key="NSMenu" ref="992780483"/>
+									<string key="NSTitle">FileReaderApp Help</string>
+									<string key="NSKeyEquiv">?</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+							<string key="NSName">_NSHelpMenu</string>
+						</object>
+					</object>
+				</object>
+				<string key="NSName">_NSMainMenu</string>
+			</object>
+			<object class="NSWindowTemplate" id="972006081">
+				<int key="NSWindowStyleMask">15</int>
+				<int key="NSWindowBacking">2</int>
+				<string key="NSWindowRect">{{335, 390}, {640, 480}}</string>
+				<int key="NSWTFlags">1954021376</int>
+				<string key="NSWindowTitle">FileReaderApp</string>
+				<string key="NSWindowClass">FileReaderWindow</string>
+				<nil key="NSViewClass"/>
+				<string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+				<object class="NSView" key="NSWindowView" id="439893737">
+					<nil key="NSNextResponder"/>
+					<int key="NSvFlags">256</int>
+					<object class="NSMutableArray" key="NSSubviews">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSCustomView" id="322594744">
+							<reference key="NSNextResponder" ref="439893737"/>
+							<int key="NSvFlags">274</int>
+							<string key="NSFrameSize">{640, 480}</string>
+							<reference key="NSSuperview" ref="439893737"/>
+							<string key="NSClassName">SkNSView</string>
+						</object>
+					</object>
+					<string key="NSFrameSize">{640, 480}</string>
+				</object>
+				<string key="NSScreenRect">{{0, 0}, {1920, 1178}}</string>
+				<string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+			</object>
+			<object class="NSCustomObject" id="976324537">
+				<string key="NSClassName">FileReaderAppDelegate</string>
+			</object>
+			<object class="NSCustomObject" id="755631768">
+				<string key="NSClassName">NSFontManager</string>
+			</object>
+		</object>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<object class="NSMutableArray" key="connectionRecords">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performMiniaturize:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1011231497"/>
+					</object>
+					<int key="connectionID">37</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">arrangeInFront:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="625202149"/>
+					</object>
+					<int key="connectionID">39</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">print:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="49223823"/>
+					</object>
+					<int key="connectionID">86</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">runPageLayout:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="294629803"/>
+					</object>
+					<int key="connectionID">87</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">clearRecentDocuments:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="759406840"/>
+					</object>
+					<int key="connectionID">127</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontStandardAboutPanel:</string>
+						<reference key="source" ref="1021"/>
+						<reference key="destination" ref="238522557"/>
+					</object>
+					<int key="connectionID">142</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performClose:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="776162233"/>
+					</object>
+					<int key="connectionID">193</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleContinuousSpellChecking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="948374510"/>
+					</object>
+					<int key="connectionID">222</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">undo:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1058277027"/>
+					</object>
+					<int key="connectionID">223</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copy:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="860595796"/>
+					</object>
+					<int key="connectionID">224</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">checkSpelling:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="96193923"/>
+					</object>
+					<int key="connectionID">225</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">paste:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="29853731"/>
+					</object>
+					<int key="connectionID">226</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">stopSpeaking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="680220178"/>
+					</object>
+					<int key="connectionID">227</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">cut:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="296257095"/>
+					</object>
+					<int key="connectionID">228</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">showGuessPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="679648819"/>
+					</object>
+					<int key="connectionID">230</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">redo:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="790794224"/>
+					</object>
+					<int key="connectionID">231</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">selectAll:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="583158037"/>
+					</object>
+					<int key="connectionID">232</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">startSpeaking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="731782645"/>
+					</object>
+					<int key="connectionID">233</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">delete:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="437104165"/>
+					</object>
+					<int key="connectionID">235</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performZoom:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="575023229"/>
+					</object>
+					<int key="connectionID">240</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="447796847"/>
+					</object>
+					<int key="connectionID">241</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">centerSelectionInVisibleArea:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="88285865"/>
+					</object>
+					<int key="connectionID">245</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleGrammarChecking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="967646866"/>
+					</object>
+					<int key="connectionID">347</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleSmartInsertDelete:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="605118523"/>
+					</object>
+					<int key="connectionID">355</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticQuoteSubstitution:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="197661976"/>
+					</object>
+					<int key="connectionID">356</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticLinkDetection:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="708854459"/>
+					</object>
+					<int key="connectionID">357</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">saveDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1023925487"/>
+					</object>
+					<int key="connectionID">362</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">saveDocumentAs:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="117038363"/>
+					</object>
+					<int key="connectionID">363</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">revertDocumentToSaved:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="579971712"/>
+					</object>
+					<int key="connectionID">364</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">runToolbarCustomizationPalette:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="237841660"/>
+					</object>
+					<int key="connectionID">365</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleToolbarShown:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="102151532"/>
+					</object>
+					<int key="connectionID">366</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hide:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="755159360"/>
+					</object>
+					<int key="connectionID">367</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hideOtherApplications:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="342932134"/>
+					</object>
+					<int key="connectionID">368</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">unhideAllApplications:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="908899353"/>
+					</object>
+					<int key="connectionID">370</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">newDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="705341025"/>
+					</object>
+					<int key="connectionID">373</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="722745758"/>
+					</object>
+					<int key="connectionID">374</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">addFontTrait:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="305399458"/>
+					</object>
+					<int key="connectionID">421</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">addFontTrait:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="814362025"/>
+					</object>
+					<int key="connectionID">422</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">modifyFont:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="885547335"/>
+					</object>
+					<int key="connectionID">423</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontFontPanel:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="159677712"/>
+					</object>
+					<int key="connectionID">424</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">modifyFont:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="158063935"/>
+					</object>
+					<int key="connectionID">425</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">raiseBaseline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="941806246"/>
+					</object>
+					<int key="connectionID">426</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">lowerBaseline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1045724900"/>
+					</object>
+					<int key="connectionID">427</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copyFont:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="596732606"/>
+					</object>
+					<int key="connectionID">428</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">subscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1037576581"/>
+					</object>
+					<int key="connectionID">429</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">superscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="644725453"/>
+					</object>
+					<int key="connectionID">430</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">tightenKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="677519740"/>
+					</object>
+					<int key="connectionID">431</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">underline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="330926929"/>
+					</object>
+					<int key="connectionID">432</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontColorPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1012600125"/>
+					</object>
+					<int key="connectionID">433</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useAllLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="663508465"/>
+					</object>
+					<int key="connectionID">434</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">loosenKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="238351151"/>
+					</object>
+					<int key="connectionID">435</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteFont:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="393423671"/>
+					</object>
+					<int key="connectionID">436</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">unscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="257962622"/>
+					</object>
+					<int key="connectionID">437</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useStandardKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="252969304"/>
+					</object>
+					<int key="connectionID">438</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useStandardLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="706297211"/>
+					</object>
+					<int key="connectionID">439</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">turnOffLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="568384683"/>
+					</object>
+					<int key="connectionID">440</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">turnOffKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="766922938"/>
+					</object>
+					<int key="connectionID">441</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">terminate:</string>
+						<reference key="source" ref="1050"/>
+						<reference key="destination" ref="632727374"/>
+					</object>
+					<int key="connectionID">449</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticSpellingCorrection:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="795346622"/>
+					</object>
+					<int key="connectionID">456</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontSubstitutionsPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="65139061"/>
+					</object>
+					<int key="connectionID">458</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticDashSubstitution:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="672708820"/>
+					</object>
+					<int key="connectionID">461</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticTextReplacement:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="537092702"/>
+					</object>
+					<int key="connectionID">463</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">uppercaseWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1060694897"/>
+					</object>
+					<int key="connectionID">464</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">capitalizeWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="56570060"/>
+					</object>
+					<int key="connectionID">467</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">lowercaseWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="879586729"/>
+					</object>
+					<int key="connectionID">468</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteAsPlainText:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="82994268"/>
+					</object>
+					<int key="connectionID">486</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="326711663"/>
+					</object>
+					<int key="connectionID">487</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="270902937"/>
+					</object>
+					<int key="connectionID">488</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="159080638"/>
+					</object>
+					<int key="connectionID">489</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">showHelp:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="105068016"/>
+					</object>
+					<int key="connectionID">493</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="1021"/>
+						<reference key="destination" ref="976324537"/>
+					</object>
+					<int key="connectionID">495</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignCenter:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="630155264"/>
+					</object>
+					<int key="connectionID">518</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="883618387"/>
+					</object>
+					<int key="connectionID">519</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="644046920"/>
+					</object>
+					<int key="connectionID">520</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="512868991"/>
+					</object>
+					<int key="connectionID">521</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copyRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="231811626"/>
+					</object>
+					<int key="connectionID">522</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignJustified:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="945678886"/>
+					</object>
+					<int key="connectionID">523</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="875092757"/>
+					</object>
+					<int key="connectionID">524</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionNatural:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="551969625"/>
+					</object>
+					<int key="connectionID">525</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionLeftToRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="249532473"/>
+					</object>
+					<int key="connectionID">526</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionRightToLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="607364498"/>
+					</object>
+					<int key="connectionID">527</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionNatural:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="380031999"/>
+					</object>
+					<int key="connectionID">528</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionLeftToRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="825984362"/>
+					</object>
+					<int key="connectionID">529</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionRightToLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="560145579"/>
+					</object>
+					<int key="connectionID">530</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">fView</string>
+						<reference key="source" ref="972006081"/>
+						<reference key="destination" ref="322594744"/>
+					</object>
+					<int key="connectionID">535</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">window</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="972006081"/>
+					</object>
+					<int key="connectionID">536</int>
+				</object>
+			</object>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<object class="NSArray" key="orderedObjects">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<object class="NSArray" key="object" id="0">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="children" ref="1048"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="1021"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="1014"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">First Responder</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-3</int>
+						<reference key="object" ref="1050"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Application</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">29</int>
+						<reference key="object" ref="649796088"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="713487014"/>
+							<reference ref="694149608"/>
+							<reference ref="952259628"/>
+							<reference ref="379814623"/>
+							<reference ref="586577488"/>
+							<reference ref="302598603"/>
+							<reference ref="448692316"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">19</int>
+						<reference key="object" ref="713487014"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="835318025"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">56</int>
+						<reference key="object" ref="694149608"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="110575045"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">217</int>
+						<reference key="object" ref="952259628"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="789758025"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">83</int>
+						<reference key="object" ref="379814623"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="720053764"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">81</int>
+						<reference key="object" ref="720053764"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1023925487"/>
+							<reference ref="117038363"/>
+							<reference ref="49223823"/>
+							<reference ref="722745758"/>
+							<reference ref="705341025"/>
+							<reference ref="1025936716"/>
+							<reference ref="294629803"/>
+							<reference ref="776162233"/>
+							<reference ref="425164168"/>
+							<reference ref="579971712"/>
+							<reference ref="1010469920"/>
+						</object>
+						<reference key="parent" ref="379814623"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">75</int>
+						<reference key="object" ref="1023925487"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">80</int>
+						<reference key="object" ref="117038363"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">78</int>
+						<reference key="object" ref="49223823"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">72</int>
+						<reference key="object" ref="722745758"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">82</int>
+						<reference key="object" ref="705341025"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">124</int>
+						<reference key="object" ref="1025936716"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1065607017"/>
+						</object>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">77</int>
+						<reference key="object" ref="294629803"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">73</int>
+						<reference key="object" ref="776162233"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">79</int>
+						<reference key="object" ref="425164168"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">112</int>
+						<reference key="object" ref="579971712"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">74</int>
+						<reference key="object" ref="1010469920"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">125</int>
+						<reference key="object" ref="1065607017"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="759406840"/>
+						</object>
+						<reference key="parent" ref="1025936716"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">126</int>
+						<reference key="object" ref="759406840"/>
+						<reference key="parent" ref="1065607017"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">205</int>
+						<reference key="object" ref="789758025"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="437104165"/>
+							<reference ref="583158037"/>
+							<reference ref="1058277027"/>
+							<reference ref="212016141"/>
+							<reference ref="296257095"/>
+							<reference ref="29853731"/>
+							<reference ref="860595796"/>
+							<reference ref="1040322652"/>
+							<reference ref="790794224"/>
+							<reference ref="892235320"/>
+							<reference ref="972420730"/>
+							<reference ref="676164635"/>
+							<reference ref="507821607"/>
+							<reference ref="288088188"/>
+							<reference ref="82994268"/>
+						</object>
+						<reference key="parent" ref="952259628"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">202</int>
+						<reference key="object" ref="437104165"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">198</int>
+						<reference key="object" ref="583158037"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">207</int>
+						<reference key="object" ref="1058277027"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">214</int>
+						<reference key="object" ref="212016141"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">199</int>
+						<reference key="object" ref="296257095"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">203</int>
+						<reference key="object" ref="29853731"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">197</int>
+						<reference key="object" ref="860595796"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">206</int>
+						<reference key="object" ref="1040322652"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">215</int>
+						<reference key="object" ref="790794224"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">218</int>
+						<reference key="object" ref="892235320"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="963351320"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">216</int>
+						<reference key="object" ref="972420730"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="769623530"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">200</int>
+						<reference key="object" ref="769623530"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="948374510"/>
+							<reference ref="96193923"/>
+							<reference ref="679648819"/>
+							<reference ref="967646866"/>
+							<reference ref="859480356"/>
+							<reference ref="795346622"/>
+						</object>
+						<reference key="parent" ref="972420730"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">219</int>
+						<reference key="object" ref="948374510"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">201</int>
+						<reference key="object" ref="96193923"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">204</int>
+						<reference key="object" ref="679648819"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">220</int>
+						<reference key="object" ref="963351320"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="270902937"/>
+							<reference ref="88285865"/>
+							<reference ref="159080638"/>
+							<reference ref="326711663"/>
+							<reference ref="447796847"/>
+						</object>
+						<reference key="parent" ref="892235320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">213</int>
+						<reference key="object" ref="270902937"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">210</int>
+						<reference key="object" ref="88285865"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">221</int>
+						<reference key="object" ref="159080638"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">208</int>
+						<reference key="object" ref="326711663"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">209</int>
+						<reference key="object" ref="447796847"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">57</int>
+						<reference key="object" ref="110575045"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="238522557"/>
+							<reference ref="755159360"/>
+							<reference ref="908899353"/>
+							<reference ref="632727374"/>
+							<reference ref="646227648"/>
+							<reference ref="609285721"/>
+							<reference ref="481834944"/>
+							<reference ref="304266470"/>
+							<reference ref="1046388886"/>
+							<reference ref="1056857174"/>
+							<reference ref="342932134"/>
+						</object>
+						<reference key="parent" ref="694149608"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">58</int>
+						<reference key="object" ref="238522557"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">134</int>
+						<reference key="object" ref="755159360"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">150</int>
+						<reference key="object" ref="908899353"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">136</int>
+						<reference key="object" ref="632727374"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">144</int>
+						<reference key="object" ref="646227648"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">129</int>
+						<reference key="object" ref="609285721"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">143</int>
+						<reference key="object" ref="481834944"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">236</int>
+						<reference key="object" ref="304266470"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">131</int>
+						<reference key="object" ref="1046388886"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="752062318"/>
+						</object>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">149</int>
+						<reference key="object" ref="1056857174"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">145</int>
+						<reference key="object" ref="342932134"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">130</int>
+						<reference key="object" ref="752062318"/>
+						<reference key="parent" ref="1046388886"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">24</int>
+						<reference key="object" ref="835318025"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="299356726"/>
+							<reference ref="625202149"/>
+							<reference ref="575023229"/>
+							<reference ref="1011231497"/>
+						</object>
+						<reference key="parent" ref="713487014"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">92</int>
+						<reference key="object" ref="299356726"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">5</int>
+						<reference key="object" ref="625202149"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">239</int>
+						<reference key="object" ref="575023229"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">23</int>
+						<reference key="object" ref="1011231497"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">295</int>
+						<reference key="object" ref="586577488"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="466310130"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">296</int>
+						<reference key="object" ref="466310130"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="102151532"/>
+							<reference ref="237841660"/>
+						</object>
+						<reference key="parent" ref="586577488"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">297</int>
+						<reference key="object" ref="102151532"/>
+						<reference key="parent" ref="466310130"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">298</int>
+						<reference key="object" ref="237841660"/>
+						<reference key="parent" ref="466310130"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">211</int>
+						<reference key="object" ref="676164635"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="785027613"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">212</int>
+						<reference key="object" ref="785027613"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="680220178"/>
+							<reference ref="731782645"/>
+						</object>
+						<reference key="parent" ref="676164635"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">195</int>
+						<reference key="object" ref="680220178"/>
+						<reference key="parent" ref="785027613"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">196</int>
+						<reference key="object" ref="731782645"/>
+						<reference key="parent" ref="785027613"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">346</int>
+						<reference key="object" ref="967646866"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">348</int>
+						<reference key="object" ref="507821607"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="698887838"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">349</int>
+						<reference key="object" ref="698887838"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="605118523"/>
+							<reference ref="197661976"/>
+							<reference ref="708854459"/>
+							<reference ref="65139061"/>
+							<reference ref="19036812"/>
+							<reference ref="672708820"/>
+							<reference ref="537092702"/>
+						</object>
+						<reference key="parent" ref="507821607"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">350</int>
+						<reference key="object" ref="605118523"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">351</int>
+						<reference key="object" ref="197661976"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">354</int>
+						<reference key="object" ref="708854459"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">371</int>
+						<reference key="object" ref="972006081"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="439893737"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">372</int>
+						<reference key="object" ref="439893737"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="322594744"/>
+						</object>
+						<reference key="parent" ref="972006081"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">375</int>
+						<reference key="object" ref="302598603"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="941447902"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">376</int>
+						<reference key="object" ref="941447902"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="792887677"/>
+							<reference ref="215659978"/>
+						</object>
+						<reference key="parent" ref="302598603"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">377</int>
+						<reference key="object" ref="792887677"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="786677654"/>
+						</object>
+						<reference key="parent" ref="941447902"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">388</int>
+						<reference key="object" ref="786677654"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="159677712"/>
+							<reference ref="305399458"/>
+							<reference ref="814362025"/>
+							<reference ref="330926929"/>
+							<reference ref="533507878"/>
+							<reference ref="158063935"/>
+							<reference ref="885547335"/>
+							<reference ref="901062459"/>
+							<reference ref="767671776"/>
+							<reference ref="691570813"/>
+							<reference ref="769124883"/>
+							<reference ref="739652853"/>
+							<reference ref="1012600125"/>
+							<reference ref="214559597"/>
+							<reference ref="596732606"/>
+							<reference ref="393423671"/>
+						</object>
+						<reference key="parent" ref="792887677"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">389</int>
+						<reference key="object" ref="159677712"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">390</int>
+						<reference key="object" ref="305399458"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">391</int>
+						<reference key="object" ref="814362025"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">392</int>
+						<reference key="object" ref="330926929"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">393</int>
+						<reference key="object" ref="533507878"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">394</int>
+						<reference key="object" ref="158063935"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">395</int>
+						<reference key="object" ref="885547335"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">396</int>
+						<reference key="object" ref="901062459"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">397</int>
+						<reference key="object" ref="767671776"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="175441468"/>
+						</object>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">398</int>
+						<reference key="object" ref="691570813"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1058217995"/>
+						</object>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">399</int>
+						<reference key="object" ref="769124883"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="18263474"/>
+						</object>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">400</int>
+						<reference key="object" ref="739652853"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">401</int>
+						<reference key="object" ref="1012600125"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">402</int>
+						<reference key="object" ref="214559597"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">403</int>
+						<reference key="object" ref="596732606"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">404</int>
+						<reference key="object" ref="393423671"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">405</int>
+						<reference key="object" ref="18263474"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="257962622"/>
+							<reference ref="644725453"/>
+							<reference ref="1037576581"/>
+							<reference ref="941806246"/>
+							<reference ref="1045724900"/>
+						</object>
+						<reference key="parent" ref="769124883"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">406</int>
+						<reference key="object" ref="257962622"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">407</int>
+						<reference key="object" ref="644725453"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">408</int>
+						<reference key="object" ref="1037576581"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">409</int>
+						<reference key="object" ref="941806246"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">410</int>
+						<reference key="object" ref="1045724900"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">411</int>
+						<reference key="object" ref="1058217995"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="706297211"/>
+							<reference ref="568384683"/>
+							<reference ref="663508465"/>
+						</object>
+						<reference key="parent" ref="691570813"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">412</int>
+						<reference key="object" ref="706297211"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">413</int>
+						<reference key="object" ref="568384683"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">414</int>
+						<reference key="object" ref="663508465"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">415</int>
+						<reference key="object" ref="175441468"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="252969304"/>
+							<reference ref="766922938"/>
+							<reference ref="677519740"/>
+							<reference ref="238351151"/>
+						</object>
+						<reference key="parent" ref="767671776"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">416</int>
+						<reference key="object" ref="252969304"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">417</int>
+						<reference key="object" ref="766922938"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">418</int>
+						<reference key="object" ref="677519740"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">419</int>
+						<reference key="object" ref="238351151"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">420</int>
+						<reference key="object" ref="755631768"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">450</int>
+						<reference key="object" ref="288088188"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="579392910"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">451</int>
+						<reference key="object" ref="579392910"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1060694897"/>
+							<reference ref="879586729"/>
+							<reference ref="56570060"/>
+						</object>
+						<reference key="parent" ref="288088188"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">452</int>
+						<reference key="object" ref="1060694897"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">453</int>
+						<reference key="object" ref="859480356"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">454</int>
+						<reference key="object" ref="795346622"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">457</int>
+						<reference key="object" ref="65139061"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">459</int>
+						<reference key="object" ref="19036812"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">460</int>
+						<reference key="object" ref="672708820"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">462</int>
+						<reference key="object" ref="537092702"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">465</int>
+						<reference key="object" ref="879586729"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">466</int>
+						<reference key="object" ref="56570060"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">485</int>
+						<reference key="object" ref="82994268"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">490</int>
+						<reference key="object" ref="448692316"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="992780483"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">491</int>
+						<reference key="object" ref="992780483"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="105068016"/>
+						</object>
+						<reference key="parent" ref="448692316"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">492</int>
+						<reference key="object" ref="105068016"/>
+						<reference key="parent" ref="992780483"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">494</int>
+						<reference key="object" ref="976324537"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">496</int>
+						<reference key="object" ref="215659978"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="446991534"/>
+						</object>
+						<reference key="parent" ref="941447902"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">497</int>
+						<reference key="object" ref="446991534"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="875092757"/>
+							<reference ref="630155264"/>
+							<reference ref="945678886"/>
+							<reference ref="512868991"/>
+							<reference ref="163117631"/>
+							<reference ref="31516759"/>
+							<reference ref="908105787"/>
+							<reference ref="644046920"/>
+							<reference ref="231811626"/>
+							<reference ref="883618387"/>
+						</object>
+						<reference key="parent" ref="215659978"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">498</int>
+						<reference key="object" ref="875092757"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">499</int>
+						<reference key="object" ref="630155264"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">500</int>
+						<reference key="object" ref="945678886"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">501</int>
+						<reference key="object" ref="512868991"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">502</int>
+						<reference key="object" ref="163117631"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">503</int>
+						<reference key="object" ref="31516759"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="956096989"/>
+						</object>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">504</int>
+						<reference key="object" ref="908105787"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">505</int>
+						<reference key="object" ref="644046920"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">506</int>
+						<reference key="object" ref="231811626"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">507</int>
+						<reference key="object" ref="883618387"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">508</int>
+						<reference key="object" ref="956096989"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="257099033"/>
+							<reference ref="551969625"/>
+							<reference ref="249532473"/>
+							<reference ref="607364498"/>
+							<reference ref="508151438"/>
+							<reference ref="981751889"/>
+							<reference ref="380031999"/>
+							<reference ref="825984362"/>
+							<reference ref="560145579"/>
+						</object>
+						<reference key="parent" ref="31516759"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">509</int>
+						<reference key="object" ref="257099033"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">510</int>
+						<reference key="object" ref="551969625"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">511</int>
+						<reference key="object" ref="249532473"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">512</int>
+						<reference key="object" ref="607364498"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">513</int>
+						<reference key="object" ref="508151438"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">514</int>
+						<reference key="object" ref="981751889"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">515</int>
+						<reference key="object" ref="380031999"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">516</int>
+						<reference key="object" ref="825984362"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">517</int>
+						<reference key="object" ref="560145579"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">533</int>
+						<reference key="object" ref="322594744"/>
+						<reference key="parent" ref="439893737"/>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="flattenedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="NSArray" key="dict.sortedKeys">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>-3.IBPluginDependency</string>
+					<string>112.IBPluginDependency</string>
+					<string>112.ImportedFromIB2</string>
+					<string>124.IBPluginDependency</string>
+					<string>124.ImportedFromIB2</string>
+					<string>125.IBPluginDependency</string>
+					<string>125.ImportedFromIB2</string>
+					<string>125.editorWindowContentRectSynchronizationRect</string>
+					<string>126.IBPluginDependency</string>
+					<string>126.ImportedFromIB2</string>
+					<string>129.IBPluginDependency</string>
+					<string>129.ImportedFromIB2</string>
+					<string>130.IBPluginDependency</string>
+					<string>130.ImportedFromIB2</string>
+					<string>130.editorWindowContentRectSynchronizationRect</string>
+					<string>131.IBPluginDependency</string>
+					<string>131.ImportedFromIB2</string>
+					<string>134.IBPluginDependency</string>
+					<string>134.ImportedFromIB2</string>
+					<string>136.IBPluginDependency</string>
+					<string>136.ImportedFromIB2</string>
+					<string>143.IBPluginDependency</string>
+					<string>143.ImportedFromIB2</string>
+					<string>144.IBPluginDependency</string>
+					<string>144.ImportedFromIB2</string>
+					<string>145.IBPluginDependency</string>
+					<string>145.ImportedFromIB2</string>
+					<string>149.IBPluginDependency</string>
+					<string>149.ImportedFromIB2</string>
+					<string>150.IBPluginDependency</string>
+					<string>150.ImportedFromIB2</string>
+					<string>19.IBPluginDependency</string>
+					<string>19.ImportedFromIB2</string>
+					<string>195.IBPluginDependency</string>
+					<string>195.ImportedFromIB2</string>
+					<string>196.IBPluginDependency</string>
+					<string>196.ImportedFromIB2</string>
+					<string>197.IBPluginDependency</string>
+					<string>197.ImportedFromIB2</string>
+					<string>198.IBPluginDependency</string>
+					<string>198.ImportedFromIB2</string>
+					<string>199.IBPluginDependency</string>
+					<string>199.ImportedFromIB2</string>
+					<string>200.IBEditorWindowLastContentRect</string>
+					<string>200.IBPluginDependency</string>
+					<string>200.ImportedFromIB2</string>
+					<string>200.editorWindowContentRectSynchronizationRect</string>
+					<string>201.IBPluginDependency</string>
+					<string>201.ImportedFromIB2</string>
+					<string>202.IBPluginDependency</string>
+					<string>202.ImportedFromIB2</string>
+					<string>203.IBPluginDependency</string>
+					<string>203.ImportedFromIB2</string>
+					<string>204.IBPluginDependency</string>
+					<string>204.ImportedFromIB2</string>
+					<string>205.IBEditorWindowLastContentRect</string>
+					<string>205.IBPluginDependency</string>
+					<string>205.ImportedFromIB2</string>
+					<string>205.editorWindowContentRectSynchronizationRect</string>
+					<string>206.IBPluginDependency</string>
+					<string>206.ImportedFromIB2</string>
+					<string>207.IBPluginDependency</string>
+					<string>207.ImportedFromIB2</string>
+					<string>208.IBPluginDependency</string>
+					<string>208.ImportedFromIB2</string>
+					<string>209.IBPluginDependency</string>
+					<string>209.ImportedFromIB2</string>
+					<string>210.IBPluginDependency</string>
+					<string>210.ImportedFromIB2</string>
+					<string>211.IBPluginDependency</string>
+					<string>211.ImportedFromIB2</string>
+					<string>212.IBPluginDependency</string>
+					<string>212.ImportedFromIB2</string>
+					<string>212.editorWindowContentRectSynchronizationRect</string>
+					<string>213.IBPluginDependency</string>
+					<string>213.ImportedFromIB2</string>
+					<string>214.IBPluginDependency</string>
+					<string>214.ImportedFromIB2</string>
+					<string>215.IBPluginDependency</string>
+					<string>215.ImportedFromIB2</string>
+					<string>216.IBPluginDependency</string>
+					<string>216.ImportedFromIB2</string>
+					<string>217.IBPluginDependency</string>
+					<string>217.ImportedFromIB2</string>
+					<string>218.IBPluginDependency</string>
+					<string>218.ImportedFromIB2</string>
+					<string>219.IBPluginDependency</string>
+					<string>219.ImportedFromIB2</string>
+					<string>220.IBEditorWindowLastContentRect</string>
+					<string>220.IBPluginDependency</string>
+					<string>220.ImportedFromIB2</string>
+					<string>220.editorWindowContentRectSynchronizationRect</string>
+					<string>221.IBPluginDependency</string>
+					<string>221.ImportedFromIB2</string>
+					<string>23.IBPluginDependency</string>
+					<string>23.ImportedFromIB2</string>
+					<string>236.IBPluginDependency</string>
+					<string>236.ImportedFromIB2</string>
+					<string>239.IBPluginDependency</string>
+					<string>239.ImportedFromIB2</string>
+					<string>24.IBEditorWindowLastContentRect</string>
+					<string>24.IBPluginDependency</string>
+					<string>24.ImportedFromIB2</string>
+					<string>24.editorWindowContentRectSynchronizationRect</string>
+					<string>29.IBEditorWindowLastContentRect</string>
+					<string>29.IBPluginDependency</string>
+					<string>29.ImportedFromIB2</string>
+					<string>29.WindowOrigin</string>
+					<string>29.editorWindowContentRectSynchronizationRect</string>
+					<string>295.IBPluginDependency</string>
+					<string>296.IBEditorWindowLastContentRect</string>
+					<string>296.IBPluginDependency</string>
+					<string>296.editorWindowContentRectSynchronizationRect</string>
+					<string>297.IBPluginDependency</string>
+					<string>298.IBPluginDependency</string>
+					<string>346.IBPluginDependency</string>
+					<string>346.ImportedFromIB2</string>
+					<string>348.IBPluginDependency</string>
+					<string>348.ImportedFromIB2</string>
+					<string>349.IBEditorWindowLastContentRect</string>
+					<string>349.IBPluginDependency</string>
+					<string>349.ImportedFromIB2</string>
+					<string>349.editorWindowContentRectSynchronizationRect</string>
+					<string>350.IBPluginDependency</string>
+					<string>350.ImportedFromIB2</string>
+					<string>351.IBPluginDependency</string>
+					<string>351.ImportedFromIB2</string>
+					<string>354.IBPluginDependency</string>
+					<string>354.ImportedFromIB2</string>
+					<string>371.IBEditorWindowLastContentRect</string>
+					<string>371.IBPluginDependency</string>
+					<string>371.IBWindowTemplateEditedContentRect</string>
+					<string>371.NSWindowTemplate.visibleAtLaunch</string>
+					<string>371.editorWindowContentRectSynchronizationRect</string>
+					<string>371.windowTemplate.maxSize</string>
+					<string>371.windowTemplate.minSize</string>
+					<string>372.IBPluginDependency</string>
+					<string>375.IBPluginDependency</string>
+					<string>376.IBEditorWindowLastContentRect</string>
+					<string>376.IBPluginDependency</string>
+					<string>377.IBPluginDependency</string>
+					<string>388.IBEditorWindowLastContentRect</string>
+					<string>388.IBPluginDependency</string>
+					<string>389.IBPluginDependency</string>
+					<string>390.IBPluginDependency</string>
+					<string>391.IBPluginDependency</string>
+					<string>392.IBPluginDependency</string>
+					<string>393.IBPluginDependency</string>
+					<string>394.IBPluginDependency</string>
+					<string>395.IBPluginDependency</string>
+					<string>396.IBPluginDependency</string>
+					<string>397.IBPluginDependency</string>
+					<string>398.IBPluginDependency</string>
+					<string>399.IBPluginDependency</string>
+					<string>400.IBPluginDependency</string>
+					<string>401.IBPluginDependency</string>
+					<string>402.IBPluginDependency</string>
+					<string>403.IBPluginDependency</string>
+					<string>404.IBPluginDependency</string>
+					<string>405.IBPluginDependency</string>
+					<string>406.IBPluginDependency</string>
+					<string>407.IBPluginDependency</string>
+					<string>408.IBPluginDependency</string>
+					<string>409.IBPluginDependency</string>
+					<string>410.IBPluginDependency</string>
+					<string>411.IBPluginDependency</string>
+					<string>412.IBPluginDependency</string>
+					<string>413.IBPluginDependency</string>
+					<string>414.IBPluginDependency</string>
+					<string>415.IBPluginDependency</string>
+					<string>416.IBPluginDependency</string>
+					<string>417.IBPluginDependency</string>
+					<string>418.IBPluginDependency</string>
+					<string>419.IBPluginDependency</string>
+					<string>450.IBPluginDependency</string>
+					<string>451.IBEditorWindowLastContentRect</string>
+					<string>451.IBPluginDependency</string>
+					<string>452.IBPluginDependency</string>
+					<string>453.IBPluginDependency</string>
+					<string>454.IBPluginDependency</string>
+					<string>457.IBPluginDependency</string>
+					<string>459.IBPluginDependency</string>
+					<string>460.IBPluginDependency</string>
+					<string>462.IBPluginDependency</string>
+					<string>465.IBPluginDependency</string>
+					<string>466.IBPluginDependency</string>
+					<string>485.IBPluginDependency</string>
+					<string>490.IBPluginDependency</string>
+					<string>491.IBEditorWindowLastContentRect</string>
+					<string>491.IBPluginDependency</string>
+					<string>492.IBPluginDependency</string>
+					<string>496.IBPluginDependency</string>
+					<string>497.IBEditorWindowLastContentRect</string>
+					<string>497.IBPluginDependency</string>
+					<string>498.IBPluginDependency</string>
+					<string>499.IBPluginDependency</string>
+					<string>5.IBPluginDependency</string>
+					<string>5.ImportedFromIB2</string>
+					<string>500.IBPluginDependency</string>
+					<string>501.IBPluginDependency</string>
+					<string>502.IBPluginDependency</string>
+					<string>503.IBPluginDependency</string>
+					<string>504.IBPluginDependency</string>
+					<string>505.IBPluginDependency</string>
+					<string>506.IBPluginDependency</string>
+					<string>507.IBPluginDependency</string>
+					<string>508.IBEditorWindowLastContentRect</string>
+					<string>508.IBPluginDependency</string>
+					<string>509.IBPluginDependency</string>
+					<string>510.IBPluginDependency</string>
+					<string>511.IBPluginDependency</string>
+					<string>512.IBPluginDependency</string>
+					<string>513.IBPluginDependency</string>
+					<string>514.IBPluginDependency</string>
+					<string>515.IBPluginDependency</string>
+					<string>516.IBPluginDependency</string>
+					<string>517.IBPluginDependency</string>
+					<string>533.IBPluginDependency</string>
+					<string>533.IBViewBoundsToFrameTransform</string>
+					<string>56.IBPluginDependency</string>
+					<string>56.ImportedFromIB2</string>
+					<string>57.IBEditorWindowLastContentRect</string>
+					<string>57.IBPluginDependency</string>
+					<string>57.ImportedFromIB2</string>
+					<string>57.editorWindowContentRectSynchronizationRect</string>
+					<string>58.IBPluginDependency</string>
+					<string>58.ImportedFromIB2</string>
+					<string>72.IBPluginDependency</string>
+					<string>72.ImportedFromIB2</string>
+					<string>73.IBPluginDependency</string>
+					<string>73.ImportedFromIB2</string>
+					<string>74.IBPluginDependency</string>
+					<string>74.ImportedFromIB2</string>
+					<string>75.IBPluginDependency</string>
+					<string>75.ImportedFromIB2</string>
+					<string>77.IBPluginDependency</string>
+					<string>77.ImportedFromIB2</string>
+					<string>78.IBPluginDependency</string>
+					<string>78.ImportedFromIB2</string>
+					<string>79.IBPluginDependency</string>
+					<string>79.ImportedFromIB2</string>
+					<string>80.IBPluginDependency</string>
+					<string>80.ImportedFromIB2</string>
+					<string>81.IBEditorWindowLastContentRect</string>
+					<string>81.IBPluginDependency</string>
+					<string>81.ImportedFromIB2</string>
+					<string>81.editorWindowContentRectSynchronizationRect</string>
+					<string>82.IBPluginDependency</string>
+					<string>82.ImportedFromIB2</string>
+					<string>83.IBPluginDependency</string>
+					<string>83.ImportedFromIB2</string>
+					<string>92.IBPluginDependency</string>
+					<string>92.ImportedFromIB2</string>
+				</object>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{522, 812}, {146, 23}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{436, 809}, {64, 6}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{753, 187}, {275, 113}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {275, 83}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{547, 180}, {254, 283}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{187, 434}, {243, 243}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {167, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{753, 217}, {238, 103}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {241, 103}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{654, 239}, {194, 73}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{525, 802}, {197, 73}}</string>
+					<string>{{380, 836}, {489, 20}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{74, 862}</string>
+					<string>{{6, 978}, {478, 20}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{604, 269}, {231, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{475, 832}, {234, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{746, 287}, {220, 133}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {215, 63}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{358, 214}, {480, 360}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{358, 214}, {480, 360}}</string>
+					<integer value="1"/>
+					<string>{{33, 99}, {480, 360}}</string>
+					<string>{3.40282e+38, 3.40282e+38}</string>
+					<string>{0, 0}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{591, 420}, {83, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{523, 2}, {178, 283}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{753, 197}, {170, 63}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{725, 289}, {246, 23}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{674, 260}, {204, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{878, 180}, {164, 173}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">AULIAABDAgAAA</bytes>
+					</object>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{286, 129}, {275, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{23, 794}, {245, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{452, 109}, {196, 203}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{145, 474}, {199, 203}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="unlocalizedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="activeLocalization"/>
+			<object class="NSMutableDictionary" key="localizations">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="sourceID"/>
+			<int key="maxID">536</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">FileReaderAppDelegate</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">window</string>
+						<string key="NS.object.0">SkNSWindow</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">window</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">window</string>
+							<string key="candidateClassName">SkNSWindow</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">FileReaderAppDelegate.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">FileReaderWindow</string>
+					<string key="superclassName">SkNSWindow</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">fView</string>
+						<string key="NS.object.0">SkNSView</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">fView</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">fView</string>
+							<string key="candidateClassName">SkNSView</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">FileReaderWindow.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkNSView</string>
+					<string key="superclassName">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">SkNSView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkNSWindow</string>
+					<string key="superclassName">NSWindow</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">SkNSWindow.h</string>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<string key="superclassName">NSResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="822405504">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSApplication.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="850738725">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSApplicationScripting.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="624831158">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSColorPanel.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSHelpManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSPageLayout.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSUserInterfaceItemSearching.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSBrowser</string>
+					<string key="superclassName">NSControl</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSBrowser.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSControl</string>
+					<string key="superclassName">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="310914472">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSControl.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSDocument</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="actions">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>printDocument:</string>
+							<string>revertDocumentToSaved:</string>
+							<string>runPageLayout:</string>
+							<string>saveDocument:</string>
+							<string>saveDocumentAs:</string>
+							<string>saveDocumentTo:</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="actionInfosByName">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>printDocument:</string>
+							<string>revertDocumentToSaved:</string>
+							<string>runPageLayout:</string>
+							<string>saveDocument:</string>
+							<string>saveDocumentAs:</string>
+							<string>saveDocumentTo:</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<object class="IBActionInfo">
+								<string key="name">printDocument:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">revertDocumentToSaved:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">runPageLayout:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">saveDocument:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">saveDocumentAs:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">saveDocumentTo:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDocument.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSDocument</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDocumentScripting.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSDocumentController</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="actions">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>clearRecentDocuments:</string>
+							<string>newDocument:</string>
+							<string>openDocument:</string>
+							<string>saveAllDocuments:</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="actionInfosByName">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>clearRecentDocuments:</string>
+							<string>newDocument:</string>
+							<string>openDocument:</string>
+							<string>saveAllDocuments:</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<object class="IBActionInfo">
+								<string key="name">clearRecentDocuments:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">newDocument:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">openDocument:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+							<object class="IBActionInfo">
+								<string key="name">saveAllDocuments:</string>
+								<string key="candidateClassName">id</string>
+							</object>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDocumentController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSFontManager</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="946436764">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSFontManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSFormatter</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFormatter.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSMatrix</string>
+					<string key="superclassName">NSControl</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSMatrix.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSMenu</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="1056362899">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSMenu.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSMenuItem</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="472958451">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSMenuItem.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSMovieView</string>
+					<string key="superclassName">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSMovieView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSAccessibility.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="822405504"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="850738725"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="624831158"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="310914472"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDictionaryController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDragging.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="946436764"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSFontPanel.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSKeyValueBinding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="1056362899"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSNibLoading.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSOutlineView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSPasteboard.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSSavePanel.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="809545482">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSTableView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSToolbarItem.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="260078765">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSClassDescription.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObjectScripting.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSPortCoder.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptClassDescription.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptObjectSpecifiers.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptWhoseTests.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLDownload.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSInterfaceStyle.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSResponder</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSResponder.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSTableView</string>
+					<string key="superclassName">NSControl</string>
+					<reference key="sourceIdentifier" ref="809545482"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSText</string>
+					<string key="superclassName">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSText.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSTextView</string>
+					<string key="superclassName">NSText</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSTextView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSClipView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSView</string>
+					<reference key="sourceIdentifier" ref="472958451"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSRulerView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSView</string>
+					<string key="superclassName">NSResponder</string>
+					<reference key="sourceIdentifier" ref="260078765"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSWindow</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDrawer.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSWindow</string>
+					<string key="superclassName">NSResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSWindow.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSWindow</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSWindowScripting.h</string>
+					</object>
+				</object>
+			</object>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+			<integer value="1060" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+			<integer value="3000" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<string key="IBDocument.LastKnownRelativeProjectPath">../FileReaderApp.xcodeproj</string>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSArray" key="dict.sortedKeys">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<string>NSMenuCheckmark</string>
+				<string>NSMenuMixedState</string>
+			</object>
+			<object class="NSMutableArray" key="dict.values">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<string>{9, 8}</string>
+				<string>{7, 2}</string>
+			</object>
+		</object>
+	</data>
+</archive>
diff --git a/experimental/FileReaderApp/FileReaderApp-Info.plist b/experimental/FileReaderApp/FileReaderApp-Info.plist
new file mode 100644
index 0000000..f696cb2
--- /dev/null
+++ b/experimental/FileReaderApp/FileReaderApp-Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>com.yourcompany.${PRODUCT_NAME:rfc1034identifier}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>${MACOSX_DEPLOYMENT_TARGET}</string>
+	<key>NSMainNibFile</key>
+	<string>MainMenu</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>
diff --git a/experimental/FileReaderApp/FileReaderAppDelegate.h b/experimental/FileReaderApp/FileReaderAppDelegate.h
new file mode 100644
index 0000000..113f62e
--- /dev/null
+++ b/experimental/FileReaderApp/FileReaderAppDelegate.h
@@ -0,0 +1,15 @@
+
+/*
+ * 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 <Cocoa/Cocoa.h>
+#import "SkNSWindow.h"
+@interface FileReaderAppDelegate : NSObject <NSApplicationDelegate> {
+    SkNSWindow *window;
+}
+
+@property (assign) IBOutlet SkNSWindow *window;
+@end
diff --git a/experimental/FileReaderApp/FileReaderAppDelegate.mm b/experimental/FileReaderApp/FileReaderAppDelegate.mm
new file mode 100644
index 0000000..6b706b6
--- /dev/null
+++ b/experimental/FileReaderApp/FileReaderAppDelegate.mm
@@ -0,0 +1,10 @@
+#import "FileReaderAppDelegate.h"
+
+@implementation FileReaderAppDelegate
+@synthesize window;
+
+-(void) applicationDidFinishLaunching:(NSNotification *)aNotification {
+    //Load specified skia views after launching
+    [window installSkViews];
+}
+@end
diff --git a/experimental/FileReaderApp/FileReaderApp_Prefix.pch b/experimental/FileReaderApp/FileReaderApp_Prefix.pch
new file mode 100644
index 0000000..8d43cae
--- /dev/null
+++ b/experimental/FileReaderApp/FileReaderApp_Prefix.pch
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'CocoaSampleApp' target in the 'CocoaSampleApp' project
+//
+
+#ifdef __OBJC__
+    #import <Cocoa/Cocoa.h>
+#endif
diff --git a/experimental/FileReaderApp/FileReaderWindow.h b/experimental/FileReaderApp/FileReaderWindow.h
new file mode 100644
index 0000000..f83f918
--- /dev/null
+++ b/experimental/FileReaderApp/FileReaderWindow.h
@@ -0,0 +1,15 @@
+
+/*
+ * 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 "SkNSWindow.h"
+#import "ReaderView.h"
+@interface FileReaderWindow : SkNSWindow {
+    IBOutlet SkNSView* fView;
+    ReaderView* fReaderView;
+}
+@end
+
diff --git a/experimental/FileReaderApp/FileReaderWindow.mm b/experimental/FileReaderApp/FileReaderWindow.mm
new file mode 100644
index 0000000..87b9ab1
--- /dev/null
+++ b/experimental/FileReaderApp/FileReaderWindow.mm
@@ -0,0 +1,35 @@
+#import "FileReaderWindow.h"
+#import "SkGradientShader.h"
+
+bool gNeverSetToTrueJustNeedToFoolLinker;
+static void init_effects() {
+  if (gNeverSetToTrueJustNeedToFoolLinker) {
+    SkPoint p = SkPoint::Make(0,0);
+    SkPoint q = SkPoint::Make(100,100);
+    SkPoint pts[] = {p, q};
+    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
+    SkScalar pos[] = { 0, 1.0};
+    SkGradientShader::CreateLinear(pts, colors, pos, 2, 
+                                   SkShader::kMirror_TileMode);
+  }
+}
+
+@implementation FileReaderWindow
+-(void) installSkViews {
+    init_effects();
+    fReaderView = new ReaderView;
+    fReaderView->setVisibleP(true);
+    fReaderView->setSize([self frame].size.width, [self frame].size.height);
+    [fView addSkView:fReaderView];
+    [fView setNeedsDisplay:YES];
+    fReaderView->unref();
+    //TODO - Temporary fix. Inval doesn't Seem to be working. 
+    [NSTimer scheduledTimerWithTimeInterval:0.01 target:self 
+                                   selector:@selector(redraw) userInfo:nil 
+                                    repeats:YES];
+}
+
+- (void)redraw {
+    [fView setNeedsDisplay:YES];
+}
+@end
\ No newline at end of file
diff --git a/experimental/FileReaderApp/ReaderView.cpp b/experimental/FileReaderApp/ReaderView.cpp
new file mode 100644
index 0000000..78a7a21
--- /dev/null
+++ b/experimental/FileReaderApp/ReaderView.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 "ReaderView.h"
+#include "SkGPipe.h"
+#include "SkCanvas.h"
+
+#include <stdio.h>
+
+#define FILE_PATH   "/Users/yangsu/Code/test/test.a"
+ReaderView::ReaderView() {
+    fBGColor = 0xFFDDDDDD;
+    fFilePos = 0;
+    fBufferBitmaps[0].setConfig(SkBitmap::kARGB_8888_Config, 640, 480);
+    fBufferBitmaps[0].allocPixels(NULL);
+    fBufferBitmaps[1].setConfig(SkBitmap::kARGB_8888_Config, 640, 480);
+    fBufferBitmaps[1].allocPixels(NULL);
+    fFront  = 0;
+    fBack   = 1;
+}
+
+void ReaderView::draw(SkCanvas* canvas) {
+    canvas->drawColor(fBGColor);
+
+    SkAutoCanvasRestore acr(canvas, true);
+
+    //Create a temporary canvas and reader object that draws into the back
+    //bitmap so that the incremental changes or incomplete reads are not shown
+    //on screen
+    SkCanvas bufferCanvas(fBufferBitmaps[fBack]);
+    SkGPipeReader reader(&bufferCanvas);
+
+    //The file specified by FILE_PATH MUST exist
+    FILE* f = fopen(FILE_PATH, "rb");
+    SkASSERT(f != NULL);
+
+    fseek(f, 0, SEEK_END);
+    int size = ftell(f) * sizeof(char);
+    if (size <= fFilePos) {
+        fFilePos = 0;
+    }
+
+    //Resume from the last read location
+    fseek(f, fFilePos, SEEK_SET);
+    int toBeRead = size - fFilePos;
+    if (size > 0 && toBeRead > 0) {
+        void* block = sk_malloc_throw(toBeRead);
+        fread(block, 1, toBeRead, f);
+
+        size_t bytesRead;
+        SkGPipeReader::Status fStatus = reader.playback(block, toBeRead, &bytesRead);
+        SkASSERT(SkGPipeReader::kError_Status != fStatus);
+        SkASSERT(toBeRead >= bytesRead);
+
+        //if the reader reaches a done verb, a frame is complete.
+        //Update the file location and swap the front and back bitmaps to show
+        //the new frame
+        if (SkGPipeReader::kDone_Status == fStatus) {
+            fFilePos += bytesRead;
+            fFront = fFront ^ 0x1;
+            fBack = fBack ^ 0x1;
+        }
+        sk_free(block);
+    }
+
+    fclose(f);
+
+    //the front bitmap is always drawn
+    canvas->drawBitmap(fBufferBitmaps[fFront], 0, 0, NULL);
+    this->inval(NULL);
+}
+
diff --git a/experimental/FileReaderApp/ReaderView.h b/experimental/FileReaderApp/ReaderView.h
new file mode 100644
index 0000000..e3032d3
--- /dev/null
+++ b/experimental/FileReaderApp/ReaderView.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.
+ */
+#include "SkView.h"
+#include "SkColor.h"
+#include "SkBitmap.h"
+
+/*
+ * Pipe Reader with File IO. This view reads from the data file produced by the
+ * Pipe Writer.
+ */
+
+class ReaderView : public SkView {
+public:
+    ReaderView();
+    virtual void draw(SkCanvas* canvas);
+
+private:
+    int     fFilePos;
+    int     fFront;
+    int     fBack;
+    SkColor fBGColor;
+    SkBitmap fBufferBitmaps[2];
+    typedef SkView INHERITED;
+};
+
diff --git a/experimental/FileReaderApp/main.m b/experimental/FileReaderApp/main.m
new file mode 100644
index 0000000..cd85c6d
--- /dev/null
+++ b/experimental/FileReaderApp/main.m
@@ -0,0 +1,16 @@
+//
+//  main.m
+//  CocoaSampleApp
+//
+//  Created by Yang Su on 6/14/11.
+//  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 <Cocoa/Cocoa.h>
+
+int main(int argc, char *argv[])
+{
+    return NSApplicationMain(argc,  (const char **) argv);
+}
diff --git a/experimental/Intersection/ActiveEdge_Test.cpp b/experimental/Intersection/ActiveEdge_Test.cpp
new file mode 100755
index 0000000..8ded8ff
--- /dev/null
+++ b/experimental/Intersection/ActiveEdge_Test.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 "Simplify.h"
+
+namespace UnitTest {
+
+#include "EdgeWalker.cpp"
+
+} // end of UnitTest namespace
+
+#include "Intersection_Tests.h"
+
+SkPoint leftRight[][4] = {
+// equal length
+    {{10, 10}, {10, 50},   {20, 10}, {20, 50}},
+    {{10, 10}, {10, 50},   {10, 10}, {20, 50}},
+    {{10, 10}, {10, 50},   {20, 10}, {10, 50}},
+// left top higher
+    {{10,  0}, {10, 50},   {20, 10}, {20, 50}},
+    {{10,  0}, {10, 50},   {10, 10}, {20, 50}},
+    {{10,  0}, {10, 50},   {20, 10}, {10, 50}},
+    {{10,  0}, {10, 50},   {20, 10}, {10 + 0.000001f, 40}},
+// left top lower
+    {{10, 20}, {10, 50},   {20, 10}, {20, 50}},
+    {{10, 20}, {10, 50},   {10, 10}, {20, 50}},
+    {{10, 20}, {10, 50},   {20, 10}, {10, 50}},
+    {{10, 20}, {10, 50},   {20, 10}, {10 + 0.000001f, 40}},
+    {{10, 20}, {10, 50},   { 0,  0}, {50, 50}},
+// left bottom higher
+    {{10, 10}, {10, 40},   {20, 10}, {20, 50}},
+    {{10, 10}, {10, 40},   {10, 10}, {20, 50}},
+    {{10, 10}, {10, 40},   {20, 10}, {10, 50}},
+    {{10, 10}, {10, 40},   {20, 10}, { 0 + 0.000001f, 70}},
+// left bottom lower
+    {{10, 10}, {10, 60},   {20, 10}, {20, 50}},
+    {{10, 10}, {10, 60},   {10, 10}, {20, 50}},
+    {{10, 10}, {10, 60},   {20, 10}, {10 + 0.000001f, 50}},
+    {{10, 10}, {10, 60},   {20, 10}, {10 + 0.000001f, 40}},
+    {{10, 10}, {10, 60},   { 0,  0}, {20 + 0.000001f, 20}},
+};
+
+size_t leftRightCount = sizeof(leftRight) / sizeof(leftRight[0]);
+
+// older code that worked mostly
+static bool operator_less_than(const UnitTest::ActiveEdge& lh,
+        const UnitTest::ActiveEdge& rh) {
+    if (rh.fAbove.fY - lh.fAbove.fY > lh.fBelow.fY - rh.fAbove.fY
+            && lh.fBelow.fY < rh.fBelow.fY
+            || lh.fAbove.fY - rh.fAbove.fY < rh.fBelow.fY - lh.fAbove.fY
+            && rh.fBelow.fY < lh.fBelow.fY) {
+        const SkPoint& check = rh.fBelow.fY <= lh.fBelow.fY
+                && lh.fBelow != rh.fBelow ? rh.fBelow :
+                rh.fAbove;
+        return (check.fY - lh.fAbove.fY) * (lh.fBelow.fX - lh.fAbove.fX)
+                < (lh.fBelow.fY - lh.fAbove.fY) * (check.fX - lh.fAbove.fX);
+    }
+    const SkPoint& check = lh.fBelow.fY <= rh.fBelow.fY
+            && lh.fBelow != rh.fBelow ? lh.fBelow : lh.fAbove;
+    return (rh.fBelow.fY - rh.fAbove.fY) * (check.fX - rh.fAbove.fX)
+            < (check.fY - rh.fAbove.fY) * (rh.fBelow.fX - rh.fAbove.fX);
+}
+
+
+void ActiveEdge_Test() {
+    UnitTest::InEdge leftIn, rightIn;
+    UnitTest::ActiveEdge left, right;
+    left.fWorkEdge.fEdge = &leftIn;
+    right.fWorkEdge.fEdge = &rightIn;
+    for (size_t x = 0; x < leftRightCount; ++x) {
+        left.fAbove = leftRight[x][0];
+        left.fTangent = left.fBelow = leftRight[x][1];
+        right.fAbove = leftRight[x][2];
+        right.fTangent = right.fBelow = leftRight[x][3];
+        SkASSERT(left < right);
+        SkASSERT(operator_less_than(left, right));
+        SkASSERT(!(right < left));
+        SkASSERT(!operator_less_than(right, left));
+    }
+}
+
+
+
+
diff --git a/experimental/Intersection/AddTestOutput/main.cpp b/experimental/Intersection/AddTestOutput/main.cpp
new file mode 100644
index 0000000..95955ce
--- /dev/null
+++ b/experimental/Intersection/AddTestOutput/main.cpp
@@ -0,0 +1,111 @@
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+#include <stdio.h>
+
+static bool replace(const char* fun, const char* dir, const char* filename, const char* marker,
+        const char* marker2, const char* replace, size_t replaceLen) {
+    SkString outFileStr(dir);
+    outFileStr.append(filename);
+    SkFILEStream opStreamIn(outFileStr.c_str());
+    if (!opStreamIn.isValid()) {
+        SkDebugf("%s couldn't find %s\n", fun, outFileStr.c_str());
+        return false;
+    }
+    SkTDArray<char> opData;
+    opData.setCount(opStreamIn.getLength());
+    size_t opLen = opData.count();
+    opStreamIn.read(opData.begin(), opLen);
+    opStreamIn.setPath(NULL);
+    SkFILEWStream opStreamOut(outFileStr.c_str());
+    if (!opStreamOut.isValid()) {
+        SkDebugf("%s couldn't open for writing %s\n", fun, outFileStr.c_str());
+        return false;
+    }
+
+    char* opInsert = strstr(opData.begin(), marker);
+    if (!opInsert) {
+        SkDebugf("%s missing marker in %s\n", fun, outFileStr.c_str());
+        return false;
+    }
+    const char* opInsertEnd = opInsert + strlen(marker);
+    if (marker2) {
+        char* opInsert2 = strstr(opInsert, marker2);
+        if (!opInsert2) {
+            SkDebugf("%s missing marker second half in %s\n", fun, outFileStr.c_str());
+            return false;
+        }
+        opInsertEnd = opInsert2 + strlen(marker2);
+    }
+    opStreamOut.write(opData.begin(), opInsert - opData.begin());
+    opStreamOut.write(replace, replaceLen);
+    opStreamOut.write(opInsertEnd, opLen - (opInsertEnd - opData.begin()));
+    opStreamOut.flush();
+    return true;
+}
+
+int main (int argc, char * const argv[]) {
+    if (argc != 2) {
+        SkDebugf("%s expected filename\n", argv[0]);
+        return 0;
+    }
+    const char* dir = "../../experimental/Intersection/";
+    SkString inFileStr;
+    if (argv[1][0] != '/') {
+        inFileStr.append(dir);
+    }
+    inFileStr.append(argv[1]);
+    SkFILEStream inFile(inFileStr.c_str());
+    if (!inFile.isValid()) {
+        SkDebugf("%s couldn't find %s\n", argv[0], argv[1]);
+        return 0;
+    }
+    SkTDArray<char> inData;
+    inData.setCount(inFile.getLength());
+    size_t inLen = inData.count();
+    inFile.read(inData.begin(), inLen);
+    inFile.setPath(NULL);
+    char* insert = strstr(inData.begin(), "\n\n\n");
+    if (!insert) {
+        SkDebugf("%s missing two blank line delimiter in %s\n", argv[0], argv[1]);
+        return 0;
+    }
+    insert += 1; // include first blank line
+    const char opMarker[] =
+            "</div>" "\n"
+            "\n"
+            "<script type=\"text/javascript\">" "\n"
+            "\n"
+            "var testDivs = ["  "\n"
+            ;
+    if (!replace(argv[0], dir, "op.htm", opMarker, NULL, inData.begin(),
+            insert - inData.begin())) {
+        return 0;
+    }
+    const char newMarker[] =
+            "static void (*firstTest)() = "
+            ;
+    const char newMarker2[] =
+            ";"  "\n"
+            "\n"
+            "static struct {"  "\n"
+            "    void (*fun)();"  "\n"
+            "    const char* str;"  "\n"
+            "} tests[] = {"  "\n"
+            ;
+    if (!replace(argv[0], dir, "SimplifyNew_Test.cpp", newMarker, newMarker2, insert + 2,
+            inLen - (insert - inData.begin()) - 2)) {
+        return 0;
+    }
+    const char simplifyMarker[] =
+            "#if 1 // set to 1 for multiple thread -- no debugging"
+            ;
+    const char simplifyReplace[] =
+            "#if 0 // set to 1 for multiple thread -- no debugging"
+            ;
+    if (!replace(argv[0], dir, "Simplify.cpp", simplifyMarker, NULL, simplifyReplace,
+            sizeof(simplifyReplace) - 1)) {
+        return 0;
+    }
+    return 0;
+}
diff --git a/experimental/Intersection/ConvexHull.cpp b/experimental/Intersection/ConvexHull.cpp
new file mode 100644
index 0000000..ad932e3
--- /dev/null
+++ b/experimental/Intersection/ConvexHull.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "CurveUtilities.h"
+#include "IntersectionUtilities.h"
+
+/* Given a cubic, find the convex hull described by the end and control points.
+   The hull may have 3 or 4 points. Cubics that degenerate into a point or line
+   are not considered.
+
+   The hull is computed by assuming that three points, if unique and non-linear,
+   form a triangle. The fourth point may replace one of the first three, may be
+   discarded if in the triangle or on an edge, or may be inserted between any of
+   the three to form a convex quadralateral.
+
+   The indices returned in order describe the convex hull.
+*/
+int convex_hull(const Cubic& cubic, char order[4]) {
+    size_t index;
+    // find top point
+    size_t yMin = 0;
+    for (index = 1; index < 4; ++index) {
+        if (cubic[yMin].y > cubic[index].y || (cubic[yMin].y == cubic[index].y
+                && cubic[yMin].x > cubic[index].x)) {
+            yMin = index;
+        }
+    }
+    order[0] = yMin;
+    int midX = -1;
+    int backupYMin = -1;
+    for (int pass = 0; pass < 2; ++pass) {
+        for (index = 0; index < 4; ++index) {
+            if (index == yMin) {
+                continue;
+            }
+            // rotate line from (yMin, index) to axis
+            // see if remaining two points are both above or below
+            // use this to find mid
+            int mask = other_two(yMin, index);
+            int side1 = yMin ^ mask;
+            int side2 = index ^ mask;
+            Cubic rotPath;
+            if (!rotate(cubic, yMin, index, rotPath)) { // ! if cbc[yMin]==cbc[idx]
+                order[1] = side1;
+                order[2] = side2;
+                return 3;
+            }
+            int sides = side(rotPath[side1].y - rotPath[yMin].y);
+            sides ^= side(rotPath[side2].y - rotPath[yMin].y);
+            if (sides == 2) { // '2' means one remaining point <0, one >0
+                if (midX >= 0) {
+                    printf("%s unexpected mid\n", __FUNCTION__); // there can be only one mid
+                }
+                midX = index;
+            } else if (sides == 0) { // '0' means both to one side or the other
+                backupYMin = index;
+            }
+        }
+        if (midX >= 0) {
+            break;
+        }
+        if (backupYMin < 0) {
+            break;
+        }
+        yMin = backupYMin;
+        backupYMin = -1;
+    }
+    if (midX < 0) {
+        midX = yMin ^ 3; // choose any other point
+    }
+    int mask = other_two(yMin, midX);
+    int least = yMin ^ mask;
+    int most = midX ^ mask;
+    order[0] = yMin;
+    order[1] = least;
+
+    // see if mid value is on same side of line (least, most) as yMin
+    Cubic midPath;
+    if (!rotate(cubic, least, most, midPath)) { // ! if cbc[least]==cbc[most]
+        order[2] = midX;
+        return 3;
+    }
+    int midSides = side(midPath[yMin].y - midPath[least].y);
+    midSides ^= side(midPath[midX].y - midPath[least].y);
+    if (midSides != 2) {  // if mid point is not between
+        order[2] = most;
+        return 3; // result is a triangle
+    }
+    order[2] = midX;
+    order[3] = most;
+    return 4; // result is a quadralateral
+}
+
+/* Find the convex hull for cubics with the x-axis interval regularly spaced.
+   Cubics computed as distance functions are formed this way.
+
+   connectTo0[0], connectTo0[1] are the point indices that cubic[0] connects to.
+   connectTo3[0], connectTo3[1] are the point indices that cubic[3] connects to.
+
+   Returns true if cubic[1] to cubic[2] also forms part of the hull.
+*/
+bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]) {
+    double projectedY[4];
+    projectedY[0] = 0;
+    int index;
+    for (index = 1; index < 4; ++index) {
+        projectedY[index] = (cubic[index].y - cubic[0].y) * (3.0 / index);
+    }
+    int lower0Index = 1;
+    int upper0Index = 1;
+    for (index = 2; index < 4; ++index) {
+        if (approximately_greater(projectedY[lower0Index], projectedY[index])) {
+            lower0Index = index;
+        }
+        if (approximately_lesser(projectedY[upper0Index], projectedY[index])) {
+            upper0Index = index;
+        }
+    }
+    connectTo0[0] = lower0Index;
+    connectTo0[1] = upper0Index;
+    for (index = 0; index < 3; ++index) {
+        projectedY[index] = (cubic[3].y - cubic[index].y) * (3.0 / (3 - index));
+    }
+    projectedY[3] = 0;
+    int lower3Index = 2;
+    int upper3Index = 2;
+    for (index = 1; index > -1; --index) {
+        if (approximately_greater(projectedY[lower3Index], projectedY[index])) {
+            lower3Index = index;
+        }
+        if (approximately_lesser(projectedY[upper3Index], projectedY[index])) {
+            upper3Index = index;
+        }
+    }
+    connectTo3[0] = lower3Index;
+    connectTo3[1] = upper3Index;
+    return (1 << lower0Index | 1 << upper0Index
+            | 1 << lower3Index | 1 << upper3Index) == 0x0F;
+}
+
diff --git a/experimental/Intersection/ConvexHull_Test.cpp b/experimental/Intersection/ConvexHull_Test.cpp
new file mode 100644
index 0000000..bd83840
--- /dev/null
+++ b/experimental/Intersection/ConvexHull_Test.cpp
@@ -0,0 +1,471 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "Intersection_Tests.h"
+#include "IntersectionUtilities.h"
+
+const Cubic convex[] = {
+    {{0, 0}, {2, 0}, {2, 1}, {0, 1}},
+    {{1, 0}, {1, 1}, {0, 1}, {0, 0}},
+    {{1, 1}, {0, 1}, {0, 0}, {1, 0}},
+    {{0, 1}, {0, 0}, {1, 0}, {1, 1}},
+    {{0, 0}, {10, 0}, {10, 10}, {5, 6}},
+};
+
+size_t convex_count = sizeof(convex) / sizeof(convex[0]);
+
+const Cubic bowtie[] = {
+    {{0, 0}, {1, 1}, {1, 0}, {0, 1}},
+    {{1, 0}, {0, 1}, {1, 1}, {0, 0}},
+    {{1, 1}, {0, 0}, {0, 1}, {1, 0}},
+    {{0, 1}, {1, 0}, {0, 0}, {1, 1}},
+};
+
+size_t bowtie_count = sizeof(bowtie) / sizeof(bowtie[0]);
+
+const Cubic arrow[] = {
+    {{0, 0}, {10, 0}, {10, 10}, {5, 4}},
+    {{10, 0}, {10, 10}, {5, 4}, {0, 0}},
+    {{10, 10}, {5, 4}, {0, 0}, {10, 0}},
+    {{5, 4}, {0, 0}, {10, 0}, {10, 10}},
+};
+
+size_t arrow_count = sizeof(arrow) / sizeof(arrow[0]);
+
+const Cubic three[] = {
+    {{1, 0}, {1, 0}, {1, 1}, {0, 1}}, // 0 == 1
+    {{0, 0}, {1, 1}, {1, 1}, {0, 1}}, // 1 == 2
+    {{0, 0}, {1, 0}, {0, 1}, {0, 1}}, // 2 == 3
+    {{1, 0}, {1, 1}, {1, 0}, {0, 1}}, // 0 == 2
+    {{1, 0}, {1, 1}, {0, 1}, {1, 0}}, // 0 == 3
+    {{0, 0}, {1, 0}, {1, 1}, {1, 0}}, // 1 == 3
+};
+
+size_t three_count = sizeof(three) / sizeof(three[0]);
+
+const Cubic triangle[] = {
+    {{0, 0}, {1, 0}, {2, 0}, {0, 1}}, // extra point on horz
+    {{1, 0}, {2, 0}, {0, 1}, {0, 0}},
+    {{2, 0}, {0, 1}, {0, 0}, {1, 0}},
+    {{0, 1}, {0, 0}, {1, 0}, {2, 0}},
+
+    {{0, 0}, {0, 1}, {0, 2}, {1, 1}}, // extra point on vert
+    {{0, 1}, {0, 2}, {1, 1}, {0, 0}},
+    {{0, 2}, {1, 1}, {0, 0}, {0, 1}},
+    {{1, 1}, {0, 0}, {0, 1}, {0, 2}},
+
+    {{0, 0}, {1, 1}, {2, 2}, {2, 0}}, // extra point on diag
+    {{1, 1}, {2, 2}, {2, 0}, {0, 0}},
+    {{2, 2}, {2, 0}, {0, 0}, {1, 1}},
+    {{2, 0}, {0, 0}, {1, 1}, {2, 2}},
+
+    {{0, 0}, {2, 0}, {2, 2}, {1, 1}}, // extra point on diag
+    {{2, 0}, {2, 2}, {1, 1}, {0, 0}},
+    {{2, 2}, {1, 1}, {0, 0}, {2, 0}},
+    {{1, 1}, {0, 0}, {2, 0}, {2, 2}},
+};
+
+size_t triangle_count = sizeof(triangle) / sizeof(triangle[0]);
+
+const struct CubicDataSet {
+    const Cubic* data;
+    size_t size;
+} cubicDataSet[] = {
+    { three, three_count },
+    { convex, convex_count },
+    { bowtie, bowtie_count },
+    { arrow, arrow_count },
+    { triangle, triangle_count },
+};
+
+size_t cubicDataSet_count = sizeof(cubicDataSet) / sizeof(cubicDataSet[0]);
+
+typedef double Matrix3x2[3][2];
+
+static bool rotateToAxis(const _Point& a, const _Point& b, Matrix3x2& matrix) {
+    double dx = b.x - a.x;
+    double dy = b.y - a.y;
+    double length = sqrt(dx * dx + dy * dy);
+    if (length == 0) {
+        return false;
+    }
+    double invLength = 1 / length;
+    matrix[0][0] = dx * invLength;
+    matrix[1][0] = dy * invLength;
+    matrix[2][0] = 0;
+    matrix[0][1] = -dy * invLength;
+    matrix[1][1] = dx * invLength;
+    matrix[2][1] = 0;
+    return true;
+}
+
+static void transform(const Cubic& cubic, const Matrix3x2& matrix, Cubic& rotPath) {
+    for (int index = 0; index < 4; ++index) {
+        rotPath[index].x = cubic[index].x * matrix[0][0]
+                + cubic[index].y * matrix[1][0] + matrix[2][0];
+        rotPath[index].y = cubic[index].x * matrix[0][1]
+                + cubic[index].y * matrix[1][1] + matrix[2][1];
+    }
+}
+
+// brute force way to find convex hull:
+// pick two points
+// rotate all four until the two points are horizontal
+// are the remaining two points both above or below the horizontal line?
+// if so, the two points must be an edge of the convex hull
+static int rotate_to_hull(const Cubic& cubic, char order[4], size_t idx, size_t inr) {
+    bool debug_rotate_to_hull = false;
+    int outsidePtSet[4];
+    memset(outsidePtSet, -1, sizeof(outsidePtSet));
+    for (int outer = 0; outer < 3; ++outer) {
+        for (int priorOuter = 0; priorOuter < outer; ++priorOuter) {
+            if (cubic[outer].approximatelyEqual(cubic[priorOuter])) {
+                goto skip;
+            }
+        }
+        for (int inner = outer + 1; inner < 4; ++inner) {
+            for (int priorInner = outer + 1; priorInner < inner; ++priorInner) {
+                if (cubic[inner].approximatelyEqual(cubic[priorInner])) {
+                    goto skipInner;
+                }
+            }
+            if (cubic[outer].approximatelyEqual(cubic[inner])) {
+                continue;
+            }
+            Matrix3x2 matrix;
+            if (!rotateToAxis(cubic[outer], cubic[inner], matrix)) {
+                continue;
+            }
+            Cubic rotPath;
+            transform(cubic, matrix, rotPath);
+            int sides[3];
+            int zeroes;
+            zeroes = -1;
+            bzero(sides, sizeof(sides));
+            if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] src=(%g,%g) rot=", __FUNCTION__,
+                    (int)idx, (int)inr, (int)outer, (int)inner,
+                    cubic[inner].x, cubic[inner].y);
+            for (int index = 0; index < 4; ++index) {
+                if (debug_rotate_to_hull) printf("(%g,%g) ", rotPath[index].x, rotPath[index].y);
+                sides[side(rotPath[index].y - rotPath[inner].y)]++;
+                if (index != outer && index != inner
+                        && side(rotPath[index].y - rotPath[inner].y) == 1)
+                    zeroes = index;
+            }
+            if (debug_rotate_to_hull) printf("sides=(%d,%d,%d)\n", sides[0], sides[1], sides[2]);
+            if (sides[0] && sides[2]) {
+                continue;
+            }
+            if (sides[1] == 3 && zeroes >= 0) {
+                // verify that third point is between outer, inner
+                // if either of remaining two equals outer or equal, pick lower
+                if (rotPath[zeroes].approximatelyEqual(rotPath[inner])
+                        && zeroes < inner) {
+                    if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] zeroes < inner\n",
+                        __FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
+                    continue;
+                }
+                 if (rotPath[zeroes].approximatelyEqual(rotPath[outer])
+                        && zeroes < outer) {
+                    if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] zeroes < outer\n",
+                        __FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
+                    continue;
+                }
+                if (rotPath[zeroes].x < rotPath[inner].x
+                        && rotPath[zeroes].x < rotPath[outer].x) {
+                    if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] zeroes < inner && outer\n",
+                        __FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
+                    continue;
+                }
+                if (rotPath[zeroes].x > rotPath[inner].x
+                        && rotPath[zeroes].x > rotPath[outer].x) {
+                    if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] zeroes > inner && outer\n",
+                        __FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
+                    continue;
+                }
+            }
+            if (outsidePtSet[outer] < 0) {
+                outsidePtSet[outer] = inner;
+            } else {
+                if (outsidePtSet[inner] > 0) {
+                    if (debug_rotate_to_hull) printf("%s [%d,%d] [o=%d,i=%d] too many rays from one point\n",
+                        __FUNCTION__, (int)idx, (int)inr, (int)outer, (int)inner);
+                }
+                outsidePtSet[inner] = outer;
+            }
+skipInner:
+            ;
+        }
+skip:
+        ;
+    }
+    int totalSides = 0;
+    int first = 0;
+    for (; first < 4; ++first) {
+        if (outsidePtSet[first] >= 0) {
+            break;
+        }
+    }
+    if (first > 3) {
+        order[0] = 0;
+        return 1;
+    }
+    int next = first;
+    do {
+        order[totalSides++] = next;
+        next = outsidePtSet[next];
+    } while (next != -1 && next != first);
+    return totalSides;
+}
+
+int firstIndex = 0;
+int firstInner = 0;
+
+void ConvexHull_Test() {
+    for (size_t index = firstIndex; index < cubicDataSet_count; ++index) {
+        const CubicDataSet& set = cubicDataSet[index];
+        for (size_t inner = firstInner; inner < set.size; ++inner) {
+            const Cubic& cubic = set.data[inner];
+            char order[4], cmpOrder[4];
+            int cmp = rotate_to_hull(cubic, cmpOrder, index, inner);
+            if (cmp < 3) {
+                continue;
+            }
+            int result = convex_hull(cubic, order);
+            if (cmp != result) {
+                printf("%s [%d,%d] result=%d cmp=%d\n", __FUNCTION__,
+                    (int)index, (int)inner, result, cmp);
+                continue;
+            }
+            // check for same indices
+            char pts = 0;
+            char cmpPts = 0;
+            int pt, bit;
+            for (pt = 0; pt < cmp; ++pt) {
+                if (pts & 1 << order[pt]) {
+                    printf("%s [%d,%d] duplicate index in order: %d,%d,%d",
+                            __FUNCTION__, (int)index, (int)inner,
+                            order[0], order[1], order[2]);
+                    if (cmp == 4) {
+                        printf(",%d", order[3]);
+                    }
+                    printf("\n");
+                    goto next;
+                }
+                if (cmpPts & 1 << cmpOrder[pt]) {
+                    printf("%s [%d,%d] duplicate index in order: %d,%d,%d",
+                            __FUNCTION__, (int)index, (int)inner,
+                            cmpOrder[0], cmpOrder[1], cmpOrder[2]);
+                    if (cmp == 4) {
+                        printf(",%d", cmpOrder[3]);
+                    }
+                    printf("\n");
+                    goto next;
+                }
+                pts |= 1 << order[pt];
+                cmpPts |= 1 << cmpOrder[pt];
+            }
+            for (bit = 0; bit < 4; ++bit) {
+                if (pts & 1 << bit) {
+                    continue;
+                }
+                for (pt = 0; pt < cmp; ++pt) {
+                    if (order[pt] == bit) {
+                        continue;
+                    }
+                    if (cubic[order[pt]] == cubic[bit]) {
+                        pts |= 1 << bit;
+                    }
+                }
+            }
+            for (bit = 0; bit < 4; ++bit) {
+                if (cmpPts & 1 << bit) {
+                    continue;
+                }
+                for (pt = 0; pt < cmp; ++pt) {
+                    if (cmpOrder[pt] == bit) {
+                        continue;
+                    }
+                    if (cubic[cmpOrder[pt]] == cubic[bit]) {
+                        cmpPts |= 1 << bit;
+                    }
+                }
+            }
+            if (pts != cmpPts) {
+                printf("%s [%d,%d] mismatch indices: order=%d,%d,%d",
+                        __FUNCTION__, (int)index, (int)inner,
+                        order[0], order[1], order[2]);
+                if (cmp == 4) {
+                    printf(",%d", order[3]);
+                }
+                printf(" cmpOrder=%d,%d,%d", cmpOrder[0], cmpOrder[1], cmpOrder[2]);
+                if (cmp == 4) {
+                    printf(",%d", cmpOrder[3]);
+                }
+                printf("\n");
+                continue;
+            }
+            if (cmp == 4) { // check for bow ties
+                int match = 0;
+                while (cmpOrder[match] != order[0]) {
+                    ++match;
+                }
+                if (cmpOrder[match ^ 2] != order[2]) {
+                    printf("%s [%d,%d] bowtie mismatch: order=%d,%d,%d,%d"
+                            " cmpOrder=%d,%d,%d,%d\n",
+                            __FUNCTION__, (int)index, (int)inner,
+                            order[0], order[1], order[2], order[3],
+                            cmpOrder[0], cmpOrder[1], cmpOrder[2], cmpOrder[3]);
+                }
+            }
+    next:
+            ;
+        }
+    }
+}
+
+const double a = 1.0/3;
+const double b = 2.0/3;
+
+const Cubic x_cubic[] = {
+    {{0, 0}, {a, 0}, {b, 0}, {1, 0}}, // 0
+    {{0, 0}, {a, 0}, {b, 0}, {1, 1}}, // 1
+    {{0, 0}, {a, 0}, {b, 1}, {1, 0}}, // 2
+    {{0, 0}, {a, 0}, {b, 1}, {1, 1}}, // 3
+    {{0, 0}, {a, 1}, {b, 0}, {1, 0}}, // 4
+    {{0, 0}, {a, 1}, {b, 0}, {1, 1}}, // 5
+    {{0, 0}, {a, 1}, {b, 1}, {1, 0}}, // 6
+    {{0, 0}, {a, 1}, {b, 1}, {1, 1}}, // 7
+    {{0, 1}, {a, 0}, {b, 0}, {1, 0}}, // 8
+    {{0, 1}, {a, 0}, {b, 0}, {1, 1}}, // 9
+    {{0, 1}, {a, 0}, {b, 1}, {1, 0}}, // 10
+    {{0, 1}, {a, 0}, {b, 1}, {1, 1}}, // 11
+    {{0, 1}, {a, 1}, {b, 0}, {1, 0}}, // 12
+    {{0, 1}, {a, 1}, {b, 0}, {1, 1}}, // 13
+    {{0, 1}, {a, 1}, {b, 1}, {1, 0}}, // 14
+    {{0, 1}, {a, 1}, {b, 1}, {1, 1}}, // 15
+};
+
+size_t x_cubic_count = sizeof(x_cubic) / sizeof(x_cubic[0]);
+
+static int first_x_test = 0;
+
+void ConvexHull_X_Test() {
+    for (size_t index = first_x_test; index < x_cubic_count; ++index) {
+        const Cubic& cubic = x_cubic[index];
+        char connectTo0[2] = {-1, -1};
+        char connectTo3[2] = {-1, -1};
+        convex_x_hull(cubic, connectTo0, connectTo3);
+        int idx, cmp;
+        for (idx = 0; idx < 2; ++idx) {
+            if (connectTo0[idx] >= 1 && connectTo0[idx] < 4) {
+                continue;
+            } else {
+                printf("%s connectTo0[idx]=%d", __FUNCTION__, connectTo0[idx]);
+            }
+            if (connectTo3[idx] >= 0 && connectTo3[idx] < 3) {
+                continue;
+            } else {
+                printf("%s connectTo3[idx]=%d", __FUNCTION__, connectTo3[idx]);
+            }
+            goto nextTest;
+        }
+        char rOrder[4];
+        char cmpOrder[4];
+        cmp = rotate_to_hull(cubic, cmpOrder, index, 0);
+        if (index == 0 || index == 15) {
+            // FIXME: make rotate_to_hull work for degenerate 2 edge hull cases
+            cmpOrder[0] = 0;
+            cmpOrder[1] = 3;
+            cmp = 2;
+        }
+        if (cmp < 3) {
+            // FIXME: make rotate_to_hull work for index == 3 etc
+            continue;
+        }
+        for (idx = 0; idx < cmp; ++idx) {
+            if (cmpOrder[idx] == 0) {
+                rOrder[0] = cmpOrder[(idx + 1) % cmp];
+                rOrder[1] = cmpOrder[(idx + cmp - 1) % cmp];
+            } else if (cmpOrder[idx] == 3) {
+                rOrder[2] = cmpOrder[(idx + 1) % cmp];
+                rOrder[3] = cmpOrder[(idx + cmp - 1) % cmp];
+            }
+        }
+        if (connectTo0[0] != connectTo0[1]) {
+            if (rOrder[0] == rOrder[1]) {
+                printf("%s [%d] (1) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                    __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
+                    connectTo3[0], connectTo3[1],
+                    rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
+                continue;
+            }
+            int unused = 6 - connectTo0[0] - connectTo0[1];
+            int rUnused = 6 - rOrder[0] - rOrder[1];
+            if (unused != rUnused) {
+                printf("%s [%d] (2) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                    __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
+                    connectTo3[0], connectTo3[1],
+                    rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
+                continue;
+            }
+        } else {
+            if (rOrder[0] != rOrder[1]) {
+                printf("%s [%d] (3) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                    __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
+                    connectTo3[0], connectTo3[1],
+                    rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
+                continue;
+            }
+            if (connectTo0[0] != rOrder[0]) {
+                printf("%s [%d] (4) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                    __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
+                    connectTo3[0], connectTo3[1],
+                    rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
+                continue;
+            }
+        }
+        if (connectTo3[0] != connectTo3[1]) {
+             if (rOrder[2] == rOrder[3]) {
+                printf("%s [%d] (5) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                    __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
+                    connectTo3[0], connectTo3[1],
+                    rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
+                continue;
+            }
+           int unused = 6 - connectTo3[0] - connectTo3[1];
+           int rUnused = 6 - rOrder[2] - rOrder[3];
+            if (unused != rUnused) {
+                printf("%s [%d] (6) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                    __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
+                    connectTo3[0], connectTo3[1],
+                    rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
+                continue;
+            }
+        } else {
+            if (rOrder[2] != rOrder[3]) {
+                printf("%s [%d] (7) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                    __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
+                    connectTo3[0], connectTo3[1],
+                    rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
+                continue;
+            }
+            if (connectTo3[1] != rOrder[3]) {
+                printf("%s [%d] (8) order=(%d,%d,%d,%d) r_order=(%d,%d,%d,%d)\n",
+                    __FUNCTION__, (int)index, connectTo0[0], connectTo0[1],
+                    connectTo3[0], connectTo3[1],
+                    rOrder[0], rOrder[1], rOrder[2], rOrder[3]);
+                continue;
+            }
+        }
+nextTest:
+        ;
+    }
+}
+
+
+
diff --git a/experimental/Intersection/CubeRoot.cpp b/experimental/Intersection/CubeRoot.cpp
new file mode 100644
index 0000000..82d2732
--- /dev/null
+++ b/experimental/Intersection/CubeRoot.cpp
@@ -0,0 +1,400 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+// http://metamerist.com/cbrt/CubeRoot.cpp
+//
+
+#include <math.h>
+#include "CubicUtilities.h"
+
+#define TEST_ALTERNATIVES 0
+#if TEST_ALTERNATIVES
+typedef float  (*cuberootfnf) (float);
+typedef double (*cuberootfnd) (double);
+
+// estimate bits of precision (32-bit float case)
+inline int bits_of_precision(float a, float b)
+{
+    const double kd = 1.0 / log(2.0);
+
+    if (a==b)
+        return 23;
+
+    const double kdmin = pow(2.0, -23.0);
+
+    double d = fabs(a-b);
+    if (d < kdmin)
+        return 23;
+
+    return int(-log(d)*kd);
+}
+
+// estiamte bits of precision (64-bit double case)
+inline int bits_of_precision(double a, double b)
+{
+    const double kd = 1.0 / log(2.0);
+
+    if (a==b)
+        return 52;
+
+    const double kdmin = pow(2.0, -52.0);
+
+    double d = fabs(a-b);
+    if (d < kdmin)
+        return 52;
+
+    return int(-log(d)*kd);
+}
+
+// cube root via x^(1/3)
+static float pow_cbrtf(float x)
+{
+    return (float) pow(x, 1.0f/3.0f);
+}
+
+// cube root via x^(1/3)
+static double pow_cbrtd(double x)
+{
+    return pow(x, 1.0/3.0);
+}
+
+// cube root approximation using bit hack for 32-bit float
+static  float cbrt_5f(float f)
+{
+    unsigned int* p = (unsigned int *) &f;
+    *p = *p/3 + 709921077;
+    return f;
+}
+#endif
+
+// cube root approximation using bit hack for 64-bit float
+// adapted from Kahan's cbrt
+static  double cbrt_5d(double d)
+{
+    const unsigned int B1 = 715094163;
+    double t = 0.0;
+    unsigned int* pt = (unsigned int*) &t;
+    unsigned int* px = (unsigned int*) &d;
+    pt[1]=px[1]/3+B1;
+    return t;
+}
+
+#if TEST_ALTERNATIVES
+// cube root approximation using bit hack for 64-bit float
+// adapted from Kahan's cbrt
+#if 0
+static  double quint_5d(double d)
+{
+    return sqrt(sqrt(d));
+
+    const unsigned int B1 = 71509416*5/3;
+    double t = 0.0;
+    unsigned int* pt = (unsigned int*) &t;
+    unsigned int* px = (unsigned int*) &d;
+    pt[1]=px[1]/5+B1;
+    return t;
+}
+#endif
+
+// iterative cube root approximation using Halley's method (float)
+static  float cbrta_halleyf(const float a, const float R)
+{
+    const float a3 = a*a*a;
+    const float b= a * (a3 + R + R) / (a3 + a3 + R);
+    return b;
+}
+#endif
+
+// iterative cube root approximation using Halley's method (double)
+static  double cbrta_halleyd(const double a, const double R)
+{
+    const double a3 = a*a*a;
+    const double b= a * (a3 + R + R) / (a3 + a3 + R);
+    return b;
+}
+
+#if TEST_ALTERNATIVES
+// iterative cube root approximation using Newton's method (float)
+static  float cbrta_newtonf(const float a, const float x)
+{
+//    return (1.0 / 3.0) * ((a + a) + x / (a * a));
+    return a - (1.0f / 3.0f) * (a - x / (a*a));
+}
+
+// iterative cube root approximation using Newton's method (double)
+static  double cbrta_newtond(const double a, const double x)
+{
+    return (1.0/3.0) * (x / (a*a) + 2*a);
+}
+
+// cube root approximation using 1 iteration of Halley's method (double)
+static double halley_cbrt1d(double d)
+{
+    double a = cbrt_5d(d);
+    return cbrta_halleyd(a, d);
+}
+
+// cube root approximation using 1 iteration of Halley's method (float)
+static float halley_cbrt1f(float d)
+{
+    float a = cbrt_5f(d);
+    return cbrta_halleyf(a, d);
+}
+
+// cube root approximation using 2 iterations of Halley's method (double)
+static double halley_cbrt2d(double d)
+{
+    double a = cbrt_5d(d);
+    a = cbrta_halleyd(a, d);
+    return cbrta_halleyd(a, d);
+}
+#endif
+
+// cube root approximation using 3 iterations of Halley's method (double)
+static double halley_cbrt3d(double d)
+{
+    double a = cbrt_5d(d);
+    a = cbrta_halleyd(a, d);
+    a = cbrta_halleyd(a, d);
+    return cbrta_halleyd(a, d);
+}
+
+#if TEST_ALTERNATIVES
+// cube root approximation using 2 iterations of Halley's method (float)
+static float halley_cbrt2f(float d)
+{
+    float a = cbrt_5f(d);
+    a = cbrta_halleyf(a, d);
+    return cbrta_halleyf(a, d);
+}
+
+// cube root approximation using 1 iteration of Newton's method (double)
+static double newton_cbrt1d(double d)
+{
+    double a = cbrt_5d(d);
+    return cbrta_newtond(a, d);
+}
+
+// cube root approximation using 2 iterations of Newton's method (double)
+static double newton_cbrt2d(double d)
+{
+    double a = cbrt_5d(d);
+    a = cbrta_newtond(a, d);
+    return cbrta_newtond(a, d);
+}
+
+// cube root approximation using 3 iterations of Newton's method (double)
+static double newton_cbrt3d(double d)
+{
+    double a = cbrt_5d(d);
+    a = cbrta_newtond(a, d);
+    a = cbrta_newtond(a, d);
+    return cbrta_newtond(a, d);
+}
+
+// cube root approximation using 4 iterations of Newton's method (double)
+static double newton_cbrt4d(double d)
+{
+    double a = cbrt_5d(d);
+    a = cbrta_newtond(a, d);
+    a = cbrta_newtond(a, d);
+    a = cbrta_newtond(a, d);
+    return cbrta_newtond(a, d);
+}
+
+// cube root approximation using 2 iterations of Newton's method (float)
+static float newton_cbrt1f(float d)
+{
+    float a = cbrt_5f(d);
+    return cbrta_newtonf(a, d);
+}
+
+// cube root approximation using 2 iterations of Newton's method (float)
+static float newton_cbrt2f(float d)
+{
+    float a = cbrt_5f(d);
+    a = cbrta_newtonf(a, d);
+    return cbrta_newtonf(a, d);
+}
+
+// cube root approximation using 3 iterations of Newton's method (float)
+static float newton_cbrt3f(float d)
+{
+    float a = cbrt_5f(d);
+    a = cbrta_newtonf(a, d);
+    a = cbrta_newtonf(a, d);
+    return cbrta_newtonf(a, d);
+}
+
+// cube root approximation using 4 iterations of Newton's method (float)
+static float newton_cbrt4f(float d)
+{
+    float a = cbrt_5f(d);
+    a = cbrta_newtonf(a, d);
+    a = cbrta_newtonf(a, d);
+    a = cbrta_newtonf(a, d);
+    return cbrta_newtonf(a, d);
+}
+
+static double TestCubeRootf(const char* szName, cuberootfnf cbrt, double rA, double rB, int rN)
+{
+    const int N = rN;
+
+    float dd = float((rB-rA) / N);
+
+    // calculate 1M numbers
+    int i=0;
+    float d = (float) rA;
+
+    double s = 0.0;
+
+    for(d=(float) rA, i=0; i<N; i++, d += dd)
+    {
+        s += cbrt(d);
+    }
+
+    double bits = 0.0;
+    double worstx=0.0;
+    double worsty=0.0;
+    int minbits=64;
+
+    for(d=(float) rA, i=0; i<N; i++, d += dd)
+    {
+        float a = cbrt((float) d);
+        float b = (float) pow((double) d, 1.0/3.0);
+
+        int bc = bits_of_precision(a, b);
+        bits += bc;
+
+        if (b > 1.0e-6)
+        {
+            if (bc < minbits)
+            {
+                minbits = bc;
+                worstx = d;
+                worsty = a;
+            }
+        }
+    }
+
+    bits /= N;
+
+    printf(" %3d mbp  %6.3f abp\n", minbits, bits);
+
+    return s;
+}
+
+
+static double TestCubeRootd(const char* szName, cuberootfnd cbrt, double rA, double rB, int rN)
+{
+    const int N = rN;
+
+    double dd = (rB-rA) / N;
+
+    int i=0;
+
+    double s = 0.0;
+    double d = 0.0;
+
+    for(d=rA, i=0; i<N; i++, d += dd)
+    {
+        s += cbrt(d);
+    }
+
+
+    double bits = 0.0;
+    double worstx = 0.0;
+    double worsty = 0.0;
+    int minbits = 64;
+    for(d=rA, i=0; i<N; i++, d += dd)
+    {
+        double a = cbrt(d);
+        double b = pow(d, 1.0/3.0);
+
+        int bc = bits_of_precision(a, b); // min(53, count_matching_bitsd(a, b) - 12);
+        bits += bc;
+
+        if (b > 1.0e-6)
+        {
+            if (bc < minbits)
+            {
+                bits_of_precision(a, b);
+                minbits = bc;
+                worstx = d;
+                worsty = a;
+            }
+        }
+    }
+
+    bits /= N;
+
+    printf(" %3d mbp  %6.3f abp\n", minbits, bits);
+
+    return s;
+}
+
+static int _tmain()
+{
+    // a million uniform steps through the range from 0.0 to 1.0
+    // (doing uniform steps in the log scale would be better)
+    double a = 0.0;
+    double b = 1.0;
+    int n = 1000000;
+
+    printf("32-bit float tests\n");
+    printf("----------------------------------------\n");
+    TestCubeRootf("cbrt_5f", cbrt_5f, a, b, n);
+    TestCubeRootf("pow", pow_cbrtf, a, b, n);
+    TestCubeRootf("halley x 1", halley_cbrt1f, a, b, n);
+    TestCubeRootf("halley x 2", halley_cbrt2f, a, b, n);
+    TestCubeRootf("newton x 1", newton_cbrt1f, a, b, n);
+    TestCubeRootf("newton x 2", newton_cbrt2f, a, b, n);
+    TestCubeRootf("newton x 3", newton_cbrt3f, a, b, n);
+    TestCubeRootf("newton x 4", newton_cbrt4f, a, b, n);
+    printf("\n\n");
+
+    printf("64-bit double tests\n");
+    printf("----------------------------------------\n");
+    TestCubeRootd("cbrt_5d", cbrt_5d, a, b, n);
+    TestCubeRootd("pow", pow_cbrtd, a, b, n);
+    TestCubeRootd("halley x 1", halley_cbrt1d, a, b, n);
+    TestCubeRootd("halley x 2", halley_cbrt2d, a, b, n);
+    TestCubeRootd("halley x 3", halley_cbrt3d, a, b, n);
+    TestCubeRootd("newton x 1", newton_cbrt1d, a, b, n);
+    TestCubeRootd("newton x 2", newton_cbrt2d, a, b, n);
+    TestCubeRootd("newton x 3", newton_cbrt3d, a, b, n);
+    TestCubeRootd("newton x 4", newton_cbrt4d, a, b, n);
+    printf("\n\n");
+
+    return 0;
+}
+#endif
+
+double cube_root(double x) {
+    if (approximately_zero(x)) {
+        return 0;
+    }
+    double result = halley_cbrt3d(fabs(x));
+    if (x < 0) {
+        result = -result;
+    }
+    return result;
+}
+
+#if TEST_ALTERNATIVES
+// http://bytes.com/topic/c/answers/754588-tips-find-cube-root-program-using-c
+/* cube root */
+int icbrt(int n) {
+    int t=0, x=(n+2)/3; /* works for n=0 and n>=1 */
+    for(; t!=x;) {
+        int x3=x*x*x;
+        t=x;
+        x*=(2*n + x3);
+        x/=(2*x3 + n);
+    }
+    return x ; /* always(?) equal to floor(n^(1/3)) */
+}
+#endif
diff --git a/experimental/Intersection/CubicBezierClip.cpp b/experimental/Intersection/CubicBezierClip.cpp
new file mode 100644
index 0000000..d0141e9
--- /dev/null
+++ b/experimental/Intersection/CubicBezierClip.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 "CurveIntersection.h"
+#include "CurveUtilities.h"
+#include "LineParameters.h"
+#include <algorithm> // used for std::swap
+
+// return false if unable to clip (e.g., unable to create implicit line)
+// caller should subdivide, or create degenerate if the values are too small
+bool bezier_clip(const Cubic& cubic1, const Cubic& cubic2, double& minT, double& maxT) {
+    minT = 1;
+    maxT = 0;
+    // determine normalized implicit line equation for pt[0] to pt[3]
+    //   of the form ax + by + c = 0, where a*a + b*b == 1
+
+    // find the implicit line equation parameters
+    LineParameters endLine;
+    endLine.cubicEndPoints(cubic1);
+    if (!endLine.normalize()) {
+        printf("line cannot be normalized: need more code here\n");
+        return false;
+    }
+
+    double distance[2];
+    endLine.controlPtDistance(cubic1, distance);
+
+    // find fat line
+    double top = distance[0];
+    double bottom = distance[1];
+    if (top > bottom) {
+        std::swap(top, bottom);
+    }
+    if (top * bottom >= 0) {
+        const double scale = 3/4.0; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf (13)
+        if (top < 0) {
+            top *= scale;
+            bottom = 0;
+        } else {
+            top = 0;
+            bottom *= scale;
+        }
+    } else {
+        const double scale = 4/9.0; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf (15)
+        top *= scale;
+        bottom *= scale;
+    }
+
+    // compute intersecting candidate distance
+    Cubic distance2y; // points with X of (0, 1/3, 2/3, 1)
+    endLine.cubicDistanceY(cubic2, distance2y);
+
+    int flags = 0;
+    if (approximately_lesser(distance2y[0].y, top)) {
+        flags |= kFindTopMin;
+    } else if (approximately_greater(distance2y[0].y, bottom)) {
+        flags |= kFindBottomMin;
+    } else {
+        minT = 0;
+    }
+
+    if (approximately_lesser(distance2y[3].y, top)) {
+        flags |= kFindTopMax;
+    } else if (approximately_greater(distance2y[3].y, bottom)) {
+        flags |= kFindBottomMax;
+    } else {
+        maxT = 1;
+    }
+    // Find the intersection of distance convex hull and fat line.
+    char to_0[2];
+    char to_3[2];
+    bool do_1_2_edge = convex_x_hull(distance2y, to_0, to_3);
+    x_at(distance2y[0], distance2y[to_0[0]], top, bottom, flags, minT, maxT);
+    if (to_0[0] != to_0[1]) {
+        x_at(distance2y[0], distance2y[to_0[1]], top, bottom, flags, minT, maxT);
+    }
+    x_at(distance2y[to_3[0]], distance2y[3], top, bottom, flags, minT, maxT);
+    if (to_3[0] != to_3[1]) {
+        x_at(distance2y[to_3[1]], distance2y[3], top, bottom, flags, minT, maxT);
+    }
+    if (do_1_2_edge) {
+        x_at(distance2y[1], distance2y[2], top, bottom, flags, minT, maxT);
+    }
+
+    return minT < maxT; // returns false if distance shows no intersection
+}
+
diff --git a/experimental/Intersection/CubicBezierClip_Test.cpp b/experimental/Intersection/CubicBezierClip_Test.cpp
new file mode 100644
index 0000000..2f1e3c6
--- /dev/null
+++ b/experimental/Intersection/CubicBezierClip_Test.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 "CurveIntersection.h"
+#include "CubicIntersection_TestData.h"
+#include "Intersection_Tests.h"
+
+void CubicBezierClip_Test() {
+    for (size_t index = 0; index < tests_count; ++index) {
+        const Cubic& cubic1 = tests[index][0];
+        const Cubic& cubic2 = tests[index][1];
+        Cubic reduce1, reduce2;
+        int order1 = reduceOrder(cubic1, reduce1, kReduceOrder_NoQuadraticsAllowed);
+        int order2 = reduceOrder(cubic2, reduce2, kReduceOrder_NoQuadraticsAllowed);
+        if (order1 < 4) {
+            printf("%s [%d] cubic1 order=%d\n", __FUNCTION__, (int) index, order1);
+        }
+        if (order2 < 4) {
+            printf("%s [%d] cubic2 order=%d\n", __FUNCTION__, (int) index, order2);
+        }
+        if (order1 == 4 && order2 == 4) {
+            double minT = 0;
+            double maxT = 1;
+            bezier_clip(reduce1, reduce2, minT, maxT);
+        }
+    }
+}
diff --git a/experimental/Intersection/CubicBounds.cpp b/experimental/Intersection/CubicBounds.cpp
new file mode 100644
index 0000000..c7ca49a
--- /dev/null
+++ b/experimental/Intersection/CubicBounds.cpp
@@ -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.
+ */
+#include "CurveIntersection.h"
+#include "CurveUtilities.h"
+#include "Extrema.h"
+
+static int isBoundedByEndPoints(double a, double b, double c, double d)
+{
+    return (a <= b && a <= c && b <= d && c <= d)
+            || (a >= b && a >= c && b >= d && c >= d);
+}
+
+double leftMostT(const Cubic& cubic, double startT, double endT) {
+    double leftTs[2];
+    _Point pt[2];
+    int results = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x,
+            leftTs);
+    int best = -1;
+    for (int index = 0; index < results; ++index) {
+        if (startT > leftTs[index] || leftTs[index] > endT) {
+            continue;
+        }
+        if (best < 0) {
+            best = index;
+            continue;
+        }
+        xy_at_t(cubic, leftTs[0], pt[0].x, pt[0].y);
+        xy_at_t(cubic, leftTs[1], pt[1].x, pt[1].y);
+        if (pt[0].x > pt[1].x) {
+            best = 1;
+        }
+    }
+    if (best >= 0) {
+        return leftTs[best];
+    }
+    xy_at_t(cubic, startT, pt[0].x, pt[0].y);
+    xy_at_t(cubic, endT, pt[1].x, pt[1].y);
+    return pt[0].x <= pt[1].x ? startT : endT;
+}
+
+void _Rect::setBounds(const Cubic& cubic) {
+    set(cubic[0]);
+    add(cubic[3]);
+    double tValues[4];
+    int roots = 0;
+    if (!isBoundedByEndPoints(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x)) {
+        roots = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x,
+                cubic[3].x, tValues);
+    }
+    if (!isBoundedByEndPoints(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y)) {
+        roots += findExtrema(cubic[0].y, cubic[1].y, cubic[2].y,
+                cubic[3].y, &tValues[roots]);
+    }
+    for (int x = 0; x < roots; ++x) {
+        _Point result;
+        xy_at_t(cubic, tValues[x], result.x, result.y);
+        add(result);
+    }
+}
+
+void _Rect::setRawBounds(const Cubic& cubic) {
+    set(cubic[0]);
+    for (int x = 1; x < 4; ++x) {
+        add(cubic[x]);
+    }
+}
diff --git a/experimental/Intersection/CubicIntersection.cpp b/experimental/Intersection/CubicIntersection.cpp
new file mode 100644
index 0000000..452ef29
--- /dev/null
+++ b/experimental/Intersection/CubicIntersection.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 "CurveIntersection.h"
+#include "Intersections.h"
+#include "IntersectionUtilities.h"
+#include "LineIntersection.h"
+
+class CubicIntersections : public Intersections {
+public:
+
+CubicIntersections(const Cubic& c1, const Cubic& c2, Intersections& i)
+    : cubic1(c1)
+    , cubic2(c2)
+    , intersections(i)
+    , depth(0)
+    , splits(0) {
+}
+
+bool intersect() {
+    double minT1, minT2, maxT1, maxT2;
+    if (!bezier_clip(cubic2, cubic1, minT1, maxT1)) {
+        return false;
+    }
+    if (!bezier_clip(cubic1, cubic2, minT2, maxT2)) {
+        return false;
+    }
+    int split;
+    if (maxT1 - minT1 < maxT2 - minT2) {
+        intersections.swap();
+        minT2 = 0;
+        maxT2 = 1;
+        split = maxT1 - minT1 > tClipLimit;
+    } else {
+        minT1 = 0;
+        maxT1 = 1;
+        split = (maxT2 - minT2 > tClipLimit) << 1;
+    }
+    return chop(minT1, maxT1, minT2, maxT2, split);
+}
+
+protected:
+
+bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
+    Cubic smaller, larger;
+    // FIXME: carry last subdivide and reduceOrder result with cubic
+    sub_divide(cubic1, minT1, maxT1, intersections.swapped() ? larger : smaller);
+    sub_divide(cubic2, minT2, maxT2, intersections.swapped() ? smaller : larger);
+    Cubic smallResult;
+    if (reduceOrder(smaller, smallResult,
+            kReduceOrder_NoQuadraticsAllowed) <= 2) {
+        Cubic largeResult;
+        if (reduceOrder(larger, largeResult,
+                kReduceOrder_NoQuadraticsAllowed) <= 2) {
+            const _Line& smallLine = (const _Line&) smallResult;
+            const _Line& largeLine = (const _Line&) largeResult;
+            double smallT[2];
+            double largeT[2];
+            // FIXME: this doesn't detect or deal with coincident lines
+            if (!::intersect(smallLine, largeLine, smallT, largeT)) {
+                return false;
+            }
+            if (intersections.swapped()) {
+                smallT[0] = interp(minT2, maxT2, smallT[0]);
+                largeT[0] = interp(minT1, maxT1, largeT[0]);
+            } else {
+                smallT[0] = interp(minT1, maxT1, smallT[0]);
+                largeT[0] = interp(minT2, maxT2, largeT[0]);
+            }
+            intersections.add(smallT[0], largeT[0]);
+            return true;
+        }
+    }
+    double minT, maxT;
+    if (!bezier_clip(smaller, larger, minT, maxT)) {
+        if (minT == maxT) {
+            if (intersections.swapped()) {
+                minT1 = (minT1 + maxT1) / 2;
+                minT2 = interp(minT2, maxT2, minT);
+            } else {
+                minT1 = interp(minT1, maxT1, minT);
+                minT2 = (minT2 + maxT2) / 2;
+            }
+            intersections.add(minT1, minT2);
+            return true;
+        }
+        return false;
+    }
+
+    int split;
+    if (intersections.swapped()) {
+        double newMinT1 = interp(minT1, maxT1, minT);
+        double newMaxT1 = interp(minT1, maxT1, maxT);
+        split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1;
+#define VERBOSE 0
+#if VERBOSE
+        printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n",
+                __FUNCTION__, depth, splits, newMinT1, newMaxT1, minT1, maxT1,
+                split);
+#endif
+        minT1 = newMinT1;
+        maxT1 = newMaxT1;
+    } else {
+        double newMinT2 = interp(minT2, maxT2, minT);
+        double newMaxT2 = interp(minT2, maxT2, maxT);
+        split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit;
+#if VERBOSE
+        printf("%s d=%d s=%d new2=(%g,%g) old2=(%g,%g) split=%d\n",
+                __FUNCTION__, depth, splits, newMinT2, newMaxT2, minT2, maxT2,
+                split);
+#endif
+        minT2 = newMinT2;
+        maxT2 = newMaxT2;
+    }
+    return chop(minT1, maxT1, minT2, maxT2, split);
+}
+
+bool chop(double minT1, double maxT1, double minT2, double maxT2, int split) {
+    ++depth;
+    intersections.swap();
+    if (split) {
+        ++splits;
+        if (split & 2) {
+            double middle1 = (maxT1 + minT1) / 2;
+            intersect(minT1, middle1, minT2, maxT2);
+            intersect(middle1, maxT1, minT2, maxT2);
+        } else {
+            double middle2 = (maxT2 + minT2) / 2;
+            intersect(minT1, maxT1, minT2, middle2);
+            intersect(minT1, maxT1, middle2, maxT2);
+        }
+        --splits;
+        intersections.swap();
+        --depth;
+        return intersections.intersected();
+    }
+    bool result = intersect(minT1, maxT1, minT2, maxT2);
+    intersections.swap();
+    --depth;
+    return result;
+}
+
+private:
+
+static const double tClipLimit = 0.8; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf see Multiple intersections
+const Cubic& cubic1;
+const Cubic& cubic2;
+Intersections& intersections;
+int depth;
+int splits;
+};
+
+bool intersect(const Cubic& c1, const Cubic& c2, Intersections& i) {
+    CubicIntersections c(c1, c2, i);
+    return c.intersect();
+}
+
diff --git a/experimental/Intersection/CubicIntersection_Test.cpp b/experimental/Intersection/CubicIntersection_Test.cpp
new file mode 100644
index 0000000..983c4e3
--- /dev/null
+++ b/experimental/Intersection/CubicIntersection_Test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "CurveUtilities.h"
+#include "CubicIntersection_TestData.h"
+#include "Intersection_Tests.h"
+#include "Intersections.h"
+#include "TestUtilities.h"
+
+const int firstCubicIntersectionTest = 9;
+
+void CubicIntersection_Test() {
+    for (size_t index = firstCubicIntersectionTest; index < tests_count; ++index) {
+        const Cubic& cubic1 = tests[index][0];
+        const Cubic& cubic2 = tests[index][1];
+        Cubic reduce1, reduce2;
+        int order1 = reduceOrder(cubic1, reduce1, kReduceOrder_NoQuadraticsAllowed);
+        int order2 = reduceOrder(cubic2, reduce2, kReduceOrder_NoQuadraticsAllowed);
+        if (order1 < 4) {
+            printf("%s [%d] cubic1 order=%d\n", __FUNCTION__, (int) index, order1);
+            continue;
+        }
+        if (order2 < 4) {
+            printf("%s [%d] cubic2 order=%d\n", __FUNCTION__, (int) index, order2);
+            continue;
+        }
+        if (implicit_matches(reduce1, reduce2)) {
+            printf("%s [%d] coincident\n", __FUNCTION__, (int) index);
+            continue;
+        }
+        Intersections tIntersections;
+        intersect(reduce1, reduce2, tIntersections);
+        if (!tIntersections.intersected()) {
+            printf("%s [%d] no intersection\n", __FUNCTION__, (int) index);
+            continue;
+        }
+        for (int pt = 0; pt < tIntersections.used(); ++pt) {
+            double tt1 = tIntersections.fT[0][pt];
+            double tx1, ty1;
+            xy_at_t(cubic1, tt1, tx1, ty1);
+            double tt2 = tIntersections.fT[1][pt];
+            double tx2, ty2;
+            xy_at_t(cubic2, tt2, tx2, ty2);
+            if (!approximately_equal(tx1, tx2)) {
+                printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                    __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+            }
+            if (!approximately_equal(ty1, ty2)) {
+                printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                    __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+            }
+        }
+    }
+}
diff --git a/experimental/Intersection/CubicIntersection_TestData.cpp b/experimental/Intersection/CubicIntersection_TestData.cpp
new file mode 100644
index 0000000..645d7b0
--- /dev/null
+++ b/experimental/Intersection/CubicIntersection_TestData.cpp
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#define IN_TEST 1
+#include "CubicIntersection_TestData.h"
+#include <limits>
+
+const Cubic pointDegenerates[] = {
+    {{0, 0}, {0, 0}, {0, 0}, {0, 0}},
+    {{1, 1}, {1, 1}, {1, 1}, {1, 1}},
+    {{1 + PointEpsilon - std::numeric_limits<double>::epsilon(), 1},
+     {1, 1 + PointEpsilon - std::numeric_limits<double>::epsilon()}, {1, 1}, {1, 1}},
+    {{1 + PointEpsilon/2 - std::numeric_limits<double>::epsilon(), 1},
+     {1 - (PointEpsilon/2 - std::numeric_limits<double>::epsilon()), 1}, {1, 1}, {1, 1}}
+};
+
+const size_t pointDegenerates_count = sizeof(pointDegenerates) / sizeof(pointDegenerates[0]);
+
+const Cubic notPointDegenerates[] = {
+    {{1 + PointEpsilon + std::numeric_limits<double>::epsilon(), 1}, {1, 1 + PointEpsilon}, {1, 1}, {1, 1}},
+    {{1 + PointEpsilon/2 + std::numeric_limits<double>::epsilon(), 1}, {1 - PointEpsilon/2, 1}, {1, 1}, {1, 1}}
+};
+
+const size_t notPointDegenerates_count = sizeof(notPointDegenerates) / sizeof(notPointDegenerates[0]);
+
+// from http://www.truetex.com/bezint.htm
+const Cubic tests[][2] = {
+    { // intersects in one place (data gives bezier clip fits
+     {{0, 45},
+      {6.0094158284751593, 51.610357411322688},
+      {12.741093228940867, 55.981703949474607},
+      {20.021417396476362, 58.652245509710262}},
+     {{2.2070737699246674, 52.703494107327209},
+      {31.591482272629477, 23.811002295222025},
+      {76.824588616426425, 44.049473790502674},
+      {119.25488947221436, 55.599248272955073}}
+    },
+    { // intersects in three places
+        {{0, 45}, {50, 100}, {150,   0}, {200, 55}},
+        {{0, 55}, {50,   0}, {150, 100}, {200, 45}}
+    },
+    { // intersects in one place, cross over is nearly parallel
+        {{0,   0}, {0, 100}, {200,   0}, {200, 100}},
+        {{0, 100}, {0,   0}, {200, 100}, {200,   0}}
+    },
+    { // intersects in two places
+        {{0,   0}, {0, 100}, {200, 100}, {200,   0}},
+        {{0, 100}, {0,   0}, {200,   0}, {200, 100}}
+    },
+    {
+        {{150, 100}, {150 + 0.1, 150}, {150, 200}, {150, 250}},
+        {{250, 150}, {200, 150 + 0.1}, {150, 150}, {100, 150}}
+    },
+    { // single intersection around 168,185
+        {{200, 100}, {150, 100}, {150, 150}, {200, 150}},
+        {{250, 150}, {250, 100}, {100, 100}, {100, 150}}
+    },
+    {
+        {{1.0, 1.5}, {15.5, 0.5}, {-8.0, 3.5}, {5.0, 1.5}},
+        {{4.0, 0.5}, {5.0, 15.0}, {2.0, -8.5}, {4.0, 4.5}}
+    },
+    {
+        {{664.00168, 0},       {726.11545, 124.22757}, {736.89069, 267.89743}, {694.0017, 400.0002}},
+        {{850.66843, 115.55563}, {728.515, 115.55563}, {725.21347, 275.15309}, {694.0017, 400.0002}}
+    },
+    {
+        {{1, 1},   {12.5, 6.5}, {-4, 6.5}, {7.5, 1}},
+        {{1, 6.5}, {12.5, 1},   {-4, 1},   {.5, 6}}
+    },
+    {
+        {{315.748, 312.84}, {312.644, 318.134}, {305.836, 319.909}, {300.542, 316.804}},
+        {{317.122, 309.05}, {316.112, 315.102}, {310.385, 319.19},  {304.332, 318.179}}
+    },
+    {
+        {{1046.604051, 172.937967},  {1046.604051, 178.9763059}, {1041.76745,  183.9279165}, {1035.703842, 184.0432409}},
+        {{1046.452235, 174.7640504}, {1045.544872, 180.1973817}, {1040.837966, 184.0469882}, {1035.505925, 184.0469882}}
+    },
+    {
+        {{125.79356, 199.57382}, {51.16556, 128.93575}, {87.494,  16.67848}, {167.29361, 16.67848}},
+        {{167.29361, 55.81876}, {100.36128, 55.81876}, {68.64099, 145.4755}, {125.7942, 199.57309}}
+    }
+};
+
+const size_t tests_count = sizeof(tests) / sizeof(tests[0]);
+
+Cubic hexTests[][2] = {
+    {
+        {{0}} // placeholder for hex converted below
+    }
+};
+
+const size_t hexTests_count = sizeof(hexTests) / sizeof(hexTests[0]);
+
+static const uint64_t testx[2][8] = {
+    {
+        0xf0d0d1ca63075a40LLU, 0x9408ce996a237740LLU, 0x6d5675460fbe5e40LLU, 0x6ef501e1b7487940LLU,
+        0x9a71d2f8143d6540LLU, 0x6bc18bbe02907a40LLU, 0x5b94d92093aa6b40LLU, 0x6ac18bbe02907a40LLU
+    },
+    {
+        0x92c56ed7b6145d40LLU, 0xede4f1255edb7740LLU, 0x1138c1101af75940LLU, 0x42e4f1255edb7740LLU,
+        0x408e51603ad95640LLU, 0x1e2e8fe9dd927740LLU, 0x1cb4777cd3a75440LLU, 0x212e1390de017740LLU
+    }
+};
+
+void convert_testx() {
+    const uint64_t* inPtr = testx[0];
+    double* outPtr = &hexTests[sizeof(tests) / sizeof(tests[0]) - 1][0][0].x;
+    for (unsigned index = 0; index < sizeof(testx) / sizeof(testx[0][0]); ++index) {
+        uint64_t input = *inPtr++;
+        unsigned char* output = (unsigned char*) outPtr++;
+        for (unsigned byte = 0; byte < sizeof(input); ++byte) {
+            output[byte] = input >> (7 - byte) * 8;
+        }
+    }
+}
+
+const Cubic lines[] = {
+    {{0, 0}, {0, 0}, {0, 0}, {1, 0}}, // 0: horizontal
+    {{0, 0}, {0, 0}, {1, 0}, {0, 0}},
+    {{0, 0}, {1, 0}, {0, 0}, {0, 0}},
+    {{1, 0}, {0, 0}, {0, 0}, {0, 0}},
+    {{1, 0}, {2, 0}, {3, 0}, {4, 0}},
+    {{0, 0}, {0, 0}, {0, 0}, {0, 1}}, // 5: vertical
+    {{0, 0}, {0, 0}, {0, 1}, {0, 0}},
+    {{0, 0}, {0, 1}, {0, 0}, {0, 0}},
+    {{0, 1}, {0, 0}, {0, 0}, {0, 0}},
+    {{0, 1}, {0, 2}, {0, 3}, {0, 4}},
+    {{0, 0}, {0, 0}, {0, 0}, {1, 1}}, // 10: 3 coincident
+    {{0, 0}, {0, 0}, {1, 1}, {0, 0}},
+    {{0, 0}, {1, 1}, {0, 0}, {0, 0}},
+    {{1, 1}, {0, 0}, {0, 0}, {0, 0}},
+    {{0, 0}, {0, 0}, {1, 1}, {2, 2}}, // 14: 2 coincident
+    {{0, 0}, {1, 1}, {0, 0}, {2, 2}},
+    {{0, 0}, {1, 1}, {2, 2}, {0, 0}},
+    {{1, 1}, {0, 0}, {0, 0}, {2, 2}}, // 17:
+    {{1, 1}, {0, 0}, {2, 2}, {0, 0}},
+    {{1, 1}, {2, 2}, {0, 0}, {0, 0}},
+    {{1, 1}, {2, 2}, {3, 3}, {2, 2}}, // middle-last coincident
+    {{1, 1}, {2, 2}, {3, 3}, {3, 3}}, // middle-last coincident
+    {{1, 1}, {1, 1}, {2, 2}, {2, 2}}, // 2 pairs coincident
+    {{1, 1}, {2, 2}, {1, 1}, {2, 2}},
+    {{1, 1}, {2, 2}, {2, 2}, {1, 1}},
+    {{1, 1}, {1, 1}, {3, 3}, {3, 3}}, // first-middle middle-last coincident
+    {{1, 1}, {2, 2}, {3, 3}, {4, 4}}, // no coincident
+    {{1, 1}, {3, 3}, {2, 2}, {4, 4}},
+    {{1, 1}, {2, 2}, {4, 4}, {3, 3}},
+    {{1, 1}, {3, 3}, {4, 4}, {2, 2}},
+    {{1, 1}, {4, 4}, {2, 2}, {3, 3}},
+    {{1, 1}, {4, 4}, {3, 3}, {2, 2}},
+    {{2, 2}, {1, 1}, {3, 3}, {4, 4}},
+    {{2, 2}, {1, 1}, {4, 4}, {3, 3}},
+    {{2, 2}, {3, 3}, {1, 1}, {4, 4}},
+    {{2, 2}, {3, 3}, {4, 4}, {1, 1}},
+    {{2, 2}, {4, 4}, {1, 1}, {3, 3}},
+    {{2, 2}, {4, 4}, {3, 3}, {1, 1}},
+};
+
+const size_t lines_count = sizeof(lines) / sizeof(lines[0]);
+
+// 'not a line' tries to fool the line detection code
+const Cubic notLines[] = {
+    {{0, 0}, {0, 0}, {0, 1}, {1, 0}},
+    {{0, 0}, {0, 1}, {0, 0}, {1, 0}},
+    {{0, 0}, {0, 1}, {1, 0}, {0, 0}},
+    {{0, 1}, {0, 0}, {0, 0}, {1, 0}},
+    {{0, 1}, {0, 0}, {1, 0}, {0, 0}},
+    {{0, 1}, {1, 0}, {0, 0}, {0, 0}},
+};
+
+const size_t notLines_count = sizeof(notLines) / sizeof(notLines[0]);
+
+static const double E = PointEpsilon * 2;
+static const double F = PointEpsilon * 3;
+
+const Cubic modEpsilonLines[] = {
+    {{0, E}, {0, 0}, {0, 0}, {1, 0}}, // horizontal
+    {{0, 0}, {0, E}, {1, 0}, {0, 0}},
+    {{0, 0}, {1, 0}, {0, E}, {0, 0}},
+    {{1, 0}, {0, 0}, {0, 0}, {0, E}},
+    {{1, E}, {2, 0}, {3, 0}, {4, 0}},
+    {{E, 0}, {0, 0}, {0, 0}, {0, 1}}, // vertical
+    {{0, 0}, {E, 0}, {0, 1}, {0, 0}},
+    {{0, 0}, {0, 1}, {E, 0}, {0, 0}},
+    {{0, 1}, {0, 0}, {0, 0}, {E, 0}},
+    {{E, 1}, {0, 2}, {0, 3}, {0, 4}},
+    {{E, 0}, {0, 0}, {0, 0}, {1, 1}}, // 3 coincident
+    {{0, 0}, {E, 0}, {1, 1}, {0, 0}},
+    {{0, 0}, {1, 1}, {E, 0}, {0, 0}},
+    {{1, 1}, {0, 0}, {0, 0}, {E, 0}},
+    {{0, E}, {0, 0}, {1, 1}, {2, 2}}, // 2 coincident
+    {{0, 0}, {1, 1}, {0, E}, {2, 2}},
+    {{0, 0}, {1, 1}, {2, 2}, {0, E}},
+    {{1, 1}, {0, E}, {0, 0}, {2, 2}},
+    {{1, 1}, {0, E}, {2, 2}, {0, 0}},
+    {{1, 1}, {2, 2}, {E, 0}, {0, 0}},
+    {{1, 1}, {2, 2+E}, {3, 3}, {2, 2}}, // middle-last coincident
+    {{1, 1}, {2+E, 2}, {3, 3}, {3, 3}}, // middle-last coincident
+    {{1, 1}, {1, 1}, {2, 2}, {2+E, 2}}, // 2 pairs coincident
+    {{1, 1}, {2, 2}, {1, 1}, {2+E, 2}},
+    {{1, 1}, {2, 2}, {2, 2+E}, {1, 1}},
+    {{1, 1}, {1, 1+E}, {3, 3}, {3, 3}}, // first-middle middle-last coincident
+    {{1, 1}, {2+E, 2}, {3, 3}, {4, 4}}, // no coincident
+    {{1, 1}, {3, 3}, {2, 2}, {4, 4+F}}, // INVESTIGATE: why the epsilon is bigger
+    {{1, 1+F}, {2, 2}, {4, 4}, {3, 3}}, // INVESTIGATE: why the epsilon is bigger
+    {{1, 1}, {3, 3}, {4, 4+E}, {2, 2}},
+    {{1, 1}, {4, 4}, {2, 2}, {3, 3+E}},
+    {{1, 1}, {4, 4}, {3, 3}, {2+E, 2}},
+    {{2, 2}, {1, 1}, {3+E, 3}, {4, 4}},
+    {{2, 2}, {1+E, 1}, {4, 4}, {3, 3}},
+    {{2, 2+E}, {3, 3}, {1, 1}, {4, 4}},
+    {{2+E, 2}, {3, 3}, {4, 4}, {1, 1}},
+    {{2, 2}, {4+E, 4}, {1, 1}, {3, 3}},
+    {{2, 2}, {4, 4}, {3, 3}, {1, 1+E}},
+};
+
+const size_t modEpsilonLines_count = sizeof(modEpsilonLines) / sizeof(modEpsilonLines[0]);
+
+static const double D = PointEpsilon / 2;
+static const double G = PointEpsilon / 3;
+
+const Cubic lessEpsilonLines[] = {
+    {{0, D}, {0, 0}, {0, 0}, {1, 0}}, // horizontal
+    {{0, 0}, {0, D}, {1, 0}, {0, 0}},
+    {{0, 0}, {1, 0}, {0, D}, {0, 0}},
+    {{1, 0}, {0, 0}, {0, 0}, {0, D}},
+    {{1, D}, {2, 0}, {3, 0}, {4, 0}},
+    {{D, 0}, {0, 0}, {0, 0}, {0, 1}}, // vertical
+    {{0, 0}, {D, 0}, {0, 1}, {0, 0}},
+    {{0, 0}, {0, 1}, {D, 0}, {0, 0}},
+    {{0, 1}, {0, 0}, {0, 0}, {D, 0}},
+    {{D, 1}, {0, 2}, {0, 3}, {0, 4}},
+    {{D, 0}, {0, 0}, {0, 0}, {1, 1}}, // 3 coincident
+    {{0, 0}, {D, 0}, {1, 1}, {0, 0}},
+    {{0, 0}, {1, 1}, {D, 0}, {0, 0}},
+    {{1, 1}, {0, 0}, {0, 0}, {D, 0}},
+    {{0, D}, {0, 0}, {1, 1}, {2, 2}}, // 2 coincident
+    {{0, 0}, {1, 1}, {0, D}, {2, 2}},
+    {{0, 0}, {1, 1}, {2, 2}, {0, D}},
+    {{1, 1}, {0, D}, {0, 0}, {2, 2}},
+    {{1, 1}, {0, D}, {2, 2}, {0, 0}},
+    {{1, 1}, {2, 2}, {D, 0}, {0, 0}},
+    {{1, 1}, {2, 2+D}, {3, 3}, {2, 2}}, // middle-last coincident
+    {{1, 1}, {2+D, 2}, {3, 3}, {3, 3}}, // middle-last coincident
+    {{1, 1}, {1, 1}, {2, 2}, {2+D, 2}}, // 2 pairs coincident
+    {{1, 1}, {2, 2}, {1, 1}, {2+D, 2}},
+    {{1, 1}, {2, 2}, {2, 2+D}, {1, 1}},
+    {{1, 1}, {1, 1+D}, {3, 3}, {3, 3}}, // first-middle middle-last coincident
+    {{1, 1}, {2+D/2, 2}, {3, 3}, {4, 4}}, // no coincident (FIXME: N as opposed to N/2 failed)
+    {{1, 1}, {3, 3}, {2, 2}, {4, 4+D}},
+    {{1, 1+D}, {2, 2}, {4, 4}, {3, 3}},
+    {{1, 1}, {3, 3}, {4, 4+D}, {2, 2}},
+    {{1, 1}, {4, 4}, {2, 2}, {3, 3+D}},
+    {{1, 1}, {4, 4}, {3, 3}, {2+G, 2}}, // INVESTIGATE: why the epsilon is smaller
+    {{2, 2}, {1, 1}, {3+D, 3}, {4, 4}},
+    {{2, 2}, {1+D, 1}, {4, 4}, {3, 3}},
+    {{2, 2+D}, {3, 3}, {1, 1}, {4, 4}},
+    {{2+G, 2}, {3, 3}, {4, 4}, {1, 1}}, // INVESTIGATE: why the epsilon is smaller
+    {{2, 2}, {4+D, 4}, {1, 1}, {3, 3}},
+    {{2, 2}, {4, 4}, {3, 3}, {1, 1+D}},
+};
+
+const size_t lessEpsilonLines_count = sizeof(lessEpsilonLines) / sizeof(lessEpsilonLines[0]);
+
+static const double N = -PointEpsilon / 2;
+static const double M = -PointEpsilon / 3;
+
+const Cubic negEpsilonLines[] = {
+    {{0, N}, {0, 0}, {0, 0}, {1, 0}}, // horizontal
+    {{0, 0}, {0, N}, {1, 0}, {0, 0}},
+    {{0, 0}, {1, 0}, {0, N}, {0, 0}},
+    {{1, 0}, {0, 0}, {0, 0}, {0, N}},
+    {{1, N}, {2, 0}, {3, 0}, {4, 0}},
+    {{N, 0}, {0, 0}, {0, 0}, {0, 1}}, // vertical
+    {{0, 0}, {N, 0}, {0, 1}, {0, 0}},
+    {{0, 0}, {0, 1}, {N, 0}, {0, 0}},
+    {{0, 1}, {0, 0}, {0, 0}, {N, 0}},
+    {{N, 1}, {0, 2}, {0, 3}, {0, 4}},
+    {{N, 0}, {0, 0}, {0, 0}, {1, 1}}, // 3 coincident
+    {{0, 0}, {N, 0}, {1, 1}, {0, 0}},
+    {{0, 0}, {1, 1}, {N, 0}, {0, 0}},
+    {{1, 1}, {0, 0}, {0, 0}, {N, 0}},
+    {{0, N}, {0, 0}, {1, 1}, {2, 2}}, // 2 coincident
+    {{0, 0}, {1, 1}, {0, N}, {2, 2}},
+    {{0, 0}, {1, 1}, {2, 2}, {0, N}},
+    {{1, 1}, {0, N}, {0, 0}, {2, 2}},
+    {{1, 1}, {0, N}, {2, 2}, {0, 0}},
+    {{1, 1}, {2, 2}, {N, 0}, {0, 0}},
+    {{1, 1}, {2, 2+N}, {3, 3}, {2, 2}}, // middle-last coincident
+    {{1, 1}, {2+N, 2}, {3, 3}, {3, 3}}, // middle-last coincident
+    {{1, 1}, {1, 1}, {2, 2}, {2+N, 2}}, // 2 pairs coincident
+    {{1, 1}, {2, 2}, {1, 1}, {2+N, 2}},
+    {{1, 1}, {2, 2}, {2, 2+N}, {1, 1}},
+    {{1, 1}, {1, 1+N}, {3, 3}, {3, 3}}, // first-middle middle-last coincident
+    {{1, 1}, {2+N/2, 2}, {3, 3}, {4, 4}}, // no coincident (FIXME: N as opposed to N/2 failed)
+    {{1, 1}, {3, 3}, {2, 2}, {4, 4+N}},
+    {{1, 1+N}, {2, 2}, {4, 4}, {3, 3}},
+    {{1, 1}, {3, 3}, {4, 4+N}, {2, 2}},
+    {{1, 1}, {4, 4}, {2, 2}, {3, 3+N}},
+    {{1, 1}, {4, 4}, {3, 3}, {2+M, 2}}, // INVESTIGATE: why the epsilon is smaller
+    {{2, 2}, {1, 1}, {3+N, 3}, {4, 4}},
+    {{2, 2}, {1+N, 1}, {4, 4}, {3, 3}},
+    {{2, 2+N}, {3, 3}, {1, 1}, {4, 4}},
+    {{2+M, 2}, {3, 3}, {4, 4}, {1, 1}}, // INVESTIGATE: why the epsilon is smaller
+    {{2, 2}, {4+N, 4}, {1, 1}, {3, 3}},
+    {{2, 2}, {4, 4}, {3, 3}, {1, 1+N}},
+};
+
+const size_t negEpsilonLines_count = sizeof(negEpsilonLines) / sizeof(negEpsilonLines[0]);
diff --git a/experimental/Intersection/CubicIntersection_TestData.h b/experimental/Intersection/CubicIntersection_TestData.h
new file mode 100644
index 0000000..27436f8
--- /dev/null
+++ b/experimental/Intersection/CubicIntersection_TestData.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.
+ */
+#if !defined(IN_TEST)
+    #define IN_TEST 1
+#endif
+
+#include "DataTypes.h"
+
+extern const Cubic pointDegenerates[];
+extern const Cubic notPointDegenerates[];
+extern const Cubic tests[][2];
+extern Cubic hexTests[][2];
+
+extern void convert_testx();
+
+extern const Cubic lines[];
+extern const Cubic notLines[];
+extern const Cubic modEpsilonLines[];
+extern const Cubic lessEpsilonLines[];
+extern const Cubic negEpsilonLines[];
+
+extern const size_t pointDegenerates_count;
+extern const size_t notPointDegenerates_count;
+extern const size_t tests_count;
+extern const size_t hexTests_count;
+extern const size_t lines_count;
+extern const size_t notLines_count;
+extern const size_t modEpsilonLines_count;
+extern const size_t lessEpsilonLines_count;
+extern const size_t negEpsilonLines_count;
diff --git a/experimental/Intersection/CubicLineSegments.cpp b/experimental/Intersection/CubicLineSegments.cpp
new file mode 100644
index 0000000..b7408a8
--- /dev/null
+++ b/experimental/Intersection/CubicLineSegments.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 "CubicLineSegments.h"
+#include "QuadraticLineSegments.h"
+#include <algorithm> // used for std::max
+
+// http://cagd.cs.byu.edu/~557/text/cagd.pdf 2.7
+// A hodograph is the first derivative curve
+void hodograph(const Cubic& cubic, Quadratic& hodo) {
+    hodo[0].x = 3 * (cubic[1].x - cubic[0].x);
+    hodo[0].y = 3 * (cubic[1].y - cubic[0].y);
+    hodo[1].x = 3 * (cubic[2].x - cubic[1].x);
+    hodo[1].y = 3 * (cubic[2].y - cubic[1].y);
+    hodo[2].x = 3 * (cubic[3].x - cubic[2].x);
+    hodo[2].y = 3 * (cubic[3].y - cubic[2].y);
+}
+
+// A 2nd hodograph is the second derivative curve
+void secondHodograph(const Cubic& cubic, _Line& hodo2) {
+    Quadratic hodo;
+    hodograph(cubic, hodo);
+    hodograph(hodo, hodo2);
+}
+
+// The number of line segments required to approximate the cubic
+// see  http://cagd.cs.byu.edu/~557/text/cagd.pdf 10.6
+double subDivisions(const Cubic& cubic) {
+    _Line hodo2;
+    secondHodograph(cubic, hodo2);
+    double maxX = std::max(hodo2[1].x, hodo2[1].x);
+    double maxY = std::max(hodo2[1].y, hodo2[1].y);
+    double dist = sqrt(maxX * maxX + maxY * maxY);
+    double segments = sqrt(dist / (8 * FLT_EPSILON));
+    return segments;
+}
diff --git a/experimental/Intersection/CubicLineSegments.h b/experimental/Intersection/CubicLineSegments.h
new file mode 100644
index 0000000..0c80a64
--- /dev/null
+++ b/experimental/Intersection/CubicLineSegments.h
@@ -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 "DataTypes.h"
+
+void hodograph(const Cubic& , Quadratic& hodo);
+void secondHodograph(const Cubic& , _Line& hodo2);
+double subDivisions(const Cubic& );
diff --git a/experimental/Intersection/CubicParameterization.cpp b/experimental/Intersection/CubicParameterization.cpp
new file mode 100644
index 0000000..2bae8b6
--- /dev/null
+++ b/experimental/Intersection/CubicParameterization.cpp
@@ -0,0 +1,521 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "CubicUtilities.h"
+
+/* from http://tom.cs.byu.edu/~tom/papers/cvgip84.pdf 4.1
+ *
+ * This paper proves that Syvester's method can compute the implicit form of
+ * the quadratic from the parameterzied form.
+ *
+ * Given x = a*t*t*t + b*t*t + c*t + d  (the parameterized form)
+ *       y = e*t*t*t + f*t*t + g*t + h
+ *
+ * we want to find an equation of the implicit form:
+ *
+ * A*x^3 + B*x*x*y + C*x*y*y + D*y^3 + E*x*x + F*x*y + G*y*y + H*x + I*y + J = 0
+ *
+ * The implicit form can be expressed as a 6x6 determinant, as shown.
+ *
+ * The resultant obtained by Syvester's method is
+ *
+ * |   a   b   c  (d - x)     0        0     |
+ * |   0   a   b     c     (d - x)     0     |
+ * |   0   0   a     b        c     (d - x)  |
+ * |   e   f   g  (h - y)     0        0     |
+ * |   0   e   f     g     (h - y)     0     |
+ * |   0   0   e     f        g     (h - y)  |
+ *
+ * which, according to Mathematica, expands as shown below.
+ *
+ * Resultant[a*t^3 + b*t^2 + c*t + d - x, e*t^3 + f*t^2 + g*t + h - y, t]
+ *
+ *  -d^3 e^3 + c d^2 e^2 f - b d^2 e f^2 + a d^2 f^3 - c^2 d e^2 g +
+ *  2 b d^2 e^2 g + b c d e f g - 3 a d^2 e f g - a c d f^2 g -
+ *  b^2 d e g^2 + 2 a c d e g^2 + a b d f g^2 - a^2 d g^3 + c^3 e^2 h -
+ *  3 b c d e^2 h + 3 a d^2 e^2 h - b c^2 e f h + 2 b^2 d e f h +
+ *  a c d e f h + a c^2 f^2 h - 2 a b d f^2 h + b^2 c e g h -
+ *  2 a c^2 e g h - a b d e g h - a b c f g h + 3 a^2 d f g h +
+ *  a^2 c g^2 h - b^3 e h^2 + 3 a b c e h^2 - 3 a^2 d e h^2 +
+ *  a b^2 f h^2 - 2 a^2 c f h^2 - a^2 b g h^2 + a^3 h^3 + 3 d^2 e^3 x -
+ *  2 c d e^2 f x + 2 b d e f^2 x - 2 a d f^3 x + c^2 e^2 g x -
+ *  4 b d e^2 g x - b c e f g x + 6 a d e f g x + a c f^2 g x +
+ *  b^2 e g^2 x - 2 a c e g^2 x - a b f g^2 x + a^2 g^3 x +
+ *  3 b c e^2 h x - 6 a d e^2 h x - 2 b^2 e f h x - a c e f h x +
+ *  2 a b f^2 h x + a b e g h x - 3 a^2 f g h x + 3 a^2 e h^2 x -
+ *  3 d e^3 x^2 + c e^2 f x^2 - b e f^2 x^2 + a f^3 x^2 +
+ *  2 b e^2 g x^2 - 3 a e f g x^2 + 3 a e^2 h x^2 + e^3 x^3 -
+ *  c^3 e^2 y + 3 b c d e^2 y - 3 a d^2 e^2 y + b c^2 e f y -
+ *  2 b^2 d e f y - a c d e f y - a c^2 f^2 y + 2 a b d f^2 y -
+ *  b^2 c e g y + 2 a c^2 e g y + a b d e g y + a b c f g y -
+ *  3 a^2 d f g y - a^2 c g^2 y + 2 b^3 e h y - 6 a b c e h y +
+ *  6 a^2 d e h y - 2 a b^2 f h y + 4 a^2 c f h y + 2 a^2 b g h y -
+ *  3 a^3 h^2 y - 3 b c e^2 x y + 6 a d e^2 x y + 2 b^2 e f x y +
+ *  a c e f x y - 2 a b f^2 x y - a b e g x y + 3 a^2 f g x y -
+ *  6 a^2 e h x y - 3 a e^2 x^2 y - b^3 e y^2 + 3 a b c e y^2 -
+ *  3 a^2 d e y^2 + a b^2 f y^2 - 2 a^2 c f y^2 - a^2 b g y^2 +
+ *  3 a^3 h y^2 + 3 a^2 e x y^2 - a^3 y^3
+ */
+
+enum {
+    xxx_coeff, // A
+    xxy_coeff, // B
+    xyy_coeff, // C
+    yyy_coeff, // D
+    xx_coeff,
+    xy_coeff,
+    yy_coeff,
+    x_coeff,
+    y_coeff,
+    c_coeff,
+    coeff_count
+};
+
+#define USE_SYVESTER 0 // if 0, use control-point base parametric form
+#if USE_SYVESTER
+
+// FIXME: factoring version unwritten
+// static bool straight_forward = true;
+
+/* from CubicParameterizationCode.cpp output:
+ *  double A =      e * e * e;
+ *  double B = -3 * a * e * e;
+ *  double C =  3 * a * a * e;
+ *  double D =     -a * a * a;
+ */
+static void calc_ABCD(double a, double e, double p[coeff_count]) {
+    double ee = e * e;
+    p[xxx_coeff] = e * ee;
+    p[xxy_coeff] = -3 * a * ee;
+    double aa = a * a;
+    p[xyy_coeff] = 3 * aa * e;
+    p[yyy_coeff] = -aa * a;
+}
+
+/* CubicParameterizationCode.cpp turns Mathematica output into C.
+ * Rather than edit the lines below, please edit the code there instead.
+ */
+// start of generated code
+static double calc_xx(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+         -3 * d * e * e * e
+        +     c * e * e * f
+        -     b * e * f * f
+        +     a * f * f * f
+        + 2 * b * e * e * g
+        - 3 * a * e * f * g
+        + 3 * a * e * e * h;
+}
+
+static double calc_xy(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+         -3 * b * c * e * e
+        + 6 * a * d * e * e
+        + 2 * b * b * e * f
+        +     a * c * e * f
+        - 2 * a * b * f * f
+        -     a * b * e * g
+        + 3 * a * a * f * g
+        - 6 * a * a * e * h;
+}
+
+static double calc_yy(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+             -b * b * b * e
+        + 3 * a * b * c * e
+        - 3 * a * a * d * e
+        +     a * b * b * f
+        - 2 * a * a * c * f
+        -     a * a * b * g
+        + 3 * a * a * a * h;
+}
+
+static double calc_x(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+          3 * d * d * e * e * e
+        - 2 * c * d * e * e * f
+        + 2 * b * d * e * f * f
+        - 2 * a * d * f * f * f
+        +     c * c * e * e * g
+        - 4 * b * d * e * e * g
+        -     b * c * e * f * g
+        + 6 * a * d * e * f * g
+        +     a * c * f * f * g
+        +     b * b * e * g * g
+        - 2 * a * c * e * g * g
+        -     a * b * f * g * g
+        +     a * a * g * g * g
+        + 3 * b * c * e * e * h
+        - 6 * a * d * e * e * h
+        - 2 * b * b * e * f * h
+        -     a * c * e * f * h
+        + 2 * a * b * f * f * h
+        +     a * b * e * g * h
+        - 3 * a * a * f * g * h
+        + 3 * a * a * e * h * h;
+}
+
+static double calc_y(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+             -c * c * c * e * e
+        + 3 * b * c * d * e * e
+        - 3 * a * d * d * e * e
+        +     b * c * c * e * f
+        - 2 * b * b * d * e * f
+        -     a * c * d * e * f
+        -     a * c * c * f * f
+        + 2 * a * b * d * f * f
+        -     b * b * c * e * g
+        + 2 * a * c * c * e * g
+        +     a * b * d * e * g
+        +     a * b * c * f * g
+        - 3 * a * a * d * f * g
+        -     a * a * c * g * g
+        + 2 * b * b * b * e * h
+        - 6 * a * b * c * e * h
+        + 6 * a * a * d * e * h
+        - 2 * a * b * b * f * h
+        + 4 * a * a * c * f * h
+        + 2 * a * a * b * g * h
+        - 3 * a * a * a * h * h;
+}
+
+static double calc_c(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+             -d * d * d * e * e * e
+        +     c * d * d * e * e * f
+        -     b * d * d * e * f * f
+        +     a * d * d * f * f * f
+        -     c * c * d * e * e * g
+        + 2 * b * d * d * e * e * g
+        +     b * c * d * e * f * g
+        - 3 * a * d * d * e * f * g
+        -     a * c * d * f * f * g
+        -     b * b * d * e * g * g
+        + 2 * a * c * d * e * g * g
+        +     a * b * d * f * g * g
+        -     a * a * d * g * g * g
+        +     c * c * c * e * e * h
+        - 3 * b * c * d * e * e * h
+        + 3 * a * d * d * e * e * h
+        -     b * c * c * e * f * h
+        + 2 * b * b * d * e * f * h
+        +     a * c * d * e * f * h
+        +     a * c * c * f * f * h
+        - 2 * a * b * d * f * f * h
+        +     b * b * c * e * g * h
+        - 2 * a * c * c * e * g * h
+        -     a * b * d * e * g * h
+        -     a * b * c * f * g * h
+        + 3 * a * a * d * f * g * h
+        +     a * a * c * g * g * h
+        -     b * b * b * e * h * h
+        + 3 * a * b * c * e * h * h
+        - 3 * a * a * d * e * h * h
+        +     a * b * b * f * h * h
+        - 2 * a * a * c * f * h * h
+        -     a * a * b * g * h * h
+        +     a * a * a * h * h * h;
+}
+// end of generated code
+
+#else
+
+/* more Mathematica generated code. This takes a different tack, starting with
+   the control-point based parametric formulas.  The C code is unoptimized --
+   in this form, this is a proof of concept (since the other code didn't work)
+*/
+static double calc_c(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+d*d*d*e*e*e - 3*d*d*(3*c*e*e*f + 3*b*e*(-3*f*f + 2*e*g) + a*(9*f*f*f - 9*e*f*g + e*e*h)) -
+   h*(27*c*c*c*e*e - 27*c*c*(3*b*e*f - 3*a*f*f + 2*a*e*g) +
+      h*(-27*b*b*b*e + 27*a*b*b*f - 9*a*a*b*g + a*a*a*h) +
+      9*c*(9*b*b*e*g + a*b*(-9*f*g + 3*e*h) + a*a*(3*g*g - 2*f*h))) +
+   3*d*(9*c*c*e*e*g + 9*b*b*e*(3*g*g - 2*f*h) + 3*a*b*(-9*f*g*g + 6*f*f*h + e*g*h) +
+      a*a*(9*g*g*g - 9*f*g*h + e*h*h) + 3*c*(3*b*e*(-3*f*g + e*h) + a*(9*f*f*g - 6*e*g*g - e*f*h)))
+    ;
+}
+
+// - Power(e - 3*f + 3*g - h,3)*Power(x,3)
+static double calc_xxx(double e3f3gh) {
+    return -e3f3gh * e3f3gh * e3f3gh;
+}
+
+static double calc_y(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
++ 3*(6*b*d*d*e*e - d*d*d*e*e + 18*b*b*d*e*f - 18*b*d*d*e*f -
+      9*b*d*d*f*f - 54*b*b*d*e*g + 12*b*d*d*e*g - 27*b*b*d*g*g - 18*b*b*b*e*h + 18*b*b*d*e*h +
+      18*b*b*d*f*h + a*a*a*h*h - 9*b*b*b*h*h + 9*c*c*c*e*(e + 2*h) +
+      a*a*(-3*b*h*(2*g + h) + d*(-27*g*g + 9*g*h - h*(2*e + h) + 9*f*(g + h))) +
+      a*(9*b*b*h*(2*f + h) - 3*b*d*(6*f*f - 6*f*(3*g - 2*h) + g*(-9*g + h) + e*(g + h)) +
+         d*d*(e*e + 9*f*(3*f - g) + e*(-9*f - 9*g + 2*h))) -
+      9*c*c*(d*e*(e + 2*g) + 3*b*(f*h + e*(f + h)) + a*(-3*f*f - 6*f*h + 2*(g*h + e*(g + h)))) +
+      3*c*(d*d*e*(e + 2*f) + a*a*(3*g*g + 6*g*h - 2*h*(2*f + h)) + 9*b*b*(g*h + e*(g + h)) +
+         a*d*(-9*f*f - 18*f*g + 6*g*g + f*h + e*(f + 12*g + h)) +
+         b*(d*(-3*e*e + 9*f*g + e*(9*f + 9*g - 6*h)) + 3*a*(h*(2*e - 3*g + h) - 3*f*(g + h))))) // *y
+    ;
+}
+
+static double calc_yy(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+- 3*(18*c*c*c*e - 18*c*c*d*e + 6*c*d*d*e - d*d*d*e + 3*c*d*d*f - 9*c*c*d*g + a*a*a*h + 9*c*c*c*h -
+      9*b*b*b*(e + 2*h) - a*a*(d*(e - 9*f + 18*g - 7*h) + 3*c*(2*f - 6*g + h)) +
+      a*(-9*c*c*(2*e - 6*f + 2*g - h) + d*d*(-7*e + 18*f - 9*g + h) + 3*c*d*(7*e - 17*f + 3*g + h)) +
+      9*b*b*(3*c*(e + g + h) + a*(f + 2*h) - d*(e - 2*(f - 3*g + h))) -
+      3*b*(-(d*d*(e - 6*f + 2*g)) - 3*c*d*(e + 3*f + 3*g - h) + 9*c*c*(e + f + h) + a*a*(g + 2*h) +
+         a*(c*(-3*e + 9*f + 9*g + 3*h) + d*(e + 3*f - 17*g + 7*h)))) // *Power(y,2)
+    ;
+}
+
+// + Power(a - 3*b + 3*c - d,3)*Power(y,3)
+static double calc_yyy(double a3b3cd) {
+    return a3b3cd * a3b3cd * a3b3cd;
+}
+
+static double calc_xx(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+// + Power(x,2)*
+(-3*(-9*b*e*f*f + 9*a*f*f*f + 6*b*e*e*g - 9*a*e*f*g + 27*b*e*f*g - 27*a*f*f*g + 18*a*e*g*g - 54*b*e*g*g +
+         27*a*f*g*g + 27*b*f*g*g - 18*a*g*g*g + a*e*e*h - 9*b*e*e*h + 3*a*e*f*h + 9*b*e*f*h + 9*a*f*f*h -
+         18*b*f*f*h - 21*a*e*g*h + 51*b*e*g*h - 9*a*f*g*h - 27*b*f*g*h + 18*a*g*g*h + 7*a*e*h*h - 18*b*e*h*h - 3*a*f*h*h +
+         18*b*f*h*h - 6*a*g*h*h - 3*b*g*h*h + a*h*h*h +
+         3*c*(-9*f*f*(g - 2*h) + 3*g*g*h - f*h*(9*g + 2*h) + e*e*(f - 6*g + 6*h) +
+            e*(9*f*g + 6*g*g - 17*f*h - 3*g*h + 3*h*h)) -
+         d*(e*e*e + e*e*(-6*f - 3*g + 7*h) - 9*(2*f - g)*(f*f + g*g - f*(g + h)) +
+            e*(18*f*f + 9*g*g + 3*g*h + h*h - 3*f*(3*g + 7*h)))) )
+    ;
+}
+
+// + Power(x,2)*(3*(a - 3*b + 3*c - d)*Power(e - 3*f + 3*g - h,2)*y)
+static double calc_xxy(double a3b3cd, double e3f3gh) {
+    return 3 * a3b3cd * e3f3gh * e3f3gh;
+}
+
+static double calc_x(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+// + x*
+(-3*(27*b*b*e*g*g - 27*a*b*f*g*g + 9*a*a*g*g*g - 18*b*b*e*f*h + 18*a*b*f*f*h + 3*a*b*e*g*h -
+         27*b*b*e*g*h - 9*a*a*f*g*h + 27*a*b*f*g*h - 9*a*a*g*g*h + a*a*e*h*h - 9*a*b*e*h*h +
+         27*b*b*e*h*h + 6*a*a*f*h*h - 18*a*b*f*h*h - 9*b*b*f*h*h + 3*a*a*g*h*h +
+         6*a*b*g*h*h - a*a*h*h*h + 9*c*c*(e*e*(g - 3*h) - 3*f*f*h + e*(3*f + 2*g)*h) +
+         d*d*(e*e*e - 9*f*f*f + 9*e*f*(f + g) - e*e*(3*f + 6*g + h)) +
+         d*(-3*c*(-9*f*f*g + e*e*(2*f - 6*g - 3*h) + e*(9*f*g + 6*g*g + f*h)) +
+            a*(-18*f*f*f - 18*e*g*g + 18*g*g*g - 2*e*e*h + 3*e*g*h + 2*e*h*h + 9*f*f*(3*g + 2*h) +
+               3*f*(6*e*g - 9*g*g - e*h - 6*g*h)) - 3*b*(9*f*g*g + e*e*(4*g - 3*h) - 6*f*f*h -
+               e*(6*f*f + g*(18*g + h) - 3*f*(3*g + 4*h)))) +
+         3*c*(3*b*(e*e*h + 3*f*g*h - e*(3*f*g - 6*f*h + 6*g*h + h*h)) +
+            a*(9*f*f*(g - 2*h) + f*h*(-e + 9*g + 4*h) - 3*(2*g*g*h + e*(2*g*g - 4*g*h + h*h))))) )
+    ;
+}
+
+static double calc_xy(double a, double b, double c, double d,
+                     double e, double f, double g, double h) {
+    return
+// + x*3*
+(-2*a*d*e*e - 7*d*d*e*e + 15*a*d*e*f + 21*d*d*e*f - 9*a*d*f*f - 18*d*d*f*f - 15*a*d*e*g -
+         3*d*d*e*g - 9*a*a*f*g + 9*d*d*f*g + 18*a*a*g*g + 9*a*d*g*g + 2*a*a*e*h - 2*d*d*e*h +
+         3*a*a*f*h + 15*a*d*f*h - 21*a*a*g*h - 15*a*d*g*h + 7*a*a*h*h + 2*a*d*h*h -
+         9*c*c*(2*e*e + 3*f*f + 3*f*h - 2*g*h + e*(-3*f - 4*g + h)) +
+         9*b*b*(3*g*g - 3*g*h + 2*h*(-2*f + h) + e*(-2*f + 3*g + h)) +
+         3*b*(3*c*(e*e + 3*e*(f - 3*g) + (9*f - 3*g - h)*h) + a*(6*f*f + e*g - 9*f*g - 9*g*g - 5*e*h + 9*f*h + 14*g*h - 7*h*h) +
+            d*(-e*e + 12*f*f - 27*f*g + e*(-9*f + 20*g - 5*h) + g*(9*g + h))) +
+         3*c*(a*(-(e*f) - 9*f*f + 27*f*g - 12*g*g + 5*e*h - 20*f*h + 9*g*h + h*h) +
+            d*(7*e*e + 9*f*f + 9*f*g - 6*g*g - f*h + e*(-14*f - 9*g + 5*h)))) // *y
+    ;
+}
+
+// - x*3*Power(a - 3*b + 3*c - d,2)*(e - 3*f + 3*g - h)*Power(y,2)
+static double calc_xyy(double a3b3cd, double e3f3gh) {
+    return -3 * a3b3cd * a3b3cd * e3f3gh;
+}
+
+#endif
+
+static double (*calc_proc[])(double a, double b, double c, double d,
+                             double e, double f, double g, double h) = {
+    calc_xx, calc_xy, calc_yy, calc_x, calc_y, calc_c
+};
+
+#if USE_SYVESTER
+/* Control points to parametric coefficients
+    s = 1 - t
+    Attt + 3Btts + 3Ctss + Dsss ==
+    Attt + 3B(1 - t)tt + 3C(1 - t)(t - tt) + D(1 - t)(1 - 2t + tt) ==
+    Attt + 3B(tt - ttt) + 3C(t - tt - tt + ttt) + D(1-2t+tt-t+2tt-ttt) ==
+    Attt + 3Btt - 3Bttt + 3Ct - 6Ctt + 3Cttt + D - 3Dt + 3Dtt - Dttt ==
+    D + (3C - 3D)t + (3B - 6C + 3D)tt + (A - 3B + 3C - D)ttt
+    a = A - 3*B + 3*C -   D
+    b =     3*B - 6*C + 3*D
+    c =           3*C - 3*D
+    d =                   D
+ */
+
+ /* http://www.algorithmist.net/bezier3.html
+    p = 3 * A
+    q = 3 * B
+    r = 3 * C
+    a = A
+    b = q - p
+    c = p - 2 * q + r
+    d = D - A + q - r
+
+ B(t) = a + t * (b + t * (c + t * d))
+
+ so
+
+ B(t) = a + t*b + t*t*(c + t*d)
+      = a + t*b + t*t*c + t*t*t*d
+  */
+static void set_abcd(const double* cubic, double& a, double& b, double& c,
+                     double& d) {
+    a = cubic[0];     // a = A
+    b = 3 * cubic[2]; // b = 3*B (compute rest of b lazily)
+    c = 3 * cubic[4]; // c = 3*C (compute rest of c lazily)
+    d = cubic[6];     // d = D
+    a += -b + c - d;  // a = A - 3*B + 3*C - D
+}
+
+static void calc_bc(const double d, double& b, double& c) {
+    b -= 3 * c; // b = 3*B - 3*C
+    c -= 3 * d; // c = 3*C - 3*D
+    b -= c;     // b = 3*B - 6*C + 3*D
+}
+
+static void alt_set_abcd(const double* cubic, double& a, double& b, double& c,
+                     double& d) {
+    a = cubic[0];
+    double p = 3 * a;
+    double q = 3 * cubic[2];
+    double r = 3 * cubic[4];
+    b = q - p;
+    c = p - 2 * q + r;
+    d = cubic[6] - a + q - r;
+}
+
+const bool try_alt = true;
+
+#else
+
+static void calc_ABCD(double a, double b, double c, double d,
+                      double e, double f, double g, double h,
+                      double p[coeff_count]) {
+    double a3b3cd = a - 3 * (b - c) - d;
+    double e3f3gh = e - 3 * (f - g) - h;
+    p[xxx_coeff] = calc_xxx(e3f3gh);
+    p[xxy_coeff] = calc_xxy(a3b3cd, e3f3gh);
+    p[xyy_coeff] = calc_xyy(a3b3cd, e3f3gh);
+    p[yyy_coeff] = calc_yyy(a3b3cd);
+}
+#endif
+
+bool implicit_matches(const Cubic& one, const Cubic& two) {
+    double p1[coeff_count]; // a'xxx , b'xxy , c'xyy , d'xx , e'xy , f'yy, etc.
+    double p2[coeff_count];
+#if USE_SYVESTER
+    double a1, b1, c1, d1;
+    if (try_alt)
+        alt_set_abcd(&one[0].x, a1, b1, c1, d1);
+    else
+        set_abcd(&one[0].x, a1, b1, c1, d1);
+    double e1, f1, g1, h1;
+    if (try_alt)
+        alt_set_abcd(&one[0].y, e1, f1, g1, h1);
+    else
+        set_abcd(&one[0].y, e1, f1, g1, h1);
+    calc_ABCD(a1, e1, p1);
+    double a2, b2, c2, d2;
+    if (try_alt)
+        alt_set_abcd(&two[0].x, a2, b2, c2, d2);
+    else
+        set_abcd(&two[0].x, a2, b2, c2, d2);
+    double e2, f2, g2, h2;
+    if (try_alt)
+        alt_set_abcd(&two[0].y, e2, f2, g2, h2);
+    else
+        set_abcd(&two[0].y, e2, f2, g2, h2);
+    calc_ABCD(a2, e2, p2);
+#else
+    double a1 = one[0].x;
+    double b1 = one[1].x;
+    double c1 = one[2].x;
+    double d1 = one[3].x;
+    double e1 = one[0].y;
+    double f1 = one[1].y;
+    double g1 = one[2].y;
+    double h1 = one[3].y;
+    calc_ABCD(a1, b1, c1, d1, e1, f1, g1, h1, p1);
+    double a2 = two[0].x;
+    double b2 = two[1].x;
+    double c2 = two[2].x;
+    double d2 = two[3].x;
+    double e2 = two[0].y;
+    double f2 = two[1].y;
+    double g2 = two[2].y;
+    double h2 = two[3].y;
+    calc_ABCD(a2, b2, c2, d2, e2, f2, g2, h2, p2);
+#endif
+    int first = 0;
+    for (int index = 0; index < coeff_count; ++index) {
+#if USE_SYVESTER
+        if (!try_alt && index == xx_coeff) {
+            calc_bc(d1, b1, c1);
+            calc_bc(h1, f1, g1);
+            calc_bc(d2, b2, c2);
+            calc_bc(h2, f2, g2);
+        }
+#endif
+        if (index >= xx_coeff) {
+            int procIndex = index - xx_coeff;
+            p1[index] = (*calc_proc[procIndex])(a1, b1, c1, d1, e1, f1, g1, h1);
+            p2[index] = (*calc_proc[procIndex])(a2, b2, c2, d2, e2, f2, g2, h2);
+        }
+        if (approximately_zero(p1[index]) || approximately_zero(p2[index])) {
+            first += first == index;
+            continue;
+        }
+        if (first == index) {
+            continue;
+        }
+        if (!approximately_equal(p1[index] * p2[first],
+                p1[first] * p2[index])) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static double tangent(const double* cubic, double t) {
+    double a, b, c, d;
+#if USE_SYVESTER
+    set_abcd(cubic, a, b, c, d);
+    calc_bc(d, b, c);
+#else
+    coefficients(cubic, a, b, c, d);
+#endif
+    return 3 * a * t * t + 2 * b * t + c;
+}
+
+void tangent(const Cubic& cubic, double t, _Point& result) {
+    result.x = tangent(&cubic[0].x, t);
+    result.y = tangent(&cubic[0].y, t);
+}
+
+// unit test to return and validate parametric coefficients
+#include "CubicParameterization_TestUtility.cpp"
+
+
diff --git a/experimental/Intersection/CubicParameterizationCode.cpp b/experimental/Intersection/CubicParameterizationCode.cpp
new file mode 100644
index 0000000..7a7908e
--- /dev/null
+++ b/experimental/Intersection/CubicParameterizationCode.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include <vector>
+
+/* Given:
+ * Resultant[a*t^3 + b*t^2 + c*t + d - x, e*t^3 + f*t^2 + g*t + h - y, t]
+ */
+
+const char result1[] =
+"-d^3 e^3 + c d^2 e^2 f - b d^2 e f^2 + a d^2 f^3 - c^2 d e^2 g + "
+" 2 b d^2 e^2 g + b c d e f g - 3 a d^2 e f g - a c d f^2 g - "
+" b^2 d e g^2 + 2 a c d e g^2 + a b d f g^2 - a^2 d g^3 + c^3 e^2 h - "
+" 3 b c d e^2 h + 3 a d^2 e^2 h - b c^2 e f h + 2 b^2 d e f h + "
+" a c d e f h + a c^2 f^2 h - 2 a b d f^2 h + b^2 c e g h - "
+" 2 a c^2 e g h - a b d e g h - a b c f g h + 3 a^2 d f g h + "
+" a^2 c g^2 h - b^3 e h^2 + 3 a b c e h^2 - 3 a^2 d e h^2 + "
+" a b^2 f h^2 - 2 a^2 c f h^2 - a^2 b g h^2 + a^3 h^3 + 3 d^2 e^3 x - "
+" 2 c d e^2 f x + 2 b d e f^2 x - 2 a d f^3 x + c^2 e^2 g x - "
+" 4 b d e^2 g x - b c e f g x + 6 a d e f g x + a c f^2 g x + "
+" b^2 e g^2 x - 2 a c e g^2 x - a b f g^2 x + a^2 g^3 x + "
+" 3 b c e^2 h x - 6 a d e^2 h x - 2 b^2 e f h x - a c e f h x + "
+" 2 a b f^2 h x + a b e g h x - 3 a^2 f g h x + 3 a^2 e h^2 x - "
+" 3 d e^3 x^2 + c e^2 f x^2 - b e f^2 x^2 + a f^3 x^2 + "
+" 2 b e^2 g x^2 - 3 a e f g x^2 + 3 a e^2 h x^2 + e^3 x^3 - "
+" c^3 e^2 y + 3 b c d e^2 y - 3 a d^2 e^2 y + b c^2 e f y - "
+" 2 b^2 d e f y - a c d e f y - a c^2 f^2 y + 2 a b d f^2 y - "
+" b^2 c e g y + 2 a c^2 e g y + a b d e g y + a b c f g y - "
+" 3 a^2 d f g y - a^2 c g^2 y + 2 b^3 e h y - 6 a b c e h y + "
+" 6 a^2 d e h y - 2 a b^2 f h y + 4 a^2 c f h y + 2 a^2 b g h y - "
+" 3 a^3 h^2 y - 3 b c e^2 x y + 6 a d e^2 x y + 2 b^2 e f x y + "
+" a c e f x y - 2 a b f^2 x y - a b e g x y + 3 a^2 f g x y - "
+" 6 a^2 e h x y - 3 a e^2 x^2 y - b^3 e y^2 + 3 a b c e y^2 - "
+" 3 a^2 d e y^2 + a b^2 f y^2 - 2 a^2 c f y^2 - a^2 b g y^2 + "
+" 3 a^3 h y^2 + 3 a^2 e x y^2 - a^3 y^3";
+
+const size_t len1 = sizeof(result1) - 1;
+
+/* Given:
+ * Expand[
+ * Det[{{a, b, c, (d - x),      0,      0},
+ *      {0, a, b,      c,  (d - x),     0},
+ *      {0, 0, a,      b,       c, (d - x)},
+ *      {e, f, g, (h - y),      0,      0},
+ *      {0, e, f,      g,  (h - y),     0},
+ *      {0, 0, e,      f,       g, (h - y)}}]]
+ */
+ // result1 and result2 are the same. 102 factors:
+const char result2[] =
+"-d^3 e^3       + c d^2 e^2 f   - b d^2 e f^2   + a d^2 f^3     - c^2 d e^2 g + "
+" 2 b d^2 e^2 g + b c d e f g   - 3 a d^2 e f g - a c d f^2 g   - "
+" b^2 d e g^2   + 2 a c d e g^2 + a b d f g^2   - a^2 d g^3     + c^3 e^2 h - "
+" 3 b c d e^2 h + 3 a d^2 e^2 h - b c^2 e f h   + 2 b^2 d e f h + "
+" a c d e f h   + a c^2 f^2 h   - 2 a b d f^2 h + b^2 c e g h   - "
+" 2 a c^2 e g h - a b d e g h   - a b c f g h   + 3 a^2 d f g h + "
+" a^2 c g^2 h   - b^3 e h^2     + 3 a b c e h^2 - 3 a^2 d e h^2 + "
+" a b^2 f h^2   - 2 a^2 c f h^2 - a^2 b g h^2   + a^3 h^3       + 3 d^2 e^3 x - "
+" 2 c d e^2 f x + 2 b d e f^2 x - 2 a d f^3 x   + c^2 e^2 g x   - "
+" 4 b d e^2 g x - b c e f g x   + 6 a d e f g x + a c f^2 g x   + "
+" b^2 e g^2 x   - 2 a c e g^2 x - a b f g^2 x   + a^2 g^3 x     + "
+" 3 b c e^2 h x - 6 a d e^2 h x - 2 b^2 e f h x - a c e f h x   + "
+" 2 a b f^2 h x + a b e g h x   - 3 a^2 f g h x + 3 a^2 e h^2 x - "
+" 3 d e^3 x^2   + c e^2 f x^2   - b e f^2 x^2   + a f^3 x^2     + "
+" 2 b e^2 g x^2 - 3 a e f g x^2 + 3 a e^2 h x^2 + e^3 x^3       - "
+" c^3 e^2 y     + 3 b c d e^2 y - 3 a d^2 e^2 y + b c^2 e f y   - "
+" 2 b^2 d e f y - a c d e f y   - a c^2 f^2 y   + 2 a b d f^2 y - "
+" b^2 c e g y   + 2 a c^2 e g y + a b d e g y   + a b c f g y   - "
+" 3 a^2 d f g y - a^2 c g^2 y   + 2 b^3 e h y   - 6 a b c e h y + "
+" 6 a^2 d e h y - 2 a b^2 f h y + 4 a^2 c f h y + 2 a^2 b g h y - "
+" 3 a^3 h^2 y   - 3 b c e^2 x y + 6 a d e^2 x y + 2 b^2 e f x y + "
+" a c e f x y   - 2 a b f^2 x y - a b e g x y   + 3 a^2 f g x y - "
+" 6 a^2 e h x y - 3 a e^2 x^2 y - b^3 e y^2     + 3 a b c e y^2 - "
+" 3 a^2 d e y^2 + a b^2 f y^2   - 2 a^2 c f y^2 - a^2 b g y^2   + "
+" 3 a^3 h y^2   + 3 a^2 e x y^2 - a^3 y^3";
+
+const size_t len2 = sizeof(result2) - 1;
+
+/* Given: r1 = Resultant[
+ *      a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - x,
+ *      e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, t]
+ *        Collect[r1, {x, y}, Simplify]
+ *        CForm[%]
+ *      then use regex to replace Power\(([a-h]),3\) with \1*\1*\1
+ *                            and Power\(([a-h]),2\) with \1*\1
+ * yields:
+
+d*d*d*e*e*e - 3*d*d*(3*c*e*e*f + 3*b*e*(-3*f*f + 2*e*g) + a*(9*f*f*f - 9*e*f*g + e*e*h)) -
+   h*(27*c*c*c*e*e - 27*c*c*(3*b*e*f - 3*a*f*f + 2*a*e*g) +
+      h*(-27*b*b*b*e + 27*a*b*b*f - 9*a*a*b*g + a*a*a*h) +
+      9*c*(9*b*b*e*g + a*b*(-9*f*g + 3*e*h) + a*a*(3*g*g - 2*f*h))) +
+   3*d*(9*c*c*e*e*g + 9*b*b*e*(3*g*g - 2*f*h) + 3*a*b*(-9*f*g*g + 6*f*f*h + e*g*h) +
+      a*a*(9*g*g*g - 9*f*g*h + e*h*h) + 3*c*(3*b*e*(-3*f*g + e*h) + a*(9*f*f*g - 6*e*g*g - e*f*h)))
+
+- Power(e - 3*f + 3*g - h,3)*Power(x,3)
+
++ 3*(6*b*d*d*e*e - d*d*d*e*e + 18*b*b*d*e*f - 18*b*d*d*e*f -
+      9*b*d*d*f*f - 54*b*b*d*e*g + 12*b*d*d*e*g - 27*b*b*d*g*g - 18*b*b*b*e*h + 18*b*b*d*e*h +
+      18*b*b*d*f*h + a*a*a*h*h - 9*b*b*b*h*h + 9*c*c*c*e*(e + 2*h) +
+      a*a*(-3*b*h*(2*g + h) + d*(-27*g*g + 9*g*h - h*(2*e + h) + 9*f*(g + h))) +
+      a*(9*b*b*h*(2*f + h) - 3*b*d*(6*f*f - 6*f*(3*g - 2*h) + g*(-9*g + h) + e*(g + h)) +
+         d*d*(e*e + 9*f*(3*f - g) + e*(-9*f - 9*g + 2*h))) -
+      9*c*c*(d*e*(e + 2*g) + 3*b*(f*h + e*(f + h)) + a*(-3*f*f - 6*f*h + 2*(g*h + e*(g + h)))) +
+      3*c*(d*d*e*(e + 2*f) + a*a*(3*g*g + 6*g*h - 2*h*(2*f + h)) + 9*b*b*(g*h + e*(g + h)) +
+         a*d*(-9*f*f - 18*f*g + 6*g*g + f*h + e*(f + 12*g + h)) +
+         b*(d*(-3*e*e + 9*f*g + e*(9*f + 9*g - 6*h)) + 3*a*(h*(2*e - 3*g + h) - 3*f*(g + h)))))*y
+
+- 3*(18*c*c*c*e - 18*c*c*d*e + 6*c*d*d*e - d*d*d*e + 3*c*d*d*f - 9*c*c*d*g + a*a*a*h + 9*c*c*c*h -
+      9*b*b*b*(e + 2*h) - a*a*(d*(e - 9*f + 18*g - 7*h) + 3*c*(2*f - 6*g + h)) +
+      a*(-9*c*c*(2*e - 6*f + 2*g - h) + d*d*(-7*e + 18*f - 9*g + h) + 3*c*d*(7*e - 17*f + 3*g + h)) +
+      9*b*b*(3*c*(e + g + h) + a*(f + 2*h) - d*(e - 2*(f - 3*g + h))) -
+      3*b*(-(d*d*(e - 6*f + 2*g)) - 3*c*d*(e + 3*f + 3*g - h) + 9*c*c*(e + f + h) + a*a*(g + 2*h) +
+         a*(c*(-3*e + 9*f + 9*g + 3*h) + d*(e + 3*f - 17*g + 7*h))))*Power(y,2)
+
++ Power(a - 3*b + 3*c - d,3)*Power(y,3)
+
++ Power(x,2)*(-3*(-9*b*e*f*f + 9*a*f*f*f + 6*b*e*e*g - 9*a*e*f*g + 27*b*e*f*g - 27*a*f*f*g + 18*a*e*g*g - 54*b*e*g*g +
+         27*a*f*g*g + 27*b*f*g*g - 18*a*g*g*g + a*e*e*h - 9*b*e*e*h + 3*a*e*f*h + 9*b*e*f*h + 9*a*f*f*h -
+         18*b*f*f*h - 21*a*e*g*h + 51*b*e*g*h - 9*a*f*g*h - 27*b*f*g*h + 18*a*g*g*h + 7*a*e*h*h - 18*b*e*h*h - 3*a*f*h*h +
+         18*b*f*h*h - 6*a*g*h*h - 3*b*g*h*h + a*h*h*h +
+         3*c*(-9*f*f*(g - 2*h) + 3*g*g*h - f*h*(9*g + 2*h) + e*e*(f - 6*g + 6*h) +
+            e*(9*f*g + 6*g*g - 17*f*h - 3*g*h + 3*h*h)) -
+         d*(e*e*e + e*e*(-6*f - 3*g + 7*h) - 9*(2*f - g)*(f*f + g*g - f*(g + h)) +
+            e*(18*f*f + 9*g*g + 3*g*h + h*h - 3*f*(3*g + 7*h)))) )
+
++ Power(x,2)*(3*(a - 3*b + 3*c - d)*Power(e - 3*f + 3*g - h,2)*y)
+
++ x*(-3*(27*b*b*e*g*g - 27*a*b*f*g*g + 9*a*a*g*g*g - 18*b*b*e*f*h + 18*a*b*f*f*h + 3*a*b*e*g*h -
+         27*b*b*e*g*h - 9*a*a*f*g*h + 27*a*b*f*g*h - 9*a*a*g*g*h + a*a*e*h*h - 9*a*b*e*h*h +
+         27*b*b*e*h*h + 6*a*a*f*h*h - 18*a*b*f*h*h - 9*b*b*f*h*h + 3*a*a*g*h*h +
+         6*a*b*g*h*h - a*a*h*h*h + 9*c*c*(e*e*(g - 3*h) - 3*f*f*h + e*(3*f + 2*g)*h) +
+         d*d*(e*e*e - 9*f*f*f + 9*e*f*(f + g) - e*e*(3*f + 6*g + h)) +
+         d*(-3*c*(-9*f*f*g + e*e*(2*f - 6*g - 3*h) + e*(9*f*g + 6*g*g + f*h)) +
+            a*(-18*f*f*f - 18*e*g*g + 18*g*g*g - 2*e*e*h + 3*e*g*h + 2*e*h*h + 9*f*f*(3*g + 2*h) +
+               3*f*(6*e*g - 9*g*g - e*h - 6*g*h)) - 3*b*(9*f*g*g + e*e*(4*g - 3*h) - 6*f*f*h -
+               e*(6*f*f + g*(18*g + h) - 3*f*(3*g + 4*h)))) +
+         3*c*(3*b*(e*e*h + 3*f*g*h - e*(3*f*g - 6*f*h + 6*g*h + h*h)) +
+            a*(9*f*f*(g - 2*h) + f*h*(-e + 9*g + 4*h) - 3*(2*g*g*h + e*(2*g*g - 4*g*h + h*h))))) )
+
++ x*3*(-2*a*d*e*e - 7*d*d*e*e + 15*a*d*e*f + 21*d*d*e*f - 9*a*d*f*f - 18*d*d*f*f - 15*a*d*e*g -
+         3*d*d*e*g - 9*a*a*f*g + 9*d*d*f*g + 18*a*a*g*g + 9*a*d*g*g + 2*a*a*e*h - 2*d*d*e*h +
+         3*a*a*f*h + 15*a*d*f*h - 21*a*a*g*h - 15*a*d*g*h + 7*a*a*h*h + 2*a*d*h*h -
+         9*c*c*(2*e*e + 3*f*f + 3*f*h - 2*g*h + e*(-3*f - 4*g + h)) +
+         9*b*b*(3*g*g - 3*g*h + 2*h*(-2*f + h) + e*(-2*f + 3*g + h)) +
+         3*b*(3*c*(e*e + 3*e*(f - 3*g) + (9*f - 3*g - h)*h) + a*(6*f*f + e*g - 9*f*g - 9*g*g - 5*e*h + 9*f*h + 14*g*h - 7*h*h) +
+            d*(-e*e + 12*f*f - 27*f*g + e*(-9*f + 20*g - 5*h) + g*(9*g + h))) +
+         3*c*(a*(-(e*f) - 9*f*f + 27*f*g - 12*g*g + 5*e*h - 20*f*h + 9*g*h + h*h) +
+            d*(7*e*e + 9*f*f + 9*f*g - 6*g*g - f*h + e*(-14*f - 9*g + 5*h))))*y
+
+- x*3*Power(a - 3*b + 3*c - d,2)*(e - 3*f + 3*g - h)*Power(y,2)
+
+*/
+
+const int factors = 8;
+
+struct coeff {
+    int s; // constant and coefficient sign
+    int n[factors]; // 0 or power of a (1, 2, or 3) for a through h
+};
+
+enum {
+    xxx_coeff,
+    xxy_coeff,
+    xyy_coeff,
+    yyy_coeff,
+    xx_coeff,
+    xy_coeff,
+    yy_coeff,
+    x_coeff,
+    y_coeff,
+    c_coeff,
+    coeff_count
+};
+
+typedef std::vector<coeff> coeffs;
+typedef std::vector<coeffs> n_coeffs;
+
+static char skipSpace(const char* str, size_t& index) {
+    do {
+        ++index;
+    } while (str[index] == ' ');
+    return str[index];
+}
+
+static char backSkipSpace(const char* str, size_t& end) {
+    while (str[end - 1] == ' ') {
+        --end;
+    }
+    return str[end - 1];
+}
+
+static void match(const char* str, size_t len, coeffs& co, const char pattern[]) {
+    size_t patternLen = strlen(pattern);
+    size_t index = 0;
+    while (index < len) {
+        char ch = str[index];
+        if (ch != '-' && ch != '+') {
+            printf("missing sign\n");
+        }
+        size_t end = index + 1;
+        while (str[end] != '+' && str[end] != '-' && ++end < len) {
+            ;
+        }
+        backSkipSpace(str, end);
+        size_t idx = index;
+        index = end;
+        skipSpace(str, index);
+        if (!strncmp(&str[end - patternLen], pattern, patternLen) == 0) {
+            continue;
+        }
+        size_t endCoeff = end - patternLen;
+        char last = backSkipSpace(str, endCoeff);
+        if (last == '2' || last == '3') {
+            last = str[endCoeff - 3]; // skip ^2
+        }
+        if (last == 'x' || last == 'y') {
+            continue;
+        }
+        coeff c;
+        c.s = str[idx] == '-' ? -1 : 1;
+        bzero(c.n, sizeof(c.n));
+        ch = skipSpace(str, idx);
+        if (ch >= '2' && ch <= '6') {
+            c.s *= ch - '0';
+            ch = skipSpace(str, idx);
+        }
+        while (idx < endCoeff) {
+            char x = str[idx];
+            if (x < 'a' || x > 'a' + factors) {
+                printf("expected factor\n");
+            }
+            idx++;
+            int pow = 1;
+            if (str[idx] == '^') {
+                idx++;
+                char exp = str[idx];
+                if (exp < '2' || exp > '3') {
+                    printf("expected exponent\n");
+                }
+                pow = exp - '0';
+            }
+            skipSpace(str, idx);
+            c.n[x - 'a'] = pow;
+        }
+        co.push_back(c);
+    }
+}
+
+void cubecode_test(int test);
+
+void cubecode_test(int test) {
+    const char* str = test ? result2 : result1;
+    size_t len = strlen(str);
+    n_coeffs c(coeff_count);
+    match(str, len, c[xxx_coeff], "x^3");   // 1 factor
+    match(str, len, c[xxy_coeff], "x^2 y"); // 1 factor
+    match(str, len, c[xyy_coeff], "x y^2"); // 1 factor
+    match(str, len, c[yyy_coeff], "y^3");   // 1 factor
+    match(str, len, c[xx_coeff], "x^2");    // 7 factors
+    match(str, len, c[xy_coeff], "x y");    // 8 factors
+    match(str, len, c[yy_coeff], "y^2");    // 7 factors
+    match(str, len, c[x_coeff], "x");       // 21 factors
+    match(str, len, c[y_coeff], "y");       // 21 factors
+    match(str, len, c[c_coeff], "");        // 34 factors : total 102
+#define COMPUTE_MOST_FREQUENT_EXPRESSION_TRIPLETS 0
+#define WRITE_AS_NONOPTIMIZED_C_CODE 0
+#if COMPUTE_MOST_FREQUENT_EXPRESSION_TRIPLETS
+    int count[factors][factors][factors];
+    bzero(count, sizeof(count));
+#endif
+#if WRITE_AS_NONOPTIMIZED_C_CODE
+    printf("// start of generated code");
+#endif
+    for (n_coeffs::iterator it = c.begin(); it < c.end(); ++it) {
+        coeffs& co = *it;
+#if WRITE_AS_NONOPTIMIZED_C_CODE
+        printf("\nstatic double calc_%c(double a, double b, double c, double d,"
+               "\n                     double e, double f, double g, double h) {"
+               "\n    return"
+               "\n ", 'A' + (it - c.begin()));
+        if (co[0].s > 0) {
+            printf(" ");
+        }
+        if (abs(co[0].s) == 1) {
+            printf("    ");
+        }
+#endif
+        for (coeffs::iterator ct = co.begin(); ct < co.end(); ++ct) {
+            const coeff& cf = *ct;
+#if WRITE_AS_NONOPTIMIZED_C_CODE
+            printf("        ");
+            bool firstFactor = false;
+            if (ct - co.begin() > 0 || cf.s < 0) {
+                printf("%c", cf.s < 0 ? '-' : '+');
+            }
+            if (ct - co.begin() > 0) {
+                printf(" ");
+            }
+            if (abs(cf.s) > 1) {
+                printf("%d * ", abs(cf.s));
+            } else {
+                if (ct - co.begin() > 0) {
+                    printf("    ");
+                }
+            }
+#endif
+            for (int x = 0; x < factors; ++x) {
+                if (cf.n[x] == 0) {
+                    continue;
+                }
+#if WRITE_AS_NONOPTIMIZED_C_CODE
+                for (int y = 0 ; y < cf.n[x]; ++y) {
+                    if (y > 0 || firstFactor) {
+                        printf(" * ");
+                    }
+                    printf("%c", 'a' + x);
+                }
+                firstFactor = true;
+#endif
+#if COMPUTE_MOST_FREQUENT_EXPRESSION_TRIPLETS
+                for (int y = x; y < factors; ++y) {
+                    if (cf.n[y] == 0) {
+                        continue;
+                    }
+                    if (x == y && cf.n[y] == 1) {
+                        continue;
+                    }
+                    for (int z = y; z < factors; ++z) {
+                        if (cf.n[z] == 0) {
+                            continue;
+                        }
+                        if ((x == z || y == z) && cf.n[z] == 1) {
+                            continue;
+                        }
+                        if (x == y && y == z && cf.n[z] == 2) {
+                            continue;
+                        }
+                        count[x][y][z]++;
+                    }
+                }
+#endif
+            }
+#if WRITE_AS_NONOPTIMIZED_C_CODE
+            if (ct + 1 < co.end()) {
+                printf("\n");
+            }
+#endif
+        }
+#if WRITE_AS_NONOPTIMIZED_C_CODE
+            printf(";\n}\n");
+#endif
+    }
+#if WRITE_AS_NONOPTIMIZED_C_CODE
+    printf("// end of generated code\n");
+#endif
+#if COMPUTE_MOST_FREQUENT_EXPRESSION_TRIPLETS
+    const int bestCount = 20;
+    int best[bestCount][4];
+    bzero(best, sizeof(best));
+    for (int x = 0; x < factors; ++x) {
+        for (int y = x; y < factors; ++y) {
+            for (int z = y; z < factors; ++z) {
+                if (!count[x][y][z]) {
+                    continue;
+                }
+                for (int w = 0; w < bestCount; ++w) {
+                    if (best[w][0] < count[x][y][z]) {
+                        best[w][0] = count[x][y][z];
+                        best[w][1] = x;
+                        best[w][2] = y;
+                        best[w][3] = z;
+                        break;
+                    }
+                }
+            }
+        }
+    }
+    for (int w = 0; w < bestCount; ++w) {
+        printf("%c%c%c=%d\n", 'a' + best[w][1], 'a'  + best[w][2],
+            'a' + best[w][3], best[w][0]);
+    }
+#endif
+#if WRITE_AS_NONOPTIMIZED_C_CODE
+    printf("\n");
+#endif
+}
+
+/* results: variable triplets used 10 or more times:
+aah=14
+ade=14
+aeh=14
+dee=14
+bce=13
+beg=13
+beh=12
+bbe=11
+bef=11
+cee=11
+cef=11
+def=11
+ceh=10
+deg=10
+*/
diff --git a/experimental/Intersection/CubicParameterization_Test.cpp b/experimental/Intersection/CubicParameterization_Test.cpp
new file mode 100644
index 0000000..114331a
--- /dev/null
+++ b/experimental/Intersection/CubicParameterization_Test.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 "CurveIntersection.h"
+#include "Intersection_Tests.h"
+#include "Parameterization_Test.h"
+#include "TestUtilities.h"
+
+const Quadratic quadratics[] = {
+    {{0, 0}, {1, 0}, {1, 1}},
+};
+
+const size_t quadratics_count = sizeof(quadratics) / sizeof(quadratics[0]);
+
+int firstCubicCoincidenceTest = 0;
+
+void CubicCoincidence_Test() {
+    // split large quadratic
+    // upscale quadratics to cubics
+    // compare original, parts, to see if the are coincident
+    for (size_t index = firstCubicCoincidenceTest; index < quadratics_count; ++index) {
+        const Quadratic& test = quadratics[index];
+        QuadraticPair split;
+        chop_at(test, split, 0.5);
+        Quadratic midThird;
+        sub_divide(test, 1.0/3, 2.0/3, midThird);
+        Cubic whole, first, second, mid;
+        quad_to_cubic(test, whole);
+        quad_to_cubic(split.first(), first);
+        quad_to_cubic(split.second(), second);
+        quad_to_cubic(midThird, mid);
+        if (!implicit_matches(whole, first)) {
+            printf("%s-1 %d\n", __FUNCTION__, (int)index);
+        }
+        if (!implicit_matches(whole, second)) {
+            printf("%s-2 %d\n", __FUNCTION__, (int)index);
+        }
+        if (!implicit_matches(mid, first)) {
+            printf("%s-3 %d\n", __FUNCTION__, (int)index);
+        }
+        if (!implicit_matches(mid, second)) {
+            printf("%s-4 %d\n", __FUNCTION__, (int)index);
+        }
+        if (!implicit_matches(first, second)) {
+            printf("%s-5 %d\n", __FUNCTION__, (int)index);
+        }
+    }
+}
+
+// pairs of coincident cubics
+// The on curve points of each cubic should be on both parameterized cubics.
+const Cubic cubics[] = {
+  {
+    { 1,     -1},
+    { 1.0/3,  1},
+    {-1.0/3, -1},
+    {-1,      1}
+  },
+  {
+    {-1,     1},
+    {-1.0/3, -1},
+    { 1.0/3,  1},
+    { 1,     -1}
+  },
+  {
+    {0, 2},
+    {0, 1},
+    {1, 0},
+    {2, 0}
+  }, {
+    {2, 0},
+    {1, 0},
+    {0, 1},
+    {0, 2}
+  },
+  {
+    {315.74799999999999, 312.83999999999997},
+    {312.64400000000001, 318.13400000000001},
+    {305.83600000000001, 319.90899999999999},
+    {300.54199999999997, 316.80399999999997}
+  }, {
+    {317.12200000000001, 309.05000000000001},
+    {316.11200000000002, 315.10199999999998},
+    {310.38499999999999, 319.19},
+    {304.33199999999999, 318.17899999999997}
+  }
+};
+
+const size_t cubics_count = sizeof(cubics) / sizeof(cubics[0]);
+
+int firstCubicParameterizationTest = 0;
+
+void CubicParameterization_Test() {
+    for (size_t index = firstCubicParameterizationTest; index < cubics_count; ++index) {
+        for (size_t inner = 0; inner < 4; inner += 3) {
+            if (!point_on_parameterized_curve(cubics[index], cubics[index][inner])) {
+                    printf("%s [%zu,%zu] 1 parameterization failed\n",
+                        __FUNCTION__, index, inner);
+            }
+            if (!point_on_parameterized_curve(cubics[index], cubics[index ^ 1][inner])) {
+                    printf("%s [%zu,%zu] 2 parameterization failed\n",
+                        __FUNCTION__, index, inner);
+            }
+        }
+        if (!implicit_matches(cubics[index], cubics[index ^ 1])) {
+            printf("%s %d\n", __FUNCTION__, (int)index);
+        }
+    }
+}
diff --git a/experimental/Intersection/CubicParameterization_TestUtility.cpp b/experimental/Intersection/CubicParameterization_TestUtility.cpp
new file mode 100644
index 0000000..356ca88
--- /dev/null
+++ b/experimental/Intersection/CubicParameterization_TestUtility.cpp
@@ -0,0 +1,53 @@
+// included by CubicParameterization.cpp
+// accesses internal functions to validate parameterized coefficients
+
+#include "Parameterization_Test.h"
+
+static void parameter_coeffs(const Cubic& cubic, double coeffs[coeff_count]) {
+#if USE_SYVESTER
+    double ax, bx, cx, dx;
+    if (try_alt)
+        alt_set_abcd(&cubic[0].x, ax, bx, cx, dx);
+    else
+        set_abcd(&cubic[0].x, ax, bx, cx, dx);
+    double ay, by, cy, dy;
+    if (try_alt)
+        alt_set_abcd(&cubic[0].y, ay, by, cy, dy);
+    else
+        set_abcd(&cubic[0].y, ay, by, cy, dy);
+    calc_ABCD(ax, ay, coeffs);
+    if (!try_alt) calc_bc(dx, bx, cx);
+    if (!try_alt) calc_bc(dy, by, cy);
+#else
+    double ax = cubic[0].x;
+    double bx = cubic[1].x;
+    double cx = cubic[2].x;
+    double dx = cubic[3].x;
+    double ay = cubic[0].y;
+    double by = cubic[1].y;
+    double cy = cubic[2].y;
+    double dy = cubic[3].y;
+    calc_ABCD(ax, bx, cx, dx, ay, by, cy, dy, coeffs);
+#endif
+    for (int index = xx_coeff; index < coeff_count; ++index) {
+        int procIndex = index - xx_coeff;
+        coeffs[index] = (*calc_proc[procIndex])(ax, bx, cx, dx, ay, by, cy, dy);
+    }
+}
+
+bool point_on_parameterized_curve(const Cubic& cubic, const _Point& point) {
+    double coeffs[coeff_count];
+    parameter_coeffs(cubic, coeffs);
+    double xxx = coeffs[xxx_coeff] * point.x * point.x * point.x;
+    double xxy = coeffs[xxy_coeff] * point.x * point.x * point.y;
+    double xyy = coeffs[xyy_coeff] * point.x * point.y * point.y;
+    double yyy = coeffs[yyy_coeff] * point.y * point.y * point.y;
+    double  xx = coeffs[ xx_coeff] * point.x * point.x;
+    double  xy = coeffs[ xy_coeff] * point.x * point.y;
+    double  yy = coeffs[ yy_coeff] * point.y * point.y;
+    double   x = coeffs[  x_coeff] * point.x;
+    double   y = coeffs[  y_coeff] * point.y;
+    double   c = coeffs[  c_coeff];
+    double sum = xxx + xxy + xyy + yyy + xx + xy + yy + x + y + c;
+    return approximately_zero(sum);
+}
diff --git a/experimental/Intersection/CubicReduceOrder.cpp b/experimental/Intersection/CubicReduceOrder.cpp
new file mode 100644
index 0000000..38e853a
--- /dev/null
+++ b/experimental/Intersection/CubicReduceOrder.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "Extrema.h"
+#include "IntersectionUtilities.h"
+#include "LineParameters.h"
+
+static double interp_cubic_coords(const double* src, double t)
+{
+    double ab = interp(src[0], src[2], t);
+    double bc = interp(src[2], src[4], t);
+    double cd = interp(src[4], src[6], t);
+    double abc = interp(ab, bc, t);
+    double bcd = interp(bc, cd, t);
+    return interp(abc, bcd, t);
+}
+
+static int coincident_line(const Cubic& cubic, Cubic& reduction) {
+    reduction[0] = reduction[1] = cubic[0];
+    return 1;
+}
+
+static int vertical_line(const Cubic& cubic, Cubic& reduction) {
+    double tValues[2];
+    reduction[0] = cubic[0];
+    reduction[1] = cubic[3];
+    int smaller = reduction[1].y > reduction[0].y;
+    int larger = smaller ^ 1;
+    int roots = findExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues);
+    for (int index = 0; index < roots; ++index) {
+        double yExtrema = interp_cubic_coords(&cubic[0].y, tValues[index]);
+        if (reduction[smaller].y > yExtrema) {
+            reduction[smaller].y = yExtrema;
+            continue;
+        }
+        if (reduction[larger].y < yExtrema) {
+            reduction[larger].y = yExtrema;
+        }
+    }
+    return 2;
+}
+
+static int horizontal_line(const Cubic& cubic, Cubic& reduction) {
+    double tValues[2];
+    reduction[0] = cubic[0];
+    reduction[1] = cubic[3];
+    int smaller = reduction[1].x > reduction[0].x;
+    int larger = smaller ^ 1;
+    int roots = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues);
+    for (int index = 0; index < roots; ++index) {
+        double xExtrema = interp_cubic_coords(&cubic[0].x, tValues[index]);
+        if (reduction[smaller].x > xExtrema) {
+            reduction[smaller].x = xExtrema;
+            continue;
+        }
+        if (reduction[larger].x < xExtrema) {
+            reduction[larger].x = xExtrema;
+        }
+    }
+    return 2;
+}
+
+// check to see if it is a quadratic or a line
+static int check_quadratic(const Cubic& cubic, Cubic& reduction) {
+    double dx10 = cubic[1].x - cubic[0].x;
+    double dx23 = cubic[2].x - cubic[3].x;
+    double midX = cubic[0].x + dx10 * 3 / 2;
+    if (!approximately_equal(midX - cubic[3].x, dx23 * 3 / 2)) {
+        return 0;
+    }
+    double dy10 = cubic[1].y - cubic[0].y;
+    double dy23 = cubic[2].y - cubic[3].y;
+    double midY = cubic[0].y + dy10 * 3 / 2;
+    if (!approximately_equal(midY - cubic[3].y, dy23 * 3 / 2)) {
+        return 0;
+    }
+    reduction[0] = cubic[0];
+    reduction[1].x = midX;
+    reduction[1].y = midY;
+    reduction[2] = cubic[3];
+    return 3;
+}
+
+static int check_linear(const Cubic& cubic, Cubic& reduction,
+        int minX, int maxX, int minY, int maxY) {
+    int startIndex = 0;
+    int endIndex = 3;
+    while (cubic[startIndex].approximatelyEqual(cubic[endIndex])) {
+        --endIndex;
+        if (endIndex == 0) {
+            printf("%s shouldn't get here if all four points are about equal", __FUNCTION__);
+            assert(0);
+        }
+    }
+    if (!isLinear(cubic, startIndex, endIndex)) {
+        return 0;
+    }
+    // four are colinear: return line formed by outside
+    reduction[0] = cubic[0];
+    reduction[1] = cubic[3];
+    int sameSide1;
+    int sameSide2;
+    bool useX = cubic[maxX].x - cubic[minX].x >= cubic[maxY].y - cubic[minY].y;
+    if (useX) {
+        sameSide1 = sign(cubic[0].x - cubic[1].x) + sign(cubic[3].x - cubic[1].x);
+        sameSide2 = sign(cubic[0].x - cubic[2].x) + sign(cubic[3].x - cubic[2].x);
+    } else {
+        sameSide1 = sign(cubic[0].y - cubic[1].y) + sign(cubic[3].y - cubic[1].y);
+        sameSide2 = sign(cubic[0].y - cubic[2].y) + sign(cubic[3].y - cubic[2].y);
+    }
+    if (sameSide1 == sameSide2 && (sameSide1 & 3) != 2) {
+        return 2;
+    }
+    double tValues[2];
+    int roots;
+    if (useX) {
+        roots = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues);
+    } else {
+        roots = findExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues);
+    }
+    for (int index = 0; index < roots; ++index) {
+        _Point extrema;
+        extrema.x = interp_cubic_coords(&cubic[0].x, tValues[index]);
+        extrema.y = interp_cubic_coords(&cubic[0].y, tValues[index]);
+        // sameSide > 0 means mid is smaller than either [0] or [3], so replace smaller
+        int replace;
+        if (useX) {
+            if (extrema.x < cubic[0].x ^ extrema.x < cubic[3].x) {
+                continue;
+            }
+            replace = (extrema.x < cubic[0].x | extrema.x < cubic[3].x)
+                    ^ cubic[0].x < cubic[3].x;
+        } else {
+            if (extrema.y < cubic[0].y ^ extrema.y < cubic[3].y) {
+                continue;
+            }
+            replace = (extrema.y < cubic[0].y | extrema.y < cubic[3].y)
+                    ^ cubic[0].y < cubic[3].y;
+        }
+        reduction[replace] = extrema;
+    }
+    return 2;
+}
+
+bool isLinear(const Cubic& cubic, int startIndex, int endIndex) {
+    LineParameters lineParameters;
+    lineParameters.cubicEndPoints(cubic, startIndex, endIndex);
+    double normalSquared = lineParameters.normalSquared();
+    double distance[2]; // distance is not normalized
+    int mask = other_two(startIndex, endIndex);
+    int inner1 = startIndex ^ mask;
+    int inner2 = endIndex ^ mask;
+    lineParameters.controlPtDistance(cubic, inner1, inner2, distance);
+    double limit = normalSquared;
+    int index;
+    for (index = 0; index < 2; ++index) {
+        double distSq = distance[index];
+        distSq *= distSq;
+        if (approximately_greater(distSq, limit)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+/* food for thought:
+http://objectmix.com/graphics/132906-fast-precision-driven-cubic-quadratic-piecewise-degree-reduction-algos-2-a.html
+
+Given points c1, c2, c3 and c4 of a cubic Bezier, the points of the
+corresponding quadratic Bezier are (given in convex combinations of
+points):
+
+q1 = (11/13)c1 + (3/13)c2 -(3/13)c3 + (2/13)c4
+q2 = -c1 + (3/2)c2 + (3/2)c3 - c4
+q3 = (2/13)c1 - (3/13)c2 + (3/13)c3 + (11/13)c4
+
+Of course, this curve does not interpolate the end-points, but it would
+be interesting to see the behaviour of such a curve in an applet.
+
+--
+Kalle Rutanen
+http://kaba.hilvi.org
+
+*/
+
+// reduce to a quadratic or smaller
+// look for identical points
+// look for all four points in a line
+    // note that three points in a line doesn't simplify a cubic
+// look for approximation with single quadratic
+    // save approximation with multiple quadratics for later
+int reduceOrder(const Cubic& cubic, Cubic& reduction, ReduceOrder_Flags allowQuadratics) {
+    int index, minX, maxX, minY, maxY;
+    int minXSet, minYSet;
+    minX = maxX = minY = maxY = 0;
+    minXSet = minYSet = 0;
+    for (index = 1; index < 4; ++index) {
+        if (cubic[minX].x > cubic[index].x) {
+            minX = index;
+        }
+        if (cubic[minY].y > cubic[index].y) {
+            minY = index;
+        }
+        if (cubic[maxX].x < cubic[index].x) {
+            maxX = index;
+        }
+        if (cubic[maxY].y < cubic[index].y) {
+            maxY = index;
+        }
+    }
+    for (index = 0; index < 4; ++index) {
+        if (approximately_equal(cubic[index].x, cubic[minX].x)) {
+            minXSet |= 1 << index;
+        }
+        if (approximately_equal(cubic[index].y, cubic[minY].y)) {
+            minYSet |= 1 << index;
+        }
+    }
+    if (minXSet == 0xF) { // test for vertical line
+        if (minYSet == 0xF) { // return 1 if all four are coincident
+            return coincident_line(cubic, reduction);
+        }
+        return vertical_line(cubic, reduction);
+    }
+    if (minYSet == 0xF) { // test for horizontal line
+        return horizontal_line(cubic, reduction);
+    }
+    int result = check_linear(cubic, reduction, minX, maxX, minY, maxY);
+    if (result) {
+        return result;
+    }
+    if (allowQuadratics && (result = check_quadratic(cubic, reduction))) {
+        return result;
+    }
+    memcpy(reduction, cubic, sizeof(Cubic));
+    return 4;
+}
diff --git a/experimental/Intersection/CubicReduceOrder_Test.cpp b/experimental/Intersection/CubicReduceOrder_Test.cpp
new file mode 100644
index 0000000..d01629c
--- /dev/null
+++ b/experimental/Intersection/CubicReduceOrder_Test.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "CubicIntersection_TestData.h"
+#include "Intersection_Tests.h"
+#include "QuadraticIntersection_TestData.h"
+#include "TestUtilities.h"
+
+void CubicReduceOrder_Test() {
+    size_t index;
+    Cubic reduce;
+    int order;
+    enum {
+        RunAll,
+        RunPointDegenerates,
+        RunNotPointDegenerates,
+        RunLines,
+        RunNotLines,
+        RunModEpsilonLines,
+        RunLessEpsilonLines,
+        RunNegEpsilonLines,
+        RunQuadraticLines,
+        RunQuadraticModLines,
+        RunComputedLines,
+        RunNone
+    } run = RunAll;
+    int firstTestIndex = 0;
+#if 0
+    run = RunComputedLines;
+    firstTestIndex = 18;
+#endif
+    int firstPointDegeneratesTest = run == RunAll ? 0 : run == RunPointDegenerates ? firstTestIndex : INT_MAX;
+    int firstNotPointDegeneratesTest = run == RunAll ? 0 : run == RunNotPointDegenerates ? firstTestIndex : INT_MAX;
+    int firstLinesTest = run == RunAll ? 0 : run == RunLines ? firstTestIndex : INT_MAX;
+    int firstNotLinesTest = run == RunAll ? 0 : run == RunNotLines ? firstTestIndex : INT_MAX;
+    int firstModEpsilonTest = run == RunAll ? 0 : run == RunModEpsilonLines ? firstTestIndex : INT_MAX;
+    int firstLessEpsilonTest = run == RunAll ? 0 : run == RunLessEpsilonLines ? firstTestIndex : INT_MAX;
+    int firstNegEpsilonTest = run == RunAll ? 0 : run == RunNegEpsilonLines ? firstTestIndex : INT_MAX;
+    int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex : INT_MAX;
+    int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex : INT_MAX;
+    int firstComputedLinesTest = run == RunAll ? 0 : run == RunComputedLines ? firstTestIndex : INT_MAX;
+
+    for (index = firstPointDegeneratesTest; index < pointDegenerates_count; ++index) {
+        const Cubic& cubic = pointDegenerates[index];
+        order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
+        if (order != 1) {
+            printf("[%d] pointDegenerates order=%d\n", (int) index, order);
+        }
+    }
+    for (index = firstNotPointDegeneratesTest; index < notPointDegenerates_count; ++index) {
+        const Cubic& cubic = notPointDegenerates[index];
+        order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
+        if (order == 1) {
+            printf("[%d] notPointDegenerates order=%d\n", (int) index, order);
+        }
+    }
+    for (index = firstLinesTest; index < lines_count; ++index) {
+        const Cubic& cubic = lines[index];
+        order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
+        if (order != 2) {
+            printf("[%d] lines order=%d\n", (int) index, order);
+        }
+    }
+    for (index = firstNotLinesTest; index < notLines_count; ++index) {
+        const Cubic& cubic = notLines[index];
+        order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
+        if (order == 2) {
+            printf("[%d] notLines order=%d\n", (int) index, order);
+        }
+    }
+    for (index = firstModEpsilonTest; index < modEpsilonLines_count; ++index) {
+        const Cubic& cubic = modEpsilonLines[index];
+        order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
+        if (order == 2) {
+            printf("[%d] line mod by epsilon order=%d\n", (int) index, order);
+        }
+    }
+    for (index = firstLessEpsilonTest; index < lessEpsilonLines_count; ++index) {
+        const Cubic& cubic = lessEpsilonLines[index];
+        order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
+        if (order != 2) {
+            printf("[%d] line less by epsilon/2 order=%d\n", (int) index, order);
+        }
+    }
+    for (index = firstNegEpsilonTest; index < negEpsilonLines_count; ++index) {
+        const Cubic& cubic = negEpsilonLines[index];
+        order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
+        if (order != 2) {
+            printf("[%d] line neg by epsilon/2 order=%d\n", (int) index, order);
+        }
+    }
+    for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
+        const Quadratic& quad = quadraticLines[index];
+        Cubic cubic;
+        quad_to_cubic(quad, cubic);
+        order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
+        if (order != 2) {
+            printf("[%d] line quad order=%d\n", (int) index, order);
+        }
+    }
+    for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) {
+        const Quadratic& quad = quadraticModEpsilonLines[index];
+        Cubic cubic;
+        quad_to_cubic(quad, cubic);
+        order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
+        if (order != 3) {
+            printf("[%d] line mod quad order=%d\n", (int) index, order);
+        }
+    }
+
+    // test if computed line end points are valid
+    for (index = firstComputedLinesTest; index < lines_count; ++index) {
+        const Cubic& cubic = lines[index];
+        bool controlsInside = controls_inside(cubic);
+        order = reduceOrder(cubic, reduce, kReduceOrder_QuadraticsAllowed);
+        if (reduce[0].x == reduce[1].x && reduce[0].y == reduce[1].y) {
+            printf("[%d] line computed ends match order=%d\n", (int) index, order);
+        }
+        if (controlsInside) {
+            if (       reduce[0].x != cubic[0].x && reduce[0].x != cubic[3].x
+                    || reduce[0].y != cubic[0].y && reduce[0].y != cubic[3].y
+                    || reduce[1].x != cubic[0].x && reduce[1].x != cubic[3].x
+                    || reduce[1].y != cubic[0].y && reduce[1].y != cubic[3].y) {
+                printf("[%d] line computed ends order=%d\n", (int) index, order);
+            }
+        } else {
+            // binary search for extrema, compare against actual results
+                // while a control point is outside of bounding box formed by end points, split
+            _Rect bounds = {DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX};
+            find_tight_bounds(cubic, bounds);
+            if (       !approximately_equal(reduce[0].x, bounds.left) && !approximately_equal(reduce[0].x, bounds.right)
+                    || !approximately_equal(reduce[0].y, bounds.top) && !approximately_equal(reduce[0].y, bounds.bottom)
+                    || !approximately_equal(reduce[1].x, bounds.left) && !approximately_equal(reduce[1].x, bounds.right)
+                    || !approximately_equal(reduce[1].y, bounds.top) && !approximately_equal(reduce[1].y, bounds.bottom)) {
+                printf("[%d] line computed tight bounds order=%d\n", (int) index, order);
+            }
+
+        }
+    }
+}
diff --git a/experimental/Intersection/CubicSubDivide.cpp b/experimental/Intersection/CubicSubDivide.cpp
new file mode 100644
index 0000000..e363a0d
--- /dev/null
+++ b/experimental/Intersection/CubicSubDivide.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 "CurveIntersection.h"
+#include "IntersectionUtilities.h"
+
+/*
+ Given a cubic c, t1, and t2, find a small cubic segment.
+
+ The new cubic is defined as points A, B, C, and D, where
+ s1 = 1 - t1
+ s2 = 1 - t2
+ A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1
+ D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2
+
+ We don't have B or C. So We define two equations to isolate them.
+ First, compute two reference T values 1/3 and 2/3 from t1 to t2:
+
+ c(at (2*t1 + t2)/3) == E
+ c(at (t1 + 2*t2)/3) == F
+
+ Next, compute where those values must be if we know the values of B and C:
+
+ _12   =  A*2/3 + B*1/3
+ 12_   =  A*1/3 + B*2/3
+ _23   =  B*2/3 + C*1/3
+ 23_   =  B*1/3 + C*2/3
+ _34   =  C*2/3 + D*1/3
+ 34_   =  C*1/3 + D*2/3
+ _123  = (A*2/3 + B*1/3)*2/3 + (B*2/3 + C*1/3)*1/3 = A*4/9 + B*4/9 + C*1/9
+ 123_  = (A*1/3 + B*2/3)*1/3 + (B*1/3 + C*2/3)*2/3 = A*1/9 + B*4/9 + C*4/9
+ _234  = (B*2/3 + C*1/3)*2/3 + (C*2/3 + D*1/3)*1/3 = B*4/9 + C*4/9 + D*1/9
+ 234_  = (B*1/3 + C*2/3)*1/3 + (C*1/3 + D*2/3)*2/3 = B*1/9 + C*4/9 + D*4/9
+ _1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3
+       =  A*8/27 + B*12/27 + C*6/27 + D*1/27
+       =  E
+ 1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3
+       =  A*1/27 + B*6/27 + C*12/27 + D*8/27
+       =  F
+ E*27  =  A*8    + B*12   + C*6     + D
+ F*27  =  A      + B*6    + C*12    + D*8
+
+Group the known values on one side:
+
+ M       = E*27 - A*8 - D     = B*12 + C* 6
+ N       = F*27 - A   - D*8   = B* 6 + C*12
+ M*2 - N = B*18
+ N*2 - M = C*18
+ B       = (M*2 - N)/18
+ C       = (N*2 - M)/18
+ */
+
+static double interp_cubic_coords(const double* src, double t)
+{
+    double ab = interp(src[0], src[2], t);
+    double bc = interp(src[2], src[4], t);
+    double cd = interp(src[4], src[6], t);
+    double abc = interp(ab, bc, t);
+    double bcd = interp(bc, cd, t);
+    double abcd = interp(abc, bcd, t);
+    return abcd;
+}
+
+void sub_divide(const Cubic& src, double t1, double t2, Cubic& dst) {
+    double ax = dst[0].x = interp_cubic_coords(&src[0].x, t1);
+    double ay = dst[0].y = interp_cubic_coords(&src[0].y, t1);
+    double ex = interp_cubic_coords(&src[0].x, (t1*2+t2)/3);
+    double ey = interp_cubic_coords(&src[0].y, (t1*2+t2)/3);
+    double fx = interp_cubic_coords(&src[0].x, (t1+t2*2)/3);
+    double fy = interp_cubic_coords(&src[0].y, (t1+t2*2)/3);
+    double dx = dst[3].x = interp_cubic_coords(&src[0].x, t2);
+    double dy = dst[3].y = interp_cubic_coords(&src[0].y, t2);
+    double mx = ex * 27 - ax * 8 - dx;
+    double my = ey * 27 - ay * 8 - dy;
+    double nx = fx * 27 - ax - dx * 8;
+    double ny = fy * 27 - ay - dy * 8;
+    /* bx = */ dst[1].x = (mx * 2 - nx) / 18;
+    /* by = */ dst[1].y = (my * 2 - ny) / 18;
+    /* cx = */ dst[2].x = (nx * 2 - mx) / 18;
+    /* cy = */ dst[2].y = (ny * 2 - my) / 18;
+}
+
+/* classic one t subdivision */
+static void interp_cubic_coords(const double* src, double* dst, double t)
+{
+    double ab = interp(src[0], src[2], t);
+    double bc = interp(src[2], src[4], t);
+    double cd = interp(src[4], src[6], t);
+    double abc = interp(ab, bc, t);
+    double bcd = interp(bc, cd, t);
+    double abcd = interp(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 chop_at(const Cubic& src, CubicPair& dst, double t)
+{
+    interp_cubic_coords(&src[0].x, &dst.pts[0].x, t);
+    interp_cubic_coords(&src[0].y, &dst.pts[0].y, t);
+}
diff --git a/experimental/Intersection/CubicUtilities.cpp b/experimental/Intersection/CubicUtilities.cpp
new file mode 100644
index 0000000..657d4a3
--- /dev/null
+++ b/experimental/Intersection/CubicUtilities.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CubicUtilities.h"
+#include "DataTypes.h"
+#include "QuadraticUtilities.h"
+
+void coefficients(const double* cubic, double& A, double& B, double& C, double& D) {
+    A = cubic[6]; // d
+    B = cubic[4] * 3; // 3*c
+    C = cubic[2] * 3; // 3*b
+    D = cubic[0]; // a
+    A -= D - C + B;     // A =   -a + 3*b - 3*c + d
+    B += 3 * D - 2 * C; // B =  3*a - 6*b + 3*c
+    C -= 3 * D;         // C = -3*a + 3*b
+}
+
+// cubic roots
+
+const double PI = 4 * atan(1);
+
+static bool is_unit_interval(double x) {
+    return x > 0 && x < 1;
+}
+
+// from SkGeometry.cpp (and Numeric Solutions, 5.6)
+int cubicRoots(double A, double B, double C, double D, double t[3]) {
+    if (approximately_zero(A)) {  // we're just a quadratic
+        return quadraticRoots(B, C, D, t);
+    }
+    double a, b, c;
+    {
+        double invA = 1 / A;
+        a = B * invA;
+        b = C * invA;
+        c = D * invA;
+    }
+    double a2 = a * a;
+    double Q = (a2 - b * 3) / 9;
+    double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
+    double Q3 = Q * Q * Q;
+    double R2MinusQ3 = R * R - Q3;
+    double adiv3 = a / 3;
+    double* roots = t;
+    double r;
+
+    if (R2MinusQ3 < 0)   // we have 3 real roots
+    {
+        double theta = acos(R / sqrt(Q3));
+        double neg2RootQ = -2 * sqrt(Q);
+
+        r = neg2RootQ * cos(theta / 3) - adiv3;
+        if (is_unit_interval(r))
+            *roots++ = r;
+
+        r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
+        if (is_unit_interval(r))
+            *roots++ = r;
+
+        r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
+        if (is_unit_interval(r))
+            *roots++ = r;
+    }
+    else                // we have 1 real root
+    {
+        double A = fabs(R) + sqrt(R2MinusQ3);
+        A = cube_root(A);
+        if (R > 0) {
+            A = -A;
+        }
+        if (A != 0) {
+            A += Q / A;
+        }
+        r = A - adiv3;
+        if (is_unit_interval(r))
+            *roots++ = r;
+    }
+    return (int)(roots - t);
+}
+
+// from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf
+// c(t)  = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3
+// c'(t) = -3a(1-t)^2 + 3b((1-t)^2 - 2t(1-t)) + 3c(2t(1-t) - t^2) + 3dt^2
+//       = 3(b-a)(1-t)^2 + 6(c-b)t(1-t) + 3(d-c)t^2
+double derivativeAtT(const double* cubic, double t) {
+    double one_t = 1 - t;
+    double a = cubic[0];
+    double b = cubic[2];
+    double c = cubic[4];
+    double d = cubic[6];
+    return (b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t;
+}
+
+// same as derivativeAtT
+// which is more accurate? which is faster?
+double derivativeAtT_2(const double* cubic, double t) {
+    double a = cubic[2] - cubic[0];
+    double b = cubic[4] - 2 * cubic[2] + cubic[0];
+    double c = cubic[6] + 3 * (cubic[2] - cubic[4]) - cubic[0];
+    return c * c * t * t + 2 * b * t + a;
+}
+
+void dxdy_at_t(const Cubic& cubic, double t, double& dx, double& dy) {
+    if (&dx) {
+        dx = derivativeAtT(&cubic[0].x, t);
+    }
+    if (&dy) {
+        dy = derivativeAtT(&cubic[0].y, t);
+    }
+}
+
+bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath) {
+    double dy = cubic[index].y - cubic[zero].y;
+    double dx = cubic[index].x - cubic[zero].x;
+    if (approximately_equal(dy, 0)) {
+        if (approximately_equal(dx, 0)) {
+            return false;
+        }
+        memcpy(rotPath, cubic, sizeof(Cubic));
+        return true;
+    }
+    for (int index = 0; index < 4; ++index) {
+        rotPath[index].x = cubic[index].x * dx + cubic[index].y * dy;
+        rotPath[index].y = cubic[index].y * dx - cubic[index].x * dy;
+    }
+    return true;
+}
+
+double secondDerivativeAtT(const double* cubic, double t) {
+    double a = cubic[0];
+    double b = cubic[2];
+    double c = cubic[4];
+    double d = cubic[6];
+    return (c - 2 * b + a) * (1 - t) + (d - 2 * c + b) * t;
+}
+
+void xy_at_t(const Cubic& cubic, double t, double& x, double& y) {
+    double one_t = 1 - t;
+    double one_t2 = one_t * one_t;
+    double a = one_t2 * one_t;
+    double b = 3 * one_t2 * t;
+    double t2 = t * t;
+    double c = 3 * one_t * t2;
+    double d = t2 * t;
+    if (&x) {
+        x = a * cubic[0].x + b * cubic[1].x + c * cubic[2].x + d * cubic[3].x;
+    }
+    if (&y) {
+        y = a * cubic[0].y + b * cubic[1].y + c * cubic[2].y + d * cubic[3].y;
+    }
+}
diff --git a/experimental/Intersection/CubicUtilities.h b/experimental/Intersection/CubicUtilities.h
new file mode 100644
index 0000000..ffaeeb2
--- /dev/null
+++ b/experimental/Intersection/CubicUtilities.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.
+ */
+#if !defined CUBIC_UTILITIES_H
+#define CUBIC_UTILITIES_H
+
+#include "DataTypes.h"
+
+double cube_root(double x);
+void coefficients(const double* cubic, double& A, double& B, double& C, double& D);
+int cubicRoots(double A, double B, double C, double D, double t[3]);
+double derivativeAtT(const double* cubic, double t);
+// competing version that should produce same results
+double derivativeAtT_2(const double* cubic, double t);
+void dxdy_at_t(const Cubic& , double t, double& x, double& y);
+bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath);
+double secondDerivativeAtT(const double* cubic, double t);
+void xy_at_t(const Cubic& , double t, double& x, double& y);
+
+#endif
diff --git a/experimental/Intersection/CurveIntersection.h b/experimental/Intersection/CurveIntersection.h
new file mode 100644
index 0000000..664145b
--- /dev/null
+++ b/experimental/Intersection/CurveIntersection.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 CurveIntersection_DEFINE
+#define CurveIntersection_DEFINE
+
+#include "DataTypes.h"
+
+class Intersections;
+
+// unit-testable utilities
+double axialIntersect(const Quadratic& q1, const _Point& p, bool vert);
+bool bezier_clip(const Cubic& cubic1, const Cubic& cubic2, double& minT, double& maxT);
+bool bezier_clip(const Quadratic& q1, const Quadratic& q2, double& minT, double& maxT);
+void chop_at(const Cubic& src, CubicPair& dst, double t);
+void chop_at(const Quadratic& src, QuadraticPair& dst, double t);
+int convex_hull(const Cubic& cubic, char order[4]);
+bool convex_x_hull(const Cubic& cubic, char connectTo0[2], char connectTo3[2]);
+bool implicit_matches(const Cubic& cubic1, const Cubic& cubic2);
+bool implicit_matches(const _Line& line1, const _Line& line2);
+bool implicit_matches_ulps(const _Line& one, const _Line& two, int ulps);
+bool implicit_matches(const Quadratic& quad1, const Quadratic& quad2);
+void sub_divide(const Cubic& src, double t1, double t2, Cubic& dst);
+void sub_divide(const _Line& src, double t1, double t2, _Line& dst);
+void sub_divide(const Quadratic& src, double t1, double t2, Quadratic& dst);
+void tangent(const Cubic& cubic, double t, _Point& result);
+void tangent(const _Line& line, _Point& result);
+void tangent(const Quadratic& quad, double t, _Point& result);
+
+// main functions
+enum ReduceOrder_Flags {
+    kReduceOrder_NoQuadraticsAllowed,
+    kReduceOrder_QuadraticsAllowed
+};
+int reduceOrder(const Cubic& cubic, Cubic& reduction, ReduceOrder_Flags );
+int reduceOrder(const _Line& line, _Line& reduction);
+int reduceOrder(const Quadratic& quad, Quadratic& reduction);
+int horizontalIntersect(const Cubic& cubic, double y, double tRange[3]);
+int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
+        double tRange[3]);
+int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
+        bool flipped, Intersections&);
+int horizontalIntersect(const _Line& line, double left, double right,
+        double y, bool flipped, Intersections& );
+int horizontalIntersect(const Quadratic& quad, double left, double right,
+        double y, double tRange[2]);
+int horizontalIntersect(const Quadratic& quad, double left, double right,
+        double y, bool flipped, Intersections& );
+bool intersect(const Cubic& cubic1, const Cubic& cubic2, Intersections& );
+int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]);
+bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& );
+int intersect(const Quadratic& quad, const _Line& line, Intersections& );
+// the following flavor uses the implicit form instead of convex hulls
+bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i);
+int intersectRay(const Quadratic& quad, const _Line& line, Intersections& i);
+
+
+bool isLinear(const Quadratic& quad, int startIndex, int endIndex);
+bool isLinear(const Cubic& cubic, int startIndex, int endIndex);
+double leftMostT(const Cubic& , double startT, double endT);
+double leftMostT(const _Line& , double startT, double endT);
+double leftMostT(const Quadratic& , double startT, double endT);
+int verticalIntersect(const Cubic& cubic, double top, double bottom, double x,
+        bool flipped, Intersections& );
+int verticalIntersect(const _Line& line, double top, double bottom, double x,
+        bool flipped, Intersections& );
+int verticalIntersect(const Quadratic& quad, double top, double bottom,
+        double x, bool flipped, Intersections& );
+
+#endif
diff --git a/experimental/Intersection/CurveUtilities.h b/experimental/Intersection/CurveUtilities.h
new file mode 100644
index 0000000..4d34632
--- /dev/null
+++ b/experimental/Intersection/CurveUtilities.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef CurveUtilities_DEFINE
+#define CurveUtilities_DEFINE
+
+#include "CubicUtilities.h"
+#include "LineUtilities.h"
+#include "QuadraticUtilities.h"
+
+#endif
diff --git a/experimental/Intersection/DataTypes.cpp b/experimental/Intersection/DataTypes.cpp
new file mode 100644
index 0000000..3efffc1
--- /dev/null
+++ b/experimental/Intersection/DataTypes.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 "DataTypes.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void    *memcpy(void *, const void *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#if USE_EPSILON
+const double PointEpsilon = 0.000001;
+const double SquaredEpsilon = PointEpsilon * PointEpsilon;
+#endif
+
+const int UlpsEpsilon = 16;
+
+// from http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+union Float_t
+{
+    Float_t(float num = 0.0f) : f(num) {}
+    // Portable extraction of components.
+    bool Negative() const { return (i >> 31) != 0; }
+    int32_t RawMantissa() const { return i & ((1 << 23) - 1); }
+    int32_t RawExponent() const { return (i >> 23) & 0xFF; }
+
+    int32_t i;
+    float f;
+#ifdef _DEBUG
+    struct
+    {   // Bitfields for exploration. Do not use in production code.
+        uint32_t mantissa : 23;
+        uint32_t exponent : 8;
+        uint32_t sign : 1;
+    } parts;
+#endif
+};
+
+bool AlmostEqualUlps(float A, float B, int maxUlpsDiff)
+{
+    Float_t uA(A);
+    Float_t uB(B);
+
+    // Different signs means they do not match.
+    if (uA.Negative() != uB.Negative())
+    {
+        // Check for equality to make sure +0==-0
+        return A == B;
+    }
+
+    // Find the difference in ULPs.
+    int ulpsDiff = abs(uA.i - uB.i);
+    return ulpsDiff <= maxUlpsDiff;
+}
+
+int UlpsDiff(float A, float B)
+{
+    Float_t uA(A);
+    Float_t uB(B);
+
+    return abs(uA.i - uB.i);
+}
+
+int FloatAsInt(float A)
+{
+    Float_t uA(A);
+    return uA.i;
+}
+
+
diff --git a/experimental/Intersection/DataTypes.h b/experimental/Intersection/DataTypes.h
new file mode 100644
index 0000000..e9543d1
--- /dev/null
+++ b/experimental/Intersection/DataTypes.h
@@ -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.
+ */
+#ifndef __DataTypes_h__
+#define __DataTypes_h__
+
+#include <assert.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/types.h>
+
+bool AlmostEqualUlps(float A, float B, int maxUlpsDiff);
+int UlpsDiff(float A, float B);
+int FloatAsInt(float A);
+
+#define USE_EPSILON 0
+
+#if USE_EPSILON
+extern const double PointEpsilon;
+extern const double SquaredEpsilon;
+
+inline bool approximately_equal(double x, double y) {
+    return fabs(x - y) < PointEpsilon;
+}
+
+inline bool approximately_equal_squared(double x, double y) {
+    return fabs(x - y) < SquaredEpsilon;
+}
+
+inline bool approximately_greater(double x, double y) {
+    return x > y - PointEpsilon;
+}
+
+inline bool approximately_lesser(double x, double y) {
+    return x < y + PointEpsilon;
+}
+
+inline bool approximately_zero(double x) {
+    return fabs(x) < PointEpsilon;
+}
+
+inline bool approximately_zero_squared(double x) {
+    return fabs(x) < SquaredEpsilon;
+}
+
+inline bool approximately_negative(double x) {
+    return x < PointEpsilon;
+}
+#else
+extern const int UlpsEpsilon;
+
+#if defined(IN_TEST)
+// FIXME: move to test-only header
+const double PointEpsilon = 0.000001;
+const double SquaredEpsilon = PointEpsilon * PointEpsilon;
+#endif
+
+inline bool approximately_zero(double x) {
+
+    return fabs(x) < FLT_EPSILON;
+}
+
+inline bool precisely_zero(double x) {
+
+    return fabs(x) < DBL_EPSILON;
+}
+
+inline bool approximately_zero(float x) {
+
+    return fabs(x) < FLT_EPSILON;
+}
+
+inline bool approximately_zero_squared(double x) {
+    return fabs(x) < FLT_EPSILON * FLT_EPSILON;
+}
+
+inline bool approximately_equal(double x, double y) {
+    if (approximately_zero(x - y)) {
+        return true;
+    }
+    //  FIXME: since no other function uses ULPS, this one shouldn't either
+    return AlmostEqualUlps((float) x, (float) y, UlpsEpsilon);
+}
+
+inline bool approximately_equal_squared(double x, double y) {
+    return approximately_equal(x, y);
+}
+
+inline bool approximately_greater(double x, double y) {
+    return approximately_equal(x, y) ? false : x > y;
+}
+
+inline bool approximately_lesser(double x, double y) {
+    return approximately_equal(x, y) ? false : x < y;
+}
+
+inline double approximately_pin(double x) {
+    return approximately_zero(x) ? 0 : x;
+}
+
+inline float approximately_pin(float x) {
+    return approximately_zero(x) ? 0 : x;
+}
+
+inline bool approximately_greater_than_one(double x) {
+    return x > 1 - FLT_EPSILON;
+}
+
+inline bool approximately_less_than_zero(double x) {
+    return x < FLT_EPSILON;
+}
+
+inline bool approximately_negative(double x) {
+    return x < FLT_EPSILON;
+}
+
+inline bool precisely_negative(double x) {
+    return x < DBL_EPSILON;
+}
+
+inline bool approximately_one_or_less(double x) {
+    return x < 1 + FLT_EPSILON;
+}
+
+inline bool approximately_positive(double x) {
+    return x > -FLT_EPSILON;
+}
+
+inline bool approximately_zero_or_more(double x) {
+    return x > -FLT_EPSILON;
+}
+
+inline bool approximately_between(double a, double b, double c) {
+    assert(a <= c);
+    return a <= c ? approximately_negative(a - b) && approximately_negative(b - c)
+            : approximately_negative(b - a) && approximately_negative(c - b);
+}
+
+// returns true if (a <= b <= c) || (a >= b >= c)
+inline bool between(double a, double b, double c) {
+    assert(((a <= b && b <= c) || (a >= b && b >= c)) == ((a - b) * (c - b) <= 0));
+    return (a - b) * (c - b) <= 0;
+}
+
+#endif
+
+struct _Point {
+    double x;
+    double y;
+
+    void operator-=(const _Point& v) {
+        x -= v.x;
+        y -= v.y;
+    }
+
+    friend bool operator==(const _Point& a, const _Point& b) {
+        return a.x == b.x && a.y == b.y;
+    }
+
+    friend bool operator!=(const _Point& a, const _Point& b) {
+        return a.x!= b.x || a.y != b.y;
+    }
+
+    bool approximatelyEqual(const _Point& a) const {
+        return approximately_equal(a.y, y) && approximately_equal(a.x, x);
+    }
+
+};
+
+typedef _Point _Line[2];
+typedef _Point Quadratic[3];
+typedef _Point Cubic[4];
+
+struct _Rect {
+    double left;
+    double top;
+    double right;
+    double bottom;
+
+    void add(const _Point& pt) {
+        if (left > pt.x) {
+            left = pt.x;
+        }
+        if (top > pt.y) {
+            top = pt.y;
+        }
+        if (right < pt.x) {
+            right = pt.x;
+        }
+        if (bottom < pt.y) {
+            bottom = pt.y;
+        }
+    }
+
+    // FIXME: used by debugging only ?
+    bool contains(const _Point& pt) {
+        return approximately_between(left, pt.x, right)
+                && approximately_between(top, pt.y, bottom);
+    }
+
+    void set(const _Point& pt) {
+        left = right = pt.x;
+        top = bottom = pt.y;
+    }
+
+    void setBounds(const _Line& line) {
+        set(line[0]);
+        add(line[1]);
+    }
+
+    void setBounds(const Cubic& );
+    void setBounds(const Quadratic& );
+    void setRawBounds(const Cubic& );
+    void setRawBounds(const Quadratic& );
+};
+
+struct CubicPair {
+    const Cubic& first() const { return (const Cubic&) pts[0]; }
+    const Cubic& second() const { return (const Cubic&) pts[3]; }
+    _Point pts[7];
+};
+
+struct QuadraticPair {
+    const Quadratic& first() const { return (const Quadratic&) pts[0]; }
+    const Quadratic& second() const { return (const Quadratic&) pts[2]; }
+    _Point pts[5];
+};
+
+#endif // __DataTypes_h__
diff --git a/experimental/Intersection/EdgeDemo.cpp b/experimental/Intersection/EdgeDemo.cpp
new file mode 100644
index 0000000..973b981
--- /dev/null
+++ b/experimental/Intersection/EdgeDemo.cpp
@@ -0,0 +1,324 @@
+#include "EdgeDemo.h"
+#include "EdgeWalker_Test.h"
+#include "ShapeOps.h"
+#import "SkCanvas.h"
+#import "SkPaint.h"
+
+extern void showPath(const SkPath& path, const char* str);
+
+static bool drawPaths(SkCanvas* canvas, const SkPath& path, bool useOld)
+{
+    SkPath out;
+#define SHOW_PATH 0
+#if SHOW_PATH
+    showPath(path, "original:");
+#endif
+    if (useOld) {
+        simplify(path, true, out);
+    } else {
+        simplifyx(path, out);
+    }
+#if SHOW_PATH
+    showPath(out, "simplified:");
+#endif
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setStyle(SkPaint::kStroke_Style);
+  //  paint.setStrokeWidth(6);
+  //  paint.setColor(0x1F003f7f);
+ //   canvas->drawPath(path, paint);
+    paint.setColor(0xFF305F00);
+    paint.setStrokeWidth(1);
+    canvas->drawPath(out, paint);
+    return true;
+}
+
+// Three circles bounce inside a rectangle. The circles describe three, four
+// or five points which in turn describe a polygon. The polygon points
+// bounce inside the circles. The circles rotate and scale over time. The
+// polygons are combined into a single path, simplified, and stroked.
+static bool drawCircles(SkCanvas* canvas, int step, bool useOld)
+{
+    const int circles = 3;
+    int scales[circles];
+    int angles[circles];
+    int locs[circles * 2];
+    int pts[circles * 2 * 4];
+    int c, p;
+    for (c = 0; c < circles; ++c) {
+        scales[c] = abs(10 - (step + c * 4) % 21);
+        angles[c] = (step + c * 6) % 600;
+        locs[c * 2] = abs(130 - (step + c * 9) % 261);
+        locs[c * 2 + 1] = abs(170 - (step + c * 11) % 341);
+        for (p = 0; p < 4; ++p) {
+            pts[c * 8 + p * 2] = abs(90 - ((step + c * 121 + p * 13) % 190));
+            pts[c * 8 + p * 2 + 1] = abs(110 - ((step + c * 223 + p * 17) % 230));
+        }
+    }
+    SkPath path;
+    for (c = 0; c < circles; ++c) {
+        for (p = 0; p < 4; ++p) {
+            SkScalar x = pts[c * 8 + p * 2];
+            SkScalar y = pts[c * 8 + p * 2 + 1];
+            x *= 3 + scales[c] / 10.0f;
+            y *= 3 + scales[c] / 10.0f;
+            SkScalar angle = angles[c] * 3.1415f * 2 / 600;
+            SkScalar temp = (SkScalar) (x * cos(angle) - y * sin(angle));
+            y = (SkScalar) (x * sin(angle) + y * cos(angle));
+            x = temp;
+            x += locs[c * 2] * 200 / 130.0f;
+            y += locs[c * 2 + 1] * 200 / 170.0f;
+            x += 50;
+    //        y += 200;
+            if (p == 0) {
+                path.moveTo(x, y);
+            } else {
+                path.lineTo(x, y);
+            }
+        }
+        path.close();
+    }
+    return drawPaths(canvas, path, useOld);
+}
+
+static void createStar(SkPath& path, SkScalar innerRadius, SkScalar outerRadius,
+        SkScalar startAngle, int points, SkPoint center) {
+    SkScalar angle = startAngle;
+    for (int index = 0; index < points * 2; ++index) {
+        SkScalar radius = index & 1 ? outerRadius : innerRadius;
+        SkScalar x = (SkScalar) (radius * cos(angle));
+        SkScalar y = (SkScalar) (radius * sin(angle));
+        x += center.fX;
+        y += center.fY;
+        if (index == 0) {
+            path.moveTo(x, y);
+        } else {
+            path.lineTo(x, y);
+        }
+        angle += 3.1415f / points;
+    }
+    path.close();
+}
+
+static bool drawStars(SkCanvas* canvas, int step, bool useOld)
+{
+    SkPath path;
+    const int stars = 25;
+    int pts[stars];
+ //   static bool initialize = true;
+    int s;
+    for (s = 0; s < stars; ++s) {
+        pts[s] = 4 + (s % 7);
+    }
+    SkPoint locs[stars];
+    SkScalar angles[stars];
+    SkScalar innerRadius[stars];
+    SkScalar outerRadius[stars];
+    const int width = 640;
+    const int height = 480;
+    const int margin = 30;
+    const int minRadius = 120;
+    const int maxInner = 800;
+    const int maxOuter = 1153;
+    for (s = 0; s < stars; ++s) {
+        int starW = (int) (width - margin * 2 + (SkScalar) s * (stars - s) / stars);
+        locs[s].fX = (int) (step * (1.3f * (s + 1) / stars) + s * 121) % (starW * 2);
+        if (locs[s].fX > starW) {
+            locs[s].fX = starW * 2 - locs[s].fX;
+        }
+        locs[s].fX += margin;
+        int starH = (int) (height - margin * 2 + (SkScalar) s * s / stars);
+        locs[s].fY = (int) (step * (1.7f * (s + 1) / stars) + s * 183) % (starH * 2);
+        if (locs[s].fY > starH) {
+            locs[s].fY = starH * 2 - locs[s].fY;
+        }
+        locs[s].fY += margin;
+        angles[s] = ((step + s * 47) % (360 * 4)) * 3.1415f / 180 / 4;
+        innerRadius[s] = (step + s * 30) % (maxInner * 2);
+        if (innerRadius[s] > maxInner) {
+            innerRadius[s] = (maxInner * 2) - innerRadius[s];
+        }
+        innerRadius[s] = innerRadius[s] / 4 + minRadius;
+        outerRadius[s] = (step + s * 70) % (maxOuter * 2);
+        if (outerRadius[s] > maxOuter) {
+            outerRadius[s] = (maxOuter * 2) - outerRadius[s];
+        }
+        outerRadius[s] = outerRadius[s] / 4 + minRadius;
+        createStar(path, innerRadius[s] / 4.0f, outerRadius[s] / 4.0f,
+                angles[s], pts[s], locs[s]);
+    }
+    return drawPaths(canvas, path, useOld);
+}
+
+static void tryRoncoOnce(const SkPath& path, const SkRect& target, bool show) {
+    // capture everything in a desired rectangle
+    SkPath tiny;
+    bool closed = true;
+    SkPath::Iter iter(path, false);
+    SkPoint pts[4];
+    SkPath::Verb verb;
+    int count = 0;
+    SkPoint lastPt;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                count = 0;
+                break;
+            case SkPath::kLine_Verb:
+                count = 1;
+                break;
+            case SkPath::kQuad_Verb:
+                count = 2;
+                break;
+            case SkPath::kCubic_Verb:
+                count = 3;
+                break;
+            case SkPath::kClose_Verb:
+                if (!closed) {
+                    tiny.close();
+                    closed = true;
+                }
+                count = 0;
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+        }
+        if (!count) {
+            continue;
+        }
+        SkRect bounds;
+        bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
+        for (int i = 1; i <= count; ++i) {
+            bounds.growToInclude(pts[i].fX + 0.1f, pts[i].fY + 0.1f);
+        }
+        if (!SkRect::Intersects(target, bounds)) {
+            continue;
+        }
+        if (closed) {
+            tiny.moveTo(pts[0].fX, pts[0].fY);
+            closed = false;
+        } else if (pts[0] != lastPt) {
+            tiny.lineTo(pts[0].fX, pts[0].fY);
+        }
+        switch (verb) {
+            case SkPath::kLine_Verb:
+                tiny.lineTo(pts[1].fX, pts[1].fY);
+                lastPt = pts[1];
+                break;
+            case SkPath::kQuad_Verb:
+                tiny.quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
+                lastPt = pts[2];
+                break;
+            case SkPath::kCubic_Verb:
+                tiny.cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
+                lastPt = pts[3];
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+        }
+    }
+    if (!closed) {
+        tiny.close();
+    }
+    if (show) {
+        showPath(tiny, NULL);
+        SkDebugf("simplified:\n");
+    }
+    testSimplifyx(tiny);
+}
+
+static void tryRonco(const SkPath& path) {
+    const SkRect& overall = path.getBounds();
+    const int divs = 64;
+    SkScalar cellWidth = overall.width() / divs * 2;
+    SkScalar cellHeight = overall.height() / divs * 2;
+    SkRect target;
+    if (true) {
+        int xDiv = 28;
+        int yDiv = 17;
+        target.setXYWH(overall.fLeft + (overall.width() - cellWidth) * xDiv / divs,
+                overall.fTop + (overall.height() - cellHeight) * yDiv / divs,
+                 cellWidth, cellHeight);
+        tryRoncoOnce(path, target, true);
+    } else {
+        for (int xDiv = 0; xDiv < divs; ++xDiv) {
+            for (int yDiv = 0; yDiv < divs; ++yDiv) {
+                target.setXYWH(overall.fLeft + (overall.width() - cellWidth) * xDiv / divs,
+                        overall.fTop + (overall.height() - cellHeight) * yDiv / divs,
+                         cellWidth, cellHeight);
+                tryRoncoOnce(path, target, true);
+            }
+        }
+    }
+}
+
+static bool drawLetters(SkCanvas* canvas, int step, bool useOld)
+{
+    SkPath path;
+    const int width = 640;
+    const int height = 480;
+    const char testStr[] = "Merge";
+    const int testStrLen = sizeof(testStr) - 1;
+    SkPoint textPos[testStrLen];
+    SkScalar widths[testStrLen];
+    SkPaint paint;
+    paint.setTextSize(40);
+    paint.setAntiAlias(true);
+    paint.getTextWidths(testStr, testStrLen, widths, NULL);
+    SkScalar running = 0;
+    for (int x = 0; x < testStrLen; ++x) {
+        SkScalar width = widths[x];
+        widths[x] = running;
+        running += width;
+    }
+    SkScalar bias = (width - widths[testStrLen - 1]) / 2;
+    for (int x = 0; x < testStrLen; ++x) {
+        textPos[x].fX = bias + widths[x];
+        textPos[x].fY = height / 2;
+    }
+    paint.setTextSize(40 + step / 100.0f);
+#if 0
+    for (int mask = 0; mask < 1 << testStrLen; ++mask) {
+        char maskStr[testStrLen];
+        mask = 15;
+        for (int letter = 0; letter < testStrLen; ++letter) {
+            maskStr[letter] = mask & (1 << letter) ? testStr[letter] : ' ';
+        }
+        paint.getPosTextPath(maskStr, testStrLen, textPos, &path);
+   //     showPath(path, NULL);
+   //     SkDebugf("%d simplified:\n", mask);
+        tryRonco(path);
+        testSimplifyx(path);
+    }
+#endif
+    paint.getPosTextPath(testStr, testStrLen, textPos, &path);
+#if 0
+    tryRonco(path);
+#endif
+#if 0
+    showPath(path, NULL);
+    SkDebugf("simplified:\n");
+#endif
+    return drawPaths(canvas, path, false);
+}
+
+static bool (*drawDemos[])(SkCanvas* , int , bool ) = {
+    drawStars,
+    drawCircles,
+    drawLetters,
+};
+
+static size_t drawDemosCount = sizeof(drawDemos) / sizeof(drawDemos[0]);
+
+static bool (*firstTest)(SkCanvas* , int , bool) = drawLetters;
+
+
+bool DrawEdgeDemo(SkCanvas* canvas, int step, bool useOld) {
+    size_t index = 0;
+    if (firstTest) {
+        while (index < drawDemosCount && drawDemos[index] != firstTest) {
+            ++index;
+        }
+    }
+    return (*drawDemos[index])(canvas, step, useOld);
+}
diff --git a/experimental/Intersection/EdgeDemo.h b/experimental/Intersection/EdgeDemo.h
new file mode 100644
index 0000000..934e546
--- /dev/null
+++ b/experimental/Intersection/EdgeDemo.h
@@ -0,0 +1,3 @@
+class SkCanvas;
+
+bool DrawEdgeDemo(SkCanvas* canvas, int step, bool useOld);
diff --git a/experimental/Intersection/EdgeDemoApp-Info.plist b/experimental/Intersection/EdgeDemoApp-Info.plist
new file mode 100644
index 0000000..8e10202
--- /dev/null
+++ b/experimental/Intersection/EdgeDemoApp-Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>com.googlecode.skia.${PRODUCT_NAME:rfc1034identifier}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>${MACOSX_DEPLOYMENT_TARGET}</string>
+	<key>NSMainNibFile</key>
+	<string>EdgeDemoApp</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>
diff --git a/experimental/Intersection/EdgeDemoApp.mm b/experimental/Intersection/EdgeDemoApp.mm
new file mode 100644
index 0000000..b65a295
--- /dev/null
+++ b/experimental/Intersection/EdgeDemoApp.mm
@@ -0,0 +1,94 @@
+#include "EdgeDemo.h"
+#import "SkCanvas.h"
+#import "SkWindow.h"
+#include "SkGraphics.h"
+#include "SkCGUtils.h"
+
+#include <time.h>
+#include <sys/time.h>
+
+class SkSampleView : public SkView {
+public:
+    SkSampleView() {
+        this->setVisibleP(true);
+        this->setClipToBounds(false);
+        useOld = true;
+    };
+protected:
+    virtual void onDraw(SkCanvas* canvas) {
+        static int step = 0; // 12752; // 17908 ; // 17904; // drawLetters first error
+                             // drawStars triggers error at 23275
+                             // error is not easy to debug in its current state
+        static double seconds;
+        if (step == -1) {
+            timeval t;
+            gettimeofday(&t, NULL);
+            seconds = t.tv_sec+t.tv_usec/1000000.0;
+            step = 0;
+        }
+        canvas->drawColor(SK_ColorWHITE);
+        if (DrawEdgeDemo(canvas, step, useOld)) {
+            ++step;
+            if (step == 23270) {
+                timeval t;
+                gettimeofday(&t, NULL);
+                double last = seconds;
+                seconds = t.tv_sec+t.tv_usec/1000000.0;
+                SkDebugf("old=%d seconds=%g\n", useOld, seconds - last);
+                useOld ^= true;
+                step = 0;
+            }
+            inval(NULL);
+        }
+    }
+    
+    virtual Click* onFindClickHandler(SkScalar , SkScalar ) {
+        useOld ^= true;
+        return NULL;
+    }
+
+private:
+    bool useOld;
+    typedef SkView INHERITED; 
+};
+
+void application_init();
+void application_term();
+
+void application_init() {
+    SkGraphics::Init();
+    SkEvent::Init();
+}
+
+void application_term() {
+    SkGraphics::Term();
+    SkEvent::Term();
+}
+
+class FillLayout : public SkView::Layout {
+protected:
+    virtual void onLayoutChildren(SkView* parent) {
+        SkView* view = SkView::F2BIter(parent).next();
+        view->setSize(parent->width(), parent->height());
+    }
+};
+
+#import "SimpleApp.h"
+
+@implementation SimpleNSView
+
+- (id)initWithDefaults {
+    if ((self = [super initWithDefaults])) {
+        fWind = new SkOSWindow(self); 
+        fWind->setLayout(new FillLayout, false);
+        fWind->attachChildToFront(new SkSampleView)->unref();
+    }
+    return self;
+}
+
+- (void)drawRect:(NSRect)dirtyRect {
+    CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+    SkCGDrawBitmap(ctx, fWind->getBitmap(), 0, 0);
+}
+
+@end
diff --git a/experimental/Intersection/EdgeDemoApp.xib b/experimental/Intersection/EdgeDemoApp.xib
new file mode 100644
index 0000000..863ead1
--- /dev/null
+++ b/experimental/Intersection/EdgeDemoApp.xib
@@ -0,0 +1,3115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+	<data>
+		<int key="IBDocument.SystemTarget">1060</int>
+		<string key="IBDocument.SystemVersion">10K549</string>
+		<string key="IBDocument.InterfaceBuilderVersion">851</string>
+		<string key="IBDocument.AppKitVersion">1038.36</string>
+		<string key="IBDocument.HIToolboxVersion">461.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+			<string key="NS.object.0">851</string>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<integer value="261"/>
+		</object>
+		<object class="NSArray" key="IBDocument.PluginDependencies">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+			<integer value="1" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.RootObjects" id="110858478">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSCustomObject" id="762632889">
+				<string key="NSClassName">IBInspector</string>
+			</object>
+			<object class="NSCustomObject" id="932410077">
+				<string key="NSClassName">FirstResponder</string>
+			</object>
+			<object class="NSCustomObject" id="858592610">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSMenu" id="403715256">
+				<string key="NSTitle">AMainMenu</string>
+				<object class="NSMutableArray" key="NSMenuItems">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="NSMenuItem" id="65739106">
+						<reference key="NSMenu" ref="403715256"/>
+						<string key="NSTitle">hello</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<object class="NSCustomResource" key="NSOnImage" id="573986354">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuCheckmark</string>
+						</object>
+						<object class="NSCustomResource" key="NSMixedImage" id="622334842">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuMixedState</string>
+						</object>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="964556876">
+							<string key="NSTitle">hello</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="627636549">
+									<reference key="NSMenu" ref="964556876"/>
+									<string key="NSTitle">About hello</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="938722355">
+									<reference key="NSMenu" ref="964556876"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="1026802975">
+									<reference key="NSMenu" ref="964556876"/>
+									<string key="NSTitle">Preferences…</string>
+									<string key="NSKeyEquiv">,</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="200976220">
+									<reference key="NSMenu" ref="964556876"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="226934908">
+									<reference key="NSMenu" ref="964556876"/>
+									<string key="NSTitle">Services</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="785419060">
+										<string key="NSTitle">Services</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+										</object>
+										<string key="NSName">_NSServicesMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="1031569584">
+									<reference key="NSMenu" ref="964556876"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="650251791">
+									<reference key="NSMenu" ref="964556876"/>
+									<string key="NSTitle">Hide hello</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="890704348">
+									<reference key="NSMenu" ref="964556876"/>
+									<string key="NSTitle">Hide Others</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="373184661">
+									<reference key="NSMenu" ref="964556876"/>
+									<string key="NSTitle">Show All</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="140060996">
+									<reference key="NSMenu" ref="964556876"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="674334250">
+									<reference key="NSMenu" ref="964556876"/>
+									<string key="NSTitle">Quit hello</string>
+									<string key="NSKeyEquiv">q</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+							</object>
+							<string key="NSName">_NSAppleMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="849503563">
+						<reference key="NSMenu" ref="403715256"/>
+						<string key="NSTitle">File</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="573986354"/>
+						<reference key="NSMixedImage" ref="622334842"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="899147076">
+							<string key="NSTitle">File</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="940143719">
+									<reference key="NSMenu" ref="899147076"/>
+									<string key="NSTitle">New</string>
+									<string key="NSKeyEquiv">n</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="416202392">
+									<reference key="NSMenu" ref="899147076"/>
+									<string key="NSTitle">Open…</string>
+									<string key="NSKeyEquiv">o</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="802841280">
+									<reference key="NSMenu" ref="899147076"/>
+									<string key="NSTitle">Open Recent</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="250982885">
+										<string key="NSTitle">Open Recent</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="940066582">
+												<reference key="NSMenu" ref="250982885"/>
+												<string key="NSTitle">Clear Menu</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+										</object>
+										<string key="NSName">_NSRecentDocumentsMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="646964482">
+									<reference key="NSMenu" ref="899147076"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="113331390">
+									<reference key="NSMenu" ref="899147076"/>
+									<string key="NSTitle">Close</string>
+									<string key="NSKeyEquiv">w</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="717042078">
+									<reference key="NSMenu" ref="899147076"/>
+									<string key="NSTitle">Save</string>
+									<string key="NSKeyEquiv">s</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="259193863">
+									<reference key="NSMenu" ref="899147076"/>
+									<string key="NSTitle">Save As…</string>
+									<string key="NSKeyEquiv">S</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="523869872">
+									<reference key="NSMenu" ref="899147076"/>
+									<string key="NSTitle">Revert to Saved</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="425999990">
+									<reference key="NSMenu" ref="899147076"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="917170545">
+									<reference key="NSMenu" ref="899147076"/>
+									<string key="NSTitle">Page Setup...</string>
+									<string key="NSKeyEquiv">P</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+									<string key="NSToolTip"/>
+								</object>
+								<object class="NSMenuItem" id="600825955">
+									<reference key="NSMenu" ref="899147076"/>
+									<string key="NSTitle">Print…</string>
+									<string key="NSKeyEquiv">p</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="825040645">
+						<reference key="NSMenu" ref="403715256"/>
+						<string key="NSTitle">Edit</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="573986354"/>
+						<reference key="NSMixedImage" ref="622334842"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="411995980">
+							<string key="NSTitle">Edit</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="155877797">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Undo</string>
+									<string key="NSKeyEquiv">z</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="278282207">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Redo</string>
+									<string key="NSKeyEquiv">Z</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="287041942">
+									<reference key="NSMenu" ref="411995980"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="761075222">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Cut</string>
+									<string key="NSKeyEquiv">x</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="617541932">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Copy</string>
+									<string key="NSKeyEquiv">c</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="404580975">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Paste</string>
+									<string key="NSKeyEquiv">v</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="890043556">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Paste and Match Style</string>
+									<string key="NSKeyEquiv">V</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="572775670">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Delete</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="826813329">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Select All</string>
+									<string key="NSKeyEquiv">a</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="393301673">
+									<reference key="NSMenu" ref="411995980"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="726470551">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Find</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="759421779">
+										<string key="NSTitle">Find</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="871728498">
+												<reference key="NSMenu" ref="759421779"/>
+												<string key="NSTitle">Find…</string>
+												<string key="NSKeyEquiv">f</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="392793967">
+												<reference key="NSMenu" ref="759421779"/>
+												<string key="NSTitle">Find Next</string>
+												<string key="NSKeyEquiv">g</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="209754492">
+												<reference key="NSMenu" ref="759421779"/>
+												<string key="NSTitle">Find Previous</string>
+												<string key="NSKeyEquiv">G</string>
+												<int key="NSKeyEquivModMask">1179648</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="300840686">
+												<reference key="NSMenu" ref="759421779"/>
+												<string key="NSTitle">Use Selection for Find</string>
+												<string key="NSKeyEquiv">e</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">7</int>
+											</object>
+											<object class="NSMenuItem" id="339241627">
+												<reference key="NSMenu" ref="759421779"/>
+												<string key="NSTitle">Jump to Selection</string>
+												<string key="NSKeyEquiv">j</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="394001634">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Spelling and Grammar</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="48019006">
+										<string key="NSTitle">Spelling and Grammar</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="641875902">
+												<reference key="NSMenu" ref="48019006"/>
+												<string key="NSTitle">Show Spelling and Grammar</string>
+												<string key="NSKeyEquiv">:</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="519234687">
+												<reference key="NSMenu" ref="48019006"/>
+												<string key="NSTitle">Check Document Now</string>
+												<string key="NSKeyEquiv">;</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="566175535">
+												<reference key="NSMenu" ref="48019006"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="32560783">
+												<reference key="NSMenu" ref="48019006"/>
+												<string key="NSTitle">Check Spelling While Typing</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="74735022">
+												<reference key="NSMenu" ref="48019006"/>
+												<string key="NSTitle">Check Grammar With Spelling</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="440900682">
+												<reference key="NSMenu" ref="48019006"/>
+												<string key="NSTitle">Correct Spelling Automatically</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="723250450">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Substitutions</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="60916223">
+										<string key="NSTitle">Substitutions</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="499113305">
+												<reference key="NSMenu" ref="60916223"/>
+												<string key="NSTitle">Show Substitutions</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="604095584">
+												<reference key="NSMenu" ref="60916223"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="1037848121">
+												<reference key="NSMenu" ref="60916223"/>
+												<string key="NSTitle">Smart Copy/Paste</string>
+												<string key="NSKeyEquiv">f</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="253602211">
+												<reference key="NSMenu" ref="60916223"/>
+												<string key="NSTitle">Smart Quotes</string>
+												<string key="NSKeyEquiv">g</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="870101879">
+												<reference key="NSMenu" ref="60916223"/>
+												<string key="NSTitle">Smart Dashes</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="818701236">
+												<reference key="NSMenu" ref="60916223"/>
+												<string key="NSTitle">Smart Links</string>
+												<string key="NSKeyEquiv">G</string>
+												<int key="NSKeyEquivModMask">1179648</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="682811574">
+												<reference key="NSMenu" ref="60916223"/>
+												<string key="NSTitle">Text Replacement</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="1022343180">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Transformations</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="799869067">
+										<string key="NSTitle">Transformations</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="242386505">
+												<reference key="NSMenu" ref="799869067"/>
+												<string key="NSTitle">Make Upper Case</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="980071763">
+												<reference key="NSMenu" ref="799869067"/>
+												<string key="NSTitle">Make Lower Case</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="138959048">
+												<reference key="NSMenu" ref="799869067"/>
+												<string key="NSTitle">Capitalize</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="538735815">
+									<reference key="NSMenu" ref="411995980"/>
+									<string key="NSTitle">Speech</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="92848229">
+										<string key="NSTitle">Speech</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="242466330">
+												<reference key="NSMenu" ref="92848229"/>
+												<string key="NSTitle">Start Speaking</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="892418348">
+												<reference key="NSMenu" ref="92848229"/>
+												<string key="NSTitle">Stop Speaking</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+										</object>
+									</object>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="126299768">
+						<reference key="NSMenu" ref="403715256"/>
+						<string key="NSTitle">Format</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="573986354"/>
+						<reference key="NSMixedImage" ref="622334842"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="473359030">
+							<string key="NSTitle">Format</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="543679380">
+									<reference key="NSMenu" ref="473359030"/>
+									<string key="NSTitle">Font</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="420125509">
+										<string key="NSTitle">Font</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="1066858">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Show Fonts</string>
+												<string key="NSKeyEquiv">t</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="890909023">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Bold</string>
+												<string key="NSKeyEquiv">b</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="460689417">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Italic</string>
+												<string key="NSKeyEquiv">i</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="648810690">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Underline</string>
+												<string key="NSKeyEquiv">u</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="648523518">
+												<reference key="NSMenu" ref="420125509"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="554361477">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Bigger</string>
+												<string key="NSKeyEquiv">+</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="509852642">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Smaller</string>
+												<string key="NSKeyEquiv">-</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<int key="NSTag">4</int>
+											</object>
+											<object class="NSMenuItem" id="684419220">
+												<reference key="NSMenu" ref="420125509"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="300760265">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Kern</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="692426030">
+													<string key="NSTitle">Kern</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="827985012">
+															<reference key="NSMenu" ref="692426030"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="114397248">
+															<reference key="NSMenu" ref="692426030"/>
+															<string key="NSTitle">Use None</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="464129683">
+															<reference key="NSMenu" ref="692426030"/>
+															<string key="NSTitle">Tighten</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="984558289">
+															<reference key="NSMenu" ref="692426030"/>
+															<string key="NSTitle">Loosen</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="764895229">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Ligature</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="1027046122">
+													<string key="NSTitle">Ligature</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="170445579">
+															<reference key="NSMenu" ref="1027046122"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="1054539967">
+															<reference key="NSMenu" ref="1027046122"/>
+															<string key="NSTitle">Use None</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="193227140">
+															<reference key="NSMenu" ref="1027046122"/>
+															<string key="NSTitle">Use All</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="185308913">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Baseline</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="412235399">
+													<string key="NSTitle">Baseline</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="708934364">
+															<reference key="NSMenu" ref="412235399"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="660376852">
+															<reference key="NSMenu" ref="412235399"/>
+															<string key="NSTitle">Superscript</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="887484660">
+															<reference key="NSMenu" ref="412235399"/>
+															<string key="NSTitle">Subscript</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="239990007">
+															<reference key="NSMenu" ref="412235399"/>
+															<string key="NSTitle">Raise</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="794052095">
+															<reference key="NSMenu" ref="412235399"/>
+															<string key="NSTitle">Lower</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="770408504">
+												<reference key="NSMenu" ref="420125509"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="968537172">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Show Colors</string>
+												<string key="NSKeyEquiv">C</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="576239163">
+												<reference key="NSMenu" ref="420125509"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="951733979">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Copy Style</string>
+												<string key="NSKeyEquiv">c</string>
+												<int key="NSKeyEquivModMask">1572864</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="726071842">
+												<reference key="NSMenu" ref="420125509"/>
+												<string key="NSTitle">Paste Style</string>
+												<string key="NSKeyEquiv">v</string>
+												<int key="NSKeyEquivModMask">1572864</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+										</object>
+										<string key="NSName">_NSFontMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="432500292">
+									<reference key="NSMenu" ref="473359030"/>
+									<string key="NSTitle">Text</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="239792765">
+										<string key="NSTitle">Text</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="209955373">
+												<reference key="NSMenu" ref="239792765"/>
+												<string key="NSTitle">Align Left</string>
+												<string key="NSKeyEquiv">{</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="940415998">
+												<reference key="NSMenu" ref="239792765"/>
+												<string key="NSTitle">Center</string>
+												<string key="NSKeyEquiv">|</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="667530981">
+												<reference key="NSMenu" ref="239792765"/>
+												<string key="NSTitle">Justify</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="518803967">
+												<reference key="NSMenu" ref="239792765"/>
+												<string key="NSTitle">Align Right</string>
+												<string key="NSKeyEquiv">}</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="755110083">
+												<reference key="NSMenu" ref="239792765"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="970864302">
+												<reference key="NSMenu" ref="239792765"/>
+												<string key="NSTitle">Writing Direction</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="394707630">
+													<string key="NSTitle">Writing Direction</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="295941739">
+															<reference key="NSMenu" ref="394707630"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<string key="NSTitle">Paragraph</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="1016781112">
+															<reference key="NSMenu" ref="394707630"/>
+															<string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="909594320">
+															<reference key="NSMenu" ref="394707630"/>
+															<string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="294875881">
+															<reference key="NSMenu" ref="394707630"/>
+															<string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="125873089">
+															<reference key="NSMenu" ref="394707630"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<bool key="NSIsSeparator">YES</bool>
+															<string key="NSTitle"/>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="448907901">
+															<reference key="NSMenu" ref="394707630"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<string key="NSTitle">Selection</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="719807015">
+															<reference key="NSMenu" ref="394707630"/>
+															<string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="551093393">
+															<reference key="NSMenu" ref="394707630"/>
+															<string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+														<object class="NSMenuItem" id="968290247">
+															<reference key="NSMenu" ref="394707630"/>
+															<string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="573986354"/>
+															<reference key="NSMixedImage" ref="622334842"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="624010617">
+												<reference key="NSMenu" ref="239792765"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="178871919">
+												<reference key="NSMenu" ref="239792765"/>
+												<string key="NSTitle">Show Ruler</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="19133321">
+												<reference key="NSMenu" ref="239792765"/>
+												<string key="NSTitle">Copy Ruler</string>
+												<string key="NSKeyEquiv">c</string>
+												<int key="NSKeyEquivModMask">1310720</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+											<object class="NSMenuItem" id="971682598">
+												<reference key="NSMenu" ref="239792765"/>
+												<string key="NSTitle">Paste Ruler</string>
+												<string key="NSKeyEquiv">v</string>
+												<int key="NSKeyEquivModMask">1310720</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="573986354"/>
+												<reference key="NSMixedImage" ref="622334842"/>
+											</object>
+										</object>
+									</object>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="474036278">
+						<reference key="NSMenu" ref="403715256"/>
+						<string key="NSTitle">View</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="573986354"/>
+						<reference key="NSMixedImage" ref="622334842"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="1011573215">
+							<string key="NSTitle">View</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="84198938">
+									<reference key="NSMenu" ref="1011573215"/>
+									<string key="NSTitle">Show Toolbar</string>
+									<string key="NSKeyEquiv">t</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="468947355">
+									<reference key="NSMenu" ref="1011573215"/>
+									<string key="NSTitle">Customize Toolbar…</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="179474997">
+						<reference key="NSMenu" ref="403715256"/>
+						<string key="NSTitle">Window</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="573986354"/>
+						<reference key="NSMixedImage" ref="622334842"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="722436082">
+							<string key="NSTitle">Window</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="715853698">
+									<reference key="NSMenu" ref="722436082"/>
+									<string key="NSTitle">Minimize</string>
+									<string key="NSKeyEquiv">m</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="639724461">
+									<reference key="NSMenu" ref="722436082"/>
+									<string key="NSTitle">Zoom</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="541256952">
+									<reference key="NSMenu" ref="722436082"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+								<object class="NSMenuItem" id="801808839">
+									<reference key="NSMenu" ref="722436082"/>
+									<string key="NSTitle">Bring All to Front</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+							</object>
+							<string key="NSName">_NSWindowsMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="964815204">
+						<reference key="NSMenu" ref="403715256"/>
+						<string key="NSTitle">Help</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="573986354"/>
+						<reference key="NSMixedImage" ref="622334842"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="445400279">
+							<string key="NSTitle">Help</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="820408786">
+									<reference key="NSMenu" ref="445400279"/>
+									<string key="NSTitle">hello Help</string>
+									<string key="NSKeyEquiv">?</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="573986354"/>
+									<reference key="NSMixedImage" ref="622334842"/>
+								</object>
+							</object>
+							<string key="NSName">_NSHelpMenu</string>
+						</object>
+					</object>
+				</object>
+				<string key="NSName">_NSMainMenu</string>
+			</object>
+			<object class="NSWindowTemplate" id="407166972">
+				<int key="NSWindowStyleMask">15</int>
+				<int key="NSWindowBacking">2</int>
+				<string key="NSWindowRect">{{335, 289}, {640, 461}}</string>
+				<int key="NSWTFlags">1954021376</int>
+				<string key="NSWindowTitle">hello</string>
+				<string key="NSWindowClass">NSWindow</string>
+				<nil key="NSViewClass"/>
+				<string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
+				<object class="NSView" key="NSWindowView" id="813288216">
+					<reference key="NSNextResponder"/>
+					<int key="NSvFlags">256</int>
+					<object class="NSMutableArray" key="NSSubviews">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSCustomView" id="410337987">
+							<reference key="NSNextResponder" ref="813288216"/>
+							<int key="NSvFlags">286</int>
+							<string key="NSFrameSize">{640, 480}</string>
+							<reference key="NSSuperview" ref="813288216"/>
+							<string key="NSClassName">SimpleNSView</string>
+						</object>
+					</object>
+					<string key="NSFrameSize">{640, 461}</string>
+					<reference key="NSSuperview"/>
+				</object>
+				<string key="NSScreenRect">{{0, 0}, {1600, 2538}}</string>
+				<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
+			</object>
+		</object>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<object class="NSMutableArray" key="connectionRecords">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+			</object>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<object class="NSArray" key="orderedObjects">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<object class="NSArray" key="object" id="485438096">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="children" ref="110858478"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="762632889"/>
+						<reference key="parent" ref="485438096"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="932410077"/>
+						<reference key="parent" ref="485438096"/>
+						<string key="objectName">First Responder</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-3</int>
+						<reference key="object" ref="858592610"/>
+						<reference key="parent" ref="485438096"/>
+						<string key="objectName">Application</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">114</int>
+						<reference key="object" ref="403715256"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="964815204"/>
+							<reference ref="126299768"/>
+							<reference ref="474036278"/>
+							<reference ref="849503563"/>
+							<reference ref="825040645"/>
+							<reference ref="65739106"/>
+							<reference ref="179474997"/>
+						</object>
+						<reference key="parent" ref="485438096"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">115</int>
+						<reference key="object" ref="964815204"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="445400279"/>
+						</object>
+						<reference key="parent" ref="403715256"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">116</int>
+						<reference key="object" ref="126299768"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="473359030"/>
+						</object>
+						<reference key="parent" ref="403715256"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">117</int>
+						<reference key="object" ref="474036278"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1011573215"/>
+						</object>
+						<reference key="parent" ref="403715256"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">118</int>
+						<reference key="object" ref="849503563"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="899147076"/>
+						</object>
+						<reference key="parent" ref="403715256"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">119</int>
+						<reference key="object" ref="825040645"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="411995980"/>
+						</object>
+						<reference key="parent" ref="403715256"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">120</int>
+						<reference key="object" ref="65739106"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="964556876"/>
+						</object>
+						<reference key="parent" ref="403715256"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">121</int>
+						<reference key="object" ref="179474997"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="722436082"/>
+						</object>
+						<reference key="parent" ref="403715256"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">122</int>
+						<reference key="object" ref="722436082"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="715853698"/>
+							<reference ref="639724461"/>
+							<reference ref="801808839"/>
+							<reference ref="541256952"/>
+						</object>
+						<reference key="parent" ref="179474997"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">123</int>
+						<reference key="object" ref="715853698"/>
+						<reference key="parent" ref="722436082"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">124</int>
+						<reference key="object" ref="639724461"/>
+						<reference key="parent" ref="722436082"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">125</int>
+						<reference key="object" ref="801808839"/>
+						<reference key="parent" ref="722436082"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">126</int>
+						<reference key="object" ref="541256952"/>
+						<reference key="parent" ref="722436082"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">127</int>
+						<reference key="object" ref="964556876"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="890704348"/>
+							<reference ref="140060996"/>
+							<reference ref="226934908"/>
+							<reference ref="938722355"/>
+							<reference ref="200976220"/>
+							<reference ref="1026802975"/>
+							<reference ref="1031569584"/>
+							<reference ref="674334250"/>
+							<reference ref="373184661"/>
+							<reference ref="650251791"/>
+							<reference ref="627636549"/>
+						</object>
+						<reference key="parent" ref="65739106"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">128</int>
+						<reference key="object" ref="890704348"/>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">129</int>
+						<reference key="object" ref="140060996"/>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">130</int>
+						<reference key="object" ref="226934908"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="785419060"/>
+						</object>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">131</int>
+						<reference key="object" ref="938722355"/>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">132</int>
+						<reference key="object" ref="200976220"/>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">133</int>
+						<reference key="object" ref="1026802975"/>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">134</int>
+						<reference key="object" ref="1031569584"/>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">135</int>
+						<reference key="object" ref="674334250"/>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">136</int>
+						<reference key="object" ref="373184661"/>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">137</int>
+						<reference key="object" ref="650251791"/>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">138</int>
+						<reference key="object" ref="627636549"/>
+						<reference key="parent" ref="964556876"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">139</int>
+						<reference key="object" ref="785419060"/>
+						<reference key="parent" ref="226934908"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">140</int>
+						<reference key="object" ref="411995980"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="890043556"/>
+							<reference ref="1022343180"/>
+							<reference ref="723250450"/>
+							<reference ref="538735815"/>
+							<reference ref="394001634"/>
+							<reference ref="726470551"/>
+							<reference ref="278282207"/>
+							<reference ref="287041942"/>
+							<reference ref="617541932"/>
+							<reference ref="404580975"/>
+							<reference ref="761075222"/>
+							<reference ref="393301673"/>
+							<reference ref="155877797"/>
+							<reference ref="826813329"/>
+							<reference ref="572775670"/>
+						</object>
+						<reference key="parent" ref="825040645"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">141</int>
+						<reference key="object" ref="890043556"/>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">142</int>
+						<reference key="object" ref="1022343180"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="799869067"/>
+						</object>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">143</int>
+						<reference key="object" ref="723250450"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="60916223"/>
+						</object>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">144</int>
+						<reference key="object" ref="538735815"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="92848229"/>
+						</object>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">145</int>
+						<reference key="object" ref="394001634"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="48019006"/>
+						</object>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">146</int>
+						<reference key="object" ref="726470551"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="759421779"/>
+						</object>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">147</int>
+						<reference key="object" ref="278282207"/>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">148</int>
+						<reference key="object" ref="287041942"/>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">149</int>
+						<reference key="object" ref="617541932"/>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">150</int>
+						<reference key="object" ref="404580975"/>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">151</int>
+						<reference key="object" ref="761075222"/>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">152</int>
+						<reference key="object" ref="393301673"/>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">153</int>
+						<reference key="object" ref="155877797"/>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">154</int>
+						<reference key="object" ref="826813329"/>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">155</int>
+						<reference key="object" ref="572775670"/>
+						<reference key="parent" ref="411995980"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">156</int>
+						<reference key="object" ref="759421779"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="871728498"/>
+							<reference ref="392793967"/>
+							<reference ref="300840686"/>
+							<reference ref="339241627"/>
+							<reference ref="209754492"/>
+						</object>
+						<reference key="parent" ref="726470551"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">157</int>
+						<reference key="object" ref="871728498"/>
+						<reference key="parent" ref="759421779"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">158</int>
+						<reference key="object" ref="392793967"/>
+						<reference key="parent" ref="759421779"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">159</int>
+						<reference key="object" ref="300840686"/>
+						<reference key="parent" ref="759421779"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">160</int>
+						<reference key="object" ref="339241627"/>
+						<reference key="parent" ref="759421779"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">161</int>
+						<reference key="object" ref="209754492"/>
+						<reference key="parent" ref="759421779"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">162</int>
+						<reference key="object" ref="48019006"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="440900682"/>
+							<reference ref="566175535"/>
+							<reference ref="74735022"/>
+							<reference ref="641875902"/>
+							<reference ref="519234687"/>
+							<reference ref="32560783"/>
+						</object>
+						<reference key="parent" ref="394001634"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">163</int>
+						<reference key="object" ref="440900682"/>
+						<reference key="parent" ref="48019006"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">164</int>
+						<reference key="object" ref="566175535"/>
+						<reference key="parent" ref="48019006"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">165</int>
+						<reference key="object" ref="74735022"/>
+						<reference key="parent" ref="48019006"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">166</int>
+						<reference key="object" ref="641875902"/>
+						<reference key="parent" ref="48019006"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">167</int>
+						<reference key="object" ref="519234687"/>
+						<reference key="parent" ref="48019006"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">168</int>
+						<reference key="object" ref="32560783"/>
+						<reference key="parent" ref="48019006"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">169</int>
+						<reference key="object" ref="92848229"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="242466330"/>
+							<reference ref="892418348"/>
+						</object>
+						<reference key="parent" ref="538735815"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">170</int>
+						<reference key="object" ref="242466330"/>
+						<reference key="parent" ref="92848229"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">171</int>
+						<reference key="object" ref="892418348"/>
+						<reference key="parent" ref="92848229"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">172</int>
+						<reference key="object" ref="60916223"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="682811574"/>
+							<reference ref="870101879"/>
+							<reference ref="604095584"/>
+							<reference ref="499113305"/>
+							<reference ref="818701236"/>
+							<reference ref="253602211"/>
+							<reference ref="1037848121"/>
+						</object>
+						<reference key="parent" ref="723250450"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">173</int>
+						<reference key="object" ref="682811574"/>
+						<reference key="parent" ref="60916223"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">174</int>
+						<reference key="object" ref="870101879"/>
+						<reference key="parent" ref="60916223"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">175</int>
+						<reference key="object" ref="604095584"/>
+						<reference key="parent" ref="60916223"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">176</int>
+						<reference key="object" ref="499113305"/>
+						<reference key="parent" ref="60916223"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">177</int>
+						<reference key="object" ref="818701236"/>
+						<reference key="parent" ref="60916223"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">178</int>
+						<reference key="object" ref="253602211"/>
+						<reference key="parent" ref="60916223"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">179</int>
+						<reference key="object" ref="1037848121"/>
+						<reference key="parent" ref="60916223"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">180</int>
+						<reference key="object" ref="799869067"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="138959048"/>
+							<reference ref="980071763"/>
+							<reference ref="242386505"/>
+						</object>
+						<reference key="parent" ref="1022343180"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">181</int>
+						<reference key="object" ref="138959048"/>
+						<reference key="parent" ref="799869067"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">182</int>
+						<reference key="object" ref="980071763"/>
+						<reference key="parent" ref="799869067"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">183</int>
+						<reference key="object" ref="242386505"/>
+						<reference key="parent" ref="799869067"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">184</int>
+						<reference key="object" ref="899147076"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="425999990"/>
+							<reference ref="523869872"/>
+							<reference ref="646964482"/>
+							<reference ref="113331390"/>
+							<reference ref="917170545"/>
+							<reference ref="802841280"/>
+							<reference ref="940143719"/>
+							<reference ref="416202392"/>
+							<reference ref="600825955"/>
+							<reference ref="259193863"/>
+							<reference ref="717042078"/>
+						</object>
+						<reference key="parent" ref="849503563"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">185</int>
+						<reference key="object" ref="425999990"/>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">186</int>
+						<reference key="object" ref="523869872"/>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">187</int>
+						<reference key="object" ref="646964482"/>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">188</int>
+						<reference key="object" ref="113331390"/>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">189</int>
+						<reference key="object" ref="917170545"/>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">190</int>
+						<reference key="object" ref="802841280"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="250982885"/>
+						</object>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">191</int>
+						<reference key="object" ref="940143719"/>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">192</int>
+						<reference key="object" ref="416202392"/>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">193</int>
+						<reference key="object" ref="600825955"/>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">194</int>
+						<reference key="object" ref="259193863"/>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">195</int>
+						<reference key="object" ref="717042078"/>
+						<reference key="parent" ref="899147076"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">196</int>
+						<reference key="object" ref="250982885"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="940066582"/>
+						</object>
+						<reference key="parent" ref="802841280"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">197</int>
+						<reference key="object" ref="940066582"/>
+						<reference key="parent" ref="250982885"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">198</int>
+						<reference key="object" ref="1011573215"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="468947355"/>
+							<reference ref="84198938"/>
+						</object>
+						<reference key="parent" ref="474036278"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">199</int>
+						<reference key="object" ref="468947355"/>
+						<reference key="parent" ref="1011573215"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">200</int>
+						<reference key="object" ref="84198938"/>
+						<reference key="parent" ref="1011573215"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">201</int>
+						<reference key="object" ref="473359030"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="432500292"/>
+							<reference ref="543679380"/>
+						</object>
+						<reference key="parent" ref="126299768"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">202</int>
+						<reference key="object" ref="432500292"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="239792765"/>
+						</object>
+						<reference key="parent" ref="473359030"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">203</int>
+						<reference key="object" ref="543679380"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="420125509"/>
+						</object>
+						<reference key="parent" ref="473359030"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">204</int>
+						<reference key="object" ref="420125509"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="726071842"/>
+							<reference ref="951733979"/>
+							<reference ref="576239163"/>
+							<reference ref="968537172"/>
+							<reference ref="770408504"/>
+							<reference ref="185308913"/>
+							<reference ref="764895229"/>
+							<reference ref="300760265"/>
+							<reference ref="684419220"/>
+							<reference ref="509852642"/>
+							<reference ref="554361477"/>
+							<reference ref="648523518"/>
+							<reference ref="648810690"/>
+							<reference ref="460689417"/>
+							<reference ref="890909023"/>
+							<reference ref="1066858"/>
+						</object>
+						<reference key="parent" ref="543679380"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">205</int>
+						<reference key="object" ref="726071842"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">206</int>
+						<reference key="object" ref="951733979"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">207</int>
+						<reference key="object" ref="576239163"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">208</int>
+						<reference key="object" ref="968537172"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">209</int>
+						<reference key="object" ref="770408504"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">210</int>
+						<reference key="object" ref="185308913"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="412235399"/>
+						</object>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">211</int>
+						<reference key="object" ref="764895229"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1027046122"/>
+						</object>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">212</int>
+						<reference key="object" ref="300760265"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="692426030"/>
+						</object>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">213</int>
+						<reference key="object" ref="684419220"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">214</int>
+						<reference key="object" ref="509852642"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">215</int>
+						<reference key="object" ref="554361477"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">216</int>
+						<reference key="object" ref="648523518"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">217</int>
+						<reference key="object" ref="648810690"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">218</int>
+						<reference key="object" ref="460689417"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">219</int>
+						<reference key="object" ref="890909023"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">220</int>
+						<reference key="object" ref="1066858"/>
+						<reference key="parent" ref="420125509"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">221</int>
+						<reference key="object" ref="692426030"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="984558289"/>
+							<reference ref="464129683"/>
+							<reference ref="114397248"/>
+							<reference ref="827985012"/>
+						</object>
+						<reference key="parent" ref="300760265"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">222</int>
+						<reference key="object" ref="984558289"/>
+						<reference key="parent" ref="692426030"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">223</int>
+						<reference key="object" ref="464129683"/>
+						<reference key="parent" ref="692426030"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">224</int>
+						<reference key="object" ref="114397248"/>
+						<reference key="parent" ref="692426030"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">225</int>
+						<reference key="object" ref="827985012"/>
+						<reference key="parent" ref="692426030"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">226</int>
+						<reference key="object" ref="1027046122"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="193227140"/>
+							<reference ref="1054539967"/>
+							<reference ref="170445579"/>
+						</object>
+						<reference key="parent" ref="764895229"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">227</int>
+						<reference key="object" ref="193227140"/>
+						<reference key="parent" ref="1027046122"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">228</int>
+						<reference key="object" ref="1054539967"/>
+						<reference key="parent" ref="1027046122"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">229</int>
+						<reference key="object" ref="170445579"/>
+						<reference key="parent" ref="1027046122"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">230</int>
+						<reference key="object" ref="412235399"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="794052095"/>
+							<reference ref="239990007"/>
+							<reference ref="887484660"/>
+							<reference ref="660376852"/>
+							<reference ref="708934364"/>
+						</object>
+						<reference key="parent" ref="185308913"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">231</int>
+						<reference key="object" ref="794052095"/>
+						<reference key="parent" ref="412235399"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">232</int>
+						<reference key="object" ref="239990007"/>
+						<reference key="parent" ref="412235399"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">233</int>
+						<reference key="object" ref="887484660"/>
+						<reference key="parent" ref="412235399"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">234</int>
+						<reference key="object" ref="660376852"/>
+						<reference key="parent" ref="412235399"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">235</int>
+						<reference key="object" ref="708934364"/>
+						<reference key="parent" ref="412235399"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">236</int>
+						<reference key="object" ref="239792765"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="971682598"/>
+							<reference ref="19133321"/>
+							<reference ref="178871919"/>
+							<reference ref="624010617"/>
+							<reference ref="970864302"/>
+							<reference ref="755110083"/>
+							<reference ref="518803967"/>
+							<reference ref="667530981"/>
+							<reference ref="940415998"/>
+							<reference ref="209955373"/>
+						</object>
+						<reference key="parent" ref="432500292"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">237</int>
+						<reference key="object" ref="971682598"/>
+						<reference key="parent" ref="239792765"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">238</int>
+						<reference key="object" ref="19133321"/>
+						<reference key="parent" ref="239792765"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">239</int>
+						<reference key="object" ref="178871919"/>
+						<reference key="parent" ref="239792765"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">240</int>
+						<reference key="object" ref="624010617"/>
+						<reference key="parent" ref="239792765"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">241</int>
+						<reference key="object" ref="970864302"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="394707630"/>
+						</object>
+						<reference key="parent" ref="239792765"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">242</int>
+						<reference key="object" ref="755110083"/>
+						<reference key="parent" ref="239792765"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">243</int>
+						<reference key="object" ref="518803967"/>
+						<reference key="parent" ref="239792765"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">244</int>
+						<reference key="object" ref="667530981"/>
+						<reference key="parent" ref="239792765"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">245</int>
+						<reference key="object" ref="940415998"/>
+						<reference key="parent" ref="239792765"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">246</int>
+						<reference key="object" ref="209955373"/>
+						<reference key="parent" ref="239792765"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">247</int>
+						<reference key="object" ref="394707630"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="968290247"/>
+							<reference ref="551093393"/>
+							<reference ref="719807015"/>
+							<reference ref="448907901"/>
+							<reference ref="125873089"/>
+							<reference ref="294875881"/>
+							<reference ref="909594320"/>
+							<reference ref="1016781112"/>
+							<reference ref="295941739"/>
+						</object>
+						<reference key="parent" ref="970864302"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">248</int>
+						<reference key="object" ref="968290247"/>
+						<reference key="parent" ref="394707630"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">249</int>
+						<reference key="object" ref="551093393"/>
+						<reference key="parent" ref="394707630"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">250</int>
+						<reference key="object" ref="719807015"/>
+						<reference key="parent" ref="394707630"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">251</int>
+						<reference key="object" ref="448907901"/>
+						<reference key="parent" ref="394707630"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">252</int>
+						<reference key="object" ref="125873089"/>
+						<reference key="parent" ref="394707630"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">253</int>
+						<reference key="object" ref="294875881"/>
+						<reference key="parent" ref="394707630"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">254</int>
+						<reference key="object" ref="909594320"/>
+						<reference key="parent" ref="394707630"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">255</int>
+						<reference key="object" ref="1016781112"/>
+						<reference key="parent" ref="394707630"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">256</int>
+						<reference key="object" ref="295941739"/>
+						<reference key="parent" ref="394707630"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">257</int>
+						<reference key="object" ref="445400279"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="820408786"/>
+						</object>
+						<reference key="parent" ref="964815204"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">258</int>
+						<reference key="object" ref="820408786"/>
+						<reference key="parent" ref="445400279"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">259</int>
+						<reference key="object" ref="407166972"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="813288216"/>
+						</object>
+						<reference key="parent" ref="485438096"/>
+						<string key="objectName">Window (SimpleApp)</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">260</int>
+						<reference key="object" ref="813288216"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="410337987"/>
+						</object>
+						<reference key="parent" ref="407166972"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">261</int>
+						<reference key="object" ref="410337987"/>
+						<reference key="parent" ref="813288216"/>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="flattenedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="NSArray" key="dict.sortedKeys">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>-3.IBPluginDependency</string>
+					<string>-3.ImportedFromIB2</string>
+					<string>114.IBEditorWindowLastContentRect</string>
+					<string>114.IBPluginDependency</string>
+					<string>114.ImportedFromIB2</string>
+					<string>114.WindowOrigin</string>
+					<string>114.editorWindowContentRectSynchronizationRect</string>
+					<string>115.IBPluginDependency</string>
+					<string>116.IBPluginDependency</string>
+					<string>117.IBPluginDependency</string>
+					<string>118.IBPluginDependency</string>
+					<string>118.ImportedFromIB2</string>
+					<string>119.IBPluginDependency</string>
+					<string>119.ImportedFromIB2</string>
+					<string>120.IBPluginDependency</string>
+					<string>120.ImportedFromIB2</string>
+					<string>121.IBPluginDependency</string>
+					<string>121.ImportedFromIB2</string>
+					<string>122.IBEditorWindowLastContentRect</string>
+					<string>122.IBPluginDependency</string>
+					<string>122.ImportedFromIB2</string>
+					<string>122.editorWindowContentRectSynchronizationRect</string>
+					<string>123.IBPluginDependency</string>
+					<string>123.ImportedFromIB2</string>
+					<string>124.IBPluginDependency</string>
+					<string>124.ImportedFromIB2</string>
+					<string>125.IBPluginDependency</string>
+					<string>125.ImportedFromIB2</string>
+					<string>126.IBPluginDependency</string>
+					<string>126.ImportedFromIB2</string>
+					<string>127.IBEditorWindowLastContentRect</string>
+					<string>127.IBPluginDependency</string>
+					<string>127.ImportedFromIB2</string>
+					<string>127.editorWindowContentRectSynchronizationRect</string>
+					<string>128.IBPluginDependency</string>
+					<string>128.ImportedFromIB2</string>
+					<string>129.IBPluginDependency</string>
+					<string>129.ImportedFromIB2</string>
+					<string>130.IBPluginDependency</string>
+					<string>130.ImportedFromIB2</string>
+					<string>131.IBPluginDependency</string>
+					<string>131.ImportedFromIB2</string>
+					<string>132.IBPluginDependency</string>
+					<string>132.ImportedFromIB2</string>
+					<string>133.IBPluginDependency</string>
+					<string>133.ImportedFromIB2</string>
+					<string>134.IBPluginDependency</string>
+					<string>134.ImportedFromIB2</string>
+					<string>135.IBPluginDependency</string>
+					<string>135.ImportedFromIB2</string>
+					<string>136.IBPluginDependency</string>
+					<string>136.ImportedFromIB2</string>
+					<string>137.IBPluginDependency</string>
+					<string>137.ImportedFromIB2</string>
+					<string>138.IBPluginDependency</string>
+					<string>138.ImportedFromIB2</string>
+					<string>139.IBPluginDependency</string>
+					<string>139.ImportedFromIB2</string>
+					<string>139.editorWindowContentRectSynchronizationRect</string>
+					<string>140.IBEditorWindowLastContentRect</string>
+					<string>140.IBPluginDependency</string>
+					<string>140.ImportedFromIB2</string>
+					<string>140.editorWindowContentRectSynchronizationRect</string>
+					<string>141.IBPluginDependency</string>
+					<string>142.IBPluginDependency</string>
+					<string>143.IBPluginDependency</string>
+					<string>143.ImportedFromIB2</string>
+					<string>144.IBPluginDependency</string>
+					<string>144.ImportedFromIB2</string>
+					<string>145.IBPluginDependency</string>
+					<string>145.ImportedFromIB2</string>
+					<string>146.IBPluginDependency</string>
+					<string>146.ImportedFromIB2</string>
+					<string>147.IBPluginDependency</string>
+					<string>147.ImportedFromIB2</string>
+					<string>148.IBPluginDependency</string>
+					<string>148.ImportedFromIB2</string>
+					<string>149.IBPluginDependency</string>
+					<string>149.ImportedFromIB2</string>
+					<string>150.IBPluginDependency</string>
+					<string>150.ImportedFromIB2</string>
+					<string>151.IBPluginDependency</string>
+					<string>151.ImportedFromIB2</string>
+					<string>152.IBPluginDependency</string>
+					<string>152.ImportedFromIB2</string>
+					<string>153.IBPluginDependency</string>
+					<string>153.ImportedFromIB2</string>
+					<string>154.IBPluginDependency</string>
+					<string>154.ImportedFromIB2</string>
+					<string>155.IBPluginDependency</string>
+					<string>155.ImportedFromIB2</string>
+					<string>156.IBEditorWindowLastContentRect</string>
+					<string>156.IBPluginDependency</string>
+					<string>156.ImportedFromIB2</string>
+					<string>156.editorWindowContentRectSynchronizationRect</string>
+					<string>157.IBPluginDependency</string>
+					<string>157.ImportedFromIB2</string>
+					<string>158.IBPluginDependency</string>
+					<string>158.ImportedFromIB2</string>
+					<string>159.IBPluginDependency</string>
+					<string>159.ImportedFromIB2</string>
+					<string>160.IBPluginDependency</string>
+					<string>160.ImportedFromIB2</string>
+					<string>161.IBPluginDependency</string>
+					<string>161.ImportedFromIB2</string>
+					<string>162.IBEditorWindowLastContentRect</string>
+					<string>162.IBPluginDependency</string>
+					<string>162.ImportedFromIB2</string>
+					<string>162.editorWindowContentRectSynchronizationRect</string>
+					<string>163.IBPluginDependency</string>
+					<string>164.IBPluginDependency</string>
+					<string>165.IBPluginDependency</string>
+					<string>165.ImportedFromIB2</string>
+					<string>166.IBPluginDependency</string>
+					<string>166.ImportedFromIB2</string>
+					<string>167.IBPluginDependency</string>
+					<string>167.ImportedFromIB2</string>
+					<string>168.IBPluginDependency</string>
+					<string>168.ImportedFromIB2</string>
+					<string>169.IBPluginDependency</string>
+					<string>169.ImportedFromIB2</string>
+					<string>169.editorWindowContentRectSynchronizationRect</string>
+					<string>170.IBPluginDependency</string>
+					<string>170.ImportedFromIB2</string>
+					<string>171.IBPluginDependency</string>
+					<string>171.ImportedFromIB2</string>
+					<string>172.IBEditorWindowLastContentRect</string>
+					<string>172.IBPluginDependency</string>
+					<string>172.ImportedFromIB2</string>
+					<string>172.editorWindowContentRectSynchronizationRect</string>
+					<string>173.IBPluginDependency</string>
+					<string>174.IBPluginDependency</string>
+					<string>175.IBPluginDependency</string>
+					<string>176.IBPluginDependency</string>
+					<string>177.IBPluginDependency</string>
+					<string>177.ImportedFromIB2</string>
+					<string>178.IBPluginDependency</string>
+					<string>178.ImportedFromIB2</string>
+					<string>179.IBPluginDependency</string>
+					<string>179.ImportedFromIB2</string>
+					<string>180.IBEditorWindowLastContentRect</string>
+					<string>180.IBPluginDependency</string>
+					<string>181.IBPluginDependency</string>
+					<string>182.IBPluginDependency</string>
+					<string>183.IBPluginDependency</string>
+					<string>184.IBEditorWindowLastContentRect</string>
+					<string>184.IBPluginDependency</string>
+					<string>184.ImportedFromIB2</string>
+					<string>184.editorWindowContentRectSynchronizationRect</string>
+					<string>185.IBPluginDependency</string>
+					<string>185.ImportedFromIB2</string>
+					<string>186.IBPluginDependency</string>
+					<string>186.ImportedFromIB2</string>
+					<string>187.IBPluginDependency</string>
+					<string>187.ImportedFromIB2</string>
+					<string>188.IBPluginDependency</string>
+					<string>188.ImportedFromIB2</string>
+					<string>189.IBPluginDependency</string>
+					<string>189.ImportedFromIB2</string>
+					<string>190.IBPluginDependency</string>
+					<string>190.ImportedFromIB2</string>
+					<string>191.IBPluginDependency</string>
+					<string>191.ImportedFromIB2</string>
+					<string>192.IBPluginDependency</string>
+					<string>192.ImportedFromIB2</string>
+					<string>193.IBPluginDependency</string>
+					<string>193.ImportedFromIB2</string>
+					<string>194.IBPluginDependency</string>
+					<string>194.ImportedFromIB2</string>
+					<string>195.IBPluginDependency</string>
+					<string>195.ImportedFromIB2</string>
+					<string>196.IBPluginDependency</string>
+					<string>196.ImportedFromIB2</string>
+					<string>196.editorWindowContentRectSynchronizationRect</string>
+					<string>197.IBPluginDependency</string>
+					<string>197.ImportedFromIB2</string>
+					<string>198.IBEditorWindowLastContentRect</string>
+					<string>198.IBPluginDependency</string>
+					<string>198.editorWindowContentRectSynchronizationRect</string>
+					<string>199.IBPluginDependency</string>
+					<string>200.IBPluginDependency</string>
+					<string>201.IBEditorWindowLastContentRect</string>
+					<string>201.IBPluginDependency</string>
+					<string>202.IBPluginDependency</string>
+					<string>203.IBPluginDependency</string>
+					<string>204.IBEditorWindowLastContentRect</string>
+					<string>204.IBPluginDependency</string>
+					<string>205.IBPluginDependency</string>
+					<string>206.IBPluginDependency</string>
+					<string>207.IBPluginDependency</string>
+					<string>208.IBPluginDependency</string>
+					<string>209.IBPluginDependency</string>
+					<string>210.IBPluginDependency</string>
+					<string>211.IBPluginDependency</string>
+					<string>212.IBPluginDependency</string>
+					<string>213.IBPluginDependency</string>
+					<string>214.IBPluginDependency</string>
+					<string>215.IBPluginDependency</string>
+					<string>216.IBPluginDependency</string>
+					<string>217.IBPluginDependency</string>
+					<string>218.IBPluginDependency</string>
+					<string>219.IBPluginDependency</string>
+					<string>220.IBPluginDependency</string>
+					<string>221.IBPluginDependency</string>
+					<string>222.IBPluginDependency</string>
+					<string>223.IBPluginDependency</string>
+					<string>224.IBPluginDependency</string>
+					<string>225.IBPluginDependency</string>
+					<string>226.IBPluginDependency</string>
+					<string>227.IBPluginDependency</string>
+					<string>228.IBPluginDependency</string>
+					<string>229.IBPluginDependency</string>
+					<string>230.IBPluginDependency</string>
+					<string>231.IBPluginDependency</string>
+					<string>232.IBPluginDependency</string>
+					<string>233.IBPluginDependency</string>
+					<string>234.IBPluginDependency</string>
+					<string>235.IBPluginDependency</string>
+					<string>236.IBEditorWindowLastContentRect</string>
+					<string>236.IBPluginDependency</string>
+					<string>237.IBPluginDependency</string>
+					<string>238.IBPluginDependency</string>
+					<string>239.IBPluginDependency</string>
+					<string>240.IBPluginDependency</string>
+					<string>241.IBPluginDependency</string>
+					<string>242.IBPluginDependency</string>
+					<string>243.IBPluginDependency</string>
+					<string>244.IBPluginDependency</string>
+					<string>245.IBPluginDependency</string>
+					<string>246.IBPluginDependency</string>
+					<string>247.IBEditorWindowLastContentRect</string>
+					<string>247.IBPluginDependency</string>
+					<string>248.IBPluginDependency</string>
+					<string>249.IBPluginDependency</string>
+					<string>250.IBPluginDependency</string>
+					<string>251.IBPluginDependency</string>
+					<string>252.IBPluginDependency</string>
+					<string>253.IBPluginDependency</string>
+					<string>254.IBPluginDependency</string>
+					<string>255.IBPluginDependency</string>
+					<string>256.IBPluginDependency</string>
+					<string>257.IBEditorWindowLastContentRect</string>
+					<string>257.IBPluginDependency</string>
+					<string>258.IBPluginDependency</string>
+					<string>259.IBEditorWindowLastContentRect</string>
+					<string>259.IBPluginDependency</string>
+					<string>259.IBWindowTemplateEditedContentRect</string>
+					<string>259.NSWindowTemplate.visibleAtLaunch</string>
+					<string>259.editorWindowContentRectSynchronizationRect</string>
+					<string>259.windowTemplate.maxSize</string>
+					<string>260.IBPluginDependency</string>
+					<string>261.IBPluginDependency</string>
+					<string>261.IBViewBoundsToFrameTransform</string>
+				</object>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{380, 836}, {400, 20}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{74, 862}</string>
+					<string>{{6, 978}, {478, 20}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{654, 239}, {194, 73}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{525, 802}, {197, 73}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{286, 129}, {275, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{23, 794}, {245, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{436, 809}, {64, 6}}</string>
+					<string>{{547, 180}, {254, 283}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{187, 434}, {243, 243}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{753, 217}, {238, 103}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {241, 103}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{753, 187}, {275, 113}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {275, 83}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {167, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{746, 287}, {220, 133}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {215, 63}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{753, 197}, {170, 63}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{452, 109}, {196, 203}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{145, 474}, {199, 203}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{522, 812}, {146, 23}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{604, 269}, {231, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{475, 832}, {234, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{591, 420}, {83, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{523, 2}, {178, 283}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{674, 260}, {204, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{878, 180}, {164, 173}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{725, 289}, {246, 23}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{451, 1180}, {640, 461}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{451, 1180}, {640, 461}}</string>
+					<integer value="1"/>
+					<string>{{33, 99}, {480, 360}}</string>
+					<string>{3.40282e+38, 3.40282e+38}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<object class="NSAffineTransform"/>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="unlocalizedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="485438096"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="activeLocalization"/>
+			<object class="NSMutableDictionary" key="localizations">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="485438096"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="sourceID"/>
+			<int key="maxID">263</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSClassDescription.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObjectScripting.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSPortCoder.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptClassDescription.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptObjectSpecifiers.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptWhoseTests.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLDownload.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">PrintCore.framework/Headers/PDEPluginInterface.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">QuartzCore.framework/Headers/CAAnimation.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">QuartzCore.framework/Headers/CALayer.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">QuartzCore.framework/Headers/CIImageProvider.h</string>
+					</object>
+				</object>
+			</object>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+			<integer value="1060" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+			<integer value="3000" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<string key="IBDocument.LastKnownRelativeProjectPath">../../out/gyp/shapeops_demo.xcodeproj</string>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSArray" key="dict.sortedKeys">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<string>NSMenuCheckmark</string>
+				<string>NSMenuMixedState</string>
+			</object>
+			<object class="NSMutableArray" key="dict.values">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<string>{9, 8}</string>
+				<string>{7, 2}</string>
+			</object>
+		</object>
+	</data>
+</archive>
diff --git a/experimental/Intersection/EdgeMain.cpp b/experimental/Intersection/EdgeMain.cpp
new file mode 100644
index 0000000..442c629
--- /dev/null
+++ b/experimental/Intersection/EdgeMain.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 "Intersection_Tests.h"
+
+int main(int /*argc*/, char** /*argv*/) {
+    Intersection_Tests();
+    return 0;
+}
diff --git a/experimental/Intersection/EdgeWalker.cpp b/experimental/Intersection/EdgeWalker.cpp
new file mode 100644
index 0000000..47bd037
--- /dev/null
+++ b/experimental/Intersection/EdgeWalker.cpp
@@ -0,0 +1,2705 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Simplify.h"
+
+#undef SkASSERT
+#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
+
+// FIXME: remove once debugging is complete
+#if 01 // set to 1 for no debugging whatsoever
+
+//const bool gRunTestsInOneThread = false;
+
+#define DEBUG_ACTIVE_LESS_THAN 0
+#define DEBUG_ADD 0
+#define DEBUG_ADD_BOTTOM_TS 0
+#define DEBUG_ADD_INTERSECTING_TS 0
+#define DEBUG_ADJUST_COINCIDENT 0
+#define DEBUG_ASSEMBLE 0
+#define DEBUG_BOTTOM 0
+#define DEBUG_BRIDGE 0
+#define DEBUG_DUMP 0
+#define DEBUG_SORT_HORIZONTAL 0
+#define DEBUG_OUT 0
+#define DEBUG_OUT_LESS_THAN 0
+#define DEBUG_SPLIT 0
+#define DEBUG_STITCH_EDGE 0
+#define DEBUG_TRIM_LINE 0
+
+#else
+
+//const bool gRunTestsInOneThread = true;
+
+#define DEBUG_ACTIVE_LESS_THAN 0
+#define DEBUG_ADD 01
+#define DEBUG_ADD_BOTTOM_TS 0
+#define DEBUG_ADD_INTERSECTING_TS 0
+#define DEBUG_ADJUST_COINCIDENT 1
+#define DEBUG_ASSEMBLE 1
+#define DEBUG_BOTTOM 0
+#define DEBUG_BRIDGE 1
+#define DEBUG_DUMP 1
+#define DEBUG_SORT_HORIZONTAL 01
+#define DEBUG_OUT 01
+#define DEBUG_OUT_LESS_THAN 0
+#define DEBUG_SPLIT 1
+#define DEBUG_STITCH_EDGE 1
+#define DEBUG_TRIM_LINE 1
+
+#endif
+
+#if DEBUG_ASSEMBLE || DEBUG_BRIDGE
+static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
+#endif
+#if DEBUG_STITCH_EDGE
+static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
+#endif
+
+static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
+        Intersections& intersections) {
+    const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
+    const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
+    return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
+}
+
+static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
+        Intersections& intersections) {
+    const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
+    const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
+    intersect(aQuad, bLine, intersections);
+    return intersections.fUsed;
+}
+
+static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
+        Intersections& intersections) {
+    const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
+            {a[3].fX, a[3].fY}};
+    const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
+    return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
+}
+
+static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
+        Intersections& intersections) {
+    const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
+    const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
+    intersect(aQuad, bQuad, intersections);
+    return intersections.fUsed;
+}
+
+static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
+        Intersections& intersections) {
+    const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
+            {a[3].fX, a[3].fY}};
+    const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
+            {b[3].fX, b[3].fY}};
+    intersect(aCubic, bCubic, intersections);
+    return intersections.fUsed;
+}
+
+static int LineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
+        SkScalar y, double aRange[2]) {
+    const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
+    return horizontalLineIntersect(aLine, left, right, y, aRange);
+}
+
+static int QuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
+        SkScalar y, double aRange[3]) {
+    const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
+    return horizontalIntersect(aQuad, left, right, y, aRange);
+}
+
+static int CubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
+        SkScalar y, double aRange[4]) {
+    const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
+            {a[3].fX, a[3].fY}};
+    return horizontalIntersect(aCubic, left, right, y, aRange);
+}
+
+static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
+    const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
+    double x, y;
+    xy_at_t(line, t, x, y);
+    out->fX = SkDoubleToScalar(x);
+    out->fY = SkDoubleToScalar(y);
+}
+
+static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
+    const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
+    double x, y;
+    xy_at_t(quad, t, x, y);
+    out->fX = SkDoubleToScalar(x);
+    out->fY = SkDoubleToScalar(y);
+}
+
+static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
+    const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
+            {a[3].fX, a[3].fY}};
+    double x, y;
+    xy_at_t(cubic, t, x, y);
+    out->fX = SkDoubleToScalar(x);
+    out->fY = SkDoubleToScalar(y);
+}
+
+static SkScalar LineYAtT(const SkPoint a[2], double t) {
+    const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
+    double y;
+    xy_at_t(aLine, t, *(double*) 0, y);
+    return SkDoubleToScalar(y);
+}
+
+static SkScalar QuadYAtT(const SkPoint a[3], double t) {
+    const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
+    double y;
+    xy_at_t(quad, t, *(double*) 0, y);
+    return SkDoubleToScalar(y);
+}
+
+static SkScalar CubicYAtT(const SkPoint a[4], double t) {
+    const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
+            {a[3].fX, a[3].fY}};
+    double y;
+    xy_at_t(cubic, t, *(double*) 0, y);
+    return SkDoubleToScalar(y);
+}
+
+static void LineSubDivide(const SkPoint a[2], double startT, double endT,
+        SkPoint sub[2]) {
+    const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
+    _Line dst;
+    sub_divide(aLine, startT, endT, dst);
+    sub[0].fX = SkDoubleToScalar(dst[0].x);
+    sub[0].fY = SkDoubleToScalar(dst[0].y);
+    sub[1].fX = SkDoubleToScalar(dst[1].x);
+    sub[1].fY = SkDoubleToScalar(dst[1].y);
+}
+
+static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
+        SkPoint sub[3]) {
+    const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
+            {a[2].fX, a[2].fY}};
+    Quadratic dst;
+    sub_divide(aQuad, startT, endT, dst);
+    sub[0].fX = SkDoubleToScalar(dst[0].x);
+    sub[0].fY = SkDoubleToScalar(dst[0].y);
+    sub[1].fX = SkDoubleToScalar(dst[1].x);
+    sub[1].fY = SkDoubleToScalar(dst[1].y);
+    sub[2].fX = SkDoubleToScalar(dst[2].x);
+    sub[2].fY = SkDoubleToScalar(dst[2].y);
+}
+
+static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
+        SkPoint sub[4]) {
+    const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
+            {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
+    Cubic dst;
+    sub_divide(aCubic, startT, endT, dst);
+    sub[0].fX = SkDoubleToScalar(dst[0].x);
+    sub[0].fY = SkDoubleToScalar(dst[0].y);
+    sub[1].fX = SkDoubleToScalar(dst[1].x);
+    sub[1].fY = SkDoubleToScalar(dst[1].y);
+    sub[2].fX = SkDoubleToScalar(dst[2].x);
+    sub[2].fY = SkDoubleToScalar(dst[2].y);
+    sub[3].fX = SkDoubleToScalar(dst[3].x);
+    sub[3].fY = SkDoubleToScalar(dst[3].y);
+}
+
+static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
+        SkRect& bounds) {
+    SkPoint dst[3];
+    QuadSubDivide(a, startT, endT, dst);
+    bounds.fLeft = bounds.fRight = dst[0].fX;
+    bounds.fTop = bounds.fBottom = dst[0].fY;
+    for (int index = 1; index < 3; ++index) {
+        bounds.growToInclude(dst[index].fX, dst[index].fY);
+    }
+}
+
+static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
+        SkRect& bounds) {
+    SkPoint dst[4];
+    CubicSubDivide(a, startT, endT, dst);
+    bounds.fLeft = bounds.fRight = dst[0].fX;
+    bounds.fTop = bounds.fBottom = dst[0].fY;
+    for (int index = 1; index < 4; ++index) {
+        bounds.growToInclude(dst[index].fX, dst[index].fY);
+    }
+}
+
+static SkPath::Verb QuadReduceOrder(SkPoint a[4]) {
+    const Quadratic aQuad =  {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
+            {a[2].fX, a[2].fY}};
+    Quadratic dst;
+    int order = reduceOrder(aQuad, dst);
+    for (int index = 0; index < order; ++index) {
+        a[index].fX = SkDoubleToScalar(dst[index].x);
+        a[index].fY = SkDoubleToScalar(dst[index].y);
+    }
+    if (order == 1) { // FIXME: allow returning points, caller should discard
+        a[1] = a[0];
+        return (SkPath::Verb) order;
+    }
+    return (SkPath::Verb) (order - 1);
+}
+
+static SkPath::Verb CubicReduceOrder(SkPoint a[4]) {
+    const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
+            {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
+    Cubic dst;
+    int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
+    for (int index = 0; index < order; ++index) {
+        a[index].fX = SkDoubleToScalar(dst[index].x);
+        a[index].fY = SkDoubleToScalar(dst[index].y);
+    }
+    if (order == 1) { // FIXME: allow returning points, caller should discard
+        a[1] = a[0];
+        return (SkPath::Verb) order;
+    }
+    return (SkPath::Verb) (order - 1);
+}
+
+static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
+        const SkPoint& below) {
+    const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
+    const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
+    return implicit_matches_ulps(aLine, bLine, 32);
+}
+
+/*
+list of edges
+bounds for edge
+sort
+active T
+
+if a contour's bounds is outside of the active area, no need to create edges
+*/
+
+/* given one or more paths,
+ find the bounds of each contour, select the active contours
+ for each active contour, compute a set of edges
+ each edge corresponds to one or more lines and curves
+ leave edges unbroken as long as possible
+ when breaking edges, compute the t at the break but leave the control points alone
+
+ */
+
+void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray) {
+    SkPath::Iter iter(path, false);
+    SkPoint pts[4];
+    SkPath::Verb verb;
+    SkRect bounds;
+    bounds.setEmpty();
+    int count = 0;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                if (!bounds.isEmpty()) {
+                    *boundsArray.append() = bounds;
+                }
+                bounds.set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
+                count = 0;
+                break;
+            case SkPath::kLine_Verb:
+                count = 1;
+                break;
+            case SkPath::kQuad_Verb:
+                count = 2;
+                break;
+            case SkPath::kCubic_Verb:
+                count = 3;
+                break;
+            case SkPath::kClose_Verb:
+                count = 0;
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return;
+        }
+        for (int i = 1; i <= count; ++i) {
+            bounds.growToInclude(pts[i].fX, pts[i].fY);
+        }
+    }
+}
+
+static bool extendLine(const SkPoint line[2], const SkPoint& add) {
+    // FIXME: allow this to extend lines that have slopes that are nearly equal
+    SkScalar dx1 = line[1].fX - line[0].fX;
+    SkScalar dy1 = line[1].fY - line[0].fY;
+    SkScalar dx2 = add.fX - line[0].fX;
+    SkScalar dy2 = add.fY - line[0].fY;
+    return dx1 * dy2 == dx2 * dy1;
+}
+
+// OPTIMIZATION: this should point to a list of input data rather than duplicating
+// the line data here. This would reduce the need to assemble the results.
+struct OutEdge {
+    bool operator<(const OutEdge& rh) const {
+        const SkPoint& first = fPts[0];
+        const SkPoint& rhFirst = rh.fPts[0];
+        return first.fY == rhFirst.fY
+                ? first.fX < rhFirst.fX
+                : first.fY < rhFirst.fY;
+    }
+
+    SkPoint fPts[4];
+    int fID; // id of edge generating data
+    uint8_t fVerb; // FIXME: not read from everywhere
+    bool fCloseCall; // edge is trimmable if not originally coincident
+};
+
+class OutEdgeBuilder {
+public:
+    OutEdgeBuilder(bool fill)
+        : fFill(fill) {
+        }
+
+    void addCurve(const SkPoint line[4], SkPath::Verb verb, int id,
+            bool closeCall) {
+        OutEdge& newEdge = fEdges.push_back();
+        memcpy(newEdge.fPts, line, (verb + 1) * sizeof(SkPoint));
+        newEdge.fVerb = verb;
+        newEdge.fID = id;
+        newEdge.fCloseCall = closeCall;
+    }
+
+    bool trimLine(SkScalar y, int id) {
+        size_t count = fEdges.count();
+        while (count-- != 0) {
+            OutEdge& edge = fEdges[count];
+            if (edge.fID != id) {
+                continue;
+            }
+            if (edge.fCloseCall) {
+                return false;
+            }
+            SkASSERT(edge.fPts[0].fY <= y);
+            if (edge.fPts[1].fY <= y) {
+                continue;
+            }
+            edge.fPts[1].fX = edge.fPts[0].fX + (y - edge.fPts[0].fY)
+                    * (edge.fPts[1].fX - edge.fPts[0].fX)
+                    / (edge.fPts[1].fY - edge.fPts[0].fY);
+            edge.fPts[1].fY = y;
+#if DEBUG_TRIM_LINE
+            SkDebugf("%s edge=%d %1.9g,%1.9g\n", __FUNCTION__, id,
+                    edge.fPts[1].fX, y);
+#endif
+            return true;
+        }
+        return false;
+    }
+
+    void assemble(SkPath& simple) {
+        size_t listCount = fEdges.count();
+        if (listCount == 0) {
+            return;
+        }
+        do {
+            size_t listIndex = 0;
+            int advance = 1;
+            while (listIndex < listCount && fTops[listIndex] == 0) {
+                ++listIndex;
+            }
+            if (listIndex >= listCount) {
+                break;
+            }
+            int closeEdgeIndex = -listIndex - 1;
+            // the curve is deferred and not added right away because the
+            // following edge may extend the first curve.
+            SkPoint firstPt, lastCurve[4];
+            uint8_t lastVerb;
+#if DEBUG_ASSEMBLE
+            int firstIndex, lastIndex;
+            const int tab = 8;
+#endif
+            bool doMove = true;
+            int edgeIndex;
+            do {
+                SkPoint* ptArray = fEdges[listIndex].fPts;
+                uint8_t verb = fEdges[listIndex].fVerb;
+                SkPoint* curve[4];
+                if (advance < 0) {
+                    curve[0] = &ptArray[verb];
+                    if (verb == SkPath::kCubic_Verb) {
+                        curve[1] = &ptArray[2];
+                        curve[2] = &ptArray[1];
+                    }
+                    curve[verb] = &ptArray[0];
+                } else {
+                    curve[0] = &ptArray[0];
+                    if (verb == SkPath::kCubic_Verb) {
+                        curve[1] = &ptArray[1];
+                        curve[2] = &ptArray[2];
+                    }
+                    curve[verb] = &ptArray[verb];
+                }
+                if (verb == SkPath::kQuad_Verb) {
+                    curve[1] = &ptArray[1];
+                }
+                if (doMove) {
+                    firstPt = *curve[0];
+                    simple.moveTo(curve[0]->fX, curve[0]->fY);
+#if DEBUG_ASSEMBLE
+                    SkDebugf("%s %d moveTo (%g,%g)\n", __FUNCTION__,
+                            listIndex + 1, curve[0]->fX, curve[0]->fY);
+                    firstIndex = listIndex;
+#endif
+                    for (int index = 0; index <= verb; ++index) {
+                        lastCurve[index] = *curve[index];
+                    }
+                    doMove = false;
+                } else {
+                    bool gap = lastCurve[lastVerb] != *curve[0];
+                    if (gap || lastVerb != SkPath::kLine_Verb) { // output the accumulated curve before the gap
+                        // FIXME: see comment in bridge -- this probably
+                        // conceals errors
+                        SkASSERT(fFill && UlpsDiff(lastCurve[lastVerb].fY,
+                                curve[0]->fY) <= 10);
+                        switch (lastVerb) {
+                            case SkPath::kLine_Verb:
+                                simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
+                                break;
+                            case SkPath::kQuad_Verb:
+                                simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
+                                        lastCurve[2].fX, lastCurve[2].fY);
+                                break;
+                            case SkPath::kCubic_Verb:
+                                simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
+                                        lastCurve[2].fX, lastCurve[2].fY,
+                                        lastCurve[3].fX, lastCurve[3].fY);
+                                break;
+                        }
+#if DEBUG_ASSEMBLE
+                        SkDebugf("%*s %d %sTo (%g,%g)\n", tab, "", lastIndex + 1,
+                                kLVerbStr[lastVerb], lastCurve[lastVerb].fX,
+                                lastCurve[lastVerb].fY);
+#endif
+                    }
+                    int firstCopy = 1;
+                    if (gap || (lastVerb == SkPath::kLine_Verb
+                             && (verb != SkPath::kLine_Verb
+                             || !extendLine(lastCurve, *curve[verb])))) {
+                        // FIXME: see comment in bridge -- this probably
+                        // conceals errors
+                        SkASSERT(lastCurve[lastVerb] == *curve[0] ||
+                                (fFill && UlpsDiff(lastCurve[lastVerb].fY,
+                                curve[0]->fY) <= 10));
+                        simple.lineTo(curve[0]->fX, curve[0]->fY);
+#if DEBUG_ASSEMBLE
+                        SkDebugf("%*s %d gap lineTo (%g,%g)\n", tab, "",
+                                lastIndex + 1, curve[0]->fX, curve[0]->fY);
+#endif
+                        firstCopy = 0;
+                    } else if (lastVerb != SkPath::kLine_Verb) {
+                        firstCopy = 0;
+                    }
+                    for (int index = firstCopy; index <= verb; ++index) {
+                        lastCurve[index] = *curve[index];
+                    }
+                }
+                lastVerb = verb;
+#if DEBUG_ASSEMBLE
+                lastIndex = listIndex;
+#endif
+                if (advance < 0) {
+                    edgeIndex = fTops[listIndex];
+                    fTops[listIndex] = 0;
+                } else {
+                    edgeIndex = fBottoms[listIndex];
+                    fBottoms[listIndex] = 0;
+                }
+                if (edgeIndex) {
+                    listIndex = abs(edgeIndex) - 1;
+                    if (edgeIndex < 0) {
+                        fTops[listIndex] = 0;
+                    } else {
+                        fBottoms[listIndex] = 0;
+                    }
+                }
+                if (edgeIndex == closeEdgeIndex || edgeIndex == 0) {
+                    switch (lastVerb) {
+                        case SkPath::kLine_Verb:
+                            simple.lineTo(lastCurve[1].fX, lastCurve[1].fY);
+                            break;
+                        case SkPath::kQuad_Verb:
+                            simple.quadTo(lastCurve[1].fX, lastCurve[1].fY,
+                                    lastCurve[2].fX, lastCurve[2].fY);
+                            break;
+                        case SkPath::kCubic_Verb:
+                            simple.cubicTo(lastCurve[1].fX, lastCurve[1].fY,
+                                    lastCurve[2].fX, lastCurve[2].fY,
+                                    lastCurve[3].fX, lastCurve[3].fY);
+                            break;
+                    }
+#if DEBUG_ASSEMBLE
+                    SkDebugf("%*s %d %sTo last (%g, %g)\n", tab, "",
+                            lastIndex + 1, kLVerbStr[lastVerb],
+                            lastCurve[lastVerb].fX, lastCurve[lastVerb].fY);
+#endif
+                    if (lastCurve[lastVerb] != firstPt) {
+                        simple.lineTo(firstPt.fX, firstPt.fY);
+#if DEBUG_ASSEMBLE
+                        SkDebugf("%*s %d final line (%g, %g)\n", tab, "",
+                                firstIndex + 1, firstPt.fX, firstPt.fY);
+#endif
+                    }
+                    simple.close();
+#if DEBUG_ASSEMBLE
+                    SkDebugf("%*s   close\n", tab, "");
+#endif
+                    break;
+                }
+                // if this and next edge go different directions
+#if DEBUG_ASSEMBLE
+                SkDebugf("%*s   advance=%d edgeIndex=%d flip=%s\n", tab, "",
+                        advance, edgeIndex, advance > 0 ^ edgeIndex < 0 ?
+                        "true" : "false");
+#endif
+                if (advance > 0 ^ edgeIndex < 0) {
+                    advance = -advance;
+                }
+            } while (edgeIndex);
+        } while (true);
+    }
+
+    // sort points by y, then x
+    // if x/y is identical, sort bottoms before tops
+    // if identical and both tops/bottoms, sort by angle
+    static bool lessThan(SkTArray<OutEdge>& edges, const int one,
+            const int two) {
+        const OutEdge& oneEdge = edges[abs(one) - 1];
+        int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
+        const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
+        const OutEdge& twoEdge = edges[abs(two) - 1];
+        int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
+        const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
+        if (startPt1.fY != startPt2.fY) {
+    #if DEBUG_OUT_LESS_THAN
+            SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
+                    one, two, startPt1.fY, startPt2.fY,
+                    startPt1.fY < startPt2.fY ? "true" : "false");
+    #endif
+            return startPt1.fY < startPt2.fY;
+        }
+        if (startPt1.fX != startPt2.fX) {
+    #if DEBUG_OUT_LESS_THAN
+            SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
+                    one, two, startPt1.fX, startPt2.fX,
+                    startPt1.fX < startPt2.fX ? "true" : "false");
+    #endif
+            return startPt1.fX < startPt2.fX;
+        }
+        const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
+        const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
+        SkScalar dy1 = startPt1.fY - endPt1.fY;
+        SkScalar dy2 = startPt2.fY - endPt2.fY;
+        SkScalar dy1y2 = dy1 * dy2;
+        if (dy1y2 < 0) { // different signs
+    #if DEBUG_OUT_LESS_THAN
+                SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
+                        dy1 > 0 ? "true" : "false");
+    #endif
+            return dy1 > 0; // one < two if one goes up and two goes down
+        }
+        if (dy1y2 == 0) {
+    #if DEBUG_OUT_LESS_THAN
+            SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
+                    one, two, endPt1.fX < endPt2.fX ? "true" : "false");
+    #endif
+            return endPt1.fX < endPt2.fX;
+        }
+        SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
+        SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
+    #if DEBUG_OUT_LESS_THAN
+        SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
+                one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
+    #endif
+        return dy2 > 0 ^ dx1y2 < dx2y1;
+    }
+
+    // Sort the indices of paired points and then create more indices so
+    // assemble() can find the next edge and connect the top or bottom
+    void bridge() {
+        size_t index;
+        size_t count = fEdges.count();
+        if (!count) {
+            return;
+        }
+        SkASSERT(!fFill || count > 1);
+        fTops.setCount(count);
+        sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
+        fBottoms.setCount(count);
+        sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
+        SkTDArray<int> order;
+        for (index = 1; index <= count; ++index) {
+            *order.append() = -index;
+        }
+        for (index = 1; index <= count; ++index) {
+            *order.append() = index;
+        }
+        QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
+        int* lastPtr = order.end() - 1;
+        int* leftPtr = order.begin();
+        while (leftPtr < lastPtr) {
+            int leftIndex = *leftPtr;
+            int leftOutIndex = abs(leftIndex) - 1;
+            const OutEdge& left = fEdges[leftOutIndex];
+            int* rightPtr = leftPtr + 1;
+            int rightIndex = *rightPtr;
+            int rightOutIndex = abs(rightIndex) - 1;
+            const OutEdge& right = fEdges[rightOutIndex];
+            bool pairUp = fFill;
+            if (!pairUp) {
+                const SkPoint& leftMatch =
+                        left.fPts[leftIndex < 0 ? 0 : left.fVerb];
+                const SkPoint& rightMatch =
+                        right.fPts[rightIndex < 0 ? 0 : right.fVerb];
+                pairUp = leftMatch == rightMatch;
+            } else {
+        #if DEBUG_OUT
+        // FIXME : not happy that error in low bit is allowed
+        // this probably conceals error elsewhere
+                if (UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
+                        right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) > 1) {
+                    *fMismatches.append() = leftIndex;
+                    if (rightPtr == lastPtr) {
+                        *fMismatches.append() = rightIndex;
+                    }
+                    pairUp = false;
+                }
+        #else
+                SkASSERT(UlpsDiff(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY,
+                        right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY) <= 10);
+        #endif
+            }
+            if (pairUp) {
+                if (leftIndex < 0) {
+                    fTops[leftOutIndex] = rightIndex;
+                } else {
+                    fBottoms[leftOutIndex] = rightIndex;
+                }
+                if (rightIndex < 0) {
+                    fTops[rightOutIndex] = leftIndex;
+                } else {
+                    fBottoms[rightOutIndex] = leftIndex;
+                }
+                ++rightPtr;
+            }
+            leftPtr = rightPtr;
+        }
+#if DEBUG_OUT
+        int* mismatch = fMismatches.begin();
+        while (mismatch != fMismatches.end()) {
+            int leftIndex = *mismatch++;
+            int leftOutIndex = abs(leftIndex) - 1;
+            const OutEdge& left = fEdges[leftOutIndex];
+            const SkPoint& leftPt = left.fPts[leftIndex < 0 ? 0 : left.fVerb];
+            SkDebugf("%s left=%d %s (%1.9g,%1.9g)\n",
+                    __FUNCTION__, left.fID, leftIndex < 0 ? "top" : "bot",
+                    leftPt.fX, leftPt.fY);
+        }
+        SkASSERT(fMismatches.count() == 0);
+#endif
+#if DEBUG_BRIDGE
+    for (index = 0; index < count; ++index) {
+        const OutEdge& edge = fEdges[index];
+        uint8_t verb = edge.fVerb;
+        SkDebugf("%s %d edge=%d %s (%1.9g,%1.9g) (%1.9g,%1.9g)\n",
+                index == 0 ? __FUNCTION__ : "      ",
+                index + 1, edge.fID, kLVerbStr[verb], edge.fPts[0].fX,
+                edge.fPts[0].fY, edge.fPts[verb].fX, edge.fPts[verb].fY);
+    }
+    for (index = 0; index < count; ++index) {
+        SkDebugf("       top    of % 2d connects to %s of % 2d\n", index + 1,
+                fTops[index] < 0 ? "top   " : "bottom", abs(fTops[index]));
+        SkDebugf("       bottom of % 2d connects to %s of % 2d\n", index + 1,
+                fBottoms[index] < 0 ? "top   " : "bottom", abs(fBottoms[index]));
+    }
+#endif
+    }
+
+protected:
+    SkTArray<OutEdge> fEdges;
+    SkTDArray<int> fTops;
+    SkTDArray<int> fBottoms;
+    bool fFill;
+#if DEBUG_OUT
+    SkTDArray<int> fMismatches;
+#endif
+};
+
+// Bounds, unlike Rect, does not consider a vertical line to be empty.
+struct Bounds : public SkRect {
+    static bool Intersects(const Bounds& a, const Bounds& b) {
+        return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
+                a.fTop <= b.fBottom && b.fTop <= a.fBottom;
+    }
+
+    bool isEmpty() {
+        return fLeft > fRight || fTop > fBottom
+                || fLeft == fRight && fTop == fBottom
+                || isnan(fLeft) || isnan(fRight)
+                || isnan(fTop) || isnan(fBottom);
+    }
+};
+
+class Intercepts {
+public:
+    Intercepts()
+        : fTopIntercepts(0)
+        , fBottomIntercepts(0)
+        , fExplicit(false) {
+    }
+
+    Intercepts& operator=(const Intercepts& src) {
+        fTs = src.fTs;
+        fTopIntercepts = src.fTopIntercepts;
+        fBottomIntercepts = src.fBottomIntercepts;
+        return *this;
+    }
+
+    // OPTIMIZATION: remove this function if it's never called
+    double t(int tIndex) const {
+        if (tIndex == 0) {
+            return 0;
+        }
+        if (tIndex > fTs.count()) {
+            return 1;
+        }
+        return fTs[tIndex - 1];
+    }
+
+#if DEBUG_DUMP
+    void dump(const SkPoint* pts, SkPath::Verb verb) {
+        const char className[] = "Intercepts";
+        const int tab = 8;
+        for (int i = 0; i < fTs.count(); ++i) {
+            SkPoint out;
+            switch (verb) {
+                case SkPath::kLine_Verb:
+                    LineXYAtT(pts, fTs[i], &out);
+                    break;
+                case SkPath::kQuad_Verb:
+                    QuadXYAtT(pts, fTs[i], &out);
+                    break;
+                case SkPath::kCubic_Verb:
+                    CubicXYAtT(pts, fTs[i], &out);
+                    break;
+                default:
+                    SkASSERT(0);
+            }
+            SkDebugf("%*s.fTs[%d]=%1.9g (%1.9g,%1.9g)\n", tab + sizeof(className),
+                    className, i, fTs[i], out.fX, out.fY);
+        }
+        SkDebugf("%*s.fTopIntercepts=%u\n", tab + sizeof(className),
+                className, fTopIntercepts);
+        SkDebugf("%*s.fBottomIntercepts=%u\n", tab + sizeof(className),
+                className, fBottomIntercepts);
+        SkDebugf("%*s.fExplicit=%d\n", tab + sizeof(className),
+                className, fExplicit);
+    }
+#endif
+
+    SkTDArray<double> fTs;
+    unsigned char fTopIntercepts; // 0=init state 1=1 edge >1=multiple edges
+    unsigned char fBottomIntercepts;
+    bool fExplicit; // if set, suppress 0 and 1
+
+};
+
+struct HorizontalEdge {
+    bool operator<(const HorizontalEdge& rh) const {
+        return fY == rh.fY ? fLeft == rh.fLeft ? fRight < rh.fRight
+                : fLeft < rh.fLeft : fY < rh.fY;
+    }
+
+#if DEBUG_DUMP
+    void dump() {
+        const char className[] = "HorizontalEdge";
+        const int tab = 4;
+        SkDebugf("%*s.fLeft=%1.9g\n", tab + sizeof(className), className, fLeft);
+        SkDebugf("%*s.fRight=%1.9g\n", tab + sizeof(className), className, fRight);
+        SkDebugf("%*s.fY=%1.9g\n", tab + sizeof(className), className, fY);
+    }
+#endif
+
+    SkScalar fLeft;
+    SkScalar fRight;
+    SkScalar fY;
+};
+
+struct InEdge {
+    bool operator<(const InEdge& rh) const {
+        return fBounds.fTop == rh.fBounds.fTop
+                ? fBounds.fLeft < rh.fBounds.fLeft
+                : fBounds.fTop < rh.fBounds.fTop;
+    }
+
+    // Avoid collapsing t values that are close to the same since
+    // we walk ts to describe consecutive intersections. Since a pair of ts can
+    // be nearly equal, any problems caused by this should be taken care
+    // of later.
+    int add(double* ts, size_t count, ptrdiff_t verbIndex) {
+        // FIXME: in the pathological case where there is a ton of intercepts, binary search?
+        bool foundIntercept = false;
+        int insertedAt = -1;
+        Intercepts& intercepts = fIntercepts[verbIndex];
+        for (size_t index = 0; index < count; ++index) {
+            double t = ts[index];
+            if (t <= 0) {
+                intercepts.fTopIntercepts <<= 1;
+                fContainsIntercepts |= ++intercepts.fTopIntercepts > 1;
+                continue;
+            }
+            if (t >= 1) {
+                intercepts.fBottomIntercepts <<= 1;
+                fContainsIntercepts |= ++intercepts.fBottomIntercepts > 1;
+                continue;
+            }
+            fIntersected = true;
+            foundIntercept = true;
+            size_t tCount = intercepts.fTs.count();
+            double delta;
+            for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
+                if (t <= intercepts.fTs[idx2]) {
+                    // FIXME: ?  if (t < intercepts.fTs[idx2]) // failed
+                    delta = intercepts.fTs[idx2] - t;
+                    if (delta > 0) {
+                        insertedAt = idx2;
+                        *intercepts.fTs.insert(idx2) = t;
+                    }
+                    goto nextPt;
+                }
+            }
+            if (tCount == 0 || (delta = t - intercepts.fTs[tCount - 1]) > 0) {
+                insertedAt = tCount;
+                *intercepts.fTs.append() = t;
+            }
+    nextPt:
+            ;
+        }
+        fContainsIntercepts |= foundIntercept;
+        return insertedAt;
+    }
+
+    void addPartial(SkTArray<InEdge>& edges, int ptStart, int ptEnd,
+            int verbStart, int verbEnd) {
+        InEdge* edge = edges.push_back_n(1);
+        int verbCount = verbEnd - verbStart;
+        edge->fIntercepts.push_back_n(verbCount);
+     //   uint8_t* verbs = &fVerbs[verbStart];
+        for (int ceptIdx = 0; ceptIdx < verbCount; ++ceptIdx) {
+            edge->fIntercepts[ceptIdx] = fIntercepts[verbStart + ceptIdx];
+        }
+        edge->fPts.append(ptEnd - ptStart, &fPts[ptStart]);
+        edge->fVerbs.append(verbCount, &fVerbs[verbStart]);
+        edge->setBounds();
+        edge->fWinding = fWinding;
+        edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
+    }
+
+    void addSplit(SkTArray<InEdge>& edges, SkPoint* pts, uint8_t verb,
+            Intercepts& intercepts, int firstT, int lastT, bool flipped) {
+        InEdge* edge = edges.push_back_n(1);
+        edge->fIntercepts.push_back_n(1);
+        if (firstT == 0) {
+            *edge->fIntercepts[0].fTs.append() = 0;
+        } else {
+            *edge->fIntercepts[0].fTs.append() = intercepts.fTs[firstT - 1];
+        }
+        bool add1 = lastT == intercepts.fTs.count();
+        edge->fIntercepts[0].fTs.append(lastT - firstT, &intercepts.fTs[firstT]);
+        if (add1) {
+            *edge->fIntercepts[0].fTs.append() = 1;
+        }
+        edge->fIntercepts[0].fExplicit = true;
+        edge->fPts.append(verb + 1, pts);
+        edge->fVerbs.append(1, &verb);
+        // FIXME: bounds could be better for partial Ts
+        edge->setSubBounds();
+        edge->fContainsIntercepts = fContainsIntercepts; // FIXME: may not be correct -- but do we need to know?
+        if (flipped) {
+            edge->flipTs();
+            edge->fWinding = -fWinding;
+        } else {
+            edge->fWinding = fWinding;
+        }
+    }
+
+    bool cached(const InEdge* edge) {
+        // FIXME: in the pathological case where there is a ton of edges, binary search?
+        size_t count = fCached.count();
+        for (size_t index = 0; index < count; ++index) {
+            if (edge == fCached[index]) {
+                return true;
+            }
+            if (edge < fCached[index]) {
+                *fCached.insert(index) = edge;
+                return false;
+            }
+        }
+        *fCached.append() = edge;
+        return false;
+    }
+
+    void complete(signed char winding) {
+        setBounds();
+        fIntercepts.push_back_n(fVerbs.count());
+        if ((fWinding = winding) < 0) { // reverse verbs, pts, if bottom to top
+            flip();
+        }
+        fContainsIntercepts = fIntersected = false;
+    }
+
+    void flip() {
+        size_t index;
+        size_t last = fPts.count() - 1;
+        for (index = 0; index < last; ++index, --last) {
+            SkTSwap<SkPoint>(fPts[index], fPts[last]);
+        }
+        last = fVerbs.count() - 1;
+        for (index = 0; index < last; ++index, --last) {
+            SkTSwap<uint8_t>(fVerbs[index], fVerbs[last]);
+        }
+    }
+
+    void flipTs() {
+        SkASSERT(fIntercepts.count() == 1);
+        Intercepts& intercepts = fIntercepts[0];
+        SkASSERT(intercepts.fExplicit);
+        SkTDArray<double>& ts = intercepts.fTs;
+        size_t index;
+        size_t last = ts.count() - 1;
+        for (index = 0; index < last; ++index, --last) {
+            SkTSwap<double>(ts[index], ts[last]);
+        }
+    }
+
+    void reset() {
+        fCached.reset();
+        fIntercepts.reset();
+        fPts.reset();
+        fVerbs.reset();
+        fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
+        fWinding = 0;
+        fContainsIntercepts = false;
+        fIntersected = false;
+    }
+
+    void setBounds() {
+        SkPoint* ptPtr = fPts.begin();
+        SkPoint* ptLast = fPts.end();
+        if (ptPtr == ptLast) {
+            SkDebugf("%s empty edge\n", __FUNCTION__);
+            SkASSERT(0);
+            // FIXME: delete empty edge?
+            return;
+        }
+        fBounds.set(ptPtr->fX, ptPtr->fY, ptPtr->fX, ptPtr->fY);
+        ++ptPtr;
+        while (ptPtr != ptLast) {
+            fBounds.growToInclude(ptPtr->fX, ptPtr->fY);
+            ++ptPtr;
+        }
+    }
+
+    // recompute bounds based on subrange of T values
+    void setSubBounds() {
+        SkASSERT(fIntercepts.count() == 1);
+        Intercepts& intercepts = fIntercepts[0];
+        SkASSERT(intercepts.fExplicit);
+        SkASSERT(fVerbs.count() == 1);
+        SkTDArray<double>& ts = intercepts.fTs;
+        if (fVerbs[0] == SkPath::kQuad_Verb) {
+            SkASSERT(fPts.count() == 3);
+            QuadSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
+        } else {
+            SkASSERT(fVerbs[0] == SkPath::kCubic_Verb);
+            SkASSERT(fPts.count() == 4);
+            CubicSubBounds(fPts.begin(), ts[0], ts[ts.count() - 1], fBounds);
+        }
+    }
+
+    void splitInflectionPts(SkTArray<InEdge>& edges) {
+        if (!fIntersected) {
+            return;
+        }
+        uint8_t* verbs = fVerbs.begin();
+        SkPoint* pts = fPts.begin();
+        int lastVerb = 0;
+        int lastPt = 0;
+        uint8_t verb;
+        bool edgeSplit = false;
+        for (int ceptIdx = 0; ceptIdx < fIntercepts.count(); ++ceptIdx, pts += verb) {
+            Intercepts& intercepts = fIntercepts[ceptIdx];
+            verb = *verbs++;
+            if (verb <= SkPath::kLine_Verb) {
+                continue;
+            }
+            size_t tCount = intercepts.fTs.count();
+            if (!tCount) {
+                continue;
+            }
+            size_t tIndex = (size_t) -1;
+            SkScalar y = pts[0].fY;
+            int lastSplit = 0;
+            int firstSplit = -1;
+            bool curveSplit = false;
+            while (++tIndex < tCount) {
+                double nextT = intercepts.fTs[tIndex];
+                SkScalar nextY = verb == SkPath::kQuad_Verb
+                        ? QuadYAtT(pts, nextT) : CubicYAtT(pts, nextT);
+                if (nextY < y) {
+                    edgeSplit = curveSplit = true;
+                    if (firstSplit < 0) {
+                        firstSplit = tIndex;
+                        int nextPt = pts - fPts.begin();
+                        int nextVerb = verbs - 1 - fVerbs.begin();
+                        if (lastVerb < nextVerb) {
+                            addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
+            #if DEBUG_SPLIT
+                            SkDebugf("%s addPartial 1\n", __FUNCTION__);
+            #endif
+                        }
+                        lastPt = nextPt;
+                        lastVerb = nextVerb;
+                    }
+                } else {
+                    if (firstSplit >= 0) {
+                        if (lastSplit < firstSplit) {
+                            addSplit(edges, pts, verb, intercepts,
+                                    lastSplit, firstSplit, false);
+            #if DEBUG_SPLIT
+                            SkDebugf("%s addSplit 1 tIndex=%d,%d\n",
+                                    __FUNCTION__, lastSplit, firstSplit);
+            #endif
+                        }
+                        addSplit(edges, pts, verb, intercepts,
+                                firstSplit, tIndex, true);
+            #if DEBUG_SPLIT
+                        SkDebugf("%s addSplit 2 tIndex=%d,%d flip\n",
+                                __FUNCTION__, firstSplit, tIndex);
+            #endif
+                        lastSplit = tIndex;
+                        firstSplit = -1;
+                    }
+                }
+                y = nextY;
+            }
+            if (curveSplit) {
+                if (firstSplit < 0) {
+                    firstSplit = lastSplit;
+                } else {
+                    addSplit(edges, pts, verb, intercepts, lastSplit,
+                            firstSplit, false);
+            #if DEBUG_SPLIT
+                    SkDebugf("%s addSplit 3 tIndex=%d,%d\n", __FUNCTION__,
+                            lastSplit, firstSplit);
+            #endif
+                }
+                addSplit(edges, pts, verb, intercepts, firstSplit,
+                        tIndex, pts[verb].fY < y);
+            #if DEBUG_SPLIT
+                SkDebugf("%s addSplit 4 tIndex=%d,%d %s\n", __FUNCTION__,
+                        firstSplit, tIndex, pts[verb].fY < y ? "flip" : "");
+            #endif
+            }
+        }
+        // collapse remainder -- if there's nothing left, clear it somehow?
+        if (edgeSplit) {
+            int nextVerb = verbs - 1 - fVerbs.begin();
+            if (lastVerb < nextVerb) {
+                int nextPt = pts - fPts.begin();
+                addPartial(edges, lastPt, nextPt, lastVerb, nextVerb);
+            #if DEBUG_SPLIT
+                SkDebugf("%s addPartial 2\n", __FUNCTION__);
+            #endif
+            }
+            // OPTIMIZATION: reuse the edge instead of marking it empty
+            reset();
+        }
+    }
+
+#if DEBUG_DUMP
+    void dump() {
+        int i;
+        const char className[] = "InEdge";
+        const int tab = 4;
+        SkDebugf("InEdge %p (edge=%d)\n", this, fID);
+        for (i = 0; i < fCached.count(); ++i) {
+            SkDebugf("%*s.fCached[%d]=0x%08x\n", tab + sizeof(className),
+                    className, i, fCached[i]);
+        }
+        uint8_t* verbs = fVerbs.begin();
+        SkPoint* pts = fPts.begin();
+        for (i = 0; i < fIntercepts.count(); ++i) {
+            SkDebugf("%*s.fIntercepts[%d]:\n", tab + sizeof(className),
+                    className, i);
+            fIntercepts[i].dump(pts, (SkPath::Verb) *verbs);
+            pts += *verbs++;
+        }
+        for (i = 0; i < fPts.count(); ++i) {
+            SkDebugf("%*s.fPts[%d]=(%1.9g,%1.9g)\n", tab + sizeof(className),
+                    className, i, fPts[i].fX, fPts[i].fY);
+        }
+        for (i = 0; i < fVerbs.count(); ++i) {
+            SkDebugf("%*s.fVerbs[%d]=%d\n", tab + sizeof(className),
+                    className, i, fVerbs[i]);
+        }
+        SkDebugf("%*s.fBounds=(%1.9g, %1.9g, %1.9g, %1.9g)\n", tab + sizeof(className),
+                className, fBounds.fLeft, fBounds.fTop,
+                fBounds.fRight, fBounds.fBottom);
+        SkDebugf("%*s.fWinding=%d\n", tab + sizeof(className), className,
+                fWinding);
+        SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
+                className, fContainsIntercepts);
+        SkDebugf("%*s.fIntersected=%d\n", tab + sizeof(className),
+                className, fIntersected);
+    }
+#endif
+
+    // FIXME: temporary data : move this to a separate struct?
+    SkTDArray<const InEdge*> fCached; // list of edges already intercepted
+    SkTArray<Intercepts> fIntercepts; // one per verb
+
+    // persistent data
+    SkTDArray<SkPoint> fPts;
+    SkTDArray<uint8_t> fVerbs;
+    Bounds fBounds;
+    int fID;
+    signed char fWinding;
+    bool fContainsIntercepts;
+    bool fIntersected;
+};
+
+class InEdgeBuilder {
+public:
+
+InEdgeBuilder(const SkPath& path, bool ignoreHorizontal, SkTArray<InEdge>& edges,
+        SkTDArray<HorizontalEdge>& horizontalEdges)
+    : fPath(path)
+    , fCurrentEdge(NULL)
+    , fEdges(edges)
+    , fHorizontalEdges(horizontalEdges)
+    , fIgnoreHorizontal(ignoreHorizontal)
+    , fContainsCurves(false)
+{
+    walk();
+}
+
+bool containsCurves() const {
+    return fContainsCurves;
+}
+
+protected:
+
+void addEdge() {
+    SkASSERT(fCurrentEdge);
+    fCurrentEdge->fPts.append(fPtCount - fPtOffset, &fPts[fPtOffset]);
+    fPtOffset = 1;
+    *fCurrentEdge->fVerbs.append() = fVerb;
+}
+
+bool complete() {
+    if (fCurrentEdge && fCurrentEdge->fVerbs.count()) {
+        fCurrentEdge->complete(fWinding);
+        fCurrentEdge = NULL;
+        return true;
+    }
+    return false;
+}
+
+int direction(SkPath::Verb verb) {
+    fPtCount = verb + 1;
+    if (fIgnoreHorizontal && isHorizontal()) {
+        return 0;
+    }
+    return fPts[0].fY == fPts[verb].fY
+            ? fPts[0].fX == fPts[verb].fX ? 0 : fPts[0].fX < fPts[verb].fX
+            ? 1 : -1 : fPts[0].fY < fPts[verb].fY ? 1 : -1;
+}
+
+bool isHorizontal() {
+    SkScalar y = fPts[0].fY;
+    for (int i = 1; i < fPtCount; ++i) {
+        if (fPts[i].fY != y) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void startEdge() {
+    if (!fCurrentEdge) {
+        fCurrentEdge = fEdges.push_back_n(1);
+    }
+    fWinding = 0;
+    fPtOffset = 0;
+}
+
+void walk() {
+    SkPath::Iter iter(fPath, true);
+    int winding = 0;
+    while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
+        switch (fVerb) {
+            case SkPath::kMove_Verb:
+                startEdge();
+                continue;
+            case SkPath::kLine_Verb:
+                winding = direction(SkPath::kLine_Verb);
+                break;
+            case SkPath::kQuad_Verb:
+                fVerb = QuadReduceOrder(fPts);
+                winding = direction(fVerb);
+                fContainsCurves |= fVerb == SkPath::kQuad_Verb;
+                break;
+            case SkPath::kCubic_Verb:
+                fVerb = CubicReduceOrder(fPts);
+                winding = direction(fVerb);
+                fContainsCurves |= fVerb >= SkPath::kQuad_Verb;
+                break;
+            case SkPath::kClose_Verb:
+                SkASSERT(fCurrentEdge);
+                complete();
+                continue;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return;
+        }
+        if (winding == 0) {
+            HorizontalEdge* horizontalEdge = fHorizontalEdges.append();
+            // FIXME: for degenerate quads and cubics, compute x extremes
+            horizontalEdge->fLeft = fPts[0].fX;
+            horizontalEdge->fRight = fPts[fVerb].fX;
+            horizontalEdge->fY = fPts[0].fY;
+            if (horizontalEdge->fLeft > horizontalEdge->fRight) {
+                SkTSwap<SkScalar>(horizontalEdge->fLeft, horizontalEdge->fRight);
+            }
+            if (complete()) {
+                startEdge();
+            }
+            continue;
+        }
+        if (fWinding + winding == 0) {
+            // FIXME: if prior verb or this verb is a horizontal line, reverse
+            // it instead of starting a new edge
+            SkASSERT(fCurrentEdge);
+            if (complete()) {
+                startEdge();
+            }
+        }
+        fWinding = winding;
+        addEdge();
+    }
+    if (!complete()) {
+        if (fCurrentEdge) {
+            fEdges.pop_back();
+        }
+    }
+}
+
+private:
+    const SkPath& fPath;
+    InEdge* fCurrentEdge;
+    SkTArray<InEdge>& fEdges;
+    SkTDArray<HorizontalEdge>& fHorizontalEdges;
+    SkPoint fPts[4];
+    SkPath::Verb fVerb;
+    int fPtCount;
+    int fPtOffset;
+    int8_t fWinding;
+    bool fIgnoreHorizontal;
+    bool fContainsCurves;
+};
+
+struct WorkEdge {
+    SkScalar bottom() const {
+        return fPts[verb()].fY;
+    }
+
+    void init(const InEdge* edge) {
+        fEdge = edge;
+        fPts = edge->fPts.begin();
+        fVerb = edge->fVerbs.begin();
+    }
+
+    bool advance() {
+        SkASSERT(fVerb < fEdge->fVerbs.end());
+        fPts += *fVerb++;
+        return fVerb != fEdge->fVerbs.end();
+    }
+
+    const SkPoint* lastPoints() const {
+        SkASSERT(fPts >= fEdge->fPts.begin() + lastVerb());
+        return &fPts[-lastVerb()];
+    }
+
+    SkPath::Verb lastVerb() const {
+        SkASSERT(fVerb > fEdge->fVerbs.begin());
+        return (SkPath::Verb) fVerb[-1];
+    }
+
+    const SkPoint* points() const {
+        return fPts;
+    }
+
+    SkPath::Verb verb() const {
+        return (SkPath::Verb) *fVerb;
+    }
+
+    ptrdiff_t verbIndex() const {
+        return fVerb - fEdge->fVerbs.begin();
+    }
+
+    int winding() const {
+        return fEdge->fWinding;
+    }
+
+    const InEdge* fEdge;
+    const SkPoint* fPts;
+    const uint8_t* fVerb;
+};
+
+// always constructed with SkTDArray because new edges are inserted
+// this may be a inappropriate optimization, suggesting that a separate array of
+// ActiveEdge* may be faster to insert and search
+
+// OPTIMIZATION: Brian suggests that global sorting should be unnecessary, since
+// as active edges are introduced, only local sorting should be required
+class ActiveEdge {
+public:
+    // this logic must be kept in sync with tooCloseToCall
+    // callers expect this to only read fAbove, fTangent
+    bool operator<(const ActiveEdge& rh) const {
+        if (fVerb == rh.fVerb) {
+            // FIXME: don't know what to do if verb is quad, cubic
+            return abCompare(fAbove, fBelow, rh.fAbove, rh.fBelow);
+        }
+        // figure out which is quad, line
+        // if cached data says line did not intersect quad, use top/bottom
+        if (fVerb != SkPath::kLine_Verb ? noIntersect(rh) : rh.noIntersect(*this)) {
+            return abCompare(fAbove, fBelow, rh.fAbove, rh.fBelow);
+        }
+        // use whichever of top/tangent tangent/bottom overlaps more
+        // with line top/bot
+        // assumes quad/cubic can already be upconverted to cubic/cubic
+        const SkPoint* line[2];
+        const SkPoint* curve[4];
+        if (fVerb != SkPath::kLine_Verb) {
+            line[0] = &rh.fAbove;
+            line[1] = &rh.fBelow;
+            curve[0] = &fAbove;
+            curve[1] = &fTangent;
+            curve[2] = &fBelow;
+        } else {
+            line[0] = &fAbove;
+            line[1] = &fBelow;
+            curve[0] = &rh.fAbove;
+            curve[1] = &rh.fTangent;
+            curve[2] = &rh.fBelow;
+        }
+        // FIXME: code has been abandoned, incomplete....
+        return false;
+    }
+
+    bool abCompare(const SkPoint& a1, const SkPoint& a2, const SkPoint& b1,
+            const SkPoint& b2) const {
+        double topD = a1.fX - b1.fX;
+        if (b1.fY < a1.fY) {
+            topD = (b2.fY - b1.fY) * topD - (a1.fY - b1.fY) * (b2.fX - b1.fX);
+        } else if (b1.fY > a1.fY) {
+            topD = (a2.fY - a1.fY) * topD + (b1.fY - a1.fY) * (a2.fX - a1.fX);
+        }
+        double botD = a2.fX - b2.fX;
+        if (b2.fY > a2.fY) {
+            botD = (b2.fY - b1.fY) * botD - (a2.fY - b2.fY) * (b2.fX - b1.fX);
+        } else if (b2.fY < a2.fY) {
+            botD = (a2.fY - a1.fY) * botD + (b2.fY - a2.fY) * (a2.fX - a1.fX);
+        }
+        // return sign of greater absolute value
+        return (fabs(topD) > fabs(botD) ? topD : botD) < 0;
+    }
+
+    // If a pair of edges are nearly coincident for some span, add a T in the
+    // edge so it can be shortened to match the other edge. Note that another
+    // approach is to trim the edge after it is added to the OutBuilder list --
+    // FIXME: since this has no effect if the edge is already done (i.e.,
+    // fYBottom >= y) maybe this can only be done by calling trimLine later.
+    void addTatYBelow(SkScalar y) {
+        if (fBelow.fY <= y || fYBottom >= y) {
+            return;
+        }
+        addTatYInner(y);
+        fFixBelow = true;
+    }
+
+    void addTatYAbove(SkScalar y) {
+        if (fBelow.fY <= y) {
+            return;
+        }
+        addTatYInner(y);
+    }
+
+    void addTatYInner(SkScalar y) {
+        if (fWorkEdge.fPts[0].fY > y) {
+            backup(y);
+        }
+        SkScalar left = fWorkEdge.fPts[0].fX;
+        SkScalar right = fWorkEdge.fPts[1].fX;
+        if (left > right) {
+            SkTSwap(left, right);
+        }
+        double ts[2];
+        SkASSERT(fWorkEdge.fVerb[0] == SkPath::kLine_Verb);
+        int pts = LineIntersect(fWorkEdge.fPts, left, right, y, ts);
+        SkASSERT(pts == 1);
+        // An ActiveEdge or WorkEdge has no need to modify the T values computed
+        // in the InEdge, except in the following case. If a pair of edges are
+        // nearly coincident, this may not be detected when the edges are
+        // intersected. Later, when sorted, and this near-coincidence is found,
+        // an additional t value must be added, requiring the cast below.
+        InEdge* writable = const_cast<InEdge*>(fWorkEdge.fEdge);
+        int insertedAt = writable->add(ts, pts, fWorkEdge.verbIndex());
+    #if DEBUG_ADJUST_COINCIDENT
+        SkDebugf("%s edge=%d y=%1.9g t=%1.9g\n", __FUNCTION__, ID(), y, ts[0]);
+    #endif
+        if (insertedAt >= 0) {
+            if (insertedAt + 1 < fTIndex) {
+                SkASSERT(insertedAt + 2 == fTIndex);
+                --fTIndex;
+            }
+        }
+    }
+
+    bool advanceT() {
+        SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
+        return ++fTIndex <= fTs->count() - fExplicitTs;
+    }
+
+    bool advance() {
+    // FIXME: flip sense of next
+        bool result = fWorkEdge.advance();
+        fDone = !result;
+        initT();
+        return result;
+    }
+
+    void backup(SkScalar y) {
+        do {
+            SkASSERT(fWorkEdge.fEdge->fVerbs.begin() < fWorkEdge.fVerb);
+            fWorkEdge.fPts -= *--fWorkEdge.fVerb;
+            SkASSERT(fWorkEdge.fEdge->fPts.begin() <= fWorkEdge.fPts);
+        } while (fWorkEdge.fPts[0].fY >= y);
+        initT();
+        SkASSERT(!fExplicitTs);
+        fTIndex = fTs->count() + 1;
+    }
+
+    void calcAboveBelow(double tAbove, double tBelow) {
+        fVerb = fWorkEdge.verb();
+        switch (fVerb) {
+            case SkPath::kLine_Verb:
+                LineXYAtT(fWorkEdge.fPts, tAbove, &fAbove);
+                LineXYAtT(fWorkEdge.fPts, tBelow, &fTangent);
+                fBelow = fTangent;
+                break;
+            case SkPath::kQuad_Verb:
+                // FIXME: put array in struct to avoid copy?
+                SkPoint quad[3];
+                QuadSubDivide(fWorkEdge.fPts, tAbove, tBelow, quad);
+                fAbove = quad[0];
+                fTangent = quad[0] != quad[1] ? quad[1] : quad[2];
+                fBelow = quad[2];
+                break;
+            case SkPath::kCubic_Verb:
+                SkPoint cubic[3];
+                CubicSubDivide(fWorkEdge.fPts, tAbove, tBelow, cubic);
+                fAbove = cubic[0];
+                // FIXME: can't see how quad logic for how tangent is used
+                // extends to cubic
+                fTangent = cubic[0] != cubic[1] ? cubic[1]
+                        : cubic[0] != cubic[2] ? cubic[2] : cubic[3];
+                fBelow = cubic[3];
+                break;
+            default:
+                SkASSERT(0);
+        }
+    }
+
+    void calcLeft(SkScalar y) {
+        // OPTIMIZE: put a kDone_Verb at the end of the verb list?
+        if (fDone || fBelow.fY > y) {
+            return; // nothing to do; use last
+        }
+        calcLeft();
+        if (fAbove.fY == fBelow.fY) {
+            SkDebugf("%s edge=%d fAbove.fY != fBelow.fY %1.9g\n", __FUNCTION__,
+                    ID(), fAbove.fY);
+        }
+    }
+
+    void calcLeft() {
+        int add = (fTIndex <= fTs->count() - fExplicitTs) - 1;
+        double tAbove = t(fTIndex + add);
+        double tBelow = t(fTIndex - ~add);
+        // OPTIMIZATION: if fAbove, fBelow have already been computed
+        //  for the fTIndex, don't do it again
+        calcAboveBelow(tAbove, tBelow);
+        // For identical x, this lets us know which edge is first.
+        // If both edges have T values < 1, check x at next T (fBelow).
+        SkASSERT(tAbove != tBelow);
+        // FIXME: this can loop forever
+        // need a break if we hit the end
+        // FIXME: in unit test, figure out how explicit Ts work as well
+        while (fAbove.fY == fBelow.fY) {
+            if (add < 0 || fTIndex == fTs->count()) {
+                add -= 1;
+                SkASSERT(fTIndex + add >= 0);
+                tAbove = t(fTIndex + add);
+            } else {
+                add += 1;
+                SkASSERT(fTIndex - ~add <= fTs->count() + 1);
+                tBelow = t(fTIndex - ~add);
+            }
+            calcAboveBelow(tAbove, tBelow);
+        }
+        fTAbove = tAbove;
+        fTBelow = tBelow;
+    }
+
+    bool done(SkScalar bottom) const {
+        return fDone || fYBottom >= bottom;
+    }
+
+    void fixBelow() {
+        if (fFixBelow) {
+            fTBelow = nextT();
+            calcAboveBelow(fTAbove, fTBelow);
+            fFixBelow = false;
+        }
+    }
+
+    void init(const InEdge* edge) {
+        fWorkEdge.init(edge);
+        fDone = false;
+        initT();
+        fBelow.fY = SK_ScalarMin;
+        fYBottom = SK_ScalarMin;
+    }
+
+    void initT() {
+        const Intercepts& intercepts = fWorkEdge.fEdge->fIntercepts.front();
+        SkASSERT(fWorkEdge.verbIndex() <= fWorkEdge.fEdge->fIntercepts.count());
+        const Intercepts* interceptPtr = &intercepts + fWorkEdge.verbIndex();
+        fTs = &interceptPtr->fTs;
+        fExplicitTs = interceptPtr->fExplicit;
+  //  the above is conceptually the same as
+  //    fTs = &fWorkEdge.fEdge->fIntercepts[fWorkEdge.verbIndex()].fTs;
+  //  but templated arrays don't allow returning a pointer to the end() element
+        fTIndex = 0;
+        if (!fDone) {
+            fVerb = fWorkEdge.verb();
+        }
+        SkASSERT(fVerb > SkPath::kMove_Verb);
+    }
+
+    // OPTIMIZATION: record if two edges are coincident when the are intersected
+    // It's unclear how to do this -- seems more complicated than recording the
+    // t values, since the same t values could exist intersecting non-coincident
+    // edges.
+    bool isCoincidentWith(const ActiveEdge* edge) const {
+        if (fAbove != edge->fAbove || fBelow != edge->fBelow) {
+            return false;
+        }
+        if (fVerb != edge->fVerb) {
+            return false;
+        }
+        switch (fVerb) {
+            case SkPath::kLine_Verb:
+                return true;
+            default:
+                // FIXME: add support for quads, cubics
+                SkASSERT(0);
+                return false;
+        }
+        return false;
+    }
+
+    bool isUnordered(const ActiveEdge* edge) const {
+        return fAbove == edge->fAbove && fBelow == edge->fBelow;
+    }
+
+//    SkPath::Verb lastVerb() const {
+//        return fDone ? fWorkEdge.lastVerb() : fWorkEdge.verb();
+//    }
+
+    const SkPoint* lastPoints() const {
+        return fDone ? fWorkEdge.lastPoints() : fWorkEdge.points();
+    }
+
+    bool noIntersect(const ActiveEdge& ) const {
+        // incomplete
+        return false;
+    }
+
+    // The shortest close call edge should be moved into a position where
+    // it contributes if the winding is transitioning to or from zero.
+    bool swapClose(const ActiveEdge* next, int prev, int wind, int mask) const {
+#if DEBUG_ADJUST_COINCIDENT
+        SkDebugf("%s edge=%d (%g) next=%d (%g) prev=%d wind=%d nextWind=%d\n",
+                __FUNCTION__, ID(), fBelow.fY, next->ID(), next->fBelow.fY,
+                prev, wind, wind + next->fWorkEdge.winding());
+#endif
+        if ((prev & mask) == 0 || (wind & mask) == 0) {
+            return next->fBelow.fY < fBelow.fY;
+        }
+        int nextWinding = wind + next->fWorkEdge.winding();
+        if ((nextWinding & mask) == 0) {
+            return fBelow.fY < next->fBelow.fY;
+        }
+        return false;
+    }
+
+    bool swapCoincident(const ActiveEdge* edge, SkScalar bottom) const {
+        if (fBelow.fY >= bottom || fDone || edge->fDone) {
+            return false;
+        }
+        ActiveEdge thisWork = *this;
+        ActiveEdge edgeWork = *edge;
+        while ((thisWork.advanceT() || thisWork.advance())
+                && (edgeWork.advanceT() || edgeWork.advance())) {
+            thisWork.calcLeft();
+            edgeWork.calcLeft();
+            if (thisWork < edgeWork) {
+                return false;
+            }
+            if (edgeWork < thisWork) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool swapUnordered(const ActiveEdge* edge, SkScalar /* bottom */) const {
+        SkASSERT(fVerb != SkPath::kLine_Verb
+                || edge->fVerb != SkPath::kLine_Verb);
+        if (fDone || edge->fDone) {
+            return false;
+        }
+        ActiveEdge thisWork, edgeWork;
+        extractAboveBelow(thisWork);
+        edge->extractAboveBelow(edgeWork);
+        return edgeWork < thisWork;
+    }
+
+    bool tooCloseToCall(const ActiveEdge* edge) const {
+        int ulps;
+        double t1, t2, b1, b2;
+        // This logic must be kept in sync with operator <
+        if (edge->fAbove.fY < fAbove.fY) {
+            t1 = (edge->fTangent.fY - edge->fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
+            t2 = (fAbove.fY - edge->fAbove.fY) * (edge->fTangent.fX - edge->fAbove.fX);
+        } else if (edge->fAbove.fY > fAbove.fY) {
+            t1 = (fTangent.fY - fAbove.fY) * (fAbove.fX - edge->fAbove.fX);
+            t2 = (fAbove.fY - edge->fAbove.fY) * (fTangent.fX - fAbove.fX);
+        } else {
+            t1 = fAbove.fX;
+            t2 = edge->fAbove.fX;
+        }
+        if (edge->fTangent.fY > fTangent.fY) {
+            b1 = (edge->fTangent.fY - edge->fAbove.fY) * (fTangent.fX - edge->fTangent.fX);
+            b2 = (fTangent.fY - edge->fTangent.fY) * (edge->fTangent.fX - edge->fAbove.fX);
+        } else if (edge->fTangent.fY < fTangent.fY) {
+            b1 = (fTangent.fY - fAbove.fY) * (fTangent.fX - edge->fTangent.fX);
+            b2 = (fTangent.fY - edge->fTangent.fY) * (fTangent.fX - fAbove.fX);
+        } else {
+            b1 = fTangent.fX;
+            b2 = edge->fTangent.fX;
+        }
+        if (fabs(t1 - t2) > fabs(b1 - b2)) {
+            ulps = UlpsDiff((float) t1, (float) t2);
+        } else {
+            ulps = UlpsDiff((float) b1, (float) b2);
+        }
+#if DEBUG_ADJUST_COINCIDENT
+        SkDebugf("%s this=%d edge=%d ulps=%d\n", __FUNCTION__, ID(), edge->ID(),
+                ulps);
+#endif
+        if (ulps < 0 || ulps > 32) {
+            return false;
+        }
+        if (fVerb == SkPath::kLine_Verb && edge->fVerb == SkPath::kLine_Verb) {
+            return true;
+        }
+        if (fVerb != SkPath::kLine_Verb && edge->fVerb != SkPath::kLine_Verb) {
+            return false;
+        }
+
+        double ts[2];
+        bool isLine = true;
+        bool curveQuad = true;
+        if (fVerb == SkPath::kCubic_Verb) {
+            ts[0] = (fTAbove * 2 + fTBelow) / 3;
+            ts[1] = (fTAbove + fTBelow * 2) / 3;
+            curveQuad = isLine = false;
+        } else if (edge->fVerb == SkPath::kCubic_Verb) {
+            ts[0] = (edge->fTAbove * 2 + edge->fTBelow) / 3;
+            ts[1] = (edge->fTAbove + edge->fTBelow * 2) / 3;
+            curveQuad = false;
+        } else if (fVerb == SkPath::kQuad_Verb) {
+                ts[0] = fTAbove;
+                ts[1] = (fTAbove + fTBelow) / 2;
+                isLine = false;
+        } else {
+            SkASSERT(edge->fVerb == SkPath::kQuad_Verb);
+            ts[0] = edge->fTAbove;
+            ts[1] = (edge->fTAbove + edge->fTBelow) / 2;
+        }
+        const SkPoint* curvePts = isLine ? edge->lastPoints() : lastPoints();
+        const ActiveEdge* lineEdge = isLine ? this : edge;
+        SkPoint curveSample[2];
+        for (int index = 0; index < 2; ++index) {
+            if (curveQuad) {
+                QuadXYAtT(curvePts, ts[index], &curveSample[index]);
+            } else {
+                CubicXYAtT(curvePts, ts[index], &curveSample[index]);
+            }
+        }
+        return IsCoincident(curveSample, lineEdge->fAbove, lineEdge->fBelow);
+    }
+
+    double nextT() const {
+        SkASSERT(fTIndex <= fTs->count() - fExplicitTs);
+        return t(fTIndex + 1);
+    }
+
+    double t() const {
+        return t(fTIndex);
+    }
+
+    double t(int tIndex) const {
+        if (fExplicitTs) {
+            SkASSERT(tIndex < fTs->count());
+            return (*fTs)[tIndex];
+        }
+        if (tIndex == 0) {
+            return 0;
+        }
+        if (tIndex > fTs->count()) {
+            return 1;
+        }
+        return (*fTs)[tIndex - 1];
+    }
+
+    // FIXME: debugging only
+    int ID() const {
+        return fWorkEdge.fEdge->fID;
+    }
+
+private:
+    // utility used only by swapUnordered
+    void extractAboveBelow(ActiveEdge& extracted) const {
+        SkPoint curve[4];
+        switch (fVerb) {
+            case SkPath::kLine_Verb:
+                extracted.fAbove = fAbove;
+                extracted.fTangent = fTangent;
+                return;
+            case SkPath::kQuad_Verb:
+                QuadSubDivide(lastPoints(), fTAbove, fTBelow, curve);
+                break;
+            case SkPath::kCubic_Verb:
+                CubicSubDivide(lastPoints(), fTAbove, fTBelow, curve);
+                break;
+            default:
+                SkASSERT(0);
+        }
+        extracted.fAbove = curve[0];
+        extracted.fTangent = curve[1];
+    }
+
+public:
+    WorkEdge fWorkEdge;
+    const SkTDArray<double>* fTs;
+    SkPoint fAbove;
+    SkPoint fTangent;
+    SkPoint fBelow;
+    double fTAbove; // OPTIMIZATION: only required if edge has quads or cubics
+    double fTBelow;
+    SkScalar fYBottom;
+    int fCoincident;
+    int fTIndex;
+    SkPath::Verb fVerb;
+    bool fSkip; // OPTIMIZATION: use bitfields?
+    bool fCloseCall;
+    bool fDone;
+    bool fFixBelow;
+    bool fExplicitTs;
+};
+
+static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
+    size_t count = activeEdges.count();
+    for (size_t index = 0; index < count; ++index) {
+        if (edge == activeEdges[index].fWorkEdge.fEdge) {
+            return;
+        }
+    }
+    ActiveEdge* active = activeEdges.append();
+    active->init(edge);
+}
+
+// Find any intersections in the range of active edges. A pair of edges, on
+// either side of another edge, may change the winding contribution for part of
+// the edge.
+// Keep horizontal edges just for
+// the purpose of computing when edges change their winding contribution, since
+// this is essentially computing the horizontal intersection.
+static void addBottomT(InEdge** currentPtr, InEdge** lastPtr,
+        HorizontalEdge** horizontal) {
+    InEdge** testPtr = currentPtr - 1;
+    HorizontalEdge* horzEdge = *horizontal;
+    SkScalar left = horzEdge->fLeft;
+    SkScalar bottom = horzEdge->fY;
+    while (++testPtr != lastPtr) {
+        InEdge* test = *testPtr;
+        if (test->fBounds.fBottom <= bottom || test->fBounds.fRight <= left) {
+            continue;
+        }
+        WorkEdge wt;
+        wt.init(test);
+        do {
+            HorizontalEdge** sorted = horizontal;
+            horzEdge = *sorted;
+            do {
+                double wtTs[4];
+                int pts;
+                uint8_t verb = wt.verb();
+                switch (verb) {
+                    case SkPath::kLine_Verb:
+                        pts = LineIntersect(wt.fPts, horzEdge->fLeft,
+                                horzEdge->fRight, horzEdge->fY, wtTs);
+                        break;
+                    case SkPath::kQuad_Verb:
+                        pts = QuadIntersect(wt.fPts, horzEdge->fLeft,
+                                horzEdge->fRight, horzEdge->fY, wtTs);
+                        break;
+                    case SkPath::kCubic_Verb:
+                        pts = CubicIntersect(wt.fPts, horzEdge->fLeft,
+                                horzEdge->fRight, horzEdge->fY, wtTs);
+                        break;
+                }
+                if (pts) {
+#if DEBUG_ADD_BOTTOM_TS
+                    for (int x = 0; x < pts; ++x) {
+                        SkDebugf("%s y=%g wtTs[0]=%g (%g,%g", __FUNCTION__,
+                                horzEdge->fY, wtTs[x], wt.fPts[0].fX, wt.fPts[0].fY);
+                        for (int y = 0; y < verb; ++y) {
+                            SkDebugf(" %g,%g", wt.fPts[y + 1].fX, wt.fPts[y + 1].fY));
+                        }
+                        SkDebugf(")\n");
+                    }
+                    if (pts > verb) {
+                        SkASSERT(0); // FIXME ? should this work?
+                        SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
+                    }
+#endif
+                    test->add(wtTs, pts, wt.verbIndex());
+                }
+                horzEdge = *++sorted;
+            } while (horzEdge->fY == bottom
+                    && horzEdge->fLeft <= test->fBounds.fRight);
+        } while (wt.advance());
+    }
+}
+
+#if DEBUG_ADD_INTERSECTING_TS
+static void debugShowLineIntersection(int pts, const WorkEdge& wt,
+        const WorkEdge& wn, const double wtTs[2], const double wnTs[2]) {
+    if (!pts) {
+        return;
+    }
+    SkPoint wtOutPt, wnOutPt;
+    LineXYAtT(wt.fPts, wtTs[0], &wtOutPt);
+    LineXYAtT(wn.fPts, wnTs[0], &wnOutPt);
+    SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
+            __FUNCTION__,
+            wtTs[0], wt.fPts[0].fX, wt.fPts[0].fY,
+            wt.fPts[1].fX, wt.fPts[1].fY, wtOutPt.fX, wtOutPt.fY);
+    if (pts == 2) {
+        SkDebugf("%s wtTs[1]=%g\n", __FUNCTION__, wtTs[1]);
+    }
+    SkDebugf("%s wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
+            __FUNCTION__,
+            wnTs[0], wn.fPts[0].fX, wn.fPts[0].fY,
+            wn.fPts[1].fX, wn.fPts[1].fY, wnOutPt.fX, wnOutPt.fY);
+    if (pts == 2) {
+        SkDebugf("%s wnTs[1]=%g\n", __FUNCTION__, wnTs[1]);
+    }
+}
+#else
+static void debugShowLineIntersection(int , const WorkEdge& ,
+        const WorkEdge& , const double [2], const double [2]) {
+}
+#endif
+
+static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
+    InEdge** testPtr = currentPtr - 1;
+    // FIXME: lastPtr should be past the point of interest, so
+    // test below should be  lastPtr - 2
+    // that breaks testSimplifyTriangle22, so further investigation is needed
+    while (++testPtr != lastPtr - 1) {
+        InEdge* test = *testPtr;
+        InEdge** nextPtr = testPtr;
+        do {
+            InEdge* next = *++nextPtr;
+            // FIXME: this compares against the sentinel sometimes
+            // OPTIMIZATION: this may never be needed since this gets called
+            // in two passes now. Verify that double hits are appropriate.
+            if (test->cached(next)) {
+                continue;
+            }
+            if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
+                continue;
+            }
+            WorkEdge wt, wn;
+            wt.init(test);
+            wn.init(next);
+            do {
+                int pts;
+                Intersections ts;
+                bool swap = false;
+                switch (wt.verb()) {
+                    case SkPath::kLine_Verb:
+                        switch (wn.verb()) {
+                            case SkPath::kLine_Verb: {
+                                pts = LineIntersect(wt.fPts, wn.fPts, ts);
+                                debugShowLineIntersection(pts, wt, wn,
+                                        ts.fT[0], ts.fT[1]);
+                                break;
+                            }
+                            case SkPath::kQuad_Verb: {
+                                swap = true;
+                                pts = QuadLineIntersect(wn.fPts, wt.fPts, ts);
+                                break;
+                            }
+                            case SkPath::kCubic_Verb: {
+                                swap = true;
+                                pts = CubicLineIntersect(wn.fPts, wt.fPts, ts);
+                                break;
+                            }
+                            default:
+                                SkASSERT(0);
+                        }
+                        break;
+                    case SkPath::kQuad_Verb:
+                        switch (wn.verb()) {
+                            case SkPath::kLine_Verb: {
+                                pts = QuadLineIntersect(wt.fPts, wn.fPts, ts);
+                                break;
+                            }
+                            case SkPath::kQuad_Verb: {
+                                pts = QuadIntersect(wt.fPts, wn.fPts, ts);
+                                break;
+                            }
+                            case SkPath::kCubic_Verb: {
+                                // FIXME: promote quad to cubic
+                                pts = CubicIntersect(wt.fPts, wn.fPts, ts);
+                                break;
+                            }
+                            default:
+                                SkASSERT(0);
+                        }
+                        break;
+                    case SkPath::kCubic_Verb:
+                        switch (wn.verb()) {
+                            case SkPath::kLine_Verb: {
+                                pts = CubicLineIntersect(wt.fPts, wn.fPts, ts);
+                                break;
+                            }
+                            case SkPath::kQuad_Verb: {
+                                 // FIXME: promote quad to cubic
+                                pts = CubicIntersect(wt.fPts, wn.fPts, ts);
+                                break;
+                            }
+                            case SkPath::kCubic_Verb: {
+                                pts = CubicIntersect(wt.fPts, wn.fPts, ts);
+                                break;
+                            }
+                            default:
+                                SkASSERT(0);
+                        }
+                        break;
+                    default:
+                        SkASSERT(0);
+                }
+                test->add(ts.fT[swap], pts, wt.verbIndex());
+                next->add(ts.fT[!swap], pts, wn.verbIndex());
+            } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
+        } while (nextPtr != lastPtr);
+    }
+}
+
+static InEdge** advanceEdges(SkTDArray<ActiveEdge>* activeEdges,
+        InEdge** currentPtr, InEdge** lastPtr,  SkScalar y) {
+    InEdge** testPtr = currentPtr - 1;
+    while (++testPtr != lastPtr) {
+        if ((*testPtr)->fBounds.fBottom > y) {
+            continue;
+        }
+        if (activeEdges) {
+            InEdge* test = *testPtr;
+            ActiveEdge* activePtr = activeEdges->begin() - 1;
+            ActiveEdge* lastActive = activeEdges->end();
+            while (++activePtr != lastActive) {
+                if (activePtr->fWorkEdge.fEdge == test) {
+                    activeEdges->remove(activePtr - activeEdges->begin());
+                    break;
+                }
+            }
+        }
+        if (testPtr == currentPtr) {
+            ++currentPtr;
+        }
+    }
+    return currentPtr;
+}
+
+// OPTIMIZE: inline?
+static HorizontalEdge** advanceHorizontal(HorizontalEdge** edge, SkScalar y) {
+    while ((*edge)->fY < y) {
+        ++edge;
+    }
+    return edge;
+}
+
+// compute bottom taking into account any intersected edges
+static SkScalar computeInterceptBottom(SkTDArray<ActiveEdge>& activeEdges,
+        SkScalar y, SkScalar bottom) {
+    ActiveEdge* activePtr = activeEdges.begin() - 1;
+    ActiveEdge* lastActive = activeEdges.end();
+    while (++activePtr != lastActive) {
+        const InEdge* test = activePtr->fWorkEdge.fEdge;
+        if (!test->fContainsIntercepts) {
+            continue;
+        }
+        WorkEdge wt;
+        wt.init(test);
+        do {
+            const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
+            if (intercepts.fTopIntercepts > 1) {
+                SkScalar yTop = wt.fPts[0].fY;
+                if (yTop > y && bottom > yTop) {
+                    bottom = yTop;
+                }
+            }
+            if (intercepts.fBottomIntercepts > 1) {
+                SkScalar yBottom = wt.fPts[wt.verb()].fY;
+                if (yBottom > y && bottom > yBottom) {
+                    bottom = yBottom;
+                }
+            }
+            const SkTDArray<double>& fTs = intercepts.fTs;
+            size_t count = fTs.count();
+            for (size_t index = 0; index < count; ++index) {
+                SkScalar yIntercept;
+                switch (wt.verb()) {
+                    case SkPath::kLine_Verb: {
+                        yIntercept = LineYAtT(wt.fPts, fTs[index]);
+                        break;
+                    }
+                    case SkPath::kQuad_Verb: {
+                        yIntercept = QuadYAtT(wt.fPts, fTs[index]);
+                        break;
+                    }
+                    case SkPath::kCubic_Verb: {
+                        yIntercept = CubicYAtT(wt.fPts, fTs[index]);
+                        break;
+                    }
+                    default:
+                        SkASSERT(0); // should never get here
+                }
+                if (yIntercept > y && bottom > yIntercept) {
+                    bottom = yIntercept;
+                }
+            }
+        } while (wt.advance());
+    }
+#if DEBUG_BOTTOM
+    SkDebugf("%s bottom=%1.9g\n", __FUNCTION__, bottom);
+#endif
+    return bottom;
+}
+
+static SkScalar findBottom(InEdge** currentPtr,
+        InEdge** edgeListEnd, SkTDArray<ActiveEdge>* activeEdges, SkScalar y,
+        bool /*asFill*/, InEdge**& testPtr) {
+    InEdge* current = *currentPtr;
+    SkScalar bottom = current->fBounds.fBottom;
+
+    // find the list of edges that cross y
+    InEdge* test = *testPtr;
+    while (testPtr != edgeListEnd) {
+        SkScalar testTop = test->fBounds.fTop;
+        if (bottom <= testTop) {
+            break;
+        }
+        SkScalar testBottom = test->fBounds.fBottom;
+        // OPTIMIZATION: Shortening the bottom is only interesting when filling
+        // and when the edge is to the left of a longer edge. If it's a framing
+        // edge, or part of the right, it won't effect the longer edges.
+        if (testTop > y) {
+            bottom = testTop;
+            break;
+        }
+        if (y < testBottom) {
+            if (bottom > testBottom) {
+                bottom = testBottom;
+            }
+            if (activeEdges) {
+                addToActive(*activeEdges, test);
+            }
+        }
+        test = *++testPtr;
+    }
+#if DEBUG_BOTTOM
+    SkDebugf("%s %d bottom=%1.9g\n", __FUNCTION__, activeEdges ? 2 : 1, bottom);
+#endif
+    return bottom;
+}
+
+static void makeEdgeList(SkTArray<InEdge>& edges, InEdge& edgeSentinel,
+        SkTDArray<InEdge*>& edgeList) {
+    size_t edgeCount = edges.count();
+    if (edgeCount == 0) {
+        return;
+    }
+    int id = 0;
+    for (size_t index = 0; index < edgeCount; ++index) {
+        InEdge& edge = edges[index];
+        if (!edge.fWinding) {
+            continue;
+        }
+        edge.fID = ++id;
+        *edgeList.append() = &edge;
+    }
+    *edgeList.append() = &edgeSentinel;
+    QSort<InEdge>(edgeList.begin(), edgeList.end() - 1);
+}
+
+static void makeHorizontalList(SkTDArray<HorizontalEdge>& edges,
+        HorizontalEdge& edgeSentinel, SkTDArray<HorizontalEdge*>& edgeList) {
+    size_t edgeCount = edges.count();
+    if (edgeCount == 0) {
+        return;
+    }
+    for (size_t index = 0; index < edgeCount; ++index) {
+        *edgeList.append() = &edges[index];
+    }
+    edgeSentinel.fLeft = edgeSentinel.fRight = edgeSentinel.fY = SK_ScalarMax;
+    *edgeList.append() = &edgeSentinel;
+    QSort<HorizontalEdge>(edgeList.begin(), edgeList.end() - 1);
+}
+
+static void skipCoincidence(int lastWinding, int winding, int windingMask,
+            ActiveEdge* activePtr, ActiveEdge* firstCoincident) {
+    if (((lastWinding & windingMask) == 0) ^ (winding & windingMask) != 0) {
+        return;
+    }
+    // FIXME: ? shouldn't this be if (lastWinding & windingMask) ?
+    if (lastWinding) {
+#if DEBUG_ADJUST_COINCIDENT
+        SkDebugf("%s edge=%d 1 set skip=false\n", __FUNCTION__, activePtr->ID());
+#endif
+        activePtr->fSkip = false;
+    } else {
+#if DEBUG_ADJUST_COINCIDENT
+        SkDebugf("%s edge=%d 2 set skip=false\n", __FUNCTION__, firstCoincident->ID());
+#endif
+        firstCoincident->fSkip = false;
+    }
+}
+
+static void sortHorizontal(SkTDArray<ActiveEdge>& activeEdges,
+        SkTDArray<ActiveEdge*>& edgeList, SkScalar y) {
+    size_t edgeCount = activeEdges.count();
+    if (edgeCount == 0) {
+        return;
+    }
+#if DEBUG_SORT_HORIZONTAL
+    const int tab = 3; // FIXME: debugging only
+    SkDebugf("%s y=%1.9g\n", __FUNCTION__, y);
+#endif
+    size_t index;
+    for (index = 0; index < edgeCount; ++index) {
+        ActiveEdge& activeEdge = activeEdges[index];
+        do {
+            activeEdge.calcLeft(y);
+            // skip segments that don't span y
+            if (activeEdge.fAbove != activeEdge.fBelow) {
+                break;
+            }
+            if (activeEdge.fDone) {
+#if DEBUG_SORT_HORIZONTAL
+                SkDebugf("%*s edge=%d done\n", tab, "", activeEdge.ID());
+#endif
+                goto nextEdge;
+            }
+#if DEBUG_SORT_HORIZONTAL
+            SkDebugf("%*s edge=%d above==below\n", tab, "", activeEdge.ID());
+#endif
+        } while (activeEdge.advanceT() || activeEdge.advance());
+#if DEBUG_SORT_HORIZONTAL
+        SkDebugf("%*s edge=%d above=(%1.9g,%1.9g) (%1.9g) below=(%1.9g,%1.9g)"
+                " (%1.9g)\n", tab, "", activeEdge.ID(),
+                activeEdge.fAbove.fX, activeEdge.fAbove.fY, activeEdge.fTAbove,
+                activeEdge.fBelow.fX, activeEdge.fBelow.fY, activeEdge.fTBelow);
+#endif
+        activeEdge.fSkip = activeEdge.fCloseCall = activeEdge.fFixBelow = false;
+        *edgeList.append() = &activeEdge;
+nextEdge:
+        ;
+    }
+    QSort<ActiveEdge>(edgeList.begin(), edgeList.end() - 1);
+}
+
+// remove coincident edges
+// OPTIMIZE: remove edges? This is tricky because the current logic expects
+// the winding count to be maintained while skipping coincident edges. In
+// addition to removing the coincident edges, the remaining edges would need
+// to have a different winding value, possibly different per intercept span.
+static SkScalar adjustCoincident(SkTDArray<ActiveEdge*>& edgeList,
+        int windingMask, SkScalar y, SkScalar bottom, OutEdgeBuilder& outBuilder)
+{
+#if DEBUG_ADJUST_COINCIDENT
+    SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
+#endif
+    size_t edgeCount = edgeList.count();
+    if (edgeCount == 0) {
+        return bottom;
+    }
+    ActiveEdge* activePtr, * nextPtr = edgeList[0];
+    size_t index;
+    bool foundCoincident = false;
+    size_t firstIndex = 0;
+    for (index = 1; index < edgeCount; ++index) {
+        activePtr = nextPtr;
+        nextPtr = edgeList[index];
+        if (firstIndex != index - 1 && activePtr->fVerb > SkPath::kLine_Verb
+                && nextPtr->fVerb == SkPath::kLine_Verb
+                && activePtr->isUnordered(nextPtr)) {
+            // swap the line with the curve
+            // back up to the previous edge and retest
+            SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
+            SkASSERT(index > 1);
+            index -= 2;
+            nextPtr = edgeList[index];
+            continue;
+        }
+        bool closeCall = false;
+        activePtr->fCoincident = firstIndex;
+        if (activePtr->isCoincidentWith(nextPtr)
+                || (closeCall = activePtr->tooCloseToCall(nextPtr))) {
+            activePtr->fSkip = nextPtr->fSkip = foundCoincident = true;
+            activePtr->fCloseCall = nextPtr->fCloseCall = closeCall;
+        } else if (activePtr->isUnordered(nextPtr)) {
+            foundCoincident = true;
+        } else {
+            firstIndex = index;
+        }
+    }
+    nextPtr->fCoincident = firstIndex;
+    if (!foundCoincident) {
+        return bottom;
+    }
+    int winding = 0;
+    nextPtr = edgeList[0];
+    for (index = 1; index < edgeCount; ++index) {
+        int priorWinding = winding;
+        winding += activePtr->fWorkEdge.winding();
+        activePtr = nextPtr;
+        nextPtr = edgeList[index];
+        SkASSERT(activePtr == edgeList[index - 1]);
+        SkASSERT(nextPtr == edgeList[index]);
+        if (activePtr->fCoincident != nextPtr->fCoincident) {
+            continue;
+        }
+        // the coincident edges may not have been sorted above -- advance
+        // the edges and resort if needed
+        // OPTIMIZE: if sorting is done incrementally as new edges are added
+        // and not all at once as is done here, fold this test into the
+        // current less than test.
+        while ((!activePtr->fSkip || !nextPtr->fSkip)
+                && activePtr->fCoincident == nextPtr->fCoincident) {
+            if (activePtr->swapUnordered(nextPtr, bottom)) {
+                winding -= activePtr->fWorkEdge.winding();
+                SkASSERT(activePtr == edgeList[index - 1]);
+                SkASSERT(nextPtr == edgeList[index]);
+                SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
+                if (--index == 0) {
+                    winding += activePtr->fWorkEdge.winding();
+                    break;
+                }
+                // back up one
+                activePtr = edgeList[index - 1];
+                continue;
+            }
+            SkASSERT(activePtr == edgeList[index - 1]);
+            SkASSERT(nextPtr == edgeList[index]);
+            break;
+        }
+        if (activePtr->fSkip && nextPtr->fSkip) {
+            if (activePtr->fCloseCall ? activePtr->swapClose(nextPtr,
+                    priorWinding, winding, windingMask)
+                    : activePtr->swapCoincident(nextPtr, bottom)) {
+                winding -= activePtr->fWorkEdge.winding();
+                SkASSERT(activePtr == edgeList[index - 1]);
+                SkASSERT(nextPtr == edgeList[index]);
+                SkTSwap<ActiveEdge*>(edgeList[index - 1], edgeList[index]);
+                SkTSwap<ActiveEdge*>(activePtr, nextPtr);
+                winding += activePtr->fWorkEdge.winding();
+                SkASSERT(activePtr == edgeList[index - 1]);
+                SkASSERT(nextPtr == edgeList[index]);
+            }
+        }
+    }
+    int firstCoincidentWinding = 0;
+    ActiveEdge* firstCoincident = NULL;
+    winding = 0;
+    activePtr = edgeList[0];
+    for (index = 1; index < edgeCount; ++index) {
+        int priorWinding = winding;
+        winding += activePtr->fWorkEdge.winding();
+        nextPtr = edgeList[index];
+        if (activePtr->fSkip && nextPtr->fSkip
+                && activePtr->fCoincident == nextPtr->fCoincident) {
+            if (!firstCoincident) {
+                firstCoincident = activePtr;
+                firstCoincidentWinding = priorWinding;
+            }
+            if (activePtr->fCloseCall) {
+                // If one of the edges has already been added to out as a non
+                // coincident edge, trim it back to the top of this span
+                if (outBuilder.trimLine(y, activePtr->ID())) {
+                    activePtr->addTatYAbove(y);
+            #if DEBUG_ADJUST_COINCIDENT
+                    SkDebugf("%s 1 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
+                            __FUNCTION__, activePtr->ID(), y, activePtr->fYBottom);
+            #endif
+                    activePtr->fYBottom = y;
+                }
+                if (outBuilder.trimLine(y, nextPtr->ID())) {
+                    nextPtr->addTatYAbove(y);
+            #if DEBUG_ADJUST_COINCIDENT
+                    SkDebugf("%s 2 edge=%d y=%1.9g (was fYBottom=%1.9g)\n",
+                            __FUNCTION__, nextPtr->ID(), y, nextPtr->fYBottom);
+            #endif
+                    nextPtr->fYBottom = y;
+                }
+                // add missing t values so edges can be the same length
+                SkScalar testY = activePtr->fBelow.fY;
+                nextPtr->addTatYBelow(testY);
+                if (bottom > testY && testY > y) {
+            #if DEBUG_ADJUST_COINCIDENT
+                    SkDebugf("%s 3 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
+                            __FUNCTION__, activePtr->ID(), testY, bottom);
+            #endif
+                    bottom = testY;
+                }
+                testY = nextPtr->fBelow.fY;
+                activePtr->addTatYBelow(testY);
+                if (bottom > testY && testY > y) {
+            #if DEBUG_ADJUST_COINCIDENT
+                    SkDebugf("%s 4 edge=%d bottom=%1.9g (was bottom=%1.9g)\n",
+                            __FUNCTION__, nextPtr->ID(), testY, bottom);
+            #endif
+                    bottom = testY;
+                }
+            }
+        } else if (firstCoincident) {
+            skipCoincidence(firstCoincidentWinding, winding, windingMask,
+                    activePtr, firstCoincident);
+            firstCoincident = NULL;
+        }
+        activePtr = nextPtr;
+    }
+    if (firstCoincident) {
+        winding += activePtr->fWorkEdge.winding();
+        skipCoincidence(firstCoincidentWinding, winding, windingMask, activePtr,
+                firstCoincident);
+    }
+    // fix up the bottom for close call edges. OPTIMIZATION: maybe this could
+    // be in the loop above, but moved here since loop above reads fBelow and
+    // it felt unsafe to write it in that loop
+    for (index = 0; index < edgeCount; ++index) {
+        (edgeList[index])->fixBelow();
+    }
+    return bottom;
+}
+
+// stitch edge and t range that satisfies operation
+static void stitchEdge(SkTDArray<ActiveEdge*>& edgeList, SkScalar
+#if DEBUG_STITCH_EDGE
+y
+#endif
+,
+        SkScalar bottom, int windingMask, bool fill, OutEdgeBuilder& outBuilder) {
+    int winding = 0;
+    ActiveEdge** activeHandle = edgeList.begin() - 1;
+    ActiveEdge** lastActive = edgeList.end();
+#if DEBUG_STITCH_EDGE
+    const int tab = 7; // FIXME: debugging only
+    SkDebugf("%s y=%1.9g bottom=%1.9g\n", __FUNCTION__, y, bottom);
+#endif
+    while (++activeHandle != lastActive) {
+        ActiveEdge* activePtr = *activeHandle;
+        const WorkEdge& wt = activePtr->fWorkEdge;
+        int lastWinding = winding;
+        winding += wt.winding();
+#if DEBUG_STITCH_EDGE
+        SkDebugf("%*s edge=%d lastWinding=%d winding=%d skip=%d close=%d"
+                " above=%1.9g below=%1.9g\n",
+                tab-4, "", activePtr->ID(), lastWinding,
+                winding, activePtr->fSkip, activePtr->fCloseCall,
+                activePtr->fTAbove, activePtr->fTBelow);
+#endif
+        if (activePtr->done(bottom)) {
+#if DEBUG_STITCH_EDGE
+            SkDebugf("%*s fDone=%d || fYBottom=%1.9g >= bottom\n", tab, "",
+                    activePtr->fDone, activePtr->fYBottom);
+#endif
+            continue;
+        }
+        int opener = (lastWinding & windingMask) == 0;
+        bool closer = (winding & windingMask) == 0;
+        SkASSERT(!opener | !closer);
+        bool inWinding = opener | closer;
+        SkPoint clippedPts[4];
+        const SkPoint* clipped = NULL;
+        bool moreToDo, aboveBottom;
+        do {
+            double currentT = activePtr->t();
+            const SkPoint* points = wt.fPts;
+            double nextT;
+            SkPath::Verb verb = activePtr->fVerb;
+            do {
+                nextT = activePtr->nextT();
+                // FIXME: obtuse: want efficient way to say
+                // !currentT && currentT != 1 || !nextT && nextT != 1
+                if (currentT * nextT != 0 || currentT + nextT != 1) {
+                    // OPTIMIZATION: if !inWinding, we only need
+                    // clipped[1].fY
+                    switch (verb) {
+                        case SkPath::kLine_Verb:
+                            LineSubDivide(points, currentT, nextT, clippedPts);
+                            break;
+                        case SkPath::kQuad_Verb:
+                            QuadSubDivide(points, currentT, nextT, clippedPts);
+                            break;
+                        case SkPath::kCubic_Verb:
+                            CubicSubDivide(points, currentT, nextT, clippedPts);
+                            break;
+                        default:
+                            SkASSERT(0);
+                            break;
+                    }
+                    clipped = clippedPts;
+                } else {
+                    clipped = points;
+                }
+                if (inWinding && !activePtr->fSkip && (fill ? clipped[0].fY
+                        != clipped[verb].fY : clipped[0] != clipped[verb])) {
+#if DEBUG_STITCH_EDGE
+                    SkDebugf("%*s add%s %1.9g,%1.9g %1.9g,%1.9g edge=%d"
+                            " v=%d t=(%1.9g,%1.9g)\n", tab, "",
+                            kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
+                            clipped[verb].fX, clipped[verb].fY,
+                            activePtr->ID(),
+                            activePtr->fWorkEdge.fVerb
+                            - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
+                            currentT, nextT);
+#endif
+                    outBuilder.addCurve(clipped, (SkPath::Verb) verb,
+                            activePtr->fWorkEdge.fEdge->fID,
+                            activePtr->fCloseCall);
+                } else {
+#if DEBUG_STITCH_EDGE
+                    SkDebugf("%*s skip%s %1.9g,%1.9g %1.9g,%1.9g"
+                            " edge=%d v=%d t=(%1.9g,%1.9g)\n", tab, "",
+                            kUVerbStr[verb], clipped[0].fX, clipped[0].fY,
+                            clipped[verb].fX, clipped[verb].fY,
+                            activePtr->ID(),
+                            activePtr->fWorkEdge.fVerb
+                            - activePtr->fWorkEdge.fEdge->fVerbs.begin(),
+                            currentT, nextT);
+#endif
+                }
+            // by advancing fAbove/fBelow, the next call to sortHorizontal
+            // will use these values if they're still valid instead of
+            // recomputing
+                if (clipped[verb].fY > activePtr->fBelow.fY
+                        && bottom >= activePtr->fBelow.fY
+                        && verb == SkPath::kLine_Verb) {
+                    activePtr->fAbove = activePtr->fBelow;
+                    activePtr->fBelow = activePtr->fTangent = clipped[verb];
+                    activePtr->fTAbove = activePtr->fTBelow < 1
+                            ? activePtr->fTBelow : 0;
+                    activePtr->fTBelow = nextT;
+                }
+                currentT = nextT;
+                moreToDo = activePtr->advanceT();
+                activePtr->fYBottom = clipped[verb].fY; // was activePtr->fCloseCall ? bottom :
+
+                // clearing the fSkip/fCloseCall bit here means that trailing edges
+                // fall out of sync, if one edge is long and another is a series of short pieces
+                // if fSkip/fCloseCall is set, need to recompute coincidence/too-close-to-call
+                // after advancing
+                // another approach would be to restrict bottom to smaller part of close call
+                // maybe this is already happening with coincidence when intersection is computed,
+                // and needs to be added to the close call computation as well
+                // this is hard to do because that the bottom is important is not known when
+                // the lines are intersected; only when the computation for edge sorting is done
+                // does the need for new bottoms become apparent.
+                // maybe this is good incentive to scrap the current sort and do an insertion
+                // sort that can take this into consideration when the x value is computed
+
+                // FIXME: initialized in sortHorizontal, cleared here as well so
+                // that next edge is not skipped -- but should skipped edges ever
+                // continue? (probably not)
+                aboveBottom = clipped[verb].fY < bottom;
+                if (clipped[0].fY != clipped[verb].fY) {
+                    activePtr->fSkip = false;
+                    activePtr->fCloseCall = false;
+                    aboveBottom &= !activePtr->fCloseCall;
+                }
+#if DEBUG_STITCH_EDGE
+                 else {
+                    if (activePtr->fSkip || activePtr->fCloseCall) {
+                        SkDebugf("%s skip or close == %1.9g\n", __FUNCTION__,
+                                clippedPts[0].fY);
+                    }
+                }
+#endif
+            } while (moreToDo & aboveBottom);
+        } while ((moreToDo || activePtr->advance()) & aboveBottom);
+    }
+}
+
+#if DEBUG_DUMP
+static void dumpEdgeList(const SkTDArray<InEdge*>& edgeList,
+        const InEdge& edgeSentinel) {
+    InEdge** debugPtr = edgeList.begin();
+    do {
+        (*debugPtr++)->dump();
+    } while (*debugPtr != &edgeSentinel);
+}
+#else
+static void dumpEdgeList(const SkTDArray<InEdge*>& ,
+        const InEdge& ) {
+}
+#endif
+
+void simplify(const SkPath& path, bool asFill, SkPath& simple) {
+    // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
+    int windingMask = (path.getFillType() & 1) ? 1 : -1;
+    simple.reset();
+    simple.setFillType(SkPath::kEvenOdd_FillType);
+    // turn path into list of edges increasing in y
+    // if an edge is a quad or a cubic with a y extrema, note it, but leave it
+    // unbroken. Once we have a list, sort it, then walk the list (walk edges
+    // twice that have y extrema's on top)  and detect crossings -- look for raw
+    // bounds that cross over, then tight bounds that cross
+    SkTArray<InEdge> edges;
+    SkTDArray<HorizontalEdge> horizontalEdges;
+    InEdgeBuilder builder(path, asFill, edges, horizontalEdges);
+    SkTDArray<InEdge*> edgeList;
+    InEdge edgeSentinel;
+    edgeSentinel.reset();
+    makeEdgeList(edges, edgeSentinel, edgeList);
+    SkTDArray<HorizontalEdge*> horizontalList;
+    HorizontalEdge horizontalSentinel;
+    makeHorizontalList(horizontalEdges, horizontalSentinel, horizontalList);
+    InEdge** currentPtr = edgeList.begin();
+    if (!currentPtr) {
+        return;
+    }
+    // find all intersections between edges
+// beyond looking for horizontal intercepts, we need to know if any active edges
+// intersect edges below 'bottom', but above the active edge segment.
+// maybe it makes more sense to compute all intercepts before doing anything
+// else, since the intercept list is long-lived, at least in the current design.
+    SkScalar y = (*currentPtr)->fBounds.fTop;
+    HorizontalEdge** currentHorizontal = horizontalList.begin();
+    do {
+        InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
+        SkScalar bottom = findBottom(currentPtr, edgeList.end(),
+                NULL, y, asFill, lastPtr);
+        if (lastPtr > currentPtr) {
+            if (currentHorizontal) {
+                if ((*currentHorizontal)->fY < SK_ScalarMax) {
+                    addBottomT(currentPtr, lastPtr, currentHorizontal);
+                }
+                currentHorizontal = advanceHorizontal(currentHorizontal, bottom);
+            }
+            addIntersectingTs(currentPtr, lastPtr);
+        }
+        y = bottom;
+        currentPtr = advanceEdges(NULL, currentPtr, lastPtr, y);
+    } while (*currentPtr != &edgeSentinel);
+    // if a quadratic or cubic now has an intermediate T value, see if the Ts
+    // on either side cause the Y values to monotonically increase. If not, split
+    // the curve at the new T.
+
+    // try an alternate approach which does not split curves or stitch edges
+    // (may still need adjustCoincident, though)
+    // the idea is to output non-intersecting contours, then figure out their
+    // respective winding contribution
+    // each contour will need to know whether it is CW or CCW, and then whether
+    // a ray from that contour hits any a contour that contains it. The ray can
+    // move to the left and then arbitrarily move up or down (as long as it never
+    // moves to the right) to find a reference sibling contour or containing
+    // contour. If the contour is part of an intersection, the companion contour
+    // that is part of the intersection can determine the containership.
+    if (builder.containsCurves()) {
+        currentPtr = edgeList.begin();
+        SkTArray<InEdge> splits;
+        do {
+            (*currentPtr)->splitInflectionPts(splits);
+        } while (*++currentPtr != &edgeSentinel);
+        if (splits.count()) {
+            for (int index = 0; index < splits.count(); ++index) {
+                edges.push_back(splits[index]);
+            }
+            edgeList.reset();
+            makeEdgeList(edges, edgeSentinel, edgeList);
+        }
+    }
+    dumpEdgeList(edgeList, edgeSentinel);
+    // walk the sorted edges from top to bottom, computing accumulated winding
+    SkTDArray<ActiveEdge> activeEdges;
+    OutEdgeBuilder outBuilder(asFill);
+    currentPtr = edgeList.begin();
+    y = (*currentPtr)->fBounds.fTop;
+    do {
+        InEdge** lastPtr = currentPtr; // find the edge below the bottom of the first set
+        SkScalar bottom = findBottom(currentPtr, edgeList.end(),
+                &activeEdges, y, asFill, lastPtr);
+        if (lastPtr > currentPtr) {
+            bottom = computeInterceptBottom(activeEdges, y, bottom);
+            SkTDArray<ActiveEdge*> activeEdgeList;
+            sortHorizontal(activeEdges, activeEdgeList, y);
+            bottom = adjustCoincident(activeEdgeList, windingMask, y, bottom,
+                outBuilder);
+            stitchEdge(activeEdgeList, y, bottom, windingMask, asFill, outBuilder);
+        }
+        y = bottom;
+        // OPTIMIZATION: as edges expire, InEdge allocations could be released
+        currentPtr = advanceEdges(&activeEdges, currentPtr, lastPtr, y);
+    } while (*currentPtr != &edgeSentinel);
+    // assemble output path from string of pts, verbs
+    outBuilder.bridge();
+    outBuilder.assemble(simple);
+}
diff --git a/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp b/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp
new file mode 100755
index 0000000..44e6b9d
--- /dev/null
+++ b/experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+
+static void* testSimplify4x4QuadralateralsMain(void* data)
+{
+    SkASSERT(data);
+    State4& state = *(State4*) data;
+    char pathStr[1024];
+    bzero(pathStr, sizeof(pathStr));
+    do {
+        int ax = state.a & 0x03;
+        int ay = state.a >> 2;
+        int bx = state.b & 0x03;
+        int by = state.b >> 2;
+        int cx = state.c & 0x03;
+        int cy = state.c >> 2;
+        int dx = state.d & 0x03;
+        int dy = state.d >> 2;
+        for (int e = 0 ; e < 16; ++e) {
+            int ex = e & 0x03;
+            int ey = e >> 2;
+            for (int f = e ; f < 16; ++f) {
+                int fx = f & 0x03;
+                int fy = f >> 2;
+                for (int g = f ; g < 16; ++g) {
+                    int gx = g & 0x03;
+                    int gy = g >> 2;
+                    for (int h = g ; h < 16; ++h) {
+                        int hx = h & 0x03;
+                        int hy = h >> 2;
+                        SkPath path, out;
+                        path.setFillType(SkPath::kWinding_FillType);
+                        path.moveTo(ax, ay);
+                        path.lineTo(bx, by);
+                        path.lineTo(cx, cy);
+                        path.lineTo(dx, dy);
+                        path.close();
+                        path.moveTo(ex, ey);
+                        path.lineTo(fx, fy);
+                        path.lineTo(gx, gy);
+                        path.lineTo(hx, hy);
+                        path.close();
+                        if (1) {  // gdb: set print elements 400
+                            char* str = pathStr;
+                            str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
+                            str += sprintf(str, "    path.lineTo(%d, %d);\n", bx, by);
+                            str += sprintf(str, "    path.lineTo(%d, %d);\n", cx, cy);
+                            str += sprintf(str, "    path.lineTo(%d, %d);\n", dx, dy);
+                            str += sprintf(str, "    path.close();\n");
+                            str += sprintf(str, "    path.moveTo(%d, %d);\n", ex, ey);
+                            str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
+                            str += sprintf(str, "    path.lineTo(%d, %d);\n", gx, gy);
+                            str += sprintf(str, "    path.lineTo(%d, %d);\n", hx, hy);
+                            str += sprintf(str, "    path.close();\n");
+                        }
+                        outputProgress(state, pathStr, SkPath::kWinding_FillType);
+                        testSimplifyx(path, false, out, state, pathStr);
+                        state.testsRun++;
+                        path.setFillType(SkPath::kEvenOdd_FillType);
+                        outputProgress(state, pathStr, SkPath::kEvenOdd_FillType);
+                        testSimplifyx(path, true, out, state, pathStr);
+                        state.testsRun++;
+                    }
+                }
+            }
+        }
+    } while (runNextTestSet(state));
+    return NULL;
+}
+
+void Simplify4x4QuadralateralsThreaded_Test(int& testsRun)
+{
+    SkDebugf("%s\n", __FUNCTION__);
+#ifdef SK_DEBUG
+    gDebugMaxWindSum = 4; // FIXME: 3?
+    gDebugMaxWindValue = 4;
+#endif
+    const char testStr[] = "testQuadralateral";
+    initializeTests(testStr, sizeof(testStr));
+    int testsStart = testsRun;
+    for (int a = 0; a < 16; ++a) {
+        for (int b = a ; b < 16; ++b) {
+            for (int c = b ; c < 16; ++c) {
+                for (int d = c; d < 16; ++d) {
+                    testsRun += dispatchTest4(testSimplify4x4QuadralateralsMain,
+                            a, b, c, d);
+                }
+                if (!gRunTestsInOneThread) SkDebugf(".");
+            }
+            if (!gRunTestsInOneThread) SkDebugf("%d", b);
+        }
+        if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
+    }
+    testsRun += waitForCompletion();
+    SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
+}
+
+
+static void* testSimplify4x4NondegeneratesMain(void* data) {
+    SkASSERT(data);
+    State4& state = *(State4*) data;
+    char pathStr[1024];
+    bzero(pathStr, sizeof(pathStr));
+    do {
+        int ax = state.a & 0x03;
+        int ay = state.a >> 2;
+        int bx = state.b & 0x03;
+        int by = state.b >> 2;
+        int cx = state.c & 0x03;
+        int cy = state.c >> 2;
+        for (int d = 0; d < 15; ++d) {
+            int dx = d & 0x03;
+            int dy = d >> 2;
+            for (int e = d + 1; e < 16; ++e) {
+                int ex = e & 0x03;
+                int ey = e >> 2;
+                for (int f = d + 1; f < 16; ++f) {
+                    if (e == f) {
+                        continue;
+                    }
+                    int fx = f & 0x03;
+                    int fy = f >> 2;
+                    if ((ex - dx) * (fy - dy) == (ey - dy) * (fx - dx)) {
+                        continue;
+                    }
+                    SkPath path, out;
+                    path.setFillType(SkPath::kWinding_FillType);
+                    path.moveTo(ax, ay);
+                    path.lineTo(bx, by);
+                    path.lineTo(cx, cy);
+                    path.close();
+                    path.moveTo(dx, dy);
+                    path.lineTo(ex, ey);
+                    path.lineTo(fx, fy);
+                    path.close();
+                    if (1) {
+                        char* str = pathStr;
+                        str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
+                        str += sprintf(str, "    path.lineTo(%d, %d);\n", bx, by);
+                        str += sprintf(str, "    path.lineTo(%d, %d);\n", cx, cy);
+                        str += sprintf(str, "    path.close();\n");
+                        str += sprintf(str, "    path.moveTo(%d, %d);\n", dx, dy);
+                        str += sprintf(str, "    path.lineTo(%d, %d);\n", ex, ey);
+                        str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
+                        str += sprintf(str, "    path.close();\n");
+                    }
+                    outputProgress(state, pathStr, SkPath::kWinding_FillType);
+                    testSimplifyx(path, false, out, state, pathStr);
+                    state.testsRun++;
+                    path.setFillType(SkPath::kEvenOdd_FillType);
+                    outputProgress(state, pathStr, SkPath::kEvenOdd_FillType);
+                    testSimplifyx(path, true, out, state, pathStr);
+                    state.testsRun++;
+                }
+            }
+        }
+    } while (runNextTestSet(state));
+    return NULL;
+}
+
+void SimplifyNondegenerate4x4TrianglesThreaded_Test(int& testsRun) {
+    SkDebugf("%s\n", __FUNCTION__);
+#ifdef SK_DEBUG
+    gDebugMaxWindSum = 2;
+    gDebugMaxWindValue = 2;
+#endif
+    const char testStr[] = "testNondegenerate";
+    initializeTests(testStr, sizeof(testStr));
+    int testsStart = testsRun;
+    for (int a = 0; a < 15; ++a) {
+        int ax = a & 0x03;
+        int ay = a >> 2;
+        for (int b = a + 1; b < 16; ++b) {
+            int bx = b & 0x03;
+            int by = b >> 2;
+            for (int c = a + 1; c < 16; ++c) {
+                if (b == c) {
+                    continue;
+                }
+                int cx = c & 0x03;
+                int cy = c >> 2;
+                if ((bx - ax) * (cy - ay) == (by - ay) * (cx - ax)) {
+                    continue;
+                }
+                testsRun += dispatchTest4(testSimplify4x4NondegeneratesMain,
+                        a, b, c, 0);
+            }
+            if (!gRunTestsInOneThread) SkDebugf(".");
+        }
+        if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
+    }
+    testsRun += waitForCompletion();
+    SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
+}
+
+static void* testSimplify4x4DegeneratesMain(void* data) {
+    SkASSERT(data);
+    State4& state = *(State4*) data;
+    char pathStr[1024];
+    bzero(pathStr, sizeof(pathStr));
+    do {
+        int ax = state.a & 0x03;
+        int ay = state.a >> 2;
+        int bx = state.b & 0x03;
+        int by = state.b >> 2;
+        int cx = state.c & 0x03;
+        int cy = state.c >> 2;
+        for (int d = 0; d < 16; ++d) {
+            int dx = d & 0x03;
+            int dy = d >> 2;
+            for (int e = d ; e < 16; ++e) {
+                int ex = e & 0x03;
+                int ey = e >> 2;
+                for (int f = d ; f < 16; ++f) {
+                    int fx = f & 0x03;
+                    int fy = f >> 2;
+                    if (state.d && (ex - dx) * (fy - dy)
+                            != (ey - dy) * (fx - dx)) {
+                        continue;
+                    }
+                    SkPath path, out;
+                    path.setFillType(SkPath::kWinding_FillType);
+                    path.moveTo(ax, ay);
+                    path.lineTo(bx, by);
+                    path.lineTo(cx, cy);
+                    path.close();
+                    path.moveTo(dx, dy);
+                    path.lineTo(ex, ey);
+                    path.lineTo(fx, fy);
+                    path.close();
+                    if (1) {
+                        char* str = pathStr;
+                        str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
+                        str += sprintf(str, "    path.lineTo(%d, %d);\n", bx, by);
+                        str += sprintf(str, "    path.lineTo(%d, %d);\n", cx, cy);
+                        str += sprintf(str, "    path.close();\n");
+                        str += sprintf(str, "    path.moveTo(%d, %d);\n", dx, dy);
+                        str += sprintf(str, "    path.lineTo(%d, %d);\n", ex, ey);
+                        str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
+                        str += sprintf(str, "    path.close();\n");
+                    }
+                    outputProgress(state, pathStr, SkPath::kWinding_FillType);
+                    testSimplifyx(path, false, out, state, pathStr);
+                    state.testsRun++;
+                    path.setFillType(SkPath::kEvenOdd_FillType);
+                    outputProgress(state, pathStr, SkPath::kEvenOdd_FillType);
+                    testSimplifyx(path, true, out, state, pathStr);
+                    state.testsRun++;
+                }
+            }
+        }
+    } while (runNextTestSet(state));
+    return NULL;
+}
+
+void SimplifyDegenerate4x4TrianglesThreaded_Test(int& testsRun) {
+    SkDebugf("%s\n", __FUNCTION__);
+#ifdef SK_DEBUG
+    gDebugMaxWindSum = 2;
+    gDebugMaxWindValue = 2;
+#endif
+    const char testStr[] = "testDegenerate";
+    initializeTests(testStr, sizeof(testStr));
+    int testsStart = testsRun;
+    for (int a = 0; a < 16; ++a) {
+        int ax = a & 0x03;
+        int ay = a >> 2;
+        for (int b = a ; b < 16; ++b) {
+            int bx = b & 0x03;
+            int by = b >> 2;
+            for (int c = a ; c < 16; ++c) {
+                int cx = c & 0x03;
+                int cy = c >> 2;
+                bool abcIsATriangle = (bx - ax) * (cy - ay) != (by - ay) * (cx - ax);
+                testsRun += dispatchTest4(testSimplify4x4DegeneratesMain,
+                        a, b, c, abcIsATriangle);
+            }
+            if (!gRunTestsInOneThread) SkDebugf(".");
+        }
+        if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
+    }
+    testsRun += waitForCompletion();
+    SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
+}
+
diff --git a/experimental/Intersection/EdgeWalkerPolygons_Mismatches.cpp b/experimental/Intersection/EdgeWalkerPolygons_Mismatches.cpp
new file mode 100644
index 0000000..a20e2e6
--- /dev/null
+++ b/experimental/Intersection/EdgeWalkerPolygons_Mismatches.cpp
@@ -0,0 +1,1622 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "SkBitmap.h"
+
+// edges that didn't match
+struct misMatch {
+    SkPath::FillType fType;
+    int a, b, c, d, e, f, g, h;
+} misMatches[] = {
+/*
+___________________________________|___________________________________
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx__|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx__
+__xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx___|__xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx___
+__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____|__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____
+___xxxxxxxxxx__xxxxxxxxxxxxxx______|___xxxxxxxxxx__xxxxxxxxxxxxxx______
+___xxxxxxx______xxxxxxxxxxx________|___xxxxxxx______xxxxxxxxxxx________
+____xxx__________xxxxxxxxx_________|____xxx__________xxxxxxxxx_________
+____x_____________xxxxxxx__________|____x_____________xxxxxxx__________
+__xx_______________xxxx____________|__xxx______________xxxx____________
+___xx_______________xx_____________|___xx_______________xx_____________
+____________________x______________|____________________x______________
+______x____________xxx_____________|______x____________xxx_____________
+______xxx_________xxxxx____________|______xxx_________xxxxx____________
+_______xxxx_____xxxxxxxx___________|_______xxxx_____xxxxxxxx___________
+_______xxxxxx__xxxxxxxxx___________|_______xxxxxx__xxxxxxxxxx__________
+________xxxxx__xxxxxxxxxx__________|________xxxxx__xxxxxxxxxx__________
+________xxxx_____xxxxxxxxx_________|________xxxx_____xxxxxxxxx_________
+_________xxx_______xxxxxxxx________|_________xxx_______xxxxxxxx________
+_________xxx_________xxxxxxx_______|_________xxx_________xxxxxxx_______
+__________xx____________xxxxx______|__________xx____________xxxxx______
+__________xx______________xxxx_____|__________xx______________xxxx_____
+___________x________________xxx____|___________x________________xxx____
+___________x__________________xx___|___________x__________________xx___
+___________x____________________x__|___________x____________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 0, 3, 9, 13, 1, 2, 4, 15 },
+/*
+___________________________________|___________________________________
+_xxxxxxxxxxx_________xxxxxxxxxxxx__|_xxxxxxxxxxx_________xxxxxxxxxxxx__
+__xxxxxxxxxxx______xxxxxxxxxxxxx___|__xxxxxxxxxxx______xxxxxxxxxxxxx___
+__xxxxxxxxxxxx__xxxxxxxxxxxxxx_____|__xxxxxxxxxxxx__xxxxxxxxxxxxxx_____
+___xxxxxxxxxx__xxxxxxxxxxxxxx______|___xxxxxxxxxx__xxxxxxxxxxxxxx______
+___xxxxxxx______xxxxxxxxxxx________|___xxxxxxx______xxxxxxxxxxx________
+____xxx__________xxxxxxxxx_________|____xxx__________xxxxxxxxx_________
+____x_____________xxxxxxx__________|____x_____________xxxxxxx__________
+__xx_______________xxxx____________|__xxx______________xxxx____________
+___xx_______________xx_____________|___xx_______________xx_____________
+____________________x______________|____________________x______________
+______x____________xxx_____________|______x____________xxx_____________
+______xxx_________xxxxx____________|______xxx_________xxxxx____________
+_______xxxx_____xxxxxxxx___________|_______xxxx_____xxxxxxxx___________
+_______xxxxxx__xxxxxxxxx___________|_______xxxxxx__xxxxxxxxxx__________
+________xxxxx__xxxxxxxxxx__________|________xxxxx__xxxxxxxxxx__________
+________xxxx_____xxxxxxxxx_________|________xxxx_____xxxxxxxxx_________
+_________xxx_______xxxxxxxx________|_________xxx_______xxxxxxxx________
+_________xxx_________xxxxxxx_______|_________xxx_________xxxxxxx_______
+__________xx____________xxxxx______|__________xx____________xxxxx______
+__________xx______________xxxx_____|__________xx______________xxxx_____
+___________x________________xxx____|___________x________________xxx____
+___________x__________________xx___|___________x__________________xx___
+___________x____________________x__|___________x____________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 0, 3, 9, 13, 1, 2, 4, 15 },
+/*
+___________________________________|___________________________________
+_x__________xxxxxxxxx______________|_x__________xxxxxxxxx______________
+__xxx________xxxxxx________________|__xxx________xxxxxx________________
+__xxxxxx______xx___________________|__xxxxxx______xx___________________
+___xxxxxxxx__xx____________________|___xxxxxxxx__xx____________________
+___xxxxxxx____xx___________________|___xxxxxxx____xx___________________
+____xxx_________x__________________|____xxx_________x__________________
+____x_____________x________________|____x_____________x________________
+__xx_______________xxx_____________|__xxx______________xxx_____________
+___xx_______________xx_____________|___xx_______________xx_____________
+____________________x______________|____________________x______________
+______x____________xxx_____________|______x____________xxx_____________
+______xxx_________xxxxx____________|______xxx_________xxxxx____________
+_______xxxx_____xxxxxxxx___________|_______xxxx_____xxxxxxxx___________
+_______xxxxxx__xxxxxxxxx___________|_______xxxxxx__xxxxxxxxxx__________
+________xxxxx__xxxxxxxxxx__________|________xxxxx__xxxxxxxxxx__________
+________xxxx_____xxxxxxxxx_________|________xxxx_____xxxxxxxxx_________
+_________xxx_______xxxxxxxx________|_________xxx_______xxxxxxxx________
+_________xxx_________xxxxxxx_______|_________xxx_________xxxxxxx_______
+__________xx____________xxxxx______|__________xx____________xxxxx______
+__________xx______________xxxx_____|__________xx______________xxxx_____
+___________x________________xxx____|___________x________________xxx____
+___________x__________________xx___|___________x__________________xx___
+___________x____________________x__|___________x____________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 0, 6, 9, 13, 1, 2, 4, 15 },
+/*
+___________________________________|___________________________________
+_x__________xxxxxxxxx______________|_x__________xxxxxxxxx______________
+__xxx________xxxxxx________________|__xxx________xxxxxx________________
+__xxxxxx______xx___________________|__xxxxxx______xx___________________
+___xxxxxxxx__xx____________________|___xxxxxxxx__xx____________________
+___xxxxxxx____xx___________________|___xxxxxxx____xx___________________
+____xxx_________x__________________|____xxx_________x__________________
+____x_____________x________________|____x_____________x________________
+__xx_______________xxx_____________|__xxx______________xxx_____________
+___xx_______________xx_____________|___xx_______________xx_____________
+____________________x______________|____________________x______________
+______x____________xxx_____________|______x____________xxx_____________
+______xxx_________xxxxx____________|______xxx_________xxxxx____________
+_______xxxx_____xxxxxxxx___________|_______xxxx_____xxxxxxxx___________
+_______xxxxxx__xxxxxxxxx___________|_______xxxxxx__xxxxxxxxxx__________
+________xxxxx__xxxxxxxxxx__________|________xxxxx__xxxxxxxxxx__________
+________xxxx_____xxxxxxxxx_________|________xxxx_____xxxxxxxxx_________
+_________xxx_______xxxxxxxx________|_________xxx_______xxxxxxxx________
+_________xxx_________xxxxxxx_______|_________xxx_________xxxxxxx_______
+__________xx____________xxxxx______|__________xx____________xxxxx______
+__________xx______________xxxx_____|__________xx______________xxxx_____
+___________x________________xxx____|___________x________________xxx____
+___________x__________________xx___|___________x__________________xx___
+___________x____________________x__|___________x____________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 0, 6, 9, 13, 1, 2, 4, 15 },
+/*
+___________________________________|___________________________________
+_xx__________________x_____________|_xx__________________x_____________
+__xxxxx____________xx______________|__xxxxx____________xx______________
+__xxxxxxxxxx____xxxx_______________|__xxxxxxxxxx____xxxx_______________
+___xxxxxxxxxx___xxx________________|___xxxxxxxxxx___xxx________________
+___xxxxxxx_________x_______________|___xxxxxxx_________x_______________
+____xxx___________xxxxxx___________|____xxx___________xxxxxx___________
+____x____________xxxxxxxxxxx_______|____x____________xxxxxxxxxxx_______
+__xx____________xxxxxxxxxxxxxxxxx__|__xxx___________xxxxxxxxxxxxxxxxx__
+__xxx__________xxxxxxxxxxxxxxxx____|__xxx__________xxxxxxxxxxxxxxxx____
+___xx_________xxxxxxxxxxxx_________|___xx_________xxxxxxxxxxxx_________
+_____x_______xxxxxxxxx_____________|_____x_______xxxxxxxxx_____________
+____________xxxxxx_________________|____________xxxxxx_________________
+_______x___xxx_____________________|_______x___xxx_____________________
+_______xx__________________________|_______xx__________________________
+_____xxx_x_________________________|_____xxx_x_________________________
+_xxxxxxxxxxx_______________________|_xxxxxxxxxxx_______________________
+__xxxxx__xx________________________|__xxxxx__xx________________________
+____xx_____________________________|____xx_____________________________
+________xx_________________________|________xx_________________________
+_____x_xxx_________________________|_____x_xxx_________________________
+____x___xxx________________________|____x___xxx________________________
+___x_____xx________________________|___x_____xx________________________
+___________________________________|___________________________________
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 0, 7, 8, 13, 2, 4, 9, 12 },
+/*
+___________________________________|___________________________________
+_xx__________________x_____________|_xx__________________x_____________
+__xxxxx____________xx______________|__xxxxx____________xx______________
+__xxxxxxxxxx____xxxx_______________|__xxxxxxxxxx____xxxx_______________
+___xxxxxxxxxx___xxx________________|___xxxxxxxxxx___xxx________________
+___xxxxxxx_________x_______________|___xxxxxxx_________x_______________
+____xxx___________xxxxxx___________|____xxx___________xxxxxx___________
+____x____________xxxxxxxxxxx_______|____x____________xxxxxxxxxxx_______
+__xx____________xxxxxxxxxxxxxxxxx__|__xxx___________xxxxxxxxxxxxxxxxx__
+__xxx__________xxxxxxxxxxxxxxxx____|__xxx__________xxxxxxxxxxxxxxxx____
+___xx_________xxxxxxxxxxxx_________|___xx_________xxxxxxxxxxxx_________
+_____x_______xxxxxxxxx_____________|_____x_______xxxxxxxxx_____________
+____________xxxxxx_________________|____________xxxxxx_________________
+_______x___xxx_____________________|_______x___xxx_____________________
+_______xx__________________________|_______xx__________________________
+_____xxx_x_________________________|_____xxx_x_________________________
+_xxxxxxxxxxx_______________________|_xxxxxxxxxxx_______________________
+__xxxxx__xx________________________|__xxxxx__xx________________________
+____xx_____________________________|____xx_____________________________
+________xx_________________________|________xx_________________________
+_____x_xxx_________________________|_____x_xxx_________________________
+____x___xxx________________________|____x___xxx________________________
+___x_____xx________________________|___x_____xx________________________
+___________________________________|___________________________________
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 0, 7, 8, 13, 2, 4, 9, 12 },
+/*
+___________________________________|___________________________________
+_x_________x_______________________|_x_________x_______________________
+__x_______xxx______________________|__x_______xxx______________________
+___x____xxxxxx_____________________|___x____xxxxxx_____________________
+____xx_xxxxxxxx____________________|____xx_xxxxxxxx____________________
+_____x_xxxxxxxxx___________________|_____x_xxxxxxxxx___________________
+____xx___xxxxxxxx__________________|____xx___xxxxxxxx__________________
+___xxxx___xxxxxxxx_________________|___xxxx___xxxxxxxx_________________
+_xxxxxxx___xxxxxxxx________________|_xxxxxxx____xxxxxxx________________
+__xxxxxxx____xxxxxxx_______________|__xxxxxxx____xxxxxxx_______________
+___xxxxxxx____xxxxxxx______________|___xxxxxxx____xxxxxxx______________
+_____xxxxxx_____xxxxxx_____________|_____xxxxxx_____xxxxxx_____________
+______xxxxxx_____xxxxxx____________|______xxxxxx_____xxxxxx____________
+________xxxxx______xxxxx___________|________xxxxx______xxxxx___________
+_________xxxxx______xxxx___________|_________xxxxx______xxxx___________
+__________xxxxx______xxxx__________|__________xxxxx______xxxx__________
+____________xxx________xxx_________|____________xxx________xxx_________
+_______________x___________xxxxx___|_______________x___________xxxxx___
+____________________________xxx____|____________________________xxx____
+__________________xx_______________|__________________xx_________x_____
+___________________xxxx_____xx_____|___________________xxxx_____xx_____
+____________________xxxxxx_xxxx____|____________________xxxxxx_xxxx____
+_____________________xxxx____xxx___|_____________________xxxx____xxx___
+______________________xx_______xx__|______________________xx_______xx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 0, 10, 11, 14, 1, 4, 9, 15 },
+/*
+___________________________________|___________________________________
+_x_________x_______________________|_x_________x_______________________
+__x_______xxx______________________|__x_______xxx______________________
+___x____xxxxxx_____________________|___x____xxxxxx_____________________
+____xx_xxxxxxxx____________________|____xx_xxxxxxxx____________________
+_____x_xxxxxxxxx___________________|_____x_xxxxxxxxx___________________
+____xx___xxxxxxxx__________________|____xx___xxxxxxxx__________________
+___xxxx___xxxxxxxx_________________|___xxxx___xxxxxxxx_________________
+_xxxxxxx___xxxxxxxx________________|_xxxxxxx____xxxxxxx________________
+__xxxxxxx____xxxxxxx_______________|__xxxxxxx____xxxxxxx_______________
+___xxxxxxx____xxxxxxx______________|___xxxxxxx____xxxxxxx______________
+_____xxxxxx_____xxxxxx_____________|_____xxxxxx_____xxxxxx_____________
+______xxxxxx_____xxxxxx____________|______xxxxxx_____xxxxxx____________
+________xxxxx______xxxxx___________|________xxxxx______xxxxx___________
+_________xxxxx______xxxx___________|_________xxxxx______xxxx___________
+__________xxxxx______xxxx__________|__________xxxxx______xxxx__________
+____________xxx________xxx_________|____________xxx________xxx_________
+_______________x___________xxxxx___|_______________x___________xxxxx___
+____________________________xxx____|____________________________xxx____
+__________________xx_______________|__________________xx_________x_____
+___________________xxxx_____xx_____|___________________xxxx_____xx_____
+____________________xxxxxx_xxxx____|____________________xxxxxx_xxxx____
+_____________________xxxx____xxx___|_____________________xxxx____xxx___
+______________________xx_______xx__|______________________xx_______xx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 0, 10, 11, 14, 1, 4, 9, 15 },
+/*
+___________________________________|___________________________________
+_____________xxxxxxxxxxxxxxxxxxx___|_____________xxxxxxxxxxxxxxxxxxx___
+___________xx___xxxxxxxxxxx________|___________xx___xxxxxxxxxxx________
+___________xxx_____xxxx____________|___________xxx_____xxxx____________
+__________xxxxx____xxx_____________|__________xxxxx____xxx_____________
+__________xxxxx_xxxxxxxx___________|__________xxxxx_xxxxxxxxx__________
+_________xx______xxxxxxxxxx________|_________x_______xxxxxxxxxx________
+______xxx_________xxxxxxxxxxxx_____|______xxx_________xxxxxxxxxxxx_____
+__xxxxxx___________xxxxxxxxxxxxxx__|__xxxxxx___________xxxxxxxxxxxxxx__
+___xxxxx____________xxxxxxxxxxxx___|___xxxxx____________xxxxxxxxxxxx___
+_____xx______________xxxxxxxxx_____|_____xx______________xxxxxxxxx_____
+______________________xxxxxx_______|______________________xxxxxx_______
+_______xx______________xxx_________|_______xx______________xxx_________
+______xxxxx________________________|______xxxxx________________________
+______xxxxxxx_________xx___________|______xxxxxxx_________xxx__________
+_____xxxxxxxxxx____xxxxxx__________|_____xxxxxxxxxx____xxxxxx__________
+_____xxxxxxxxxxxxxxxxxxxxx_________|_____xxxxxxxxxxxxxxxxxxxxx_________
+____xxxxxxxxxxx____xxxxxxxx________|____xxxxxxxxxxx____xxxxxxxx________
+____xxxxxxxxx________xxxxxxx_______|____xxxxxxxxx________xxxxxxx_______
+___xxxxxxxx_____________xxxxx______|___xxxxxxxx_____________xxxxx______
+___xxxxxx_________________xxxx_____|___xxxxxx_________________xxxx_____
+__xxxxx_____________________xxx____|__xxxxx_____________________xxx____
+__xxx_________________________xx___|__xxx_________________________xx___
+_xx_____________________________x__|_xx_____________________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 1, 7, 12, 1, 3, 4, 15 },
+/*
+___________________________________|___________________________________
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx__|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx__
+__xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx___|__xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx___
+__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____|__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____
+___xxxxxxxxxx__xxxxxxxxxxxxxx______|___xxxxxxxxxx__xxxxxxxxxxxxxx______
+___xxxxxxx______xxxxxxxxxxx________|___xxxxxxx______xxxxxxxxxxx________
+____xxx__________xxxxxxxxx_________|____xxx__________xxxxxxxxx_________
+____x_____________xxxxxxx__________|____x_____________xxxxxxx__________
+__xx_______________xxxx____________|__xxx______________xxxx____________
+___xx_______________xx_____________|___xx_______________xx_____________
+____________________x______________|____________________x______________
+______x____________xxx_____________|______x____________xxx_____________
+______xxx_________xxxxx____________|______xxx_________xxxxx____________
+_______xxxx_____xxxxxxxx___________|_______xxxx_____xxxxxxxx___________
+_______xxxxxx__xxxxxxxxx___________|_______xxxxxx__xxxxxxxxxx__________
+________xxxxx__xxxxxxxxxx__________|________xxxxx__xxxxxxxxxx__________
+________xxxx_____xxxxxxxxx_________|________xxxx_____xxxxxxxxx_________
+_________xxx_______xxxxxxxx________|_________xxx_______xxxxxxxx________
+_________xxx_________xxxxxxx_______|_________xxx_________xxxxxxx_______
+__________xx____________xxxxx______|__________xx____________xxxxx______
+__________xx______________xxxx_____|__________xx______________xxxx_____
+___________x________________xxx____|___________x________________xxx____
+___________x__________________xx___|___________x__________________xx___
+___________x____________________x__|___________x____________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 1, 2, 4, 15, 0, 3, 9, 13 },
+/*
+___________________________________|___________________________________
+_xxxxxxxxxxx_________xxxxxxxxxxxx__|_xxxxxxxxxxx_________xxxxxxxxxxxx__
+__xxxxxxxxxxx______xxxxxxxxxxxxx___|__xxxxxxxxxxx______xxxxxxxxxxxxx___
+__xxxxxxxxxxxx__xxxxxxxxxxxxxx_____|__xxxxxxxxxxxx__xxxxxxxxxxxxxx_____
+___xxxxxxxxxx__xxxxxxxxxxxxxx______|___xxxxxxxxxx__xxxxxxxxxxxxxx______
+___xxxxxxx______xxxxxxxxxxx________|___xxxxxxx______xxxxxxxxxxx________
+____xxx__________xxxxxxxxx_________|____xxx__________xxxxxxxxx_________
+____x_____________xxxxxxx__________|____x_____________xxxxxxx__________
+__xx_______________xxxx____________|__xxx______________xxxx____________
+___xx_______________xx_____________|___xx_______________xx_____________
+____________________x______________|____________________x______________
+______x____________xxx_____________|______x____________xxx_____________
+______xxx_________xxxxx____________|______xxx_________xxxxx____________
+_______xxxx_____xxxxxxxx___________|_______xxxx_____xxxxxxxx___________
+_______xxxxxx__xxxxxxxxx___________|_______xxxxxx__xxxxxxxxxx__________
+________xxxxx__xxxxxxxxxx__________|________xxxxx__xxxxxxxxxx__________
+________xxxx_____xxxxxxxxx_________|________xxxx_____xxxxxxxxx_________
+_________xxx_______xxxxxxxx________|_________xxx_______xxxxxxxx________
+_________xxx_________xxxxxxx_______|_________xxx_________xxxxxxx_______
+__________xx____________xxxxx______|__________xx____________xxxxx______
+__________xx______________xxxx_____|__________xx______________xxxx_____
+___________x________________xxx____|___________x________________xxx____
+___________x__________________xx___|___________x__________________xx___
+___________x____________________x__|___________x____________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 2, 4, 15, 0, 3, 9, 13 },
+/*
+___________________________________|___________________________________
+_x__________xxxxxxxxx______________|_x__________xxxxxxxxx______________
+__xxx________xxxxxx________________|__xxx________xxxxxx________________
+__xxxxxx______xx___________________|__xxxxxx______xx___________________
+___xxxxxxxx__xx____________________|___xxxxxxxx__xx____________________
+___xxxxxxx____xx___________________|___xxxxxxx____xx___________________
+____xxx_________x__________________|____xxx_________x__________________
+____x_____________x________________|____x_____________x________________
+__xx_______________xxx_____________|__xxx______________xxx_____________
+___xx_______________xx_____________|___xx_______________xx_____________
+____________________x______________|____________________x______________
+______x____________xxx_____________|______x____________xxx_____________
+______xxx_________xxxxx____________|______xxx_________xxxxx____________
+_______xxxx_____xxxxxxxx___________|_______xxxx_____xxxxxxxx___________
+_______xxxxxx__xxxxxxxxx___________|_______xxxxxx__xxxxxxxxxx__________
+________xxxxx__xxxxxxxxxx__________|________xxxxx__xxxxxxxxxx__________
+________xxxx_____xxxxxxxxx_________|________xxxx_____xxxxxxxxx_________
+_________xxx_______xxxxxxxx________|_________xxx_______xxxxxxxx________
+_________xxx_________xxxxxxx_______|_________xxx_________xxxxxxx_______
+__________xx____________xxxxx______|__________xx____________xxxxx______
+__________xx______________xxxx_____|__________xx______________xxxx_____
+___________x________________xxx____|___________x________________xxx____
+___________x__________________xx___|___________x__________________xx___
+___________x____________________x__|___________x____________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 1, 2, 4, 15, 0, 6, 9, 13 },
+/*
+___________________________________|___________________________________
+_x__________xxxxxxxxx______________|_x__________xxxxxxxxx______________
+__xxx________xxxxxx________________|__xxx________xxxxxx________________
+__xxxxxx______xx___________________|__xxxxxx______xx___________________
+___xxxxxxxx__xx____________________|___xxxxxxxx__xx____________________
+___xxxxxxx____xx___________________|___xxxxxxx____xx___________________
+____xxx_________x__________________|____xxx_________x__________________
+____x_____________x________________|____x_____________x________________
+__xx_______________xxx_____________|__xxx______________xxx_____________
+___xx_______________xx_____________|___xx_______________xx_____________
+____________________x______________|____________________x______________
+______x____________xxx_____________|______x____________xxx_____________
+______xxx_________xxxxx____________|______xxx_________xxxxx____________
+_______xxxx_____xxxxxxxx___________|_______xxxx_____xxxxxxxx___________
+_______xxxxxx__xxxxxxxxx___________|_______xxxxxx__xxxxxxxxxx__________
+________xxxxx__xxxxxxxxxx__________|________xxxxx__xxxxxxxxxx__________
+________xxxx_____xxxxxxxxx_________|________xxxx_____xxxxxxxxx_________
+_________xxx_______xxxxxxxx________|_________xxx_______xxxxxxxx________
+_________xxx_________xxxxxxx_______|_________xxx_________xxxxxxx_______
+__________xx____________xxxxx______|__________xx____________xxxxx______
+__________xx______________xxxx_____|__________xx______________xxxx_____
+___________x________________xxx____|___________x________________xxx____
+___________x__________________xx___|___________x__________________xx___
+___________x____________________x__|___________x____________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 2, 4, 15, 0, 6, 9, 13 },
+/*
+___________________________________|___________________________________
+_______________________xxxxxxxxx___|_______________________xxxxxxxxx___
+___________xx____________xx________|___________xx____________xx________
+___________xxx_________xxx_________|___________xxx_________xxx_________
+__________xxxxx____xxxxxxxxx_______|__________xxxxx____xxxxxxxxx_______
+__________xxxxx_xxxxxxxxxxxxx______|__________xxxxx_xxxxxxxxxxxxx______
+_________xx______xxxxxxxxxxxxx_____|_________x_______xxxxxxxxxxxxx_____
+______xxx_________xxxxxxxxxxxxxx___|______xxx_________xxxxxxxxxxxxxx___
+__xxxxxx___________xxxxxxxxxxxxxx__|__xxxxxx___________xxxxxxxxxxxxxx__
+___xxxxx____________xxxxxxxxxxxx___|___xxxxx____________xxxxxxxxxxxx___
+_____xx______________xxxxxxxxx_____|_____xx______________xxxxxxxxx_____
+______________________xxxxxx_______|______________________xxxxxx_______
+_______xx______________xxx_________|_______xx______________xxx_________
+______xxxxx________________________|______xxxxx________________________
+______xxxxxxx_________xx___________|______xxxxxxx_________xxx__________
+_____xxxxxxxxxx____xxxxxx__________|_____xxxxxxxxxx____xxxxxx__________
+_____xxxxxxxxxxxxxxxxxxxxx_________|_____xxxxxxxxxxxxxxxxxxxxx_________
+____xxxxxxxxxxx____xxxxxxxx________|____xxxxxxxxxxx____xxxxxxxx________
+____xxxxxxxxx________xxxxxxx_______|____xxxxxxxxx________xxxxxxx_______
+___xxxxxxxx_____________xxxxx______|___xxxxxxxx_____________xxxxx______
+___xxxxxx_________________xxxx_____|___xxxxxx_________________xxxx_____
+__xxxxx_____________________xxx____|__xxxxx_____________________xxx____
+__xxx_________________________xx___|__xxx_________________________xx___
+_xx_____________________________x__|_xx_____________________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 2, 7, 12, 1, 3, 4, 15 },
+/*
+___________________________________|___________________________________
+_____________xxxxxxxxxxxxxxxxxxx___|_____________xxxxxxxxxxxxxxxxxxx___
+___________xx___xxxxxxxxxxx________|___________xx___xxxxxxxxxxx________
+___________xxx_____xxxx____________|___________xxx_____xxxx____________
+__________xxxxx____xxx_____________|__________xxxxx____xxx_____________
+__________xxxxx_xxxxxxxx___________|__________xxxxx_xxxxxxxxx__________
+_________xx______xxxxxxxxxx________|_________x_______xxxxxxxxxx________
+______xxx_________xxxxxxxxxxxx_____|______xxx_________xxxxxxxxxxxx_____
+__xxxxxx___________xxxxxxxxxxxxxx__|__xxxxxx___________xxxxxxxxxxxxxx__
+___xxxxx____________xxxxxxxxxxxx___|___xxxxx____________xxxxxxxxxxxx___
+_____xx______________xxxxxxxxx_____|_____xx______________xxxxxxxxx_____
+______________________xxxxxx_______|______________________xxxxxx_______
+_______xx______________xxx_________|_______xx______________xxx_________
+______xxxxx________________________|______xxxxx________________________
+______xxxxxxx_________xx___________|______xxxxxxx_________xxx__________
+_____xxxxxxxxxx____xxxxxx__________|_____xxxxxxxxxx____xxxxxx__________
+_____xxxxxxxxxxxxxxxxxxxxx_________|_____xxxxxxxxxxxxxxxxxxxxx_________
+____xxxxxxxxxxx____xxxxxxxx________|____xxxxxxxxxxx____xxxxxxxx________
+____xxxxxxxxx________xxxxxxx_______|____xxxxxxxxx________xxxxxxx_______
+___xxxxxxxx_____________xxxxx______|___xxxxxxxx_____________xxxxx______
+___xxxxxx_________________xxxx_____|___xxxxxx_________________xxxx_____
+__xxxxx_____________________xxx____|__xxxxx_____________________xxx____
+__xxx_________________________xx___|__xxx_________________________xx___
+_xx_____________________________x__|_xx_____________________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 3, 4, 15, 1, 1, 7, 12 },
+/*
+___________________________________|___________________________________
+_______________________xxxxxxxxx___|_______________________xxxxxxxxx___
+___________xx____________xx________|___________xx____________xx________
+___________xxx_________xxx_________|___________xxx_________xxx_________
+__________xxxxx____xxxxxxxxx_______|__________xxxxx____xxxxxxxxx_______
+__________xxxxx_xxxxxxxxxxxxx______|__________xxxxx_xxxxxxxxxxxxx______
+_________xx______xxxxxxxxxxxxx_____|_________x_______xxxxxxxxxxxxx_____
+______xxx_________xxxxxxxxxxxxxx___|______xxx_________xxxxxxxxxxxxxx___
+__xxxxxx___________xxxxxxxxxxxxxx__|__xxxxxx___________xxxxxxxxxxxxxx__
+___xxxxx____________xxxxxxxxxxxx___|___xxxxx____________xxxxxxxxxxxx___
+_____xx______________xxxxxxxxx_____|_____xx______________xxxxxxxxx_____
+______________________xxxxxx_______|______________________xxxxxx_______
+_______xx______________xxx_________|_______xx______________xxx_________
+______xxxxx________________________|______xxxxx________________________
+______xxxxxxx_________xx___________|______xxxxxxx_________xxx__________
+_____xxxxxxxxxx____xxxxxx__________|_____xxxxxxxxxx____xxxxxx__________
+_____xxxxxxxxxxxxxxxxxxxxx_________|_____xxxxxxxxxxxxxxxxxxxxx_________
+____xxxxxxxxxxx____xxxxxxxx________|____xxxxxxxxxxx____xxxxxxxx________
+____xxxxxxxxx________xxxxxxx_______|____xxxxxxxxx________xxxxxxx_______
+___xxxxxxxx_____________xxxxx______|___xxxxxxxx_____________xxxxx______
+___xxxxxx_________________xxxx_____|___xxxxxx_________________xxxx_____
+__xxxxx_____________________xxx____|__xxxxx_____________________xxx____
+__xxx_________________________xx___|__xxx_________________________xx___
+_xx_____________________________x__|_xx_____________________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 3, 4, 15, 1, 2, 7, 12 },
+/*
+___________________________________|___________________________________
+________________________________xx_|________________________________xx_
+___________xx______________xxxxxxx_|___________xx______________xxxxxxx_
+___________xxx_________xxxxxxxxxxx_|___________xxx_________xxxxxxxxxxx_
+__________xxxxx____xxxxxxxxxxxxxxx_|__________xxxxx____xxxxxxxxxxxxxxx_
+__________xxxxx_xxxxxxxxxxxxxxxxxx_|__________xxxxx_xxxxxxxxxxxxxxxxxx_
+_________xx______xxxxxxxxxxxxxxxxx_|_________x_______xxxxxxxxxxxxxxxxx_
+______xxx_________xxxxxxxxxxxxxxxx_|______xxx_________xxxxxxxxxxxxxxxx_
+__xxxxxx___________xxxxxxxxxxxxxxx_|__xxxxxx___________xxxxxxxxxxxxxxx_
+___xxxxx____________xxxxxxxxxxxx___|___xxxxx____________xxxxxxxxxxxx___
+_____xx______________xxxxxxxxx_____|_____xx______________xxxxxxxxx_____
+______________________xxxxxx_______|______________________xxxxxx_______
+_______xx______________xxx_________|_______xx______________xxx_________
+______xxxxx________________________|______xxxxx________________________
+______xxxxxxx_________xx___________|______xxxxxxx_________xxx__________
+_____xxxxxxxxxx____xxxxxx__________|_____xxxxxxxxxx____xxxxxx__________
+_____xxxxxxxxxxxxxxxxxxxxx_________|_____xxxxxxxxxxxxxxxxxxxxx_________
+____xxxxxxxxxxx____xxxxxxxx________|____xxxxxxxxxxx____xxxxxxxx________
+____xxxxxxxxx________xxxxxxx_______|____xxxxxxxxx________xxxxxxx_______
+___xxxxxxxx_____________xxxxx______|___xxxxxxxx_____________xxxxx______
+___xxxxxx_________________xxxx_____|___xxxxxx_________________xxxx_____
+__xxxxx_____________________xxx____|__xxxxx_____________________xxx____
+__xxx_________________________xx___|__xxx_________________________xx___
+_xx_____________________________x__|_xx_____________________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 3, 4, 15, 1, 3, 7, 12 },
+/*
+___________________________________|___________________________________
+_____________xxxxxxxxxxxxxxxxxxx___|_____________xxxxxxxxxxxxxxxxxxx___
+___________xx___xxxxxxxxxxx________|___________xx___xxxxxxxxxxx________
+___________xxx_____xxxx____________|___________xxx_____xxxx____________
+__________xxxxx____xxx_____________|__________xxxxx____xxx_____________
+__________xxxxx_xxxxxxxx___________|__________xxxxx_xxxxxxxxx__________
+_________xx______xxxxxxxxxx________|_________x_______xxxxxxxxxx________
+______xxx_________xxxxxxxxxxxx_____|______xxx_________xxxxxxxxxxxx_____
+__xxxxxx___________xxxxxxxxxxxxxx__|__xxxxxx___________xxxxxxxxxxxxxx__
+___xxxxx____________xxxxxxxxxxxx___|___xxxxx____________xxxxxxxxxxxx___
+_____xx______________xxxxxxxxx_____|_____xx______________xxxxxxxxx_____
+______________________xxxxxx_______|______________________xxxxxx_______
+_______xx______________xxx_________|_______xx______________xxx_________
+______xxxxx________________________|______xxxxx________________________
+______xxxxxxx_________xx___________|______xxxxxxx_________xxx__________
+_____xxxxxxxxxx____xxxxxx__________|_____xxxxxxxxxx____xxxxxx__________
+_____xxxxxxxxxxxxxxxxxxxxx_________|_____xxxxxxxxxxxxxxxxxxxxx_________
+____xxxxxxxxxxx____xxxxxxxx________|____xxxxxxxxxxx____xxxxxxxx________
+____xxxxxxxxx________xxxxxxx_______|____xxxxxxxxx________xxxxxxx_______
+___xxxxxxxx_____________xxxxx______|___xxxxxxxx_____________xxxxx______
+___xxxxxx_________________xxxx_____|___xxxxxx_________________xxxx_____
+__xxxxx_____________________xxx____|__xxxxx_____________________xxx____
+__xxx_________________________xx___|__xxx_________________________xx___
+_xx_____________________________x__|_xx_____________________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 3, 4, 15, 1, 7, 7, 12 },
+/*
+___________________________________|___________________________________
+_____________xxxxxxxxxxxxxxxxxxx___|_____________xxxxxxxxxxxxxxxxxxx___
+___________xx___xxxxxxxxxxx________|___________xx___xxxxxxxxxxx________
+___________xxx_____xxxx____________|___________xxx_____xxxx____________
+__________xxxxx____xxx_____________|__________xxxxx____xxx_____________
+__________xxxxx_xxxxxxxx___________|__________xxxxx_xxxxxxxxx__________
+_________xx______xxxxxxxxxx________|_________x_______xxxxxxxxxx________
+______xxx_________xxxxxxxxxxxx_____|______xxx_________xxxxxxxxxxxx_____
+__xxxxxx___________xxxxxxxxxxxxxx__|__xxxxxx___________xxxxxxxxxxxxxx__
+___xxxxx____________xxxxxxxxxxxx___|___xxxxx____________xxxxxxxxxxxx___
+_____xx______________xxxxxxxxx_____|_____xx______________xxxxxxxxx_____
+______________________xxxxxx_______|______________________xxxxxx_______
+_______xx______________xxx_________|_______xx______________xxx_________
+______xxxxx________________________|______xxxxx________________________
+______xxxxxxx_________xx___________|______xxxxxxx_________xxx__________
+_____xxxxxxxxxx____xxxxxx__________|_____xxxxxxxxxx____xxxxxx__________
+_____xxxxxxxxxxxxxxxxxxxxx_________|_____xxxxxxxxxxxxxxxxxxxxx_________
+____xxxxxxxxxxx____xxxxxxxx________|____xxxxxxxxxxx____xxxxxxxx________
+____xxxxxxxxx________xxxxxxx_______|____xxxxxxxxx________xxxxxxx_______
+___xxxxxxxx_____________xxxxx______|___xxxxxxxx_____________xxxxx______
+___xxxxxx_________________xxxx_____|___xxxxxx_________________xxxx_____
+__xxxxx_____________________xxx____|__xxxxx_____________________xxx____
+__xxx_________________________xx___|__xxx_________________________xx___
+_xx_____________________________x__|_xx_____________________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 3, 4, 15, 1, 7, 12, 12 },
+/*
+___________________________________|___________________________________
+________________________________xx_|________________________________xx_
+___________xx______________xxxxxxx_|___________xx______________xxxxxxx_
+___________xxx_________xxxxxxxxxxx_|___________xxx_________xxxxxxxxxxx_
+__________xxxxx____xxxxxxxxxxxxxxx_|__________xxxxx____xxxxxxxxxxxxxxx_
+__________xxxxx_xxxxxxxxxxxxxxxxxx_|__________xxxxx_xxxxxxxxxxxxxxxxxx_
+_________xx______xxxxxxxxxxxxxxxxx_|_________x_______xxxxxxxxxxxxxxxxx_
+______xxx_________xxxxxxxxxxxxxxxx_|______xxx_________xxxxxxxxxxxxxxxx_
+__xxxxxx___________xxxxxxxxxxxxxxx_|__xxxxxx___________xxxxxxxxxxxxxxx_
+___xxxxx____________xxxxxxxxxxxx___|___xxxxx____________xxxxxxxxxxxx___
+_____xx______________xxxxxxxxx_____|_____xx______________xxxxxxxxx_____
+______________________xxxxxx_______|______________________xxxxxx_______
+_______xx______________xxx_________|_______xx______________xxx_________
+______xxxxx________________________|______xxxxx________________________
+______xxxxxxx_________xx___________|______xxxxxxx_________xxx__________
+_____xxxxxxxxxx____xxxxxx__________|_____xxxxxxxxxx____xxxxxx__________
+_____xxxxxxxxxxxxxxxxxxxxx_________|_____xxxxxxxxxxxxxxxxxxxxx_________
+____xxxxxxxxxxx____xxxxxxxx________|____xxxxxxxxxxx____xxxxxxxx________
+____xxxxxxxxx________xxxxxxx_______|____xxxxxxxxx________xxxxxxx_______
+___xxxxxxxx_____________xxxxx______|___xxxxxxxx_____________xxxxx______
+___xxxxxx_________________xxxx_____|___xxxxxx_________________xxxx_____
+__xxxxx_____________________xxx____|__xxxxx_____________________xxx____
+__xxx_________________________xx___|__xxx_________________________xx___
+_xx_____________________________x__|_xx_____________________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 3, 7, 12, 1, 3, 4, 15 },
+/*
+___________________________________|___________________________________
+____________xxxxxxxxxxxxxxxxxxxxx__|____________xxxxxxxxxxxxxxxxxxxxx__
+_____________xxxxxxxxxxxxxxxxxxx___|_____________xxxxxxxxxxxxxxxxxxx___
+______________xxxxxxxxx_xxxxxx_____|______________xxxxxxxxx_xxxxxx_____
+_______________xxxxxxxx_xxxxx______|_______________xxxxxxxx_xxxxx______
+________________xxxxxxx__xx________|________________xxxxxxx__xx________
+_________________xxxxxx__x_________|_________________xxxxxx__x_________
+__________________xxxxx__x_________|__________________xxxxx__x_________
+___________________xxxxxxx_________|___________________xxxxxxx_________
+____________________x_xxxxx________|____________________x_xxxxx________
+__________________xx_xxxxxx________|__________________xx_xxxxxx________
+_______________xxxx___xxxxxx_______|_______________xxxx___xxxxxx_______
+____________xxxxxx_____xxxxx_______|____________xxxxxx_____xxxxx_______
+_________xxxxxxx________xxxxx______|_________xxxxxxx________xxxxx______
+_______xxxxxxxx_________xxxxx______|_______xxxxxxxx__________xxxx______
+____xxxxxxxxx____________xxxxx_____|____xxxxxxxxx____________xxxxx_____
+_xxxxxxxxxxx______________xxxx_____|_xxxxxxxxxxx______________xxxx_____
+_____xxxxxx________________xxx_____|_____xxxxxx________________xxx_____
+____________________________xxx____|____________________________xxx____
+________xxxxx________________xx____|________xxxxx________________xx____
+______xxxxxxxxxxxx____________xx___|______xxxxxxxxxxxx____________xx___
+_____xxxxxxxxxxxxxxxxx_________x___|_____xxxxxxxxxxxxxxxxx_________x___
+____xxxxxxxxxxxxxxxxxxxxxx______x__|____xxxxxxxxxxxxxxxxxxxxxx______x__
+__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____|__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 3, 12, 15, 2, 6, 8, 15 },
+/*
+___________________________________|___________________________________
+_x_________x_______________________|_x_________x_______________________
+__x_______xxx______________________|__x_______xxx______________________
+___x____xxxxxx_____________________|___x____xxxxxx_____________________
+____xx_xxxxxxxx____________________|____xx_xxxxxxxx____________________
+_____x_xxxxxxxxx___________________|_____x_xxxxxxxxx___________________
+____xx___xxxxxxxx__________________|____xx___xxxxxxxx__________________
+___xxxx___xxxxxxxx_________________|___xxxx___xxxxxxxx_________________
+_xxxxxxx___xxxxxxxx________________|_xxxxxxx____xxxxxxx________________
+__xxxxxxx____xxxxxxx_______________|__xxxxxxx____xxxxxxx_______________
+___xxxxxxx____xxxxxxx______________|___xxxxxxx____xxxxxxx______________
+_____xxxxxx_____xxxxxx_____________|_____xxxxxx_____xxxxxx_____________
+______xxxxxx_____xxxxxx____________|______xxxxxx_____xxxxxx____________
+________xxxxx______xxxxx___________|________xxxxx______xxxxx___________
+_________xxxxx______xxxx___________|_________xxxxx______xxxx___________
+__________xxxxx______xxxx__________|__________xxxxx______xxxx__________
+____________xxx________xxx_________|____________xxx________xxx_________
+_______________x___________xxxxx___|_______________x___________xxxxx___
+____________________________xxx____|____________________________xxx____
+__________________xx_______________|__________________xx_________x_____
+___________________xxxx_____xx_____|___________________xxxx_____xx_____
+____________________xxxxxx_xxxx____|____________________xxxxxx_xxxx____
+_____________________xxxx____xxx___|_____________________xxxx____xxx___
+______________________xx_______xx__|______________________xx_______xx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 1, 4, 9, 15, 0, 10, 11, 14 },
+/*
+___________________________________|___________________________________
+_x_________x_______________________|_x_________x_______________________
+__x_______xxx______________________|__x_______xxx______________________
+___x____xxxxxx_____________________|___x____xxxxxx_____________________
+____xx_xxxxxxxx____________________|____xx_xxxxxxxx____________________
+_____x_xxxxxxxxx___________________|_____x_xxxxxxxxx___________________
+____xx___xxxxxxxx__________________|____xx___xxxxxxxx__________________
+___xxxx___xxxxxxxx_________________|___xxxx___xxxxxxxx_________________
+_xxxxxxx___xxxxxxxx________________|_xxxxxxx____xxxxxxx________________
+__xxxxxxx____xxxxxxx_______________|__xxxxxxx____xxxxxxx_______________
+___xxxxxxx____xxxxxxx______________|___xxxxxxx____xxxxxxx______________
+_____xxxxxx_____xxxxxx_____________|_____xxxxxx_____xxxxxx_____________
+______xxxxxx_____xxxxxx____________|______xxxxxx_____xxxxxx____________
+________xxxxx______xxxxx___________|________xxxxx______xxxxx___________
+_________xxxxx______xxxx___________|_________xxxxx______xxxx___________
+__________xxxxx______xxxx__________|__________xxxxx______xxxx__________
+____________xxx________xxx_________|____________xxx________xxx_________
+_______________x___________xxxxx___|_______________x___________xxxxx___
+____________________________xxx____|____________________________xxx____
+__________________xx_______________|__________________xx_________x_____
+___________________xxxx_____xx_____|___________________xxxx_____xx_____
+____________________xxxxxx_xxxx____|____________________xxxxxx_xxxx____
+_____________________xxxx____xxx___|_____________________xxxx____xxx___
+______________________xx_______xx__|______________________xx_______xx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 4, 9, 15, 0, 10, 11, 14 },
+/*
+___________________________________|___________________________________
+____________x__________xxxxxxxxxx__|____________x__________xxxxxxxxxx__
+_____________x_________xxxxxxxxx___|_____________x_________xxxxxxxxx___
+______________x_________xxxxxx_____|______________x_________xxxxxx_____
+_______________xx_______xxxxx______|_______________xx_______xxxxx______
+________________xx_______xx________|________________xx_______xx________
+_________________xxx_____x_________|_________________xxx_____x_________
+__________________xxx____x_________|__________________xxx____x_________
+___________________xxx_xxx_________|___________________xxx_xxx_________
+____________________x_xxxxx________|____________________x_xxxxx________
+__________________xx_xxxxxx________|__________________xx_xxxxxx________
+_______________xxxx___xxxxxx_______|_______________xxxx___xxxxxx_______
+____________xxxxxx_____xxxxx_______|____________xxxxxx_____xxxxx_______
+_________xxxxxxx________xxxxx______|_________xxxxxxx________xxxxx______
+_______xxxxxxxx_________xxxxx______|_______xxxxxxxx__________xxxx______
+____xxxxxxxxx____________xxxxx_____|____xxxxxxxxx____________xxxxx_____
+_xxxxxxxxxxx______________xxxx_____|_xxxxxxxxxxx______________xxxx_____
+_____xxxxxx________________xxx_____|_____xxxxxx________________xxx_____
+____________________________xxx____|____________________________xxx____
+________xxxxx________________xx____|________xxxxx________________xx____
+______xxxxxxxxxxxx____________xx___|______xxxxxxxxxxxx____________xx___
+_____xxxxxxxxxxxxxxxxx_________x___|_____xxxxxxxxxxxxxxxxx_________x___
+____xxxxxxxxxxxxxxxxxxxxxx______x__|____xxxxxxxxxxxxxxxxxxxxxx______x__
+__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____|__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 6, 8, 15, 2, 3, 12, 15 },
+/*
+___________________________________|___________________________________
+____________x________x_____________|____________x________x_____________
+_____________x_____xx______________|_____________x_____xx______________
+______________x_xxxx_______________|______________x_xxxx_______________
+_____________xx__xx________________|_____________xx__xx________________
+__________xxxxxx__x________________|__________xxxxxx__x________________
+_______xxxxxxxxxx_xx_______________|_______xxxxxxxxxx_xx_______________
+_____xxxxxxxxxxxx_xxx______________|_____xxxxxxxxxxxx_xxx______________
+__xxxxxxxxxxxxxx___xxx_____________|__xxxxxxxxxxxxxx___xxx_____________
+____xxxxxxxxxxx_____xx_____________|____xxxxxxxxxxx_____xx_____________
+________xxxxxx______x______________|________xxxxxx______x______________
+____________x______xxx_____________|____________x______xxx_____________
+____________xxxxx_xxxxx____________|____________xxxxx_xxxxx____________
+___________xxxxx_____xxx___________|___________xxxxx_____xxx___________
+__________xxxxx_________x__________|__________xxxxx____________________
+_________xxxx____________xxxx______|_________xxxx____________xxxx______
+________xxxx______________xxxxxxxx_|________xxxx______________xxxxxxxx_
+_______xxxxxxxx____________xxx_____|_______xxxxxxxx____________xxx_____
+______xxxxxxxxxxx________xxx_______|______xxxxxxxxxxx________xxx_______
+_____xxxxxxxxxxxxxxx_xxxxxxxx______|_____xxxxxxxxxxxxxxx_xxxxxxxx______
+_____xxxxxxxxxxxx______xxxxxxx_____|_____xxxxxxxxxxxx______xxxxxxx_____
+____xxxxxxxxx_____________xxxxx____|____xxxxxxxxx_____________xxxxx____
+___xxxxxx____________________xxx___|___xxxxxx____________________xxx___
+__xx___________________________xx__|__xx___________________________xx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 1, 6, 9, 15, 2, 4, 11, 12 },
+/*
+___________________________________|___________________________________
+____________x________x_____________|____________x________x_____________
+_____________x_____xx______________|_____________x_____xx______________
+______________x_xxxx_______________|______________x_xxxx_______________
+_____________xx__xx________________|_____________xx__xx________________
+__________xxxxxx__x________________|__________xxxxxx__x________________
+_______xxxxxxxxxx_xx_______________|_______xxxxxxxxxx_xx_______________
+_____xxxxxxxxxxxx_xxx______________|_____xxxxxxxxxxxx_xxx______________
+__xxxxxxxxxxxxxx___xxx_____________|__xxxxxxxxxxxxxx___xxx_____________
+____xxxxxxxxxxx_____xx_____________|____xxxxxxxxxxx_____xx_____________
+________xxxxxx______x______________|________xxxxxx______x______________
+____________x______xxx_____________|____________x______xxx_____________
+____________xxxxx_xxxxx____________|____________xxxxx_xxxxx____________
+___________xxxxx_____xxx___________|___________xxxxx_____xxx___________
+__________xxxxx_________x__________|__________xxxxx____________________
+_________xxxx____________xxxx______|_________xxxx____________xxxx______
+________xxxx______________xxxxxxxx_|________xxxx______________xxxxxxxx_
+_______xxxxxxxx____________xxx_____|_______xxxxxxxx____________xxx_____
+______xxxxxxxxxxx________xxx_______|______xxxxxxxxxxx________xxx_______
+_____xxxxxxxxxxxxxxx_xxxxxxxx______|_____xxxxxxxxxxxxxxx_xxxxxxxx______
+_____xxxxxxxxxxxx______xxxxxxx_____|_____xxxxxxxxxxxx______xxxxxxx_____
+____xxxxxxxxx_____________xxxxx____|____xxxxxxxxx_____________xxxxx____
+___xxxxxx____________________xxx___|___xxxxxx____________________xxx___
+__xx___________________________xx__|__xx___________________________xx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 6, 9, 15, 2, 4, 11, 12 },
+/*
+___________________________________|___________________________________
+_____________xxxxxxxxxxxxxxxxxxx___|_____________xxxxxxxxxxxxxxxxxxx___
+___________xx___xxxxxxxxxxx________|___________xx___xxxxxxxxxxx________
+___________xxx_____xxxx____________|___________xxx_____xxxx____________
+__________xxxxx____xxx_____________|__________xxxxx____xxx_____________
+__________xxxxx_xxxxxxxx___________|__________xxxxx_xxxxxxxxx__________
+_________xx______xxxxxxxxxx________|_________x_______xxxxxxxxxx________
+______xxx_________xxxxxxxxxxxx_____|______xxx_________xxxxxxxxxxxx_____
+__xxxxxx___________xxxxxxxxxxxxxx__|__xxxxxx___________xxxxxxxxxxxxxx__
+___xxxxx____________xxxxxxxxxxxx___|___xxxxx____________xxxxxxxxxxxx___
+_____xx______________xxxxxxxxx_____|_____xx______________xxxxxxxxx_____
+______________________xxxxxx_______|______________________xxxxxx_______
+_______xx______________xxx_________|_______xx______________xxx_________
+______xxxxx________________________|______xxxxx________________________
+______xxxxxxx_________xx___________|______xxxxxxx_________xxx__________
+_____xxxxxxxxxx____xxxxxx__________|_____xxxxxxxxxx____xxxxxx__________
+_____xxxxxxxxxxxxxxxxxxxxx_________|_____xxxxxxxxxxxxxxxxxxxxx_________
+____xxxxxxxxxxx____xxxxxxxx________|____xxxxxxxxxxx____xxxxxxxx________
+____xxxxxxxxx________xxxxxxx_______|____xxxxxxxxx________xxxxxxx_______
+___xxxxxxxx_____________xxxxx______|___xxxxxxxx_____________xxxxx______
+___xxxxxx_________________xxxx_____|___xxxxxx_________________xxxx_____
+__xxxxx_____________________xxx____|__xxxxx_____________________xxx____
+__xxx_________________________xx___|__xxx_________________________xx___
+_xx_____________________________x__|_xx_____________________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 7, 7, 12, 1, 3, 4, 15 },
+/*
+___________________________________|___________________________________
+____________x____________________x_|____________x____________________x_
+_____________xxx_________________x_|_____________xxx_________________x_
+______________xxxxx_____________xx_|______________xxxxx_____________xx_
+_______________xxxxxxx__________xx_|_______________xxxxxxx__________xx_
+________________xxxxxxxx________xx_|________________xxxxxxxx________xx_
+_________________xxxxxxxxxx____xxx_|_________________xxxxxxxxxx____xxx_
+__________________xxxxxxxxxxxx_xxx_|__________________xxxxxxxxxxxx_xxx_
+___________________xxxxxxxxxxx___x_|___________________xxxxxxxxxxx___x_
+____________________xxxxxxxxxx_xxx_|____________________xxxxxxxxxx_xxx_
+_____________________xxxxx___xxxxx_|_____________________xxxxx___xxxxx_
+_____________________________xxxxx_|_____________________________xxxxx_
+__________________xxxxx_____xxxxxx_|__________________xxxxx_____xxxxxx_
+______________xxxxxxxxxx____xxxxxx_|______________xxxxxxxxxx____xxxxxx_
+__________xxxxxxxxxxxxxx___xxxxxxx_|__________xxxxxxxxxxxxxx___xxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_|_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 7, 8, 15, 3, 3, 11, 14 },
+/*
+___________________________________|___________________________________
+____________x___________________x__|____________x___________________x__
+_____________xxx___________xxxxxx__|_____________xxx___________xxxxxx__
+______________xxxxx____xxxxxxxxx___|______________xxxxx____xxxxxxxxx___
+_______________xxxx___xxxxxxxxxx___|_______________xxxx___xxxxxxxxxx___
+_______________x________xxxxxxxx___|_______________x_________xxxxxxx___
+___________xxxxxx__________xxxx____|___________xxxxxx__________xxxx____
+______xxxxxxxxxxxx____________x____|______xxxxxxxxxxxx____________x____
+__xxxxxxxxxxxxxxxxx___________xxx__|__xxxxxxxxxxxxxxxxx___________xxx__
+____xxxxxxxxxxxxxxxx__________x____|____xxxxxxxxxxxxxxxx__________x____
+________xxxxxxxxxxxxx_____xxx______|________xxxxxxxxxxxxx_____xxx______
+____________xxxxxxxxxxxxxxxxx______|_____________xxxxxxxxxxxxxxxx______
+_________________xxxxxxxxxxx_______|_________________xxxxxxxxxxx_______
+______________xxxxxxxxxxxxxx_______|______________xxxxxxxxxxxxxx_______
+__________xxxxxxxxxxxxxx_xx________|__________xxxxxxxxxxxxxxxxx________
+_____xxxxxxxxxxxxxxxxxxxx__xx______|_____xxxxxxxxxxxxxxxxxxxx__xx______
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 1, 7, 8, 15, 3, 4, 11, 14 },
+/*
+___________________________________|___________________________________
+____________x___________________x__|____________x___________________x__
+_____________xxx___________xxxxxx__|_____________xxx___________xxxxxx__
+______________xxxxx____xxxxxxxxx___|______________xxxxx____xxxxxxxxx___
+_______________xxxx___xxxxxxxxxx___|_______________xxxx___xxxxxxxxxx___
+_______________x________xxxxxxxx___|_______________x_________xxxxxxx___
+___________xxxxxx__________xxxx____|___________xxxxxx__________xxxx____
+______xxxxxxxxxxxx____________x____|______xxxxxxxxxxxx____________x____
+__xxxxxxxxxxxxxxxxx___________xxx__|__xxxxxxxxxxxxxxxxx___________xxx__
+____xxxxxxxxxxxxxxxx__________x____|____xxxxxxxxxxxxxxxx__________x____
+________xxxxxxxxxxxxx_____xxx______|________xxxxxxxxxxxxx_____xxx______
+____________xxxxxxxxxxxxxxxxx______|_____________xxxxxxxxxxxxxxxx______
+_________________x_____xxxxx_______|_________________x_____xxxxx_______
+______________xxxxxxx___xxxx_______|______________xxxxxxx___xxxx_______
+__________xxxxxxxxxxxxxx_xx________|__________xxxxxxxxxxxxxxxxx________
+_____xxxxxxxxxxxxxxxxxxxx__xx______|_____xxxxxxxxxxxxxxxxxxxx__xx______
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 7, 8, 15, 3, 4, 11, 14 },
+/*
+___________________________________|___________________________________
+____________x______________________|____________x______________________
+_____________xxx________________x__|_____________xxx________________x__
+______________xxxxx___________xx___|______________xxxxx___________xx___
+_______________xxxxxxx_______xxx___|_______________xxxxxxx_______xxx___
+________________xxxxxxxx___xxxxx___|________________xxxxxxxxx__xxxxx___
+_________________xxxxxxxxx_xxxx____|_________________xxxxxxxxx_xxxx____
+__________________xxxxxxx_____x____|__________________xxxxxxx_____x____
+___________________xxxx_______xxx__|___________________xxxx_______xxx__
+____________________xxxx______x____|____________________xxxx______x____
+_____________________xxxx_xxx______|_____________________xxxx_xxx______
+___________________________xx______|___________________________xx______
+__________________xxxxx____________|__________________xxxxx____________
+______________xxxxxxxxxx____x______|______________xxxxxxxxxx____x______
+__________xxxxxxxxxxxxxx___xxxx____|__________xxxxxxxxxxxxxx___xxxx____
+_____xxxxxxxxxxxxxxxxxxxx__xxxxx___|_____xxxxxxxxxxxxxxxxxxxx__xxxxx___
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 1, 7, 8, 15, 3, 6, 11, 14 },
+/*
+___________________________________|___________________________________
+____________x______________________|____________x______________________
+_____________xxx________________x__|_____________xxx________________x__
+______________xxxxx___________xx___|______________xxxxx___________xx___
+_______________xxxxxxx_______xxx___|_______________xxxxxxx_______xxx___
+________________xxxxxxxx___xxxxx___|________________xxxxxxxxx__xxxxx___
+_________________xxxxxxxxx_xxxx____|_________________xxxxxxxxx_xxxx____
+__________________xxxxxxx_____x____|__________________xxxxxxx_____x____
+___________________xxxx_______xxx__|___________________xxxx_______xxx__
+____________________xxxx______x____|____________________xxxx______x____
+_____________________xxxx_xxx______|_____________________xxxx_xxx______
+___________________________xx______|___________________________xx______
+__________________xxxxx____________|__________________xxxxx____________
+______________xxxxxxxxxx____x______|______________xxxxxxxxxx____x______
+__________xxxxxxxxxxxxxx___xxxx____|__________xxxxxxxxxxxxxx___xxxx____
+_____xxxxxxxxxxxxxxxxxxxx__xxxxx___|_____xxxxxxxxxxxxxxxxxxxx__xxxxx___
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 7, 8, 15, 3, 6, 11, 14 },
+/*
+___________________________________|___________________________________
+____________x____________________x_|____________x____________________x_
+_____________xxx_________________x_|_____________xxx_________________x_
+______________xxxxx_____________xx_|______________xxxxx_____________xx_
+_______________xxxxxxx__________xx_|_______________xxxxxxx__________xx_
+________________xxxxxxxx________xx_|________________xxxxxxxx________xx_
+_________________xxxxxxxxxx____xxx_|_________________xxxxxxxxxx____xxx_
+__________________xxxxxxxxxxxx_xxx_|__________________xxxxxxxxxxxx_xxx_
+___________________xxxxxxxxxxx___x_|___________________xxxxxxxxxxx___x_
+____________________xxxxxxxxxx_xxx_|____________________xxxxxxxxxx_xxx_
+_____________________xxxxx___xxxxx_|_____________________xxxxx___xxxxx_
+_____________________________xxxxx_|_____________________________xxxxx_
+__________________xxxxx_____xxxxxx_|__________________xxxxx_____xxxxxx_
+______________xxxxxxxxxx____xxxxxx_|______________xxxxxxxxxx____xxxxxx_
+__________xxxxxxxxxxxxxx___xxxxxxx_|__________xxxxxxxxxxxxxx___xxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_|_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 7, 8, 15, 3, 7, 11, 14 },
+/*
+___________________________________|___________________________________
+____________x______________________|____________x______________________
+_____________xxx___________________|_____________xxx___________________
+______________xxxxx________________|______________xxxxx________________
+_______________xxxxxxx_________x___|_______________xxxxxxx_________x___
+________________xxxxxxxx______xx___|________________xxxxxxxxx_____xx___
+_________________xxxxxxxxxx___x____|_________________xxxxxxxxxx___x____
+__________________xxxxxxxxxxx_x____|__________________xxxxxxxxxxx_x____
+___________________xxxxxxxxx__xxx__|___________________xxxxxxxxx__xxx__
+____________________xxxxxxxx__x____|____________________xxxxxxxx__x____
+_____________________xxxxx_xx______|_____________________xxxxx_xx______
+__________________________xxx______|__________________________xxx______
+__________________xxxxx___xx_______|__________________xxxxx___xx_______
+______________xxxxxxxxxx_xxx_______|______________xxxxxxxxxx_xxx_______
+__________xxxxxxxxxxxxxxxxx________|__________xxxxxxxxxxxxxxxxx________
+_____xxxxxxxxxxxxxxxxxxxxxx________|_____xxxxxxxxxxxxxxxxxxxxxx________
+_xxxxxxxxxxxxxxxxxxxxxxxxx_________|_xxxxxxxxxxxxxxxxxxxxxxxxx_________
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 1, 7, 8, 15, 3, 10, 11, 14 },
+/*
+___________________________________|___________________________________
+____________x______________________|____________x______________________
+_____________xxx___________________|_____________xxx___________________
+______________xxxxx________________|______________xxxxx________________
+_______________xxxxxxx_________x___|_______________xxxxxxx_________x___
+________________xxxxxxxx______xx___|________________xxxxxxxxx_____xx___
+_________________xxxxxxxxxx___x____|_________________xxxxxxxxxx___x____
+__________________xxxxxxxxxxx_x____|__________________xxxxxxxxxxx_x____
+___________________xxxxxxxxx__xxx__|___________________xxxxxxxxx__xxx__
+____________________xxxxxxxx__x____|____________________xxxxxxxx__x____
+_____________________xxxxx_xx______|_____________________xxxxx_xx______
+__________________________xxx______|__________________________xxx______
+__________________xxxxx___xx_______|__________________xxxxx___xx_______
+______________xxxxxxxxxx_xxx_______|______________xxxxxxxxxx_xxx_______
+__________xxxxxxxxxxxxxxxxx________|__________xxxxxxxxxxxxxx_xx________
+_____xxxxxxxxxxxxxxxxxx__xx________|_____xxxxxxxxxxxxxxxxxx__xx________
+_xxxxxxxxxxxxxxxxxxxxxx____________|_xxxxxxxxxxxxxxxxxxxxxx____________
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 7, 8, 15, 3, 10, 11, 14 },
+/*
+___________________________________|___________________________________
+____________x____________________x_|____________x____________________x_
+_____________xxx_________________x_|_____________xxx_________________x_
+______________xxxxx_____________xx_|______________xxxxx_____________xx_
+_______________xxxxxxx__________xx_|_______________xxxxxxx__________xx_
+________________xxxxxxxx________xx_|________________xxxxxxxx________xx_
+_________________xxxxxxxxxx____xxx_|_________________xxxxxxxxxx____xxx_
+__________________xxxxxxxxxxxx_xxx_|__________________xxxxxxxxxxxx_xxx_
+___________________xxxxxxxxxxx___x_|___________________xxxxxxxxxxx___x_
+____________________xxxxxxxxxx_xxx_|____________________xxxxxxxxxx_xxx_
+_____________________xxxxx___xxxxx_|_____________________xxxxx___xxxxx_
+_____________________________xxxxx_|_____________________________xxxxx_
+__________________xxxxx_____xxxxxx_|__________________xxxxx_____xxxxxx_
+______________xxxxxxxxxx____xxxxxx_|______________xxxxxxxxxx____xxxxxx_
+__________xxxxxxxxxxxxxx___xxxxxxx_|__________xxxxxxxxxxxxxx___xxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_|_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 7, 8, 15, 3, 11, 11, 14 },
+/*
+___________________________________|___________________________________
+____________x____________________x_|____________x____________________x_
+_____________xxx_________________x_|_____________xxx_________________x_
+______________xxxxx_____________xx_|______________xxxxx_____________xx_
+_______________xxxxxxx__________xx_|_______________xxxxxxx__________xx_
+________________xxxxxxxx________xx_|________________xxxxxxxx________xx_
+_________________xxxxxxxxxx____xxx_|_________________xxxxxxxxxx____xxx_
+__________________xxxxxxxxxxxx_xxx_|__________________xxxxxxxxxxxx_xxx_
+___________________xxxxxxxxxxx___x_|___________________xxxxxxxxxxx___x_
+____________________xxxxxxxxxx_xxx_|____________________xxxxxxxxxx_xxx_
+_____________________xxxxx___xxxxx_|_____________________xxxxx___xxxxx_
+_____________________________xxxxx_|_____________________________xxxxx_
+__________________xxxxx_____xxxxxx_|__________________xxxxx_____xxxxxx_
+______________xxxxxxxxxx____xxxxxx_|______________xxxxxxxxxx____xxxxxx_
+__________xxxxxxxxxxxxxx___xxxxxxx_|__________xxxxxxxxxxxxxx___xxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_|_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 7, 8, 15, 3, 11, 14, 14 },
+/*
+___________________________________|___________________________________
+_____________xxxxxxxxxxxxxxxxxxx___|_____________xxxxxxxxxxxxxxxxxxx___
+___________xx___xxxxxxxxxxx________|___________xx___xxxxxxxxxxx________
+___________xxx_____xxxx____________|___________xxx_____xxxx____________
+__________xxxxx____xxx_____________|__________xxxxx____xxx_____________
+__________xxxxx_xxxxxxxx___________|__________xxxxx_xxxxxxxxx__________
+_________xx______xxxxxxxxxx________|_________x_______xxxxxxxxxx________
+______xxx_________xxxxxxxxxxxx_____|______xxx_________xxxxxxxxxxxx_____
+__xxxxxx___________xxxxxxxxxxxxxx__|__xxxxxx___________xxxxxxxxxxxxxx__
+___xxxxx____________xxxxxxxxxxxx___|___xxxxx____________xxxxxxxxxxxx___
+_____xx______________xxxxxxxxx_____|_____xx______________xxxxxxxxx_____
+______________________xxxxxx_______|______________________xxxxxx_______
+_______xx______________xxx_________|_______xx______________xxx_________
+______xxxxx________________________|______xxxxx________________________
+______xxxxxxx_________xx___________|______xxxxxxx_________xxx__________
+_____xxxxxxxxxx____xxxxxx__________|_____xxxxxxxxxx____xxxxxx__________
+_____xxxxxxxxxxxxxxxxxxxxx_________|_____xxxxxxxxxxxxxxxxxxxxx_________
+____xxxxxxxxxxx____xxxxxxxx________|____xxxxxxxxxxx____xxxxxxxx________
+____xxxxxxxxx________xxxxxxx_______|____xxxxxxxxx________xxxxxxx_______
+___xxxxxxxx_____________xxxxx______|___xxxxxxxx_____________xxxxx______
+___xxxxxx_________________xxxx_____|___xxxxxx_________________xxxx_____
+__xxxxx_____________________xxx____|__xxxxx_____________________xxx____
+__xxx_________________________xx___|__xxx_________________________xx___
+_xx_____________________________x__|_xx_____________________________x__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 1, 7, 12, 12, 1, 3, 4, 15 },
+/*
+___________________________________|___________________________________
+____________x__________xxxxxxxxxx__|____________x__________xxxxxxxxxx__
+_____________x_________xxxxxxxxx___|_____________x_________xxxxxxxxx___
+______________x_________xxxxxx_____|______________x_________xxxxxx_____
+_______________xx_______xxxxx______|_______________xx_______xxxxx______
+________________xx_______xx________|________________xx_______xx________
+_________________xxx_____x_________|_________________xxx_____x_________
+__________________xxx____x_________|__________________xxx____x_________
+___________________xxx_xxx_________|___________________xxx_xxx_________
+____________________x_xxxxx________|____________________x_xxxxx________
+__________________xx_xxxxxx________|__________________xx_xxxxxx________
+_______________xxxx___xxxxxx_______|_______________xxxx___xxxxxx_______
+____________xxxxxx_____xxxxx_______|____________xxxxxx_____xxxxx_______
+_________xxxxxxx________xxxxx______|_________xxxxxxx________xxxxx______
+_______xxxxxxxx_________xxxxx______|_______xxxxxxxx__________xxxx______
+____xxxxxxxxx____________xxxxx_____|____xxxxxxxxx____________xxxxx_____
+_xxxxxxxxxxx______________xxxx_____|_xxxxxxxxxxx______________xxxx_____
+_____xxxxxx________________xxx_____|_____xxxxxx________________xxx_____
+____________________________xxx____|____________________________xxx____
+________xxxxx________________xx____|________xxxxx________________xx____
+______xxxxxxxxxxxx____________xx___|______xxxxxxxxxxxx____________xx___
+_____xxxxxxxxxxxxxxxxx_________x___|_____xxxxxxxxxxxxxxxxx_________x___
+____xxxxxxxxxxxxxxxxxxxxxx______x__|____xxxxxxxxxxxxxxxxxxxxxx______x__
+__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____|__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 2, 3, 12, 15, 1, 6, 8, 15 },
+/*
+___________________________________|___________________________________
+_xx__________________x_____________|_xx__________________x_____________
+__xxxxx____________xx______________|__xxxxx____________xx______________
+__xxxxxxxxxx____xxxx_______________|__xxxxxxxxxx____xxxx_______________
+___xxxxxxxxxx___xxx________________|___xxxxxxxxxx___xxx________________
+___xxxxxxx_________x_______________|___xxxxxxx_________x_______________
+____xxx___________xxxxxx___________|____xxx___________xxxxxx___________
+____x____________xxxxxxxxxxx_______|____x____________xxxxxxxxxxx_______
+__xx____________xxxxxxxxxxxxxxxxx__|__xxx___________xxxxxxxxxxxxxxxxx__
+__xxx__________xxxxxxxxxxxxxxxx____|__xxx__________xxxxxxxxxxxxxxxx____
+___xx_________xxxxxxxxxxxx_________|___xx_________xxxxxxxxxxxx_________
+_____x_______xxxxxxxxx_____________|_____x_______xxxxxxxxx_____________
+____________xxxxxx_________________|____________xxxxxx_________________
+_______x___xxx_____________________|_______x___xxx_____________________
+_______xx__________________________|_______xx__________________________
+_____xxx_x_________________________|_____xxx_x_________________________
+_xxxxxxxxxxx_______________________|_xxxxxxxxxxx_______________________
+__xxxxx__xx________________________|__xxxxx__xx________________________
+____xx_____________________________|____xx_____________________________
+________xx_________________________|________xx_________________________
+_____x_xxx_________________________|_____x_xxx_________________________
+____x___xxx________________________|____x___xxx________________________
+___x_____xx________________________|___x_____xx________________________
+___________________________________|___________________________________
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 2, 4, 9, 12, 0, 7, 8, 13 },
+/*
+___________________________________|___________________________________
+_xx__________________x_____________|_xx__________________x_____________
+__xxxxx____________xx______________|__xxxxx____________xx______________
+__xxxxxxxxxx____xxxx_______________|__xxxxxxxxxx____xxxx_______________
+___xxxxxxxxxx___xxx________________|___xxxxxxxxxx___xxx________________
+___xxxxxxx_________x_______________|___xxxxxxx_________x_______________
+____xxx___________xxxxxx___________|____xxx___________xxxxxx___________
+____x____________xxxxxxxxxxx_______|____x____________xxxxxxxxxxx_______
+__xx____________xxxxxxxxxxxxxxxxx__|__xxx___________xxxxxxxxxxxxxxxxx__
+__xxx__________xxxxxxxxxxxxxxxx____|__xxx__________xxxxxxxxxxxxxxxx____
+___xx_________xxxxxxxxxxxx_________|___xx_________xxxxxxxxxxxx_________
+_____x_______xxxxxxxxx_____________|_____x_______xxxxxxxxx_____________
+____________xxxxxx_________________|____________xxxxxx_________________
+_______x___xxx_____________________|_______x___xxx_____________________
+_______xx__________________________|_______xx__________________________
+_____xxx_x_________________________|_____xxx_x_________________________
+_xxxxxxxxxxx_______________________|_xxxxxxxxxxx_______________________
+__xxxxx__xx________________________|__xxxxx__xx________________________
+____xx_____________________________|____xx_____________________________
+________xx_________________________|________xx_________________________
+_____x_xxx_________________________|_____x_xxx_________________________
+____x___xxx________________________|____x___xxx________________________
+___x_____xx________________________|___x_____xx________________________
+___________________________________|___________________________________
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 2, 4, 9, 12, 0, 7, 8, 13 },
+/*
+___________________________________|___________________________________
+____________x________x_____________|____________x________x_____________
+_____________x_____xx______________|_____________x_____xx______________
+______________x_xxxx_______________|______________x_xxxx_______________
+_____________xx__xx________________|_____________xx__xx________________
+__________xxxxxx__x________________|__________xxxxxx__x________________
+_______xxxxxxxxxx_xx_______________|_______xxxxxxxxxx_xx_______________
+_____xxxxxxxxxxxx_xxx______________|_____xxxxxxxxxxxx_xxx______________
+__xxxxxxxxxxxxxx___xxx_____________|__xxxxxxxxxxxxxx___xxx_____________
+____xxxxxxxxxxx_____xx_____________|____xxxxxxxxxxx_____xx_____________
+________xxxxxx______x______________|________xxxxxx______x______________
+____________x______xxx_____________|____________x______xxx_____________
+____________xxxxx_xxxxx____________|____________xxxxx_xxxxx____________
+___________xxxxx_____xxx___________|___________xxxxx_____xxx___________
+__________xxxxx_________x__________|__________xxxxx____________________
+_________xxxx____________xxxx______|_________xxxx____________xxxx______
+________xxxx______________xxxxxxxx_|________xxxx______________xxxxxxxx_
+_______xxxxxxxx____________xxx_____|_______xxxxxxxx____________xxx_____
+______xxxxxxxxxxx________xxx_______|______xxxxxxxxxxx________xxx_______
+_____xxxxxxxxxxxxxxx_xxxxxxxx______|_____xxxxxxxxxxxxxxx_xxxxxxxx______
+_____xxxxxxxxxxxx______xxxxxxx_____|_____xxxxxxxxxxxx______xxxxxxx_____
+____xxxxxxxxx_____________xxxxx____|____xxxxxxxxx_____________xxxxx____
+___xxxxxx____________________xxx___|___xxxxxx____________________xxx___
+__xx___________________________xx__|__xx___________________________xx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 2, 4, 11, 12, 1, 6, 9, 15 },
+/*
+___________________________________|___________________________________
+____________x________x_____________|____________x________x_____________
+_____________x_____xx______________|_____________x_____xx______________
+______________x_xxxx_______________|______________x_xxxx_______________
+_____________xx__xx________________|_____________xx__xx________________
+__________xxxxxx__x________________|__________xxxxxx__x________________
+_______xxxxxxxxxx_xx_______________|_______xxxxxxxxxx_xx_______________
+_____xxxxxxxxxxxx_xxx______________|_____xxxxxxxxxxxx_xxx______________
+__xxxxxxxxxxxxxx___xxx_____________|__xxxxxxxxxxxxxx___xxx_____________
+____xxxxxxxxxxx_____xx_____________|____xxxxxxxxxxx_____xx_____________
+________xxxxxx______x______________|________xxxxxx______x______________
+____________x______xxx_____________|____________x______xxx_____________
+____________xxxxx_xxxxx____________|____________xxxxx_xxxxx____________
+___________xxxxx_____xxx___________|___________xxxxx_____xxx___________
+__________xxxxx_________x__________|__________xxxxx____________________
+_________xxxx____________xxxx______|_________xxxx____________xxxx______
+________xxxx______________xxxxxxxx_|________xxxx______________xxxxxxxx_
+_______xxxxxxxx____________xxx_____|_______xxxxxxxx____________xxx_____
+______xxxxxxxxxxx________xxx_______|______xxxxxxxxxxx________xxx_______
+_____xxxxxxxxxxxxxxx_xxxxxxxx______|_____xxxxxxxxxxxxxxx_xxxxxxxx______
+_____xxxxxxxxxxxx______xxxxxxx_____|_____xxxxxxxxxxxx______xxxxxxx_____
+____xxxxxxxxx_____________xxxxx____|____xxxxxxxxx_____________xxxxx____
+___xxxxxx____________________xxx___|___xxxxxx____________________xxx___
+__xx___________________________xx__|__xx___________________________xx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 2, 4, 11, 12, 1, 6, 9, 15 },
+/*
+___________________________________|___________________________________
+____________xxxxxxxxxxxxxxxxxxxxx__|____________xxxxxxxxxxxxxxxxxxxxx__
+_____________xxxxxxxxxxxxxxxxxxx___|_____________xxxxxxxxxxxxxxxxxxx___
+______________xxxxxxxxx_xxxxxx_____|______________xxxxxxxxx_xxxxxx_____
+_______________xxxxxxxx_xxxxx______|_______________xxxxxxxx_xxxxx______
+________________xxxxxxx__xx________|________________xxxxxxx__xx________
+_________________xxxxxx__x_________|_________________xxxxxx__x_________
+__________________xxxxx__x_________|__________________xxxxx__x_________
+___________________xxxxxxx_________|___________________xxxxxxx_________
+____________________x_xxxxx________|____________________x_xxxxx________
+__________________xx_xxxxxx________|__________________xx_xxxxxx________
+_______________xxxx___xxxxxx_______|_______________xxxx___xxxxxx_______
+____________xxxxxx_____xxxxx_______|____________xxxxxx_____xxxxx_______
+_________xxxxxxx________xxxxx______|_________xxxxxxx________xxxxx______
+_______xxxxxxxx_________xxxxx______|_______xxxxxxxx__________xxxx______
+____xxxxxxxxx____________xxxxx_____|____xxxxxxxxx____________xxxxx_____
+_xxxxxxxxxxx______________xxxx_____|_xxxxxxxxxxx______________xxxx_____
+_____xxxxxx________________xxx_____|_____xxxxxx________________xxx_____
+____________________________xxx____|____________________________xxx____
+________xxxxx________________xx____|________xxxxx________________xx____
+______xxxxxxxxxxxx____________xx___|______xxxxxxxxxxxx____________xx___
+_____xxxxxxxxxxxxxxxxx_________x___|_____xxxxxxxxxxxxxxxxx_________x___
+____xxxxxxxxxxxxxxxxxxxxxx______x__|____xxxxxxxxxxxxxxxxxxxxxx______x__
+__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____|__xxxxxxxxxxxxxxxxxxxxxxxxxxxx_____
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 2, 6, 8, 15, 1, 3, 12, 15 },
+/*
+___________________________________|___________________________________
+____________x____________________x_|____________x____________________x_
+_____________xxx_________________x_|_____________xxx_________________x_
+______________xxxxx_____________xx_|______________xxxxx_____________xx_
+_______________xxxxxxx__________xx_|_______________xxxxxxx__________xx_
+________________xxxxxxxx________xx_|________________xxxxxxxx________xx_
+_________________xxxxxxxxxx____xxx_|_________________xxxxxxxxxx____xxx_
+__________________xxxxxxxxxxxx_xxx_|__________________xxxxxxxxxxxx_xxx_
+___________________xxxxxxxxxxx___x_|___________________xxxxxxxxxxx___x_
+____________________xxxxxxxxxx_xxx_|____________________xxxxxxxxxx_xxx_
+_____________________xxxxx___xxxxx_|_____________________xxxxx___xxxxx_
+_____________________________xxxxx_|_____________________________xxxxx_
+__________________xxxxx_____xxxxxx_|__________________xxxxx_____xxxxxx_
+______________xxxxxxxxxx____xxxxxx_|______________xxxxxxxxxx____xxxxxx_
+__________xxxxxxxxxxxxxx___xxxxxxx_|__________xxxxxxxxxxxxxx___xxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_|_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 3, 3, 11, 14, 1, 7, 8, 15 },
+/*
+___________________________________|___________________________________
+____________x___________________x__|____________x___________________x__
+_____________xxx___________xxxxxx__|_____________xxx___________xxxxxx__
+______________xxxxx____xxxxxxxxx___|______________xxxxx____xxxxxxxxx___
+_______________xxxx___xxxxxxxxxx___|_______________xxxx___xxxxxxxxxx___
+_______________x________xxxxxxxx___|_______________x_________xxxxxxx___
+___________xxxxxx__________xxxx____|___________xxxxxx__________xxxx____
+______xxxxxxxxxxxx____________x____|______xxxxxxxxxxxx____________x____
+__xxxxxxxxxxxxxxxxx___________xxx__|__xxxxxxxxxxxxxxxxx___________xxx__
+____xxxxxxxxxxxxxxxx__________x____|____xxxxxxxxxxxxxxxx__________x____
+________xxxxxxxxxxxxx_____xxx______|________xxxxxxxxxxxxx_____xxx______
+____________xxxxxxxxxxxxxxxxx______|_____________xxxxxxxxxxxxxxxx______
+_________________xxxxxxxxxxx_______|_________________xxxxxxxxxxx_______
+______________xxxxxxxxxxxxxx_______|______________xxxxxxxxxxxxxx_______
+__________xxxxxxxxxxxxxx_xx________|__________xxxxxxxxxxxxxxxxx________
+_____xxxxxxxxxxxxxxxxxxxx__xx______|_____xxxxxxxxxxxxxxxxxxxx__xx______
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 3, 4, 11, 14, 1, 7, 8, 15 },
+/*
+___________________________________|___________________________________
+____________x___________________x__|____________x___________________x__
+_____________xxx___________xxxxxx__|_____________xxx___________xxxxxx__
+______________xxxxx____xxxxxxxxx___|______________xxxxx____xxxxxxxxx___
+_______________xxxx___xxxxxxxxxx___|_______________xxxx___xxxxxxxxxx___
+_______________x________xxxxxxxx___|_______________x_________xxxxxxx___
+___________xxxxxx__________xxxx____|___________xxxxxx__________xxxx____
+______xxxxxxxxxxxx____________x____|______xxxxxxxxxxxx____________x____
+__xxxxxxxxxxxxxxxxx___________xxx__|__xxxxxxxxxxxxxxxxx___________xxx__
+____xxxxxxxxxxxxxxxx__________x____|____xxxxxxxxxxxxxxxx__________x____
+________xxxxxxxxxxxxx_____xxx______|________xxxxxxxxxxxxx_____xxx______
+____________xxxxxxxxxxxxxxxxx______|_____________xxxxxxxxxxxxxxxx______
+_________________x_____xxxxx_______|_________________x_____xxxxx_______
+______________xxxxxxx___xxxx_______|______________xxxxxxx___xxxx_______
+__________xxxxxxxxxxxxxx_xx________|__________xxxxxxxxxxxxxxxxx________
+_____xxxxxxxxxxxxxxxxxxxx__xx______|_____xxxxxxxxxxxxxxxxxxxx__xx______
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 3, 4, 11, 14, 1, 7, 8, 15 },
+/*
+___________________________________|___________________________________
+____________x______________________|____________x______________________
+_____________xxx________________x__|_____________xxx________________x__
+______________xxxxx___________xx___|______________xxxxx___________xx___
+_______________xxxxxxx_______xxx___|_______________xxxxxxx_______xxx___
+________________xxxxxxxx___xxxxx___|________________xxxxxxxxx__xxxxx___
+_________________xxxxxxxxx_xxxx____|_________________xxxxxxxxx_xxxx____
+__________________xxxxxxx_____x____|__________________xxxxxxx_____x____
+___________________xxxx_______xxx__|___________________xxxx_______xxx__
+____________________xxxx______x____|____________________xxxx______x____
+_____________________xxxx_xxx______|_____________________xxxx_xxx______
+___________________________xx______|___________________________xx______
+__________________xxxxx____________|__________________xxxxx____________
+______________xxxxxxxxxx____x______|______________xxxxxxxxxx____x______
+__________xxxxxxxxxxxxxx___xxxx____|__________xxxxxxxxxxxxxx___xxxx____
+_____xxxxxxxxxxxxxxxxxxxx__xxxxx___|_____xxxxxxxxxxxxxxxxxxxx__xxxxx___
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 3, 6, 11, 14, 1, 7, 8, 15 },
+/*
+___________________________________|___________________________________
+____________x______________________|____________x______________________
+_____________xxx________________x__|_____________xxx________________x__
+______________xxxxx___________xx___|______________xxxxx___________xx___
+_______________xxxxxxx_______xxx___|_______________xxxxxxx_______xxx___
+________________xxxxxxxx___xxxxx___|________________xxxxxxxxx__xxxxx___
+_________________xxxxxxxxx_xxxx____|_________________xxxxxxxxx_xxxx____
+__________________xxxxxxx_____x____|__________________xxxxxxx_____x____
+___________________xxxx_______xxx__|___________________xxxx_______xxx__
+____________________xxxx______x____|____________________xxxx______x____
+_____________________xxxx_xxx______|_____________________xxxx_xxx______
+___________________________xx______|___________________________xx______
+__________________xxxxx____________|__________________xxxxx____________
+______________xxxxxxxxxx____x______|______________xxxxxxxxxx____x______
+__________xxxxxxxxxxxxxx___xxxx____|__________xxxxxxxxxxxxxx___xxxx____
+_____xxxxxxxxxxxxxxxxxxxx__xxxxx___|_____xxxxxxxxxxxxxxxxxxxx__xxxxx___
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 3, 6, 11, 14, 1, 7, 8, 15 },
+/*
+___________________________________|___________________________________
+____________x____________________x_|____________x____________________x_
+_____________xxx_________________x_|_____________xxx_________________x_
+______________xxxxx_____________xx_|______________xxxxx_____________xx_
+_______________xxxxxxx__________xx_|_______________xxxxxxx__________xx_
+________________xxxxxxxx________xx_|________________xxxxxxxx________xx_
+_________________xxxxxxxxxx____xxx_|_________________xxxxxxxxxx____xxx_
+__________________xxxxxxxxxxxx_xxx_|__________________xxxxxxxxxxxx_xxx_
+___________________xxxxxxxxxxx___x_|___________________xxxxxxxxxxx___x_
+____________________xxxxxxxxxx_xxx_|____________________xxxxxxxxxx_xxx_
+_____________________xxxxx___xxxxx_|_____________________xxxxx___xxxxx_
+_____________________________xxxxx_|_____________________________xxxxx_
+__________________xxxxx_____xxxxxx_|__________________xxxxx_____xxxxxx_
+______________xxxxxxxxxx____xxxxxx_|______________xxxxxxxxxx____xxxxxx_
+__________xxxxxxxxxxxxxx___xxxxxxx_|__________xxxxxxxxxxxxxx___xxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_|_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 3, 7, 11, 14, 1, 7, 8, 15 },
+/*
+___________________________________|___________________________________
+____________x______________________|____________x______________________
+_____________xxx___________________|_____________xxx___________________
+______________xxxxx________________|______________xxxxx________________
+_______________xxxxxxx_________x___|_______________xxxxxxx_________x___
+________________xxxxxxxx______xx___|________________xxxxxxxxx_____xx___
+_________________xxxxxxxxxx___x____|_________________xxxxxxxxxx___x____
+__________________xxxxxxxxxxx_x____|__________________xxxxxxxxxxx_x____
+___________________xxxxxxxxx__xxx__|___________________xxxxxxxxx__xxx__
+____________________xxxxxxxx__x____|____________________xxxxxxxx__x____
+_____________________xxxxx_xx______|_____________________xxxxx_xx______
+__________________________xxx______|__________________________xxx______
+__________________xxxxx___xx_______|__________________xxxxx___xx_______
+______________xxxxxxxxxx_xxx_______|______________xxxxxxxxxx_xxx_______
+__________xxxxxxxxxxxxxxxxx________|__________xxxxxxxxxxxxxxxxx________
+_____xxxxxxxxxxxxxxxxxxxxxx________|_____xxxxxxxxxxxxxxxxxxxxxx________
+_xxxxxxxxxxxxxxxxxxxxxxxxx_________|_xxxxxxxxxxxxxxxxxxxxxxxxx_________
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kWinding_FillType, 3, 10, 11, 14, 1, 7, 8, 15 },
+/*
+___________________________________|___________________________________
+____________x______________________|____________x______________________
+_____________xxx___________________|_____________xxx___________________
+______________xxxxx________________|______________xxxxx________________
+_______________xxxxxxx_________x___|_______________xxxxxxx_________x___
+________________xxxxxxxx______xx___|________________xxxxxxxxx_____xx___
+_________________xxxxxxxxxx___x____|_________________xxxxxxxxxx___x____
+__________________xxxxxxxxxxx_x____|__________________xxxxxxxxxxx_x____
+___________________xxxxxxxxx__xxx__|___________________xxxxxxxxx__xxx__
+____________________xxxxxxxx__x____|____________________xxxxxxxx__x____
+_____________________xxxxx_xx______|_____________________xxxxx_xx______
+__________________________xxx______|__________________________xxx______
+__________________xxxxx___xx_______|__________________xxxxx___xx_______
+______________xxxxxxxxxx_xxx_______|______________xxxxxxxxxx_xxx_______
+__________xxxxxxxxxxxxxxxxx________|__________xxxxxxxxxxxxxx_xx________
+_____xxxxxxxxxxxxxxxxxx__xx________|_____xxxxxxxxxxxxxxxxxx__xx________
+_xxxxxxxxxxxxxxxxxxxxxx____________|_xxxxxxxxxxxxxxxxxxxxxx____________
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 3, 10, 11, 14, 1, 7, 8, 15 },
+/*
+___________________________________|___________________________________
+____________x____________________x_|____________x____________________x_
+_____________xxx_________________x_|_____________xxx_________________x_
+______________xxxxx_____________xx_|______________xxxxx_____________xx_
+_______________xxxxxxx__________xx_|_______________xxxxxxx__________xx_
+________________xxxxxxxx________xx_|________________xxxxxxxx________xx_
+_________________xxxxxxxxxx____xxx_|_________________xxxxxxxxxx____xxx_
+__________________xxxxxxxxxxxx_xxx_|__________________xxxxxxxxxxxx_xxx_
+___________________xxxxxxxxxxx___x_|___________________xxxxxxxxxxx___x_
+____________________xxxxxxxxxx_xxx_|____________________xxxxxxxxxx_xxx_
+_____________________xxxxx___xxxxx_|_____________________xxxxx___xxxxx_
+_____________________________xxxxx_|_____________________________xxxxx_
+__________________xxxxx_____xxxxxx_|__________________xxxxx_____xxxxxx_
+______________xxxxxxxxxx____xxxxxx_|______________xxxxxxxxxx____xxxxxx_
+__________xxxxxxxxxxxxxx___xxxxxxx_|__________xxxxxxxxxxxxxx___xxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_|_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 3, 11, 11, 14, 1, 7, 8, 15 },
+/*
+___________________________________|___________________________________
+____________x____________________x_|____________x____________________x_
+_____________xxx_________________x_|_____________xxx_________________x_
+______________xxxxx_____________xx_|______________xxxxx_____________xx_
+_______________xxxxxxx__________xx_|_______________xxxxxxx__________xx_
+________________xxxxxxxx________xx_|________________xxxxxxxx________xx_
+_________________xxxxxxxxxx____xxx_|_________________xxxxxxxxxx____xxx_
+__________________xxxxxxxxxxxx_xxx_|__________________xxxxxxxxxxxx_xxx_
+___________________xxxxxxxxxxx___x_|___________________xxxxxxxxxxx___x_
+____________________xxxxxxxxxx_xxx_|____________________xxxxxxxxxx_xxx_
+_____________________xxxxx___xxxxx_|_____________________xxxxx___xxxxx_
+_____________________________xxxxx_|_____________________________xxxxx_
+__________________xxxxx_____xxxxxx_|__________________xxxxx_____xxxxxx_
+______________xxxxxxxxxx____xxxxxx_|______________xxxxxxxxxx____xxxxxx_
+__________xxxxxxxxxxxxxx___xxxxxxx_|__________xxxxxxxxxxxxxx___xxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_|_____xxxxxxxxxxxxxxxxxxxx__xxxxxxx_
+_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_|_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_
+_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___|_____xxxxxxxxxxxxxxxxxxxxx_xxxxx___
+_________xxxxxxxxxxxxxxxx___xxx____|_________xxxxxxxxxxxxxxxx___xxx____
+_____________xxxxxxxxxxxx__________|_____________xxxxxxxxxxxx____x_____
+__________________xxxxxxx___xx_____|__________________xxxxxxx___xx_____
+______________________xx___xxxx____|______________________xx___xxxx____
+________________________x_xxxxxx___|________________________x_xxxxxx___
+_______________________x______xxx__|_______________________x______xxx__
+___________________________________|___________________________________
+___________________________________|___________________________________
+*/
+{ SkPath::kEvenOdd_FillType, 3, 11, 14, 14, 1, 7, 8, 15 },
+};
+
+size_t misMatchCount = sizeof(misMatches) / sizeof(misMatches[0]);
+
+void TestMismatches();
+
+void TestMismatches() {
+    SkBitmap bitmap;
+    for (size_t index = 0; index < misMatchCount; ++index) {
+        const misMatch& miss = misMatches[index];
+        int ax = miss.a & 0x03;
+        int ay = miss.a >> 2;
+        int bx = miss.b & 0x03;
+        int by = miss.b >> 2;
+        int cx = miss.c & 0x03;
+        int cy = miss.c >> 2;
+        int dx = miss.d & 0x03;
+        int dy = miss.d >> 2;
+        int ex = miss.e & 0x03;
+        int ey = miss.e >> 2;
+        int fx = miss.f & 0x03;
+        int fy = miss.f >> 2;
+        int gx = miss.g & 0x03;
+        int gy = miss.g >> 2;
+        int hx = miss.h & 0x03;
+        int hy = miss.h >> 2;
+        SkPath path, out;
+        path.setFillType(miss.fType);
+        path.moveTo(ax, ay);
+        path.lineTo(bx, by);
+        path.lineTo(cx, cy);
+        path.lineTo(dx, dy);
+        path.close();
+        path.moveTo(ex, ey);
+        path.lineTo(fx, fy);
+        path.lineTo(gx, gy);
+        path.lineTo(hx, hy);
+        path.close();
+        testSimplify(path, true, out, bitmap);
+    }
+}
diff --git a/experimental/Intersection/EdgeWalkerPolygons_Test.cpp b/experimental/Intersection/EdgeWalkerPolygons_Test.cpp
new file mode 100644
index 0000000..5c7f250
--- /dev/null
+++ b/experimental/Intersection/EdgeWalkerPolygons_Test.cpp
@@ -0,0 +1,791 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "SkBitmap.h"
+
+static SkBitmap bitmap;
+
+static void testSimplifyTriangle() {
+    SkPath path, out;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(10,10); // triangle      |\      .
+    path.lineTo(10,30); //               |_\     .
+    path.lineTo(20,30);
+    path.close();
+    path.moveTo(20,10); // triangle        /|
+    path.lineTo(10,30); //                /_|
+    path.lineTo(20,30);
+    path.close();
+    testSimplify(path, true, out, bitmap); // expect |\/|
+                                   //        |__|
+}
+
+static void testSimplifyTriangle3() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(3, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle4() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle5() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.lineTo(2, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle6() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(3, 1);
+    path.lineTo(0, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle7() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.lineTo(0, 2);
+    path.lineTo(0, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle8() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(1, 2);
+    path.lineTo(1, 3);
+    path.lineTo(0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle9() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.lineTo(2, 1);
+    path.lineTo(0, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle10() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 0);
+    path.lineTo(0, 1);
+    path.lineTo(0, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle11() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 2);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 1);
+    path.lineTo(2, 2);
+    path.lineTo(0, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle12() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 2);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(0, 3);
+    path.lineTo(1, 1);
+    path.lineTo(2, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle13() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 3);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(0, 3);
+    path.lineTo(1, 1);
+    path.lineTo(3, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle14() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(0, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle15() {
+    SkPath path, out;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle16() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle17() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 3);
+    path.lineTo(0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle18() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(0, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle19() {
+    SkPath path, out;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.lineTo(2, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle20() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(2, 1);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(3, 2);
+    path.lineTo(0, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle21() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(2, 1);
+    path.lineTo(0, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyDegenerateTriangle1() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyDegenerateTriangle2() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 2);
+    path.lineTo(3, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyWindingParallelogram() {
+    SkPath path, out;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(20,10); // parallelogram  _
+    path.lineTo(30,30); //               \ \      .
+    path.lineTo(40,30); //                \_\     .
+    path.lineTo(30,10);
+    path.close();
+    path.moveTo(20,10); // parallelogram   _
+    path.lineTo(10,30); //                / /
+    path.lineTo(20,30); //               /_/
+    path.lineTo(30,10);
+    path.close();
+    testSimplify(path, true, out, bitmap); // expect   _
+                                   //         / \     .
+}                                  //        /___\    .
+
+static void testSimplifyXorParallelogram() {
+    SkPath path, out;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(20,10); // parallelogram  _
+    path.lineTo(30,30); //               \ \      .
+    path.lineTo(40,30); //                \_\     .
+    path.lineTo(30,10);
+    path.close();
+    path.moveTo(20,10); // parallelogram   _
+    path.lineTo(10,30); //                / /
+    path.lineTo(20,30); //               /_/
+    path.lineTo(30,10);
+    path.close();
+    testSimplify(path, true, out, bitmap); // expect   _
+}                                  //         \ /
+
+static void testSimplifyTriangle2() {
+    SkPath path, out;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(10,10); // triangle      |\      .
+    path.lineTo(10,30); //               |_\     .
+    path.lineTo(20,30);
+    path.close();
+    path.moveTo(10,10); // triangle       _
+    path.lineTo(20,10); //               \ |
+    path.lineTo(20,30); //                \|
+    path.close();                  //         _
+    testSimplify(path, true, out, bitmap); // expect | |
+}                                  //        |_|
+
+#if 0
+static void testPathTriangleRendering() {
+    SkPath one, two;
+    one.moveTo(0, 0);
+    one.lineTo(3, 3);
+    one.lineTo(0, 3);
+    one.lineTo(1, 2);
+    one.close();
+    for (float x = .1f; x <= 2.9ff; x += .1f) {
+        SkDebugf("%s x=%g\n", __FUNCTION__, x);
+        two.moveTo(0, 0);
+        two.lineTo(x, x);
+        two.lineTo(3, 3);
+        two.lineTo(0, 3);
+        two.lineTo(1, 2);
+        two.close();
+        comparePaths(one, two);
+        two.reset();
+    }
+}
+#endif
+
+static void simplify(const char* functionName, const SkPath& path,
+        bool fill, SkPath& out) {
+    if (false) SkDebugf("%s\n", functionName);
+    simplify(path, fill, out);
+}
+
+static void testSimplifySkinnyTriangle1() {
+    for (int x = 1; x < 255; ++x) {
+        SkPath path, out;
+        path.moveTo((x * 101) % 10, 0);
+        path.lineTo((x * 91) % 10, 1000);
+        path.lineTo((x * 71) % 10, 2000);
+        path.lineTo((x * 51) % 10, 3000);
+        path.close();
+        path.moveTo((x * 101) % 20, 0);
+        path.lineTo((x * 91) % 20, 1000);
+        path.lineTo((x * 71) % 20, 2000);
+        path.lineTo((x * 51) % 20, 3000);
+        path.close();
+        path.moveTo((x * 101) % 30, 0);
+        path.lineTo((x * 91) % 30, 1000);
+        path.lineTo((x * 71) % 30, 2000);
+        path.lineTo((x * 51) % 30, 3000);
+        path.close();
+        simplify(path, true, out);
+    }
+}
+
+static void testSimplifySkinnyTriangle2() {
+        SkPath path, out;
+#if 01
+path.moveTo(591.091064f, 627.534851f);
+path.lineTo(541.088135f, 560.707642f);
+path.lineTo(491.085175f, 493.880310f);
+path.lineTo(441.082214f, 427.053101f);
+//path.lineTo(591.091064f, 627.534851f);
+path.close();
+#endif
+path.moveTo(317.093445f, 592.013306f);
+path.lineTo(366.316162f, 542.986572f);
+path.lineTo(416.051514f, 486.978577f);
+path.lineTo(465.786865f, 430.970581f);
+//path.lineTo(317.093445f, 592.013306f);
+path.close();
+#if 0
+path.moveTo(289.392517f, 517.138489f);
+path.lineTo(249.886078f, 508.598022f);
+path.lineTo(217.110916f, 450.916443f);
+path.lineTo(196.621033f, 394.917633f);
+//path.lineTo(289.392517f, 517.138489f);
+path.close();
+#endif
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifySkinnyTriangle3() {
+        SkPath path, out;
+        path.moveTo(591, 627.534851f);
+        path.lineTo(541, 560.707642f);
+        path.lineTo(491, 493.880310f);
+        path.lineTo(441, 427.053101f);
+        path.close();
+        path.moveTo(317, 592.013306f);
+        path.lineTo(366, 542.986572f);
+        path.lineTo(416, 486.978577f);
+        path.lineTo(465, 430.970581f);
+        path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifySkinnyTriangle4() {
+        SkPath path, out;
+path.moveTo(572.655212f, 614.959961f);
+path.lineTo(524.618896f, 549.339600f);
+path.lineTo(476.582581f, 483.719269f);
+path.lineTo(428.546265f, 418.098938f);
+path.lineTo(572.655212f, 614.959961f);
+path.close();
+path.moveTo(312.166382f, 583.723083f);
+path.lineTo(361.047791f, 529.824219f);
+path.lineTo(409.929230f, 475.925354f);
+path.lineTo(458.810669f, 422.026520f);
+path.lineTo(312.166382f, 583.723083f);
+path.close();
+path.moveTo(278.742737f, 508.065643f);
+path.lineTo(241.475800f, 493.465118f);
+path.lineTo(210.344177f, 437.315125f);
+path.lineTo(197.019455f, 383.794556f);
+path.lineTo(278.742737f, 508.065643f);
+path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifySkinnyTriangle5() {
+        SkPath path, out;
+path.moveTo(554.690613f, 602.286072f);
+path.lineTo(508.590057f, 537.906250f);
+path.lineTo(462.489441f, 473.526520f);
+path.lineTo(416.388855f, 409.146729f);
+path.lineTo(554.690613f, 602.286072f);
+path.close();
+path.moveTo(307.216949f, 575.189270f);
+path.lineTo(355.826965f, 516.804688f);
+path.lineTo(403.815918f, 464.990753f);
+path.lineTo(451.804871f, 413.176819f);
+path.lineTo(307.216949f, 575.189270f);
+path.close();
+path.moveTo(271.998901f, 521.301025f);
+path.lineTo(234.619705f, 499.687683f);
+path.lineTo(203.059692f, 441.332336f);
+path.lineTo(195.994370f, 386.856506f);
+path.lineTo(271.998901f, 521.301025f);
+path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifySkinnyTriangle6() {
+        SkPath path, out;
+path.moveTo(591.091064f, 627.534851f);
+path.lineTo(541.088135f, 560.707642f);
+path.lineTo(491.085175f, 493.880310f);
+path.lineTo(441.082214f, 427.053101f);
+path.lineTo(591.091064f, 627.534851f);
+path.close();
+path.moveTo(317.093445f, 592.013306f);
+path.lineTo(366.316162f, 542.986572f);
+path.lineTo(416.051514f, 486.978577f);
+path.lineTo(465.786865f, 430.970581f);
+path.lineTo(317.093445f, 592.013306f);
+path.close();
+path.moveTo(289.392517f, 517.138489f);
+path.lineTo(249.886078f, 508.598022f);
+path.lineTo(217.110916f, 450.916443f);
+path.lineTo(196.621033f, 394.917633f);
+path.lineTo(289.392517f, 517.138489f);
+path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifyTriangle22() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(0, 2);
+    path.lineTo(0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle23() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyTriangle24() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifySkinnyTriangle7() {
+        SkPath path, out;
+path.moveTo(487.502319f, 550.811279f);
+path.lineTo(448.826050f, 491.720123f);
+path.lineTo(410.149780f, 432.628967f);
+path.lineTo(371.473572f, 373.537781f);
+path.lineTo(487.502319f, 550.811279f);
+path.close();
+path.moveTo(295.817108f, 532.655579f);
+path.lineTo(342.896271f, 485.912292f);
+path.lineTo(389.975433f, 439.169006f);
+path.lineTo(437.054596f, 392.425781f);
+path.lineTo(295.817108f, 532.655579f);
+path.close();
+path.moveTo(239.726822f, 575.025269f);
+path.lineTo(204.117569f, 521.429688f);
+path.lineTo(171.275452f, 454.110382f);
+path.lineTo(193.328583f, 397.859497f);
+path.lineTo(239.726822f, 575.025269f);
+path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifySkinnyTriangle8() {
+        SkPath path, out;
+path.moveTo(441.943115f, 511.678040f);
+path.lineTo(408.487549f, 456.880920f);
+path.lineTo(375.031952f, 402.083801f);
+path.lineTo(341.576385f, 347.286682f);
+path.lineTo(441.943115f, 511.678040f);
+path.close();
+path.moveTo(297.548492f, 557.246704f);
+path.lineTo(350.768494f, 507.627014f);
+path.lineTo(403.988525f, 458.007385f);
+path.lineTo(457.208527f, 408.387695f);
+path.lineTo(297.548492f, 557.246704f);
+path.close();
+path.moveTo(209.857895f, 615.802979f);
+path.lineTo(178.249481f, 534.230347f);
+path.lineTo(144.905640f, 460.056824f);
+path.lineTo(192.953125f, 404.972900f);
+path.lineTo(209.857895f, 615.802979f);
+path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifySkinnyTriangle9() {
+        SkPath path, out;
+path.moveTo(439.867065f, 528.291931f);
+path.lineTo(405.413025f, 469.107178f);
+path.lineTo(370.958954f, 409.922363f);
+path.lineTo(336.504883f, 350.737610f);
+path.lineTo(439.867065f, 528.291931f);
+path.close();
+path.moveTo(298.922455f, 573.251953f);
+path.lineTo(356.360962f, 521.905090f);
+path.lineTo(413.799438f, 470.558228f);
+path.lineTo(471.237915f, 419.211365f);
+path.lineTo(298.922455f, 573.251953f);
+path.close();
+path.moveTo(187.200775f, 643.035156f);
+path.lineTo(159.713165f, 540.993774f);
+path.lineTo(126.257164f, 462.198517f);
+path.lineTo(193.534012f, 409.266235f);
+path.lineTo(187.200775f, 643.035156f);
+path.close();
+path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifySkinnyTriangle10() {
+        SkPath path, out;
+#if 0
+path.moveTo(99.270325f, 239.365234f);
+path.lineTo(105.967056f, 173.361206f);
+path.lineTo(148.821381f, 141.309891f);
+path.lineTo(159.101013f, 189.235138f);
+path.lineTo(99.270325f, 239.365234f);
+path.close();
+#endif
+path.moveTo(213.673737f, 413.292938f);
+path.lineTo(225.200134f, 343.616821f);
+path.lineTo(236.726532f, 273.940704f);
+path.lineTo(219.386414f, 231.373322f);
+path.lineTo(213.673737f, 413.292938f);
+path.close();
+path.moveTo(43.485352f, 308.984497f);
+path.lineTo(122.610657f, 305.950134f);
+path.lineTo(201.735962f, 302.915802f);
+path.lineTo(280.861267f, 299.881470f);
+path.lineTo(43.485352f, 308.984497f);
+path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifySkinnyTriangle11() {
+        SkPath path, out;
+path.moveTo(-177.878387f, 265.368988f);
+path.lineTo(-254.415771f, 303.709961f);
+path.lineTo(-317.465363f, 271.325562f);
+path.lineTo(-374.520386f, 207.507660f);
+path.lineTo(-177.878387f, 265.368988f);
+path.close();
+path.moveTo(-63.582489f, -3.679123f);
+path.lineTo(-134.496841f, 26.434566f);
+path.lineTo(-205.411209f, 56.548256f);
+path.lineTo(-276.325562f, 86.661942f);
+path.lineTo(-63.582489f, -3.679123f);
+path.close();
+path.moveTo(-57.078423f, 162.633453f);
+path.lineTo(-95.963928f, 106.261139f);
+path.lineTo(-134.849457f, 49.888824f);
+path.lineTo(-173.734955f, -6.483480f);
+path.lineTo(-57.078423f, 162.633453f);
+path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifySkinnyTriangle12() {
+        SkPath path, out;
+path.moveTo(98.666489f, -94.295059f);
+path.lineTo(156.584320f, -61.939133f);
+path.lineTo(174.672974f, -12.343765f);
+path.lineTo(158.622345f, 52.028267f);
+path.lineTo(98.666489f, -94.295059f);
+path.close();
+path.moveTo(-133.225616f, -48.622055f);
+path.lineTo(-73.855499f, -10.375397f);
+path.lineTo(-14.485367f, 27.871277f);
+path.lineTo(44.884750f, 66.117935f);
+path.lineTo(-133.225616f, -48.622055f);
+path.close();
+path.moveTo( 9.030045f, -163.413132f);
+path.lineTo(-19.605331f, -89.588760f);
+path.lineTo(-48.240707f, -15.764404f);
+path.lineTo(-76.876053f, 58.059944f);
+path.lineTo( 9.030045f, -163.413132f);
+path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void testSimplifySkinnyTriangle13() {
+        SkPath path, out;
+path.moveTo(340.41568f, -170.97171f);
+path.lineTo(418.846893f, -142.428329f);
+path.lineTo(497.278107f, -113.884933f);
+path.lineTo(449.18222f, -45.6723022f);
+path.lineTo(340.41568f, -170.97171f);
+path.close();
+path.moveTo(326.610535f, 34.0393639f);
+path.lineTo(371.334595f, -14.9620667f);
+path.lineTo(416.058624f, -63.9634857f);
+path.lineTo(460.782654f, -112.96492f);
+path.lineTo(326.610535f, 34.0393639f);
+path.close();
+    simplify(__FUNCTION__, path, true, out);
+}
+
+static void (*simplifyTests[])() = {
+    testSimplifySkinnyTriangle13,
+    testSimplifySkinnyTriangle12,
+    testSimplifySkinnyTriangle11,
+    testSimplifySkinnyTriangle10,
+    testSimplifySkinnyTriangle9,
+    testSimplifySkinnyTriangle8,
+    testSimplifySkinnyTriangle7,
+    testSimplifySkinnyTriangle6,
+    testSimplifySkinnyTriangle5,
+    testSimplifySkinnyTriangle4,
+    testSimplifySkinnyTriangle3,
+    testSimplifySkinnyTriangle2,
+    testSimplifySkinnyTriangle1,
+    testSimplifyTriangle24,
+    testSimplifyTriangle23,
+    testSimplifyTriangle22,
+    testSimplifyDegenerateTriangle2,
+    testSimplifyDegenerateTriangle1,
+    testSimplifyTriangle21,
+    testSimplifyTriangle20,
+    testSimplifyTriangle19,
+    testSimplifyTriangle18,
+    testSimplifyTriangle17,
+    testSimplifyTriangle16,
+    testSimplifyTriangle15,
+    testSimplifyTriangle14,
+    testSimplifyTriangle13,
+    testSimplifyTriangle12,
+    testSimplifyTriangle11,
+    testSimplifyTriangle10,
+    testSimplifyTriangle7,
+    testSimplifyTriangle9,
+    testSimplifyTriangle8,
+    testSimplifyTriangle6,
+    testSimplifyTriangle5,
+    testSimplifyTriangle4,
+    testSimplifyTriangle3,
+    testSimplifyTriangle,
+    testSimplifyTriangle2,
+    testSimplifyWindingParallelogram,
+    testSimplifyXorParallelogram,
+//    testPathTriangleRendering,
+};
+
+static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
+
+static void (*firstTest)() = testSimplifySkinnyTriangle12;
+
+void SimplifyPolygonPaths_Test() {
+    size_t index = 0;
+    if (firstTest) {
+        while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
+            ++index;
+        }
+    }
+    bool firstTestComplete = false;
+    for ( ; index < simplifyTestsCount; ++index) {
+        (*simplifyTests[index])();
+        if (simplifyTests[index] == testSimplifySkinnyTriangle2) {
+            if (false) SkDebugf("%s last fast skinny test\n", __FUNCTION__);
+        }
+        firstTestComplete = true;
+    }
+}
+
diff --git a/experimental/Intersection/EdgeWalkerQuadralaterals_Test.cpp b/experimental/Intersection/EdgeWalkerQuadralaterals_Test.cpp
new file mode 100644
index 0000000..92e474b
--- /dev/null
+++ b/experimental/Intersection/EdgeWalkerQuadralaterals_Test.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "SkBitmap.h"
+
+static SkBitmap bitmap;
+
+static void testSimplifyQuad1() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(3, 2);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 3);
+    path.lineTo(1, 3);
+    path.lineTo(1, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyQuad2() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(0, 1);
+    path.lineTo(1, 1);
+    path.lineTo(0, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyQuad3() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(1, 1);
+    path.lineTo(2, 1);
+    path.lineTo(0, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyQuad4() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 1);
+    path.lineTo(3, 1);
+    path.lineTo(3, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyQuad5() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(1, 1);
+    path.lineTo(2, 1);
+    path.lineTo(0, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyQuad6() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(1, 1);
+    path.lineTo(1, 1);
+    path.lineTo(1, 1);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void (*simplifyTests[])() = {
+    testSimplifyQuad6,
+    testSimplifyQuad5,
+    testSimplifyQuad4,
+    testSimplifyQuad3,
+    testSimplifyQuad2,
+    testSimplifyQuad1,
+};
+
+static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
+
+static void (*firstTest)() = 0;
+
+void SimplifyQuadralateralPaths_Test() {
+    size_t index = 0;
+    if (firstTest) {
+        while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
+            ++index;
+        }
+    }
+    for ( ; index < simplifyTestsCount; ++index) {
+        (*simplifyTests[index])();
+    }
+}
diff --git a/experimental/Intersection/EdgeWalkerQuadratic4x4_Test.cpp b/experimental/Intersection/EdgeWalkerQuadratic4x4_Test.cpp
new file mode 100644
index 0000000..a2b6bef
--- /dev/null
+++ b/experimental/Intersection/EdgeWalkerQuadratic4x4_Test.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include <assert.h>
+
+
+static void* testSimplify4x4QuadraticsMain(void* data)
+{
+    SkASSERT(data);
+    State4& state = *(State4*) data;
+    char pathStr[1024];
+    bzero(pathStr, sizeof(pathStr));
+    do {
+        int ax = state.a & 0x03;
+        int ay = state.a >> 2;
+        int bx = state.b & 0x03;
+        int by = state.b >> 2;
+        int cx = state.c & 0x03;
+        int cy = state.c >> 2;
+        int dx = state.d & 0x03;
+        int dy = state.d >> 2;
+        for (int e = 0 ; e < 16; ++e) {
+            int ex = e & 0x03;
+            int ey = e >> 2;
+            for (int f = e ; f < 16; ++f) {
+                int fx = f & 0x03;
+                int fy = f >> 2;
+                for (int g = f ; g < 16; ++g) {
+                    int gx = g & 0x03;
+                    int gy = g >> 2;
+                    for (int h = g ; h < 16; ++h) {
+                        int hx = h & 0x03;
+                        int hy = h >> 2;
+                        SkPath path, out;
+                        path.setFillType(SkPath::kWinding_FillType);
+                        path.moveTo(ax, ay);
+                        path.quadTo(bx, by, cx, cy);
+                        path.lineTo(dx, dy);
+                        path.close();
+                        path.moveTo(ex, ey);
+                        path.lineTo(fx, fy);
+                        path.quadTo(gx, gy, hx, hy);
+                        path.close();
+                        if (1) {  // gdb: set print elements 400
+                            char* str = pathStr;
+                            str += sprintf(str, "    path.moveTo(%d, %d);\n", ax, ay);
+                            str += sprintf(str, "    path.quadTo(%d, %d, %d, %d);\n", bx, by, cx, cy);
+                            str += sprintf(str, "    path.lineTo(%d, %d);\n", dx, dy);
+                            str += sprintf(str, "    path.close();\n");
+                            str += sprintf(str, "    path.moveTo(%d, %d);\n", ex, ey);
+                            str += sprintf(str, "    path.lineTo(%d, %d);\n", fx, fy);
+                            str += sprintf(str, "    path.quadTo(%d, %d, %d, %d);\n", gx, gy, hx, hy);
+                            str += sprintf(str, "    path.close();\n");
+                        }
+                        outputProgress(state, pathStr, SkPath::kWinding_FillType);
+                        testSimplifyx(path, false, out, state, pathStr);
+                        state.testsRun++;
+                        path.setFillType(SkPath::kEvenOdd_FillType);
+                        outputProgress(state, pathStr, SkPath::kEvenOdd_FillType);
+                        testSimplifyx(path, true, out, state, pathStr);
+                        state.testsRun++;
+                    }
+                }
+            }
+        }
+    } while (runNextTestSet(state));
+    return NULL;
+}
+
+void Simplify4x4QuadraticsThreaded_Test(int& testsRun)
+{
+    SkDebugf("%s\n", __FUNCTION__);
+#ifdef SK_DEBUG
+    gDebugMaxWindSum = 4; // FIXME: 3?
+    gDebugMaxWindValue = 4;
+#endif
+    const char testStr[] = "testQuadratic";
+    initializeTests(testStr, sizeof(testStr));
+    int testsStart = testsRun;
+    for (int a = 0; a < 16; ++a) {
+        for (int b = a ; b < 16; ++b) {
+            for (int c = b ; c < 16; ++c) {
+                for (int d = c; d < 16; ++d) {
+                    testsRun += dispatchTest4(testSimplify4x4QuadraticsMain,
+                            a, b, c, d);
+                }
+                if (!gRunTestsInOneThread) SkDebugf(".");
+            }
+            if (!gRunTestsInOneThread) SkDebugf("%d", b);
+        }
+        if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
+    }
+    testsRun += waitForCompletion();
+    SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
+}
diff --git a/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp b/experimental/Intersection/EdgeWalkerQuadratics_Test.cpp
new file mode 100644
index 0000000..febc972
--- /dev/null
+++ b/experimental/Intersection/EdgeWalkerQuadratics_Test.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 "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "SkBitmap.h"
+
+static SkBitmap bitmap;
+
+static void testSimplifyQuadratic1() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.quadTo(0, 0, 0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyQuadratic2() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(20, 0, 20, 20);
+    path.close();
+    path.moveTo(20, 0);
+    path.quadTo(0, 0, 0, 20);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyQuadratic3() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(20, 0, 20, 20);
+    path.close();
+    path.moveTo(0, 20);
+    path.quadTo(0, 0, 20, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyQuadratic4() {
+    SkPath path, out;
+    path.moveTo(0, 20);
+    path.quadTo(20, 0, 40, 20);
+    path.close();
+    path.moveTo(40, 10);
+    path.quadTo(20, 30, 0, 10);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic5() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 0, 0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic6() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(1, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic7() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic8() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic9() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 2, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic10() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.quadTo(1, 1, 1, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic11() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 1);
+    path.quadTo(2, 2, 3, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic12() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 2);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(3, 0);
+    path.quadTo(1, 1, 0, 2);
+    path.lineTo(3, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic13() {
+    SkPath path, out;
+path.moveTo(0, 0);
+path.quadTo(0, 0, 1, 0);
+path.lineTo(1, 1);
+path.lineTo(0, 0);
+path.close();
+path.moveTo(0, 0);
+path.quadTo(3, 0, 1, 1);
+path.lineTo(0, 0);
+path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic14() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 1, 2, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic15() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(1, 1);
+    path.quadTo(0, 3, 3, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic16() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void testSimplifyQuadratic17() {
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(0, 1);
+    path.quadTo(2, 1, 3, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+
+static void (*simplifyTests[])() = {
+    testSimplifyQuadratic17,
+    testSimplifyQuadratic16,
+    testSimplifyQuadratic15,
+    testSimplifyQuadratic14,
+    testSimplifyQuadratic13,
+    testSimplifyQuadratic12,
+    testSimplifyQuadratic11,
+    testSimplifyQuadratic10,
+    testSimplifyQuadratic9,
+    testSimplifyQuadratic8,
+    testSimplifyQuadratic7,
+    testSimplifyQuadratic6,
+    testSimplifyQuadratic5,
+    testSimplifyQuadratic4,
+    testSimplifyQuadratic3,
+    testSimplifyQuadratic2,
+    testSimplifyQuadratic1,
+};
+
+static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
+
+static void (*firstTest)() = testSimplifyQuadratic14;
+static bool skipAll = false;
+
+void SimplifyQuadraticPaths_Test() {
+    if (skipAll) {
+        return;
+    }
+    size_t index = 0;
+    if (firstTest) {
+        while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
+            ++index;
+        }
+    }
+    bool firstTestComplete = false;
+    for ( ; index < simplifyTestsCount; ++index) {
+        (*simplifyTests[index])();
+        if (simplifyTests[index] == testSimplifyQuadratic1) {
+            SkDebugf("%s last fast quad test\n", __FUNCTION__);
+        }
+        firstTestComplete = true;
+    }
+}
diff --git a/experimental/Intersection/EdgeWalkerRectangles_Test.cpp b/experimental/Intersection/EdgeWalkerRectangles_Test.cpp
new file mode 100644
index 0000000..dae1d70
--- /dev/null
+++ b/experimental/Intersection/EdgeWalkerRectangles_Test.cpp
@@ -0,0 +1,470 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "SkBitmap.h"
+
+static SkBitmap bitmap;
+
+static void testSimplifyCoincidentInner() {
+    SkPath path, out;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addRect(10, 10, 60, 60, SkPath::kCCW_Direction);
+    path.addRect(20, 20, 50, 50, SkPath::kCW_Direction);
+    path.addRect(20, 30, 40, 40, SkPath::kCW_Direction);
+    testSimplify(path, true, out, bitmap);
+}
+
+static void testSimplifyCoincidentVertical() {
+    SkPath path, out;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addRect(10, 10, 30, 30);
+    path.addRect(10, 30, 30, 40);
+    simplify(path, true, out);
+    SkRect rect;
+    if (!out.isRect(&rect)) {
+        SkDebugf("%s expected rect\n", __FUNCTION__);
+    }
+    if (rect != SkRect::MakeLTRB(10, 10, 30, 40)) {
+        SkDebugf("%s expected union\n", __FUNCTION__);
+    }
+}
+
+static void testSimplifyCoincidentHorizontal() {
+    SkPath path, out;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addRect(10, 10, 30, 30);
+    path.addRect(30, 10, 40, 30);
+    simplify(path, true, out);
+    SkRect rect;
+    if (!out.isRect(&rect)) {
+        SkDebugf("%s expected rect\n", __FUNCTION__);
+    }
+    if (rect != SkRect::MakeLTRB(10, 10, 40, 30)) {
+        SkDebugf("%s expected union\n", __FUNCTION__);
+    }
+}
+
+static void testSimplifyMulti() {
+    SkPath path, out;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addRect(10, 10, 30, 30);
+    path.addRect(20, 20, 40, 40);
+    simplify(path, true, out);
+    SkPath expected;
+    expected.setFillType(SkPath::kEvenOdd_FillType);
+    expected.moveTo(10,10); // two cutout corners
+    expected.lineTo(10,30);
+    expected.lineTo(20,30);
+    expected.lineTo(20,40);
+    expected.lineTo(40,40);
+    expected.lineTo(40,20);
+    expected.lineTo(30,20);
+    expected.lineTo(30,10);
+    expected.lineTo(10,10);
+    expected.close();
+    if (out != expected) {
+        SkDebugf("%s expected equal\n", __FUNCTION__);
+    }
+
+    path = out;
+    path.addRect(30, 10, 40, 20);
+    path.addRect(10, 30, 20, 40);
+    simplify(path, true, out);
+    SkRect rect;
+    if (!out.isRect(&rect)) {
+        SkDebugf("%s expected rect\n", __FUNCTION__);
+    }
+    if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
+        SkDebugf("%s expected union\n", __FUNCTION__);
+    }
+
+    path = out;
+    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
+    simplify(path, true, out);
+    if (!out.isEmpty()) {
+        SkDebugf("%s expected empty\n", __FUNCTION__);
+    }
+}
+
+static void testSimplifyAddL() {
+    SkPath path, out;
+    path.moveTo(10,10); // 'L' shape
+    path.lineTo(10,40);
+    path.lineTo(40,40);
+    path.lineTo(40,20);
+    path.lineTo(30,20);
+    path.lineTo(30,10);
+    path.lineTo(10,10);
+    path.close();
+    path.addRect(30, 10, 40, 20); // missing notch of 'L'
+    simplify(path, true, out);
+    SkRect rect;
+    if (!out.isRect(&rect)) {
+        SkDebugf("%s expected rect\n", __FUNCTION__);
+    }
+    if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
+        SkDebugf("%s expected union\n", __FUNCTION__);
+    }
+}
+
+static void testSimplifyCoincidentCCW() {
+    SkPath path, out;
+    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
+    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
+    simplify(path, true, out);
+    SkRect rect;
+    if (!out.isRect(&rect)) {
+        SkDebugf("%s expected rect\n", __FUNCTION__);
+    }
+    if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
+        SkDebugf("%s expected union\n", __FUNCTION__);
+    }
+}
+
+static void testSimplifyCoincidentCW() {
+    SkPath path, out;
+    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
+    path.addRect(10, 10, 40, 40, SkPath::kCW_Direction);
+    simplify(path, true, out);
+    if (!out.isEmpty()) {
+        SkDebugf("%s expected empty\n", __FUNCTION__);
+    }
+}
+
+static void testSimplifyCorner() {
+    SkPath path, out;
+    path.addRect(10, 10, 20, 20, SkPath::kCCW_Direction);
+    path.addRect(20, 20, 40, 40, SkPath::kCW_Direction);
+    simplify(path, true, out);
+    SkTDArray<SkRect> boundsArray;
+    contourBounds(out, boundsArray);
+    if (boundsArray.count() != 2) {
+        SkDebugf("%s expected 2 contours\n", __FUNCTION__);
+        return;
+    }
+    SkRect one = SkRect::MakeLTRB(10, 10, 20, 20);
+    SkRect two = SkRect::MakeLTRB(20, 20, 40, 40);
+    if (boundsArray[0] != one && boundsArray[0] != two
+            || boundsArray[1] != one && boundsArray[1] != two) {
+        SkDebugf("%s expected match\n", __FUNCTION__);
+    }
+}
+
+static void testSimplifyDiagonal() {
+    SkRect rect2 = SkRect::MakeXYWH(10, 10, 10, 10);
+    for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
+        for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
+            for (int x = 0; x <= 20; x += 20) {
+                for (int y = 0; y <= 20; y += 20) {
+                    SkPath path, out;
+                    SkRect rect1 = SkRect::MakeXYWH(x, y, 10, 10);
+                    path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
+                    path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
+                    simplify(path, true, out);
+                    SkPath::Iter iter(out, false);
+                    SkPoint pts[4], lastLine[2];
+                    SkPath::Verb verb;
+                    SkRect bounds[2];
+                    bounds[0].setEmpty();
+                    bounds[1].setEmpty();
+                    SkRect* boundsPtr = bounds;
+                    int count = 0, segments = 0;
+                    bool lastLineSet = false;
+                    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+                        switch (verb) {
+                            case SkPath::kMove_Verb:
+                                if (!boundsPtr->isEmpty()) {
+                                    SkASSERT(boundsPtr == bounds);
+                                    ++boundsPtr;
+                                }
+                                boundsPtr->set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
+                                count = 0;
+                                lastLineSet = false;
+                                break;
+                            case SkPath::kLine_Verb:
+                                if (lastLineSet) {
+                                    SkASSERT((lastLine[1].fX - lastLine[0].fX) *
+                                        (pts[1].fY - lastLine[0].fY) !=
+                                        (lastLine[1].fY - lastLine[0].fY) *
+                                        (pts[1].fX - lastLine[0].fX));
+                                }
+                                lastLineSet = true;
+                                lastLine[0] = pts[0];
+                                lastLine[1] = pts[1];
+                                count = 1;
+                                ++segments;
+                                break;
+                            case SkPath::kClose_Verb:
+                                count = 0;
+                                break;
+                            default:
+                                SkDEBUGFAIL("bad verb");
+                                return;
+                        }
+                        for (int i = 1; i <= count; ++i) {
+                            boundsPtr->growToInclude(pts[i].fX, pts[i].fY);
+                        }
+                    }
+                    if (boundsPtr != bounds) {
+                        SkASSERT((bounds[0] == rect1 || bounds[1] == rect1)
+                                && (bounds[0] == rect2 || bounds[1] == rect2));
+                    } else {
+                        SkASSERT(segments == 8);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void assertOneContour(const SkPath& out, bool edge, bool extend) {
+    SkPath::Iter iter(out, false);
+    SkPoint pts[4];
+    SkPath::Verb verb;
+    SkRect bounds;
+    bounds.setEmpty();
+    int count = 0;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                SkASSERT(count == 0);
+                break;
+            case SkPath::kLine_Verb:
+                SkASSERT(pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY);
+                ++count;
+                break;
+            case SkPath::kClose_Verb:
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return;
+        }
+    }
+    SkASSERT(count == (extend ? 4 : edge ? 6 : 8));
+}
+
+static void testSimplifyCoincident() {
+    // outside to inside, outside to right, outside to outside
+    // left to inside, left to right, left to outside
+    // inside to right, inside to outside
+    // repeat above for left, right, bottom
+    SkScalar start[] = { 0, 10, 20 };
+    size_t startCount = sizeof(start) / sizeof(start[0]);
+    SkScalar stop[] = { 30, 40, 50 };
+    size_t stopCount = sizeof(stop) / sizeof(stop[0]);
+    SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
+    for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
+        for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
+            for (size_t startIndex = 0; startIndex < startCount; ++startIndex) {
+                for (size_t stopIndex = 0; stopIndex < stopCount; ++stopIndex) {
+                    bool extend = start[startIndex] == rect2.fLeft && stop[stopIndex] == rect2.fRight;
+                    bool edge = start[startIndex] == rect2.fLeft || stop[stopIndex] == rect2.fRight;
+                    SkRect rect1 = SkRect::MakeLTRB(start[startIndex], 0, stop[stopIndex], 10);
+                    SkPath path, out;
+                    path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
+                    path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
+                    simplify(path, true, out);
+                    assertOneContour(out, edge, extend);
+
+                    path.reset();
+                    rect1 = SkRect::MakeLTRB(start[startIndex], 40, stop[stopIndex], 50);
+                    path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
+                    path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
+                    simplify(path, true, out);
+                    assertOneContour(out, edge, extend);
+
+                    path.reset();
+                    rect1 = SkRect::MakeLTRB(0, start[startIndex], 10, stop[stopIndex]);
+                    path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
+                    path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
+                    simplify(path, true, out);
+                    assertOneContour(out, edge, extend);
+
+                    path.reset();
+                    rect1 = SkRect::MakeLTRB(40, start[startIndex], 50, stop[stopIndex]);
+                    path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
+                    path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
+                    simplify(path, true, out);
+                    assertOneContour(out, edge, extend);
+                }
+            }
+        }
+    }
+}
+
+static void testSimplifyOverlap() {
+    SkScalar start[] = { 0, 10, 20 };
+    size_t startCount = sizeof(start) / sizeof(start[0]);
+    SkScalar stop[] = { 30, 40, 50 };
+    size_t stopCount = sizeof(stop) / sizeof(stop[0]);
+    SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
+    for (size_t dir = SkPath::kCW_Direction; dir <= SkPath::kCCW_Direction; ++dir) {
+        for (size_t lefty = 0; lefty < startCount; ++lefty) {
+            for (size_t righty = 0; righty < stopCount; ++righty) {
+                for (size_t toppy = 0; toppy < startCount; ++toppy) {
+                    for (size_t botty = 0; botty < stopCount; ++botty) {
+                        SkRect rect1 = SkRect::MakeLTRB(start[lefty], start[toppy],
+                                stop[righty], stop[botty]);
+                        SkPath path, out;
+                        path.addRect(rect1, static_cast<SkPath::Direction>(dir));
+                        path.addRect(rect2, static_cast<SkPath::Direction>(dir));
+                        testSimplify(path, true, out, bitmap);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void testSimplifyOverlapTiny() {
+    SkScalar start[] = { 0, 1, 2 };
+    size_t startCount = sizeof(start) / sizeof(start[0]);
+    SkScalar stop[] = { 3, 4, 5 };
+    size_t stopCount = sizeof(stop) / sizeof(stop[0]);
+    SkRect rect2 = SkRect::MakeXYWH(1, 1, 3, 3);
+    for (size_t dir = SkPath::kCW_Direction; dir <= SkPath::kCCW_Direction; ++dir) {
+        for (size_t lefty = 0; lefty < startCount; ++lefty) {
+            for (size_t righty = 0; righty < stopCount; ++righty) {
+                for (size_t toppy = 0; toppy < startCount; ++toppy) {
+                    for (size_t botty = 0; botty < stopCount; ++botty) {
+                        SkRect rect1 = SkRect::MakeLTRB(start[lefty], start[toppy],
+                                stop[righty], stop[botty]);
+                        SkPath path, out;
+                        path.addRect(rect1, static_cast<SkPath::Direction>(dir));
+                        path.addRect(rect2, static_cast<SkPath::Direction>(dir));
+                        simplify(path, true, out);
+                        comparePathsTiny(path, out);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void testSimplifyDegenerate() {
+    SkScalar start[] = { 0, 10, 20 };
+    size_t startCount = sizeof(start) / sizeof(start[0]);
+    SkScalar stop[] = { 30, 40, 50 };
+    size_t stopCount = sizeof(stop) / sizeof(stop[0]);
+    SkRect rect2 = SkRect::MakeXYWH(10, 10, 30, 30);
+    for (size_t outDir = SkPath::kCW_Direction; outDir <= SkPath::kCCW_Direction; ++outDir) {
+        for (size_t inDir = SkPath::kCW_Direction; inDir <= SkPath::kCCW_Direction; ++inDir) {
+            for (size_t startIndex = 0; startIndex < startCount; ++startIndex) {
+                for (size_t stopIndex = 0; stopIndex < stopCount; ++stopIndex) {
+                    SkRect rect1 = SkRect::MakeLTRB(start[startIndex], 0, stop[stopIndex], 0);
+                    SkPath path, out;
+                    path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
+                    path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
+                    simplify(path, true, out);
+                    SkRect rect;
+                    if (!out.isRect(&rect)) {
+                        SkDebugf("%s 1 expected rect\n", __FUNCTION__);
+                    }
+                    if (rect != rect2) {
+                        SkDebugf("%s 1 expected union\n", __FUNCTION__);
+                    }
+
+                    path.reset();
+                    rect1 = SkRect::MakeLTRB(start[startIndex], 40, stop[stopIndex], 40);
+                    path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
+                    path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
+                    simplify(path, true, out);
+                    if (!out.isRect(&rect)) {
+                        SkDebugf("%s 2 expected rect\n", __FUNCTION__);
+                    }
+                    if (rect != rect2) {
+                        SkDebugf("%s 2 expected union\n", __FUNCTION__);
+                    }
+
+                    path.reset();
+                    rect1 = SkRect::MakeLTRB(0, start[startIndex], 0, stop[stopIndex]);
+                    path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
+                    path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
+                    simplify(path, true, out);
+                    if (!out.isRect(&rect)) {
+                        SkDebugf("%s 3 expected rect\n", __FUNCTION__);
+                    }
+                    if (rect != rect2) {
+                        SkDebugf("%s 3 expected union\n", __FUNCTION__);
+                    }
+
+                    path.reset();
+                    rect1 = SkRect::MakeLTRB(40, start[startIndex], 40, stop[stopIndex]);
+                    path.addRect(rect1, static_cast<SkPath::Direction>(outDir));
+                    path.addRect(rect2, static_cast<SkPath::Direction>(inDir));
+                    simplify(path, true, out);
+                    if (!out.isRect(&rect)) {
+                        SkDebugf("%s 4 expected rect\n", __FUNCTION__);
+                    }
+                    if (rect != rect2) {
+                        SkDebugf("%s 4 expected union\n", __FUNCTION__);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void testSimplifyDegenerate1() {
+    SkPath path, out;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.addRect( 0,  0,  0, 30);
+    path.addRect(10, 10, 40, 40);
+    simplify(path, true, out);
+    SkRect rect;
+    if (!out.isRect(&rect)) {
+        SkDebugf("%s expected rect\n", __FUNCTION__);
+    }
+    if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
+        SkDebugf("%s expected union\n", __FUNCTION__);
+    }
+}
+
+static void (*simplifyTests[])() = {
+    testSimplifyCoincidentInner,
+    testSimplifyOverlapTiny,
+    testSimplifyDegenerate1,
+    testSimplifyCorner,
+    testSimplifyDegenerate,
+    testSimplifyOverlap,
+    testSimplifyDiagonal,
+    testSimplifyCoincident,
+    testSimplifyCoincidentCW,
+    testSimplifyCoincidentCCW,
+    testSimplifyCoincidentVertical,
+    testSimplifyCoincidentHorizontal,
+    testSimplifyAddL,
+    testSimplifyMulti,
+};
+
+static size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
+
+static void (*firstTest)() = 0;
+
+void SimplifyRectangularPaths_Test() {
+    size_t index = 0;
+    if (firstTest) {
+        while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
+            ++index;
+        }
+    }
+    for ( ; index < simplifyTestsCount; ++index) {
+        if (simplifyTests[index] == testSimplifyCorner) {
+            // testSimplifyCorner fails because it expects two contours, where
+            // only one is returned. Both results are reasonable, but if two
+            // contours are desirable, or if we provide an option to choose
+            // between longer contours and more contours, turn this back on. For
+            // the moment, testSimplifyDiagonal also checks the test case, and
+            // permits either two rects or one non-crossing poly as valid
+            // unreported results.
+            continue;
+        }
+        (*simplifyTests[index])();
+    }
+}
+
diff --git a/experimental/Intersection/EdgeWalker_Test.h b/experimental/Intersection/EdgeWalker_Test.h
new file mode 100644
index 0000000..55f8bf3
--- /dev/null
+++ b/experimental/Intersection/EdgeWalker_Test.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.
+ */
+#include "ShapeOps.h"
+#include "SkBitmap.h"
+#include "SkStream.h"
+#include <pthread.h>
+
+struct State4;
+
+//extern int comparePaths(const SkPath& one, const SkPath& two);
+extern int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap);
+extern void comparePathsTiny(const SkPath& one, const SkPath& two);
+extern bool drawAsciiPaths(const SkPath& one, const SkPath& two,
+        bool drawPaths);
+extern void showPath(const SkPath& path, const char* str = NULL);
+extern bool testSimplify(const SkPath& path, bool fill, SkPath& out,
+        SkBitmap& bitmap);
+extern bool testSimplifyx(SkPath& path, bool useXor, SkPath& out,
+        State4& state, const char* pathStr);
+extern bool testSimplifyx(const SkPath& path);
+
+struct State4 {
+    State4();
+    static pthread_mutex_t addQueue;
+    static pthread_cond_t checkQueue;
+    pthread_cond_t initialized;
+    static State4* queue;
+    pthread_t threadID;
+    int index;
+    bool done;
+    bool last;
+    int a;
+    int b;
+    int c;
+    int d; // sometimes 1 if abc_is_a_triangle
+    int testsRun;
+    char filename[256];
+
+    SkBitmap bitmap;
+};
+
+void createThread(State4* statePtr, void* (*test)(void* ));
+int dispatchTest4(void* (*testFun)(void* ), int a, int b, int c, int d);
+void initializeTests(const char* testName, size_t testNameSize);
+void outputProgress(const State4& state, const char* pathStr, SkPath::FillType );
+void outputToStream(const State4& state, const char* pathStr, SkPath::FillType, SkWStream& outFile);
+bool runNextTestSet(State4& state);
+int waitForCompletion();
diff --git a/experimental/Intersection/EdgeWalker_TestUtility.cpp b/experimental/Intersection/EdgeWalker_TestUtility.cpp
new file mode 100644
index 0000000..e236cca
--- /dev/null
+++ b/experimental/Intersection/EdgeWalker_TestUtility.cpp
@@ -0,0 +1,541 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkStream.h"
+
+#include <algorithm>
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#undef SkASSERT
+#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
+
+static const char marker[] =
+    "</div>\n"
+    "\n"
+    "<script type=\"text/javascript\">\n"
+    "\n"
+    "var testDivs = [\n";
+
+static const char preferredFilename[] = "/flash/debug/XX.txt";
+static const char backupFilename[] = "../../experimental/Intersection/debugXX.txt";
+
+static bool gShowPath = false;
+static bool gComparePaths = true;
+//static bool gDrawLastAsciiPaths = true;
+//static bool gDrawAllAsciiPaths = false;
+static bool gShowOutputProgress = false;
+static bool gShowAsciiPaths = true;
+static bool gComparePathsAssert = false;
+static bool gPathStrAssert = true;
+static bool gUsePhysicalFiles = false;
+
+void showPath(const SkPath& path, const char* str) {
+    SkDebugf("%s\n", !str ? "original:" : str);
+    SkPath::Iter iter(path, true);
+    uint8_t verb;
+    SkPoint pts[4];
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pts[0].fX, pts[0].fY);
+                continue;
+            case SkPath::kLine_Verb:
+                SkDebugf("path.lineTo(%1.9g, %1.9g);\n", pts[1].fX, pts[1].fY);
+                break;
+            case SkPath::kQuad_Verb:
+                SkDebugf("path.quadTo(%1.9g, %1.9g, %1.9g, %1.9g);\n",
+                    pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
+                break;
+            case SkPath::kCubic_Verb:
+                SkDebugf("path.cubicTo(%1.9g, %1.9g, %1.9g, %1.9g);\n",
+                    pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
+                    pts[3].fX, pts[3].fY);
+                break;
+            case SkPath::kClose_Verb:
+                SkDebugf("path.close();\n");
+                continue;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return;
+        }
+    }
+}
+
+static int pathsDrawTheSame(const SkPath& one, const SkPath& two,
+        SkBitmap& bits, int& error2x2) {
+    const int bitWidth = 64;
+    const int bitHeight = 64;
+    if (bits.width() == 0) {
+        bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
+        bits.allocPixels();
+    }
+
+    SkRect larger = one.getBounds();
+    larger.join(two.getBounds());
+    SkScalar largerWidth = larger.width();
+    if (largerWidth < 4) {
+        largerWidth = 4;
+    }
+    SkScalar largerHeight = larger.height();
+    if (largerHeight < 4) {
+        largerHeight = 4;
+    }
+    SkScalar hScale = (bitWidth - 2) / largerWidth;
+    SkScalar vScale = (bitHeight - 2) / largerHeight;
+    SkMatrix scale;
+    scale.reset();
+    scale.preScale(hScale, vScale);
+    SkPath scaledOne, scaledTwo;
+    one.transform(scale, &scaledOne);
+    two.transform(scale, &scaledTwo);
+    const SkRect& bounds1 = scaledOne.getBounds();
+
+    SkCanvas canvas(bits);
+    canvas.drawColor(SK_ColorWHITE);
+    SkPaint paint;
+    canvas.save();
+    canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
+    canvas.drawPath(scaledOne, paint);
+    canvas.restore();
+    canvas.save();
+    canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
+    canvas.drawPath(scaledTwo, paint);
+    canvas.restore();
+    int errors2 = 0;
+    int errors = 0;
+    for (int y = 0; y < bitHeight - 1; ++y) {
+        uint32_t* addr1 = bits.getAddr32(0, y);
+        uint32_t* addr2 = bits.getAddr32(0, y + 1);
+        uint32_t* addr3 = bits.getAddr32(bitWidth, y);
+        uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
+        for (int x = 0; x < bitWidth - 1; ++x) {
+            // count 2x2 blocks
+            bool err = addr1[x] != addr3[x];
+            if (err) {
+                errors2 += addr1[x + 1] != addr3[x + 1]
+                        && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
+                errors++;
+            }
+        }
+    }
+    if (errors2 >= 6 || errors > 160) {
+        SkDebugf("%s errors2=%d errors=%d\n", __FUNCTION__, errors2, errors);
+    }
+    if (errors2 >= 7) {
+        drawAsciiPaths(scaledOne, scaledTwo, true);
+    }
+    error2x2 = errors2;
+    return errors;
+}
+
+bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
+    if (!drawPaths) {
+        return true;
+    }
+    if (gShowAsciiPaths) {
+        showPath(one, "one:");
+        showPath(two, "two:");
+    }
+    const SkRect& bounds1 = one.getBounds();
+    const SkRect& bounds2 = two.getBounds();
+    SkRect larger = bounds1;
+    larger.join(bounds2);
+    SkBitmap bits;
+    char out[256];
+    int bitWidth = SkScalarCeil(larger.width()) + 2;
+    if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
+        return false;
+    }
+    int bitHeight = SkScalarCeil(larger.height()) + 2;
+    if (bitHeight >= (int) sizeof(out)) {
+        return false;
+    }
+    bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
+    bits.allocPixels();
+    SkCanvas canvas(bits);
+    canvas.drawColor(SK_ColorWHITE);
+    SkPaint paint;
+    canvas.save();
+    canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
+    canvas.drawPath(one, paint);
+    canvas.restore();
+    canvas.save();
+    canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
+    canvas.drawPath(two, paint);
+    canvas.restore();
+    for (int y = 0; y < bitHeight; ++y) {
+        uint32_t* addr1 = bits.getAddr32(0, y);
+        int x;
+        char* outPtr = out;
+        for (x = 0; x < bitWidth; ++x) {
+            *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
+        }
+        *outPtr++ = '|';
+        for (x = bitWidth; x < bitWidth * 2; ++x) {
+            *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
+        }
+        *outPtr++ = '\0';
+        SkDebugf("%s\n", out);
+    }
+    return true;
+}
+
+int comparePaths(const SkPath& one, const SkPath& two, SkBitmap& bitmap) {
+    int errors2x2;
+    int errors = pathsDrawTheSame(one, two, bitmap, errors2x2);
+    if (errors2x2 == 0) {
+        return 0;
+    }
+    const int MAX_ERRORS = 8;
+    if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
+        SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
+        showPath(one);
+        showPath(two, "simplified:");
+        SkASSERT(0);
+    }
+    return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
+}
+
+// doesn't work yet
+void comparePathsTiny(const SkPath& one, const SkPath& two) {
+    const SkRect& bounds1 = one.getBounds();
+    const SkRect& bounds2 = two.getBounds();
+    SkRect larger = bounds1;
+    larger.join(bounds2);
+    SkBitmap bits;
+    int bitWidth = SkScalarCeil(larger.width()) + 2;
+    int bitHeight = SkScalarCeil(larger.height()) + 2;
+    bits.setConfig(SkBitmap::kA1_Config, bitWidth * 2, bitHeight);
+    bits.allocPixels();
+    SkCanvas canvas(bits);
+    canvas.drawColor(SK_ColorWHITE);
+    SkPaint paint;
+    canvas.save();
+    canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
+    canvas.drawPath(one, paint);
+    canvas.restore();
+    canvas.save();
+    canvas.translate(-bounds2.fLeft + 1, -bounds2.fTop + 1);
+    canvas.drawPath(two, paint);
+    canvas.restore();
+    for (int y = 0; y < bitHeight; ++y) {
+        uint8_t* addr1 = bits.getAddr1(0, y);
+        uint8_t* addr2 = bits.getAddr1(bitWidth, y);
+        for (int x = 0; x < bits.rowBytes(); ++x) {
+            SkASSERT(addr1[x] == addr2[x]);
+        }
+    }
+}
+
+bool testSimplify(const SkPath& path, bool fill, SkPath& out, SkBitmap& bitmap) {
+    if (gShowPath) {
+        showPath(path);
+    }
+    simplify(path, fill, out);
+    if (!gComparePaths) {
+        return true;
+    }
+    return comparePaths(path, out, bitmap) == 0;
+}
+
+bool testSimplifyx(SkPath& path, bool useXor, SkPath& out, State4& state,
+        const char* pathStr) {
+    SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
+    path.setFillType(fillType);
+    if (gShowPath) {
+        showPath(path);
+    }
+    simplifyx(path, out);
+    if (!gComparePaths) {
+        return true;
+    }
+    int result = comparePaths(path, out, state.bitmap);
+    if (result && gPathStrAssert) {
+        SkDebugf("addTest %s\n", state.filename);
+        char temp[8192];
+        bzero(temp, sizeof(temp));
+        SkMemoryWStream stream(temp, sizeof(temp));
+        outputToStream(state, pathStr, fillType, stream);
+        SkDebugf(temp);
+        SkASSERT(0);
+    }
+    return result == 0;
+}
+
+bool testSimplifyx(const SkPath& path) {
+    SkPath out;
+    simplifyx(path, out);
+    SkBitmap bitmap;
+    int result = comparePaths(path, out, bitmap);
+    if (result && gPathStrAssert) {
+        SkASSERT(0);
+    }
+    return result == 0;
+}
+
+const int maxThreadsAllocated = 64;
+static int maxThreads = 1;
+static int threadIndex;
+State4 threadState[maxThreadsAllocated];
+static int testNumber;
+static const char* testName;
+static bool debugThreads = false;
+
+State4* State4::queue = NULL;
+pthread_mutex_t State4::addQueue = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t State4::checkQueue = PTHREAD_COND_INITIALIZER;
+
+State4::State4() {
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150 * 2, 100);
+    bitmap.allocPixels();
+}
+
+void createThread(State4* statePtr, void* (*testFun)(void* )) {
+    int threadError = pthread_create(&statePtr->threadID, NULL, testFun,
+            (void*) statePtr);
+    SkASSERT(!threadError);
+}
+
+int dispatchTest4(void* (*testFun)(void* ), int a, int b, int c, int d) {
+    int testsRun = 0;
+    State4* statePtr;
+    if (!gRunTestsInOneThread) {
+        pthread_mutex_lock(&State4::addQueue);
+        if (threadIndex < maxThreads) {
+            statePtr = &threadState[threadIndex];
+            statePtr->testsRun = 0;
+            statePtr->a = a;
+            statePtr->b = b;
+            statePtr->c = c;
+            statePtr->d = d;
+            statePtr->done = false;
+            statePtr->index = threadIndex;
+            statePtr->last = false;
+            if (debugThreads) SkDebugf("%s %d create done=%d last=%d\n", __FUNCTION__,
+                    statePtr->index, statePtr->done, statePtr->last);
+            pthread_cond_init(&statePtr->initialized, NULL);
+            ++threadIndex;
+            createThread(statePtr, testFun);
+        } else {
+            while (!State4::queue) {
+                if (debugThreads) SkDebugf("%s checkQueue\n", __FUNCTION__);
+                pthread_cond_wait(&State4::checkQueue, &State4::addQueue);
+            }
+            statePtr = State4::queue;
+            testsRun += statePtr->testsRun;
+            statePtr->testsRun = 0;
+            statePtr->a = a;
+            statePtr->b = b;
+            statePtr->c = c;
+            statePtr->d = d;
+            statePtr->done = false;
+            State4::queue = NULL;
+            for (int index = 0; index < maxThreads; ++index) {
+                if (threadState[index].done) {
+                    State4::queue = &threadState[index];
+                }
+            }
+            if (debugThreads) SkDebugf("%s %d init done=%d last=%d queued=%d\n", __FUNCTION__,
+                    statePtr->index, statePtr->done, statePtr->last,
+                    State4::queue ? State4::queue->index : -1);
+            pthread_cond_signal(&statePtr->initialized);
+        }
+        pthread_mutex_unlock(&State4::addQueue);
+    } else {
+        statePtr = &threadState[0];
+        testsRun += statePtr->testsRun;
+        statePtr->testsRun = 0;
+        statePtr->a = a;
+        statePtr->b = b;
+        statePtr->c = c;
+        statePtr->d = d;
+        statePtr->done = false;
+        statePtr->index = threadIndex;
+        statePtr->last = false;
+        (*testFun)(statePtr);
+    }
+    return testsRun;
+}
+
+void initializeTests(const char* test, size_t testNameSize) {
+    testName = test;
+    if (!gRunTestsInOneThread) {
+        int threads = -1;
+        size_t size = sizeof(threads);
+        sysctlbyname("hw.logicalcpu_max", &threads, &size, NULL, 0);
+        if (threads > 0) {
+            maxThreads = threads;
+        } else {
+            maxThreads = 8;
+        }
+    }
+    SkFILEStream inFile("../../experimental/Intersection/op.htm");
+    if (inFile.isValid()) {
+        SkTDArray<char> inData;
+        inData.setCount(inFile.getLength());
+        size_t inLen = inData.count();
+        inFile.read(inData.begin(), inLen);
+        inFile.setPath(NULL);
+        char* insert = strstr(inData.begin(), marker);
+        if (insert) {
+            insert += sizeof(marker) - 1;
+            const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
+            testNumber = atoi(numLoc) + 1;
+        }
+    }
+    const char* filename = preferredFilename;
+    SkFILEWStream preferredTest(filename);
+    if (!preferredTest.isValid()) {
+        filename = backupFilename;
+        SkFILEWStream backupTest(filename);
+        SkASSERT(backupTest.isValid());
+    }
+    for (int index = 0; index < maxThreads; ++index) {
+        State4* statePtr = &threadState[index];
+        strcpy(statePtr->filename, filename);
+        size_t len = strlen(filename);
+        SkASSERT(statePtr->filename[len - 6] == 'X');
+        SkASSERT(statePtr->filename[len - 5] == 'X');
+        statePtr->filename[len - 6] = '0' + index / 10;
+        statePtr->filename[len - 5] = '0' + index % 10;
+    }
+    threadIndex = 0;
+}
+
+void outputProgress(const State4& state, const char* pathStr, SkPath::FillType pathFillType) {
+    if (gRunTestsInOneThread && gShowOutputProgress) {
+        if (pathFillType == SkPath::kEvenOdd_FillType) {
+            SkDebugf("    path.setFillType(SkPath::kEvenOdd_FillType);\n", pathStr);
+        }
+        SkDebugf("%s\n", pathStr);
+    }
+    if (gUsePhysicalFiles) {
+        SkFILEWStream outFile(state.filename);
+        if (!outFile.isValid()) {
+            SkASSERT(0);
+            return;
+        }
+        outputToStream(state, pathStr, pathFillType, outFile);
+        return;
+    }
+    SkFILEWStream outRam(state.filename);
+    outputToStream(state, pathStr, pathFillType, outRam);
+}
+
+static void writeTestName(SkPath::FillType pathFillType, SkWStream& outFile) {
+    outFile.writeText(testName);
+    outFile.writeDecAsText(testNumber);
+    if (pathFillType == SkPath::kEvenOdd_FillType) {
+        outFile.writeText("x");
+    }
+}
+
+void outputToStream(const State4& state, const char* pathStr, SkPath::FillType pathFillType, SkWStream& outFile) {
+    outFile.writeText("<div id=\"");
+    writeTestName(pathFillType, outFile);
+    outFile.writeText("\">\n");
+    if (pathFillType == SkPath::kEvenOdd_FillType) {
+        outFile.writeText("    path.setFillType(SkPath::kEvenOdd_FillType);\n");
+    }
+    outFile.writeText(pathStr);
+    outFile.writeText("</div>\n\n");
+
+    outFile.writeText(marker);
+    outFile.writeText("    ");
+    writeTestName(pathFillType, outFile);
+    outFile.writeText(",\n\n\n");
+
+    outFile.writeText("static void ");
+    writeTestName(pathFillType, outFile);
+    outFile.writeText("() {\n    SkPath path;\n");
+    if (pathFillType == SkPath::kEvenOdd_FillType) {
+        outFile.writeText("    path.setFillType(SkPath::kEvenOdd_FillType);\n");
+    }
+    outFile.writeText(pathStr);
+    outFile.writeText("    testSimplifyx(path);\n}\n\n");
+    outFile.writeText("static void (*firstTest)() = ");
+    writeTestName(pathFillType, outFile);
+    outFile.writeText(";\n\n");
+
+    outFile.writeText("static struct {\n");
+    outFile.writeText("    void (*fun)();\n");
+    outFile.writeText("    const char* str;\n");
+    outFile.writeText("} tests[] = {\n");
+    outFile.writeText("    TEST(");
+    writeTestName(pathFillType, outFile);
+    outFile.writeText("),\n");
+    outFile.flush();
+}
+
+bool runNextTestSet(State4& state) {
+    if (gRunTestsInOneThread) {
+        return false;
+    }
+    pthread_mutex_lock(&State4::addQueue);
+    state.done = true;
+    State4::queue = &state;
+    if (debugThreads) SkDebugf("%s %d checkQueue done=%d last=%d\n", __FUNCTION__, state.index,
+        state.done, state.last);
+    pthread_cond_signal(&State4::checkQueue);
+    while (state.done && !state.last) {
+        if (debugThreads) SkDebugf("%s %d done=%d last=%d\n", __FUNCTION__, state.index, state.done, state.last);
+        pthread_cond_wait(&state.initialized, &State4::addQueue);
+    }
+    pthread_mutex_unlock(&State4::addQueue);
+    return !state.last;
+}
+
+int waitForCompletion() {
+    int testsRun = 0;
+    if (!gRunTestsInOneThread) {
+        pthread_mutex_lock(&State4::addQueue);
+        int runningThreads = maxThreads;
+        int index;
+        while (runningThreads > 0) {
+            while (!State4::queue) {
+                if (debugThreads) SkDebugf("%s checkQueue\n", __FUNCTION__);
+                pthread_cond_wait(&State4::checkQueue, &State4::addQueue);
+            }
+            while (State4::queue) {
+                --runningThreads;
+                SkDebugf("•");
+                State4::queue->last = true;
+                State4* next = NULL;
+                for (index = 0; index < maxThreads; ++index) {
+                    State4& test = threadState[index];
+                    if (test.done && !test.last) {
+                        next = &test;
+                    }
+                }
+                if (debugThreads) SkDebugf("%s %d next=%d deQueue\n", __FUNCTION__,
+                    State4::queue->index, next ? next->index : -1);
+                pthread_cond_signal(&State4::queue->initialized);
+                State4::queue = next;
+            }
+        }
+        pthread_mutex_unlock(&State4::addQueue);
+        for (index = 0; index < maxThreads; ++index) {
+            pthread_join(threadState[index].threadID, NULL);
+            testsRun += threadState[index].testsRun;
+        }
+        SkDebugf("\n");
+    }
+#ifdef SK_DEBUG
+    gDebugMaxWindSum = SK_MaxS32;
+    gDebugMaxWindValue = SK_MaxS32;
+#endif
+    return testsRun;
+}
diff --git a/experimental/Intersection/English.lproj/InfoPlist.strings b/experimental/Intersection/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/experimental/Intersection/English.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/experimental/Intersection/English.lproj/MainMenu.xib b/experimental/Intersection/English.lproj/MainMenu.xib
new file mode 100644
index 0000000..6e4dd1f
--- /dev/null
+++ b/experimental/Intersection/English.lproj/MainMenu.xib
@@ -0,0 +1,4119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+	<data>
+		<int key="IBDocument.SystemTarget">1060</int>
+		<string key="IBDocument.SystemVersion">10A324</string>
+		<string key="IBDocument.InterfaceBuilderVersion">719</string>
+		<string key="IBDocument.AppKitVersion">1015</string>
+		<string key="IBDocument.HIToolboxVersion">418.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+			<string key="NS.object.0">719</string>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<integer value="371"/>
+			<integer value="29"/>
+		</object>
+		<object class="NSArray" key="IBDocument.PluginDependencies">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSArray" key="dict.sortedKeys" id="0">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+			</object>
+			<object class="NSMutableArray" key="dict.values">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+			</object>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSCustomObject" id="1021">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSCustomObject" id="1014">
+				<string key="NSClassName">FirstResponder</string>
+			</object>
+			<object class="NSCustomObject" id="1050">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSMenu" id="649796088">
+				<string key="NSTitle">AMainMenu</string>
+				<object class="NSMutableArray" key="NSMenuItems">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="NSMenuItem" id="694149608">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">edge</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<object class="NSCustomResource" key="NSOnImage" id="35465992">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuCheckmark</string>
+						</object>
+						<object class="NSCustomResource" key="NSMixedImage" id="502551668">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuMixedState</string>
+						</object>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="110575045">
+							<string key="NSTitle">edge</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="238522557">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">About edge</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="304266470">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="609285721">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Preferences…</string>
+									<string key="NSKeyEquiv">,</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="481834944">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1046388886">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Services</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="752062318">
+										<string key="NSTitle">Services</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+										</object>
+										<string key="NSName">_NSServicesMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="646227648">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="755159360">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Hide edge</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="342932134">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Hide Others</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="908899353">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Show All</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1056857174">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="632727374">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Quit edge</string>
+									<string key="NSKeyEquiv">q</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+							<string key="NSName">_NSAppleMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="379814623">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">File</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="720053764">
+							<string key="NSTitle">File</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="705341025">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">New</string>
+									<string key="NSKeyEquiv">n</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="722745758">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Open…</string>
+									<string key="NSKeyEquiv">o</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1025936716">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Open Recent</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="1065607017">
+										<string key="NSTitle">Open Recent</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="759406840">
+												<reference key="NSMenu" ref="1065607017"/>
+												<string key="NSTitle">Clear Menu</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+										<string key="NSName">_NSRecentDocumentsMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="425164168">
+									<reference key="NSMenu" ref="720053764"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="776162233">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Close</string>
+									<string key="NSKeyEquiv">w</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1023925487">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Save</string>
+									<string key="NSKeyEquiv">s</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="117038363">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Save As…</string>
+									<string key="NSKeyEquiv">S</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="579971712">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Revert to Saved</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1010469920">
+									<reference key="NSMenu" ref="720053764"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="294629803">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Page Setup...</string>
+									<string key="NSKeyEquiv">P</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSToolTip"/>
+								</object>
+								<object class="NSMenuItem" id="49223823">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Print…</string>
+									<string key="NSKeyEquiv">p</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="952259628">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Edit</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="789758025">
+							<string key="NSTitle">Edit</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="1058277027">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Undo</string>
+									<string key="NSKeyEquiv">z</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="790794224">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Redo</string>
+									<string key="NSKeyEquiv">Z</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1040322652">
+									<reference key="NSMenu" ref="789758025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="296257095">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Cut</string>
+									<string key="NSKeyEquiv">x</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="860595796">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Copy</string>
+									<string key="NSKeyEquiv">c</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="29853731">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Paste</string>
+									<string key="NSKeyEquiv">v</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="82994268">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Paste and Match Style</string>
+									<string key="NSKeyEquiv">V</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="437104165">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Delete</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="583158037">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Select All</string>
+									<string key="NSKeyEquiv">a</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="212016141">
+									<reference key="NSMenu" ref="789758025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="892235320">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Find</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="963351320">
+										<string key="NSTitle">Find</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="447796847">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find…</string>
+												<string key="NSKeyEquiv">f</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="326711663">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find Next</string>
+												<string key="NSKeyEquiv">g</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="270902937">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find Previous</string>
+												<string key="NSKeyEquiv">G</string>
+												<int key="NSKeyEquivModMask">1179648</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="159080638">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Use Selection for Find</string>
+												<string key="NSKeyEquiv">e</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">7</int>
+											</object>
+											<object class="NSMenuItem" id="88285865">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Jump to Selection</string>
+												<string key="NSKeyEquiv">j</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="972420730">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Spelling and Grammar</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="769623530">
+										<string key="NSTitle">Spelling and Grammar</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="679648819">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Show Spelling and Grammar</string>
+												<string key="NSKeyEquiv">:</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="96193923">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Document Now</string>
+												<string key="NSKeyEquiv">;</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="859480356">
+												<reference key="NSMenu" ref="769623530"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="948374510">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Spelling While Typing</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="967646866">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Grammar With Spelling</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="795346622">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Correct Spelling Automatically</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="507821607">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Substitutions</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="698887838">
+										<string key="NSTitle">Substitutions</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="65139061">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Show Substitutions</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="19036812">
+												<reference key="NSMenu" ref="698887838"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="605118523">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Copy/Paste</string>
+												<string key="NSKeyEquiv">f</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="197661976">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Quotes</string>
+												<string key="NSKeyEquiv">g</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="672708820">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Dashes</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="708854459">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Links</string>
+												<string key="NSKeyEquiv">G</string>
+												<int key="NSKeyEquivModMask">1179648</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="537092702">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Text Replacement</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="288088188">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Transformations</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="579392910">
+										<string key="NSTitle">Transformations</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="1060694897">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Make Upper Case</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="879586729">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Make Lower Case</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="56570060">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Capitalize</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="676164635">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Speech</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="785027613">
+										<string key="NSTitle">Speech</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="731782645">
+												<reference key="NSMenu" ref="785027613"/>
+												<string key="NSTitle">Start Speaking</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="680220178">
+												<reference key="NSMenu" ref="785027613"/>
+												<string key="NSTitle">Stop Speaking</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="302598603">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Format</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="941447902">
+							<string key="NSTitle">Format</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="792887677">
+									<reference key="NSMenu" ref="941447902"/>
+									<string key="NSTitle">Font</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="786677654">
+										<string key="NSTitle">Font</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="159677712">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Show Fonts</string>
+												<string key="NSKeyEquiv">t</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="305399458">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Bold</string>
+												<string key="NSKeyEquiv">b</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="814362025">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Italic</string>
+												<string key="NSKeyEquiv">i</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="330926929">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Underline</string>
+												<string key="NSKeyEquiv">u</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="533507878">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="158063935">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Bigger</string>
+												<string key="NSKeyEquiv">+</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="885547335">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Smaller</string>
+												<string key="NSKeyEquiv">-</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">4</int>
+											</object>
+											<object class="NSMenuItem" id="901062459">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="767671776">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Kern</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="175441468">
+													<string key="NSTitle">Kern</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="252969304">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="766922938">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Use None</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="677519740">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Tighten</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="238351151">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Loosen</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="691570813">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Ligature</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="1058217995">
+													<string key="NSTitle">Ligature</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="706297211">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="568384683">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use None</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="663508465">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use All</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="769124883">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Baseline</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="18263474">
+													<string key="NSTitle">Baseline</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="257962622">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="644725453">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Superscript</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="1037576581">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Subscript</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="941806246">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Raise</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="1045724900">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Lower</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="739652853">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="1012600125">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Show Colors</string>
+												<string key="NSKeyEquiv">C</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="214559597">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="596732606">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Copy Style</string>
+												<string key="NSKeyEquiv">c</string>
+												<int key="NSKeyEquivModMask">1572864</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="393423671">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Paste Style</string>
+												<string key="NSKeyEquiv">v</string>
+												<int key="NSKeyEquivModMask">1572864</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+										<string key="NSName">_NSFontMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="215659978">
+									<reference key="NSMenu" ref="941447902"/>
+									<string key="NSTitle">Text</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="446991534">
+										<string key="NSTitle">Text</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="875092757">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Align Left</string>
+												<string key="NSKeyEquiv">{</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="630155264">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Center</string>
+												<string key="NSKeyEquiv">|</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="945678886">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Justify</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="512868991">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Align Right</string>
+												<string key="NSKeyEquiv">}</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="163117631">
+												<reference key="NSMenu" ref="446991534"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="31516759">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Writing Direction</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="956096989">
+													<string key="NSTitle">Writing Direction</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="257099033">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<string key="NSTitle">Paragraph</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="551969625">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="249532473">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="607364498">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="508151438">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<bool key="NSIsSeparator">YES</bool>
+															<string key="NSTitle"/>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="981751889">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<string key="NSTitle">Selection</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="380031999">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="825984362">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="560145579">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="908105787">
+												<reference key="NSMenu" ref="446991534"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="644046920">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Show Ruler</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="231811626">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Copy Ruler</string>
+												<string key="NSKeyEquiv">c</string>
+												<int key="NSKeyEquivModMask">1310720</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="883618387">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Paste Ruler</string>
+												<string key="NSKeyEquiv">v</string>
+												<int key="NSKeyEquivModMask">1310720</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="586577488">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">View</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="466310130">
+							<string key="NSTitle">View</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="102151532">
+									<reference key="NSMenu" ref="466310130"/>
+									<string key="NSTitle">Show Toolbar</string>
+									<string key="NSKeyEquiv">t</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="237841660">
+									<reference key="NSMenu" ref="466310130"/>
+									<string key="NSTitle">Customize Toolbar…</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="713487014">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Window</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="835318025">
+							<string key="NSTitle">Window</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="1011231497">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Minimize</string>
+									<string key="NSKeyEquiv">m</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="575023229">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Zoom</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="299356726">
+									<reference key="NSMenu" ref="835318025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="625202149">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Bring All to Front</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+							<string key="NSName">_NSWindowsMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="448692316">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Help</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="992780483">
+							<string key="NSTitle">Help</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="105068016">
+									<reference key="NSMenu" ref="992780483"/>
+									<string key="NSTitle">edge Help</string>
+									<string key="NSKeyEquiv">?</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+							<string key="NSName">_NSHelpMenu</string>
+						</object>
+					</object>
+				</object>
+				<string key="NSName">_NSMainMenu</string>
+			</object>
+			<object class="NSWindowTemplate" id="972006081">
+				<int key="NSWindowStyleMask">15</int>
+				<int key="NSWindowBacking">2</int>
+				<string key="NSWindowRect">{{335, 390}, {480, 360}}</string>
+				<int key="NSWTFlags">1954021376</int>
+				<string key="NSWindowTitle">edge</string>
+				<string key="NSWindowClass">NSWindow</string>
+				<nil key="NSViewClass"/>
+				<string key="NSWindowContentMaxSize">{1.79769e+308, 1.79769e+308}</string>
+				<object class="NSView" key="NSWindowView" id="439893737">
+					<reference key="NSNextResponder"/>
+					<int key="NSvFlags">256</int>
+					<string key="NSFrameSize">{480, 360}</string>
+					<reference key="NSSuperview"/>
+				</object>
+				<string key="NSScreenRect">{{0, 0}, {1920, 1178}}</string>
+				<string key="NSMaxSize">{1.79769e+308, 1.79769e+308}</string>
+			</object>
+			<object class="NSCustomObject" id="976324537">
+				<string key="NSClassName">edgeAppDelegate</string>
+			</object>
+			<object class="NSCustomObject" id="755631768">
+				<string key="NSClassName">NSFontManager</string>
+			</object>
+		</object>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<object class="NSMutableArray" key="connectionRecords">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performMiniaturize:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1011231497"/>
+					</object>
+					<int key="connectionID">37</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">arrangeInFront:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="625202149"/>
+					</object>
+					<int key="connectionID">39</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">print:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="49223823"/>
+					</object>
+					<int key="connectionID">86</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">runPageLayout:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="294629803"/>
+					</object>
+					<int key="connectionID">87</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">clearRecentDocuments:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="759406840"/>
+					</object>
+					<int key="connectionID">127</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontStandardAboutPanel:</string>
+						<reference key="source" ref="1021"/>
+						<reference key="destination" ref="238522557"/>
+					</object>
+					<int key="connectionID">142</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performClose:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="776162233"/>
+					</object>
+					<int key="connectionID">193</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleContinuousSpellChecking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="948374510"/>
+					</object>
+					<int key="connectionID">222</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">undo:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1058277027"/>
+					</object>
+					<int key="connectionID">223</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copy:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="860595796"/>
+					</object>
+					<int key="connectionID">224</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">checkSpelling:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="96193923"/>
+					</object>
+					<int key="connectionID">225</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">paste:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="29853731"/>
+					</object>
+					<int key="connectionID">226</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">stopSpeaking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="680220178"/>
+					</object>
+					<int key="connectionID">227</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">cut:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="296257095"/>
+					</object>
+					<int key="connectionID">228</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">showGuessPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="679648819"/>
+					</object>
+					<int key="connectionID">230</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">redo:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="790794224"/>
+					</object>
+					<int key="connectionID">231</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">selectAll:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="583158037"/>
+					</object>
+					<int key="connectionID">232</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">startSpeaking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="731782645"/>
+					</object>
+					<int key="connectionID">233</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">delete:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="437104165"/>
+					</object>
+					<int key="connectionID">235</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performZoom:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="575023229"/>
+					</object>
+					<int key="connectionID">240</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="447796847"/>
+					</object>
+					<int key="connectionID">241</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">centerSelectionInVisibleArea:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="88285865"/>
+					</object>
+					<int key="connectionID">245</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleGrammarChecking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="967646866"/>
+					</object>
+					<int key="connectionID">347</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleSmartInsertDelete:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="605118523"/>
+					</object>
+					<int key="connectionID">355</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticQuoteSubstitution:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="197661976"/>
+					</object>
+					<int key="connectionID">356</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticLinkDetection:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="708854459"/>
+					</object>
+					<int key="connectionID">357</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">saveDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1023925487"/>
+					</object>
+					<int key="connectionID">362</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">saveDocumentAs:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="117038363"/>
+					</object>
+					<int key="connectionID">363</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">revertDocumentToSaved:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="579971712"/>
+					</object>
+					<int key="connectionID">364</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">runToolbarCustomizationPalette:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="237841660"/>
+					</object>
+					<int key="connectionID">365</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleToolbarShown:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="102151532"/>
+					</object>
+					<int key="connectionID">366</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hide:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="755159360"/>
+					</object>
+					<int key="connectionID">367</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hideOtherApplications:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="342932134"/>
+					</object>
+					<int key="connectionID">368</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">unhideAllApplications:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="908899353"/>
+					</object>
+					<int key="connectionID">370</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">newDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="705341025"/>
+					</object>
+					<int key="connectionID">373</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="722745758"/>
+					</object>
+					<int key="connectionID">374</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">addFontTrait:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="305399458"/>
+					</object>
+					<int key="connectionID">421</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">addFontTrait:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="814362025"/>
+					</object>
+					<int key="connectionID">422</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">modifyFont:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="885547335"/>
+					</object>
+					<int key="connectionID">423</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontFontPanel:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="159677712"/>
+					</object>
+					<int key="connectionID">424</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">modifyFont:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="158063935"/>
+					</object>
+					<int key="connectionID">425</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">raiseBaseline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="941806246"/>
+					</object>
+					<int key="connectionID">426</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">lowerBaseline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1045724900"/>
+					</object>
+					<int key="connectionID">427</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copyFont:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="596732606"/>
+					</object>
+					<int key="connectionID">428</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">subscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1037576581"/>
+					</object>
+					<int key="connectionID">429</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">superscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="644725453"/>
+					</object>
+					<int key="connectionID">430</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">tightenKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="677519740"/>
+					</object>
+					<int key="connectionID">431</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">underline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="330926929"/>
+					</object>
+					<int key="connectionID">432</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontColorPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1012600125"/>
+					</object>
+					<int key="connectionID">433</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useAllLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="663508465"/>
+					</object>
+					<int key="connectionID">434</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">loosenKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="238351151"/>
+					</object>
+					<int key="connectionID">435</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteFont:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="393423671"/>
+					</object>
+					<int key="connectionID">436</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">unscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="257962622"/>
+					</object>
+					<int key="connectionID">437</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useStandardKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="252969304"/>
+					</object>
+					<int key="connectionID">438</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useStandardLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="706297211"/>
+					</object>
+					<int key="connectionID">439</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">turnOffLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="568384683"/>
+					</object>
+					<int key="connectionID">440</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">turnOffKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="766922938"/>
+					</object>
+					<int key="connectionID">441</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">terminate:</string>
+						<reference key="source" ref="1050"/>
+						<reference key="destination" ref="632727374"/>
+					</object>
+					<int key="connectionID">449</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticSpellingCorrection:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="795346622"/>
+					</object>
+					<int key="connectionID">456</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontSubstitutionsPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="65139061"/>
+					</object>
+					<int key="connectionID">458</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticDashSubstitution:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="672708820"/>
+					</object>
+					<int key="connectionID">461</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticTextReplacement:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="537092702"/>
+					</object>
+					<int key="connectionID">463</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">uppercaseWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1060694897"/>
+					</object>
+					<int key="connectionID">464</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">capitalizeWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="56570060"/>
+					</object>
+					<int key="connectionID">467</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">lowercaseWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="879586729"/>
+					</object>
+					<int key="connectionID">468</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteAsPlainText:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="82994268"/>
+					</object>
+					<int key="connectionID">486</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="326711663"/>
+					</object>
+					<int key="connectionID">487</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="270902937"/>
+					</object>
+					<int key="connectionID">488</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="159080638"/>
+					</object>
+					<int key="connectionID">489</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">showHelp:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="105068016"/>
+					</object>
+					<int key="connectionID">493</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="1021"/>
+						<reference key="destination" ref="976324537"/>
+					</object>
+					<int key="connectionID">495</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignCenter:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="630155264"/>
+					</object>
+					<int key="connectionID">518</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="883618387"/>
+					</object>
+					<int key="connectionID">519</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="644046920"/>
+					</object>
+					<int key="connectionID">520</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="512868991"/>
+					</object>
+					<int key="connectionID">521</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copyRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="231811626"/>
+					</object>
+					<int key="connectionID">522</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignJustified:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="945678886"/>
+					</object>
+					<int key="connectionID">523</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="875092757"/>
+					</object>
+					<int key="connectionID">524</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionNatural:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="551969625"/>
+					</object>
+					<int key="connectionID">525</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionLeftToRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="249532473"/>
+					</object>
+					<int key="connectionID">526</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionRightToLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="607364498"/>
+					</object>
+					<int key="connectionID">527</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionNatural:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="380031999"/>
+					</object>
+					<int key="connectionID">528</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionLeftToRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="825984362"/>
+					</object>
+					<int key="connectionID">529</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionRightToLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="560145579"/>
+					</object>
+					<int key="connectionID">530</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">window</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="972006081"/>
+					</object>
+					<int key="connectionID">532</int>
+				</object>
+			</object>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<object class="NSArray" key="orderedObjects">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<reference key="object" ref="0"/>
+						<reference key="children" ref="1048"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="1021"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="1014"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">First Responder</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-3</int>
+						<reference key="object" ref="1050"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Application</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">29</int>
+						<reference key="object" ref="649796088"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="713487014"/>
+							<reference ref="694149608"/>
+							<reference ref="952259628"/>
+							<reference ref="379814623"/>
+							<reference ref="586577488"/>
+							<reference ref="302598603"/>
+							<reference ref="448692316"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">19</int>
+						<reference key="object" ref="713487014"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="835318025"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">56</int>
+						<reference key="object" ref="694149608"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="110575045"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">217</int>
+						<reference key="object" ref="952259628"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="789758025"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">83</int>
+						<reference key="object" ref="379814623"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="720053764"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">81</int>
+						<reference key="object" ref="720053764"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1023925487"/>
+							<reference ref="117038363"/>
+							<reference ref="49223823"/>
+							<reference ref="722745758"/>
+							<reference ref="705341025"/>
+							<reference ref="1025936716"/>
+							<reference ref="294629803"/>
+							<reference ref="776162233"/>
+							<reference ref="425164168"/>
+							<reference ref="579971712"/>
+							<reference ref="1010469920"/>
+						</object>
+						<reference key="parent" ref="379814623"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">75</int>
+						<reference key="object" ref="1023925487"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">80</int>
+						<reference key="object" ref="117038363"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">78</int>
+						<reference key="object" ref="49223823"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">72</int>
+						<reference key="object" ref="722745758"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">82</int>
+						<reference key="object" ref="705341025"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">124</int>
+						<reference key="object" ref="1025936716"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1065607017"/>
+						</object>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">77</int>
+						<reference key="object" ref="294629803"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">73</int>
+						<reference key="object" ref="776162233"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">79</int>
+						<reference key="object" ref="425164168"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">112</int>
+						<reference key="object" ref="579971712"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">74</int>
+						<reference key="object" ref="1010469920"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">125</int>
+						<reference key="object" ref="1065607017"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="759406840"/>
+						</object>
+						<reference key="parent" ref="1025936716"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">126</int>
+						<reference key="object" ref="759406840"/>
+						<reference key="parent" ref="1065607017"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">205</int>
+						<reference key="object" ref="789758025"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="437104165"/>
+							<reference ref="583158037"/>
+							<reference ref="1058277027"/>
+							<reference ref="212016141"/>
+							<reference ref="296257095"/>
+							<reference ref="29853731"/>
+							<reference ref="860595796"/>
+							<reference ref="1040322652"/>
+							<reference ref="790794224"/>
+							<reference ref="892235320"/>
+							<reference ref="972420730"/>
+							<reference ref="676164635"/>
+							<reference ref="507821607"/>
+							<reference ref="288088188"/>
+							<reference ref="82994268"/>
+						</object>
+						<reference key="parent" ref="952259628"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">202</int>
+						<reference key="object" ref="437104165"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">198</int>
+						<reference key="object" ref="583158037"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">207</int>
+						<reference key="object" ref="1058277027"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">214</int>
+						<reference key="object" ref="212016141"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">199</int>
+						<reference key="object" ref="296257095"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">203</int>
+						<reference key="object" ref="29853731"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">197</int>
+						<reference key="object" ref="860595796"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">206</int>
+						<reference key="object" ref="1040322652"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">215</int>
+						<reference key="object" ref="790794224"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">218</int>
+						<reference key="object" ref="892235320"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="963351320"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">216</int>
+						<reference key="object" ref="972420730"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="769623530"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">200</int>
+						<reference key="object" ref="769623530"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="948374510"/>
+							<reference ref="96193923"/>
+							<reference ref="679648819"/>
+							<reference ref="967646866"/>
+							<reference ref="859480356"/>
+							<reference ref="795346622"/>
+						</object>
+						<reference key="parent" ref="972420730"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">219</int>
+						<reference key="object" ref="948374510"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">201</int>
+						<reference key="object" ref="96193923"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">204</int>
+						<reference key="object" ref="679648819"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">220</int>
+						<reference key="object" ref="963351320"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="270902937"/>
+							<reference ref="88285865"/>
+							<reference ref="159080638"/>
+							<reference ref="326711663"/>
+							<reference ref="447796847"/>
+						</object>
+						<reference key="parent" ref="892235320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">213</int>
+						<reference key="object" ref="270902937"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">210</int>
+						<reference key="object" ref="88285865"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">221</int>
+						<reference key="object" ref="159080638"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">208</int>
+						<reference key="object" ref="326711663"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">209</int>
+						<reference key="object" ref="447796847"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">57</int>
+						<reference key="object" ref="110575045"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="238522557"/>
+							<reference ref="755159360"/>
+							<reference ref="908899353"/>
+							<reference ref="632727374"/>
+							<reference ref="646227648"/>
+							<reference ref="609285721"/>
+							<reference ref="481834944"/>
+							<reference ref="304266470"/>
+							<reference ref="1046388886"/>
+							<reference ref="1056857174"/>
+							<reference ref="342932134"/>
+						</object>
+						<reference key="parent" ref="694149608"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">58</int>
+						<reference key="object" ref="238522557"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">134</int>
+						<reference key="object" ref="755159360"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">150</int>
+						<reference key="object" ref="908899353"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">136</int>
+						<reference key="object" ref="632727374"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">144</int>
+						<reference key="object" ref="646227648"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">129</int>
+						<reference key="object" ref="609285721"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">143</int>
+						<reference key="object" ref="481834944"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">236</int>
+						<reference key="object" ref="304266470"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">131</int>
+						<reference key="object" ref="1046388886"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="752062318"/>
+						</object>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">149</int>
+						<reference key="object" ref="1056857174"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">145</int>
+						<reference key="object" ref="342932134"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">130</int>
+						<reference key="object" ref="752062318"/>
+						<reference key="parent" ref="1046388886"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">24</int>
+						<reference key="object" ref="835318025"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="299356726"/>
+							<reference ref="625202149"/>
+							<reference ref="575023229"/>
+							<reference ref="1011231497"/>
+						</object>
+						<reference key="parent" ref="713487014"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">92</int>
+						<reference key="object" ref="299356726"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">5</int>
+						<reference key="object" ref="625202149"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">239</int>
+						<reference key="object" ref="575023229"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">23</int>
+						<reference key="object" ref="1011231497"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">295</int>
+						<reference key="object" ref="586577488"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="466310130"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">296</int>
+						<reference key="object" ref="466310130"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="102151532"/>
+							<reference ref="237841660"/>
+						</object>
+						<reference key="parent" ref="586577488"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">297</int>
+						<reference key="object" ref="102151532"/>
+						<reference key="parent" ref="466310130"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">298</int>
+						<reference key="object" ref="237841660"/>
+						<reference key="parent" ref="466310130"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">211</int>
+						<reference key="object" ref="676164635"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="785027613"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">212</int>
+						<reference key="object" ref="785027613"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="680220178"/>
+							<reference ref="731782645"/>
+						</object>
+						<reference key="parent" ref="676164635"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">195</int>
+						<reference key="object" ref="680220178"/>
+						<reference key="parent" ref="785027613"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">196</int>
+						<reference key="object" ref="731782645"/>
+						<reference key="parent" ref="785027613"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">346</int>
+						<reference key="object" ref="967646866"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">348</int>
+						<reference key="object" ref="507821607"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="698887838"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">349</int>
+						<reference key="object" ref="698887838"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="605118523"/>
+							<reference ref="197661976"/>
+							<reference ref="708854459"/>
+							<reference ref="65139061"/>
+							<reference ref="19036812"/>
+							<reference ref="672708820"/>
+							<reference ref="537092702"/>
+						</object>
+						<reference key="parent" ref="507821607"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">350</int>
+						<reference key="object" ref="605118523"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">351</int>
+						<reference key="object" ref="197661976"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">354</int>
+						<reference key="object" ref="708854459"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">371</int>
+						<reference key="object" ref="972006081"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="439893737"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">372</int>
+						<reference key="object" ref="439893737"/>
+						<reference key="parent" ref="972006081"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">375</int>
+						<reference key="object" ref="302598603"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="941447902"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">376</int>
+						<reference key="object" ref="941447902"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="792887677"/>
+							<reference ref="215659978"/>
+						</object>
+						<reference key="parent" ref="302598603"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">377</int>
+						<reference key="object" ref="792887677"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="786677654"/>
+						</object>
+						<reference key="parent" ref="941447902"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">388</int>
+						<reference key="object" ref="786677654"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="159677712"/>
+							<reference ref="305399458"/>
+							<reference ref="814362025"/>
+							<reference ref="330926929"/>
+							<reference ref="533507878"/>
+							<reference ref="158063935"/>
+							<reference ref="885547335"/>
+							<reference ref="901062459"/>
+							<reference ref="767671776"/>
+							<reference ref="691570813"/>
+							<reference ref="769124883"/>
+							<reference ref="739652853"/>
+							<reference ref="1012600125"/>
+							<reference ref="214559597"/>
+							<reference ref="596732606"/>
+							<reference ref="393423671"/>
+						</object>
+						<reference key="parent" ref="792887677"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">389</int>
+						<reference key="object" ref="159677712"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">390</int>
+						<reference key="object" ref="305399458"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">391</int>
+						<reference key="object" ref="814362025"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">392</int>
+						<reference key="object" ref="330926929"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">393</int>
+						<reference key="object" ref="533507878"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">394</int>
+						<reference key="object" ref="158063935"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">395</int>
+						<reference key="object" ref="885547335"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">396</int>
+						<reference key="object" ref="901062459"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">397</int>
+						<reference key="object" ref="767671776"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="175441468"/>
+						</object>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">398</int>
+						<reference key="object" ref="691570813"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1058217995"/>
+						</object>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">399</int>
+						<reference key="object" ref="769124883"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="18263474"/>
+						</object>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">400</int>
+						<reference key="object" ref="739652853"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">401</int>
+						<reference key="object" ref="1012600125"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">402</int>
+						<reference key="object" ref="214559597"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">403</int>
+						<reference key="object" ref="596732606"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">404</int>
+						<reference key="object" ref="393423671"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">405</int>
+						<reference key="object" ref="18263474"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="257962622"/>
+							<reference ref="644725453"/>
+							<reference ref="1037576581"/>
+							<reference ref="941806246"/>
+							<reference ref="1045724900"/>
+						</object>
+						<reference key="parent" ref="769124883"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">406</int>
+						<reference key="object" ref="257962622"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">407</int>
+						<reference key="object" ref="644725453"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">408</int>
+						<reference key="object" ref="1037576581"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">409</int>
+						<reference key="object" ref="941806246"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">410</int>
+						<reference key="object" ref="1045724900"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">411</int>
+						<reference key="object" ref="1058217995"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="706297211"/>
+							<reference ref="568384683"/>
+							<reference ref="663508465"/>
+						</object>
+						<reference key="parent" ref="691570813"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">412</int>
+						<reference key="object" ref="706297211"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">413</int>
+						<reference key="object" ref="568384683"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">414</int>
+						<reference key="object" ref="663508465"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">415</int>
+						<reference key="object" ref="175441468"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="252969304"/>
+							<reference ref="766922938"/>
+							<reference ref="677519740"/>
+							<reference ref="238351151"/>
+						</object>
+						<reference key="parent" ref="767671776"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">416</int>
+						<reference key="object" ref="252969304"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">417</int>
+						<reference key="object" ref="766922938"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">418</int>
+						<reference key="object" ref="677519740"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">419</int>
+						<reference key="object" ref="238351151"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">420</int>
+						<reference key="object" ref="755631768"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">450</int>
+						<reference key="object" ref="288088188"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="579392910"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">451</int>
+						<reference key="object" ref="579392910"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1060694897"/>
+							<reference ref="879586729"/>
+							<reference ref="56570060"/>
+						</object>
+						<reference key="parent" ref="288088188"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">452</int>
+						<reference key="object" ref="1060694897"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">453</int>
+						<reference key="object" ref="859480356"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">454</int>
+						<reference key="object" ref="795346622"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">457</int>
+						<reference key="object" ref="65139061"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">459</int>
+						<reference key="object" ref="19036812"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">460</int>
+						<reference key="object" ref="672708820"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">462</int>
+						<reference key="object" ref="537092702"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">465</int>
+						<reference key="object" ref="879586729"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">466</int>
+						<reference key="object" ref="56570060"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">485</int>
+						<reference key="object" ref="82994268"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">490</int>
+						<reference key="object" ref="448692316"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="992780483"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">491</int>
+						<reference key="object" ref="992780483"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="105068016"/>
+						</object>
+						<reference key="parent" ref="448692316"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">492</int>
+						<reference key="object" ref="105068016"/>
+						<reference key="parent" ref="992780483"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">494</int>
+						<reference key="object" ref="976324537"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">496</int>
+						<reference key="object" ref="215659978"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="446991534"/>
+						</object>
+						<reference key="parent" ref="941447902"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">497</int>
+						<reference key="object" ref="446991534"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="875092757"/>
+							<reference ref="630155264"/>
+							<reference ref="945678886"/>
+							<reference ref="512868991"/>
+							<reference ref="163117631"/>
+							<reference ref="31516759"/>
+							<reference ref="908105787"/>
+							<reference ref="644046920"/>
+							<reference ref="231811626"/>
+							<reference ref="883618387"/>
+						</object>
+						<reference key="parent" ref="215659978"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">498</int>
+						<reference key="object" ref="875092757"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">499</int>
+						<reference key="object" ref="630155264"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">500</int>
+						<reference key="object" ref="945678886"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">501</int>
+						<reference key="object" ref="512868991"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">502</int>
+						<reference key="object" ref="163117631"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">503</int>
+						<reference key="object" ref="31516759"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="956096989"/>
+						</object>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">504</int>
+						<reference key="object" ref="908105787"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">505</int>
+						<reference key="object" ref="644046920"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">506</int>
+						<reference key="object" ref="231811626"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">507</int>
+						<reference key="object" ref="883618387"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">508</int>
+						<reference key="object" ref="956096989"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="257099033"/>
+							<reference ref="551969625"/>
+							<reference ref="249532473"/>
+							<reference ref="607364498"/>
+							<reference ref="508151438"/>
+							<reference ref="981751889"/>
+							<reference ref="380031999"/>
+							<reference ref="825984362"/>
+							<reference ref="560145579"/>
+						</object>
+						<reference key="parent" ref="31516759"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">509</int>
+						<reference key="object" ref="257099033"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">510</int>
+						<reference key="object" ref="551969625"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">511</int>
+						<reference key="object" ref="249532473"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">512</int>
+						<reference key="object" ref="607364498"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">513</int>
+						<reference key="object" ref="508151438"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">514</int>
+						<reference key="object" ref="981751889"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">515</int>
+						<reference key="object" ref="380031999"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">516</int>
+						<reference key="object" ref="825984362"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">517</int>
+						<reference key="object" ref="560145579"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="flattenedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="NSArray" key="dict.sortedKeys">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>-3.IBPluginDependency</string>
+					<string>112.IBPluginDependency</string>
+					<string>112.ImportedFromIB2</string>
+					<string>124.IBPluginDependency</string>
+					<string>124.ImportedFromIB2</string>
+					<string>125.IBPluginDependency</string>
+					<string>125.ImportedFromIB2</string>
+					<string>125.editorWindowContentRectSynchronizationRect</string>
+					<string>126.IBPluginDependency</string>
+					<string>126.ImportedFromIB2</string>
+					<string>129.IBPluginDependency</string>
+					<string>129.ImportedFromIB2</string>
+					<string>130.IBPluginDependency</string>
+					<string>130.ImportedFromIB2</string>
+					<string>130.editorWindowContentRectSynchronizationRect</string>
+					<string>131.IBPluginDependency</string>
+					<string>131.ImportedFromIB2</string>
+					<string>134.IBPluginDependency</string>
+					<string>134.ImportedFromIB2</string>
+					<string>136.IBPluginDependency</string>
+					<string>136.ImportedFromIB2</string>
+					<string>143.IBPluginDependency</string>
+					<string>143.ImportedFromIB2</string>
+					<string>144.IBPluginDependency</string>
+					<string>144.ImportedFromIB2</string>
+					<string>145.IBPluginDependency</string>
+					<string>145.ImportedFromIB2</string>
+					<string>149.IBPluginDependency</string>
+					<string>149.ImportedFromIB2</string>
+					<string>150.IBPluginDependency</string>
+					<string>150.ImportedFromIB2</string>
+					<string>19.IBPluginDependency</string>
+					<string>19.ImportedFromIB2</string>
+					<string>195.IBPluginDependency</string>
+					<string>195.ImportedFromIB2</string>
+					<string>196.IBPluginDependency</string>
+					<string>196.ImportedFromIB2</string>
+					<string>197.IBPluginDependency</string>
+					<string>197.ImportedFromIB2</string>
+					<string>198.IBPluginDependency</string>
+					<string>198.ImportedFromIB2</string>
+					<string>199.IBPluginDependency</string>
+					<string>199.ImportedFromIB2</string>
+					<string>200.IBEditorWindowLastContentRect</string>
+					<string>200.IBPluginDependency</string>
+					<string>200.ImportedFromIB2</string>
+					<string>200.editorWindowContentRectSynchronizationRect</string>
+					<string>201.IBPluginDependency</string>
+					<string>201.ImportedFromIB2</string>
+					<string>202.IBPluginDependency</string>
+					<string>202.ImportedFromIB2</string>
+					<string>203.IBPluginDependency</string>
+					<string>203.ImportedFromIB2</string>
+					<string>204.IBPluginDependency</string>
+					<string>204.ImportedFromIB2</string>
+					<string>205.IBEditorWindowLastContentRect</string>
+					<string>205.IBPluginDependency</string>
+					<string>205.ImportedFromIB2</string>
+					<string>205.editorWindowContentRectSynchronizationRect</string>
+					<string>206.IBPluginDependency</string>
+					<string>206.ImportedFromIB2</string>
+					<string>207.IBPluginDependency</string>
+					<string>207.ImportedFromIB2</string>
+					<string>208.IBPluginDependency</string>
+					<string>208.ImportedFromIB2</string>
+					<string>209.IBPluginDependency</string>
+					<string>209.ImportedFromIB2</string>
+					<string>210.IBPluginDependency</string>
+					<string>210.ImportedFromIB2</string>
+					<string>211.IBPluginDependency</string>
+					<string>211.ImportedFromIB2</string>
+					<string>212.IBPluginDependency</string>
+					<string>212.ImportedFromIB2</string>
+					<string>212.editorWindowContentRectSynchronizationRect</string>
+					<string>213.IBPluginDependency</string>
+					<string>213.ImportedFromIB2</string>
+					<string>214.IBPluginDependency</string>
+					<string>214.ImportedFromIB2</string>
+					<string>215.IBPluginDependency</string>
+					<string>215.ImportedFromIB2</string>
+					<string>216.IBPluginDependency</string>
+					<string>216.ImportedFromIB2</string>
+					<string>217.IBPluginDependency</string>
+					<string>217.ImportedFromIB2</string>
+					<string>218.IBPluginDependency</string>
+					<string>218.ImportedFromIB2</string>
+					<string>219.IBPluginDependency</string>
+					<string>219.ImportedFromIB2</string>
+					<string>220.IBEditorWindowLastContentRect</string>
+					<string>220.IBPluginDependency</string>
+					<string>220.ImportedFromIB2</string>
+					<string>220.editorWindowContentRectSynchronizationRect</string>
+					<string>221.IBPluginDependency</string>
+					<string>221.ImportedFromIB2</string>
+					<string>23.IBPluginDependency</string>
+					<string>23.ImportedFromIB2</string>
+					<string>236.IBPluginDependency</string>
+					<string>236.ImportedFromIB2</string>
+					<string>239.IBPluginDependency</string>
+					<string>239.ImportedFromIB2</string>
+					<string>24.IBEditorWindowLastContentRect</string>
+					<string>24.IBPluginDependency</string>
+					<string>24.ImportedFromIB2</string>
+					<string>24.editorWindowContentRectSynchronizationRect</string>
+					<string>29.IBEditorWindowLastContentRect</string>
+					<string>29.IBPluginDependency</string>
+					<string>29.ImportedFromIB2</string>
+					<string>29.WindowOrigin</string>
+					<string>29.editorWindowContentRectSynchronizationRect</string>
+					<string>295.IBPluginDependency</string>
+					<string>296.IBEditorWindowLastContentRect</string>
+					<string>296.IBPluginDependency</string>
+					<string>296.editorWindowContentRectSynchronizationRect</string>
+					<string>297.IBPluginDependency</string>
+					<string>298.IBPluginDependency</string>
+					<string>346.IBPluginDependency</string>
+					<string>346.ImportedFromIB2</string>
+					<string>348.IBPluginDependency</string>
+					<string>348.ImportedFromIB2</string>
+					<string>349.IBEditorWindowLastContentRect</string>
+					<string>349.IBPluginDependency</string>
+					<string>349.ImportedFromIB2</string>
+					<string>349.editorWindowContentRectSynchronizationRect</string>
+					<string>350.IBPluginDependency</string>
+					<string>350.ImportedFromIB2</string>
+					<string>351.IBPluginDependency</string>
+					<string>351.ImportedFromIB2</string>
+					<string>354.IBPluginDependency</string>
+					<string>354.ImportedFromIB2</string>
+					<string>371.IBEditorWindowLastContentRect</string>
+					<string>371.IBPluginDependency</string>
+					<string>371.IBWindowTemplateEditedContentRect</string>
+					<string>371.NSWindowTemplate.visibleAtLaunch</string>
+					<string>371.editorWindowContentRectSynchronizationRect</string>
+					<string>371.windowTemplate.maxSize</string>
+					<string>372.IBPluginDependency</string>
+					<string>375.IBPluginDependency</string>
+					<string>376.IBEditorWindowLastContentRect</string>
+					<string>376.IBPluginDependency</string>
+					<string>377.IBPluginDependency</string>
+					<string>388.IBEditorWindowLastContentRect</string>
+					<string>388.IBPluginDependency</string>
+					<string>389.IBPluginDependency</string>
+					<string>390.IBPluginDependency</string>
+					<string>391.IBPluginDependency</string>
+					<string>392.IBPluginDependency</string>
+					<string>393.IBPluginDependency</string>
+					<string>394.IBPluginDependency</string>
+					<string>395.IBPluginDependency</string>
+					<string>396.IBPluginDependency</string>
+					<string>397.IBPluginDependency</string>
+					<string>398.IBPluginDependency</string>
+					<string>399.IBPluginDependency</string>
+					<string>400.IBPluginDependency</string>
+					<string>401.IBPluginDependency</string>
+					<string>402.IBPluginDependency</string>
+					<string>403.IBPluginDependency</string>
+					<string>404.IBPluginDependency</string>
+					<string>405.IBPluginDependency</string>
+					<string>406.IBPluginDependency</string>
+					<string>407.IBPluginDependency</string>
+					<string>408.IBPluginDependency</string>
+					<string>409.IBPluginDependency</string>
+					<string>410.IBPluginDependency</string>
+					<string>411.IBPluginDependency</string>
+					<string>412.IBPluginDependency</string>
+					<string>413.IBPluginDependency</string>
+					<string>414.IBPluginDependency</string>
+					<string>415.IBPluginDependency</string>
+					<string>416.IBPluginDependency</string>
+					<string>417.IBPluginDependency</string>
+					<string>418.IBPluginDependency</string>
+					<string>419.IBPluginDependency</string>
+					<string>450.IBPluginDependency</string>
+					<string>451.IBEditorWindowLastContentRect</string>
+					<string>451.IBPluginDependency</string>
+					<string>452.IBPluginDependency</string>
+					<string>453.IBPluginDependency</string>
+					<string>454.IBPluginDependency</string>
+					<string>457.IBPluginDependency</string>
+					<string>459.IBPluginDependency</string>
+					<string>460.IBPluginDependency</string>
+					<string>462.IBPluginDependency</string>
+					<string>465.IBPluginDependency</string>
+					<string>466.IBPluginDependency</string>
+					<string>485.IBPluginDependency</string>
+					<string>490.IBPluginDependency</string>
+					<string>491.IBEditorWindowLastContentRect</string>
+					<string>491.IBPluginDependency</string>
+					<string>492.IBPluginDependency</string>
+					<string>496.IBPluginDependency</string>
+					<string>497.IBEditorWindowLastContentRect</string>
+					<string>497.IBPluginDependency</string>
+					<string>498.IBPluginDependency</string>
+					<string>499.IBPluginDependency</string>
+					<string>5.IBPluginDependency</string>
+					<string>5.ImportedFromIB2</string>
+					<string>500.IBPluginDependency</string>
+					<string>501.IBPluginDependency</string>
+					<string>502.IBPluginDependency</string>
+					<string>503.IBPluginDependency</string>
+					<string>504.IBPluginDependency</string>
+					<string>505.IBPluginDependency</string>
+					<string>506.IBPluginDependency</string>
+					<string>507.IBPluginDependency</string>
+					<string>508.IBEditorWindowLastContentRect</string>
+					<string>508.IBPluginDependency</string>
+					<string>509.IBPluginDependency</string>
+					<string>510.IBPluginDependency</string>
+					<string>511.IBPluginDependency</string>
+					<string>512.IBPluginDependency</string>
+					<string>513.IBPluginDependency</string>
+					<string>514.IBPluginDependency</string>
+					<string>515.IBPluginDependency</string>
+					<string>516.IBPluginDependency</string>
+					<string>517.IBPluginDependency</string>
+					<string>56.IBPluginDependency</string>
+					<string>56.ImportedFromIB2</string>
+					<string>57.IBEditorWindowLastContentRect</string>
+					<string>57.IBPluginDependency</string>
+					<string>57.ImportedFromIB2</string>
+					<string>57.editorWindowContentRectSynchronizationRect</string>
+					<string>58.IBPluginDependency</string>
+					<string>58.ImportedFromIB2</string>
+					<string>72.IBPluginDependency</string>
+					<string>72.ImportedFromIB2</string>
+					<string>73.IBPluginDependency</string>
+					<string>73.ImportedFromIB2</string>
+					<string>74.IBPluginDependency</string>
+					<string>74.ImportedFromIB2</string>
+					<string>75.IBPluginDependency</string>
+					<string>75.ImportedFromIB2</string>
+					<string>77.IBPluginDependency</string>
+					<string>77.ImportedFromIB2</string>
+					<string>78.IBPluginDependency</string>
+					<string>78.ImportedFromIB2</string>
+					<string>79.IBPluginDependency</string>
+					<string>79.ImportedFromIB2</string>
+					<string>80.IBPluginDependency</string>
+					<string>80.ImportedFromIB2</string>
+					<string>81.IBEditorWindowLastContentRect</string>
+					<string>81.IBPluginDependency</string>
+					<string>81.ImportedFromIB2</string>
+					<string>81.editorWindowContentRectSynchronizationRect</string>
+					<string>82.IBPluginDependency</string>
+					<string>82.ImportedFromIB2</string>
+					<string>83.IBPluginDependency</string>
+					<string>83.ImportedFromIB2</string>
+					<string>92.IBPluginDependency</string>
+					<string>92.ImportedFromIB2</string>
+				</object>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{522, 812}, {146, 23}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{436, 809}, {64, 6}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{753, 187}, {275, 113}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {275, 83}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{547, 180}, {254, 283}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{187, 434}, {243, 243}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {167, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{753, 217}, {238, 103}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {241, 103}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{654, 239}, {194, 73}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{525, 802}, {197, 73}}</string>
+					<string>{{380, 836}, {512, 20}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{74, 862}</string>
+					<string>{{6, 978}, {478, 20}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{604, 269}, {231, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{475, 832}, {234, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{746, 287}, {220, 133}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {215, 63}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{380, 496}, {480, 360}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{380, 496}, {480, 360}}</string>
+					<integer value="1"/>
+					<string>{{33, 99}, {480, 360}}</string>
+					<string>{3.40282e+38, 3.40282e+38}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{591, 420}, {83, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{523, 2}, {178, 283}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{753, 197}, {170, 63}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{725, 289}, {246, 23}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{674, 260}, {204, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{878, 180}, {164, 173}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{286, 129}, {275, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{23, 794}, {245, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{452, 109}, {196, 203}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{145, 474}, {199, 203}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="unlocalizedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="activeLocalization"/>
+			<object class="NSMutableDictionary" key="localizations">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="sourceID"/>
+			<int key="maxID">532</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">edgeAppDelegate</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">window</string>
+						<string key="NS.object.0">NSWindow</string>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">edgeAppDelegate.h</string>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<string key="superclassName">NSResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="822405504">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSApplication.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="850738725">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSApplicationScripting.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="624831158">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSColorPanel.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSHelpManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSPageLayout.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSApplication</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSUserInterfaceItemSearching.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSBrowser</string>
+					<string key="superclassName">NSControl</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSBrowser.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSControl</string>
+					<string key="superclassName">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="310914472">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSControl.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSDocument</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="actions">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>printDocument:</string>
+							<string>revertDocumentToSaved:</string>
+							<string>runPageLayout:</string>
+							<string>saveDocument:</string>
+							<string>saveDocumentAs:</string>
+							<string>saveDocumentTo:</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDocument.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSDocument</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDocumentScripting.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSDocumentController</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="actions">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>clearRecentDocuments:</string>
+							<string>newDocument:</string>
+							<string>openDocument:</string>
+							<string>saveAllDocuments:</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+							<string>id</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDocumentController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSFontManager</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="946436764">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSFontManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSFormatter</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFormatter.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSMatrix</string>
+					<string key="superclassName">NSControl</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSMatrix.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSMenu</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="1056362899">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSMenu.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSMenuItem</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="472958451">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSMenuItem.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSMovieView</string>
+					<string key="superclassName">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSMovieView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSAccessibility.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="822405504"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="850738725"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="624831158"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="310914472"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDictionaryController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDragging.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="946436764"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSFontPanel.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSKeyValueBinding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<reference key="sourceIdentifier" ref="1056362899"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSNibLoading.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSOutlineView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSPasteboard.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSSavePanel.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="809545482">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSTableView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSToolbarItem.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="260078765">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSClassDescription.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObjectScripting.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSPortCoder.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptClassDescription.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptObjectSpecifiers.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptWhoseTests.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLDownload.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSInterfaceStyle.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSResponder</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSResponder.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSTableView</string>
+					<string key="superclassName">NSControl</string>
+					<reference key="sourceIdentifier" ref="809545482"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSText</string>
+					<string key="superclassName">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSText.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSTextView</string>
+					<string key="superclassName">NSText</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSTextView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSClipView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSView</string>
+					<reference key="sourceIdentifier" ref="472958451"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSRulerView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSView</string>
+					<string key="superclassName">NSResponder</string>
+					<reference key="sourceIdentifier" ref="260078765"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSWindow</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSDrawer.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSWindow</string>
+					<string key="superclassName">NSResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSWindow.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSWindow</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">AppKit.framework/Headers/NSWindowScripting.h</string>
+					</object>
+				</object>
+			</object>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
+			<integer value="1060" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+			<integer value="3000" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<string key="IBDocument.LastKnownRelativeProjectPath">../edge.xcodeproj</string>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+	</data>
+</archive>
diff --git a/experimental/Intersection/Extrema.cpp b/experimental/Intersection/Extrema.cpp
new file mode 100644
index 0000000..780d877
--- /dev/null
+++ b/experimental/Intersection/Extrema.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 "DataTypes.h"
+#include "Extrema.h"
+
+static int validUnitDivide(double numer, double denom, double* ratio)
+{
+    if (numer < 0) {
+        numer = -numer;
+        denom = -denom;
+    }
+    if (denom == 0 || numer == 0 || numer >= denom)
+        return 0;
+    double r = numer / denom;
+    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
+*/
+static int findUnitQuadRoots(double A, double B, double C, double roots[2])
+{
+    if (A == 0)
+        return validUnitDivide(-C, B, roots);
+
+    double* r = roots;
+
+    double R = B*B - 4*A*C;
+    if (R < 0) {  // complex roots
+        return 0;
+    }
+    R = sqrt(R);
+
+    double Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
+    r += validUnitDivide(Q, A, r);
+    r += validUnitDivide(C, Q, r);
+    if (r - roots == 2 && approximately_equal(roots[0], roots[1])) { // nearly-equal?
+        r -= 1; // skip the double root
+    }
+    return (int)(r - roots);
+}
+
+/** 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 between 0 < t < 1
+*/
+int findExtrema(double a, double b, double c, double d, double tValues[2])
+{
+    // we divide A,B,C by 3 to simplify
+    double A = d - a + 3*(b - c);
+    double B = 2*(a - b - b + c);
+    double C = b - a;
+
+    return findUnitQuadRoots(A, B, C, tValues);
+}
+
+/** 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 findExtrema(double a, double b, double c, double tValue[1])
+{
+    /*  At + B == 0
+        t = -B / A
+    */
+    return validUnitDivide(a - b, a - b - b + c, tValue);
+}
diff --git a/experimental/Intersection/Extrema.h b/experimental/Intersection/Extrema.h
new file mode 100644
index 0000000..94113ac
--- /dev/null
+++ b/experimental/Intersection/Extrema.h
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+int findExtrema(double a, double b, double c, double d, double tValues[2]);
+int findExtrema(double a, double b, double c, double tValue[1]);
diff --git a/experimental/Intersection/Inline_Tests.cpp b/experimental/Intersection/Inline_Tests.cpp
new file mode 100644
index 0000000..00a1c6c
--- /dev/null
+++ b/experimental/Intersection/Inline_Tests.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 "CurveIntersection.h"
+#include "Intersection_Tests.h"
+#include "IntersectionUtilities.h"
+
+static void assert_that(int x, int y, const char* s) {
+    if (x == y) {
+        return;
+    }
+    printf("result=%d expected=%d %s\n", x, y, s);
+}
+
+static void side_test() {
+    assert_that(side(-1), 0, "side(-1) != 0");
+    assert_that(side(0), 1, "side(0) != 1");
+    assert_that(side(1), 2, "side(1) != 2");
+}
+
+static void sideBit_test() {
+    assert_that(sideBit(-1), 1, "sideBit(-1) != 1");
+    assert_that(sideBit(0), 2, "sideBit(0) != 2");
+    assert_that(sideBit(1), 4, "sideBit(1) != 4");
+}
+
+static void other_two_test() {
+    for (int x = 0; x < 4; ++x) {
+        for (int y = 0; y < 4; ++y) {
+            if (x == y) {
+                continue;
+            }
+            int mask = other_two(x, y);
+            int all = 1 << x;
+            all |= 1 << y;
+            all |= 1 << (x ^ mask);
+            all |= 1 << (y ^ mask);
+            if (all == 0x0F) {
+                continue;
+            }
+            printf("[%d,%d] other_two failed mask=%d [%d,%d]\n",
+                x, y, mask, x ^ mask, y ^ mask);
+        }
+    }
+}
+
+void Inline_Tests() {
+    side_test();
+    sideBit_test();
+    other_two_test();
+}
diff --git a/experimental/Intersection/IntersectionUtilities.cpp b/experimental/Intersection/IntersectionUtilities.cpp
new file mode 100644
index 0000000..e6c4abc
--- /dev/null
+++ b/experimental/Intersection/IntersectionUtilities.cpp
@@ -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.
+ */
+
+#if 0
+// snippets that one day may be useful, unused for now...
+
+// get sign, exponent, mantissa from double
+// Translate the double into sign, exponent and mantissa.
+    long bits = BitConverter.DoubleToInt64Bits(d);
+    // Note that the shift is sign-extended, hence the test against -1 not 1
+    bool negative = (bits < 0);
+    int exponent = (int) ((bits >> 52) & 0x7ffL);
+    long mantissa = bits & 0xfffffffffffffL;
+
+    // Subnormal numbers; exponent is effectively one higher,
+    // but there's no extra normalisation bit in the mantissa
+    if (exponent==0)
+    {
+        exponent++;
+    }
+    // Normal numbers; leave exponent as it is but add extra
+    // bit to the front of the mantissa
+    else
+    {
+        mantissa = mantissa | (1L<<52);
+    }
+
+    // Bias the exponent. It's actually biased by 1023, but we're
+    // treating the mantissa as m.0 rather than 0.m, so we need
+    // to subtract another 52 from it.
+    exponent -= 1075;
+
+    if (mantissa == 0)
+    {
+        return "0";
+    }
+
+    /* Normalize */
+    while((mantissa & 1) == 0)
+    {    /*  i.e., Mantissa is even */
+        mantissa >>= 1;
+        exponent++;
+    }
+#endif
diff --git a/experimental/Intersection/IntersectionUtilities.h b/experimental/Intersection/IntersectionUtilities.h
new file mode 100644
index 0000000..b7e69fb
--- /dev/null
+++ b/experimental/Intersection/IntersectionUtilities.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.
+ */
+
+// inline utilities
+/* Returns 0 if negative, 1 if zero, 2 if positive
+*/
+inline int side(double x) {
+    return (x > 0) + (x >= 0);
+}
+
+/* Returns 1 if negative, 2 if zero, 4 if positive
+*/
+inline int sideBit(double x) {
+    return 1 << side(x);
+}
+
+/* Given the set [0, 1, 2, 3], and two of the four members, compute an XOR mask
+   that computes the other two. Note that:
+
+   one ^ two == 3 for (0, 3), (1, 2)
+   one ^ two <  3 for (0, 1), (0, 2), (1, 3), (2, 3)
+   3 - (one ^ two) is either 0, 1, or 2
+   1 >> 3 - (one ^ two) is either 0 or 1
+thus:
+   returned == 2 for (0, 3), (1, 2)
+   returned == 3 for (0, 1), (0, 2), (1, 3), (2, 3)
+given that:
+   (0, 3) ^ 2 -> (2, 1)  (1, 2) ^ 2 -> (3, 0)
+   (0, 1) ^ 3 -> (3, 2)  (0, 2) ^ 3 -> (3, 1)  (1, 3) ^ 3 -> (2, 0)  (2, 3) ^ 3 -> (1, 0)
+*/
+inline int other_two(int one, int two) {
+    return 1 >> 3 - (one ^ two) ^ 3;
+}
+
+/* Returns -1 if negative, 0 if zero, 1 if positive
+*/
+inline int sign(double x) {
+    return (x > 0) - (x < 0);
+}
+
+inline double interp(double A, double B, double t) {
+    return A + (B - A) * t;
+}
diff --git a/experimental/Intersection/Intersection_Tests.cpp b/experimental/Intersection/Intersection_Tests.cpp
new file mode 100644
index 0000000..8a8cb30
--- /dev/null
+++ b/experimental/Intersection/Intersection_Tests.cpp
@@ -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.
+ */
+#include "CubicIntersection_TestData.h"
+#include "Intersection_Tests.h"
+#include "SkTypes.h"
+
+void cubecode_test(int test);
+
+#define TEST_QUADS_FIRST 0
+
+void Intersection_Tests() {
+    int testsRun = 0;
+    SimplifyNew_Test();
+    QuadraticIntersection_Test();
+    MiniSimplify_Test();
+    SimplifyAngle_Test();
+    QuarticRoot_Test();
+ //   QuadraticIntersection_Test();
+    Simplify4x4QuadraticsThreaded_Test(testsRun);
+    QuadLineIntersectThreaded_Test(testsRun);
+    LineQuadraticIntersection_Test();
+    Simplify4x4RectsThreaded_Test(testsRun);
+    SimplifyNondegenerate4x4TrianglesThreaded_Test(testsRun);
+    SimplifyDegenerate4x4TrianglesThreaded_Test(testsRun);
+    Simplify4x4QuadralateralsThreaded_Test(testsRun);
+    SkDebugf("%s total testsRun=%d\n", __FUNCTION__, testsRun);
+    QuadraticBezierClip_Test();
+    SimplifyFindNext_Test();
+    SimplifyFindTop_Test();
+    QuadraticReduceOrder_Test();
+    SimplifyAddIntersectingTs_Test();
+
+    cubecode_test(1);
+    convert_testx();
+    // tests are in dependency / complexity order
+    Inline_Tests();
+    ConvexHull_Test();
+    ConvexHull_X_Test();
+
+    LineParameter_Test();
+    LineIntersection_Test();
+    LineCubicIntersection_Test();
+
+    SimplifyQuadraticPaths_Test();
+
+    SimplifyPolygonPaths_Test();
+    SimplifyRectangularPaths_Test();
+    SimplifyQuadralateralPaths_Test();
+
+    ActiveEdge_Test();
+
+    QuadraticCoincidence_Test();
+    QuadraticIntersection_Test();
+
+    CubicParameterization_Test();
+    CubicCoincidence_Test();
+    CubicReduceOrder_Test();
+    CubicBezierClip_Test();
+    CubicIntersection_Test();
+
+}
diff --git a/experimental/Intersection/Intersection_Tests.h b/experimental/Intersection/Intersection_Tests.h
new file mode 100644
index 0000000..094e844
--- /dev/null
+++ b/experimental/Intersection/Intersection_Tests.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.
+ */
+#if !defined(IN_TEST)
+    #define IN_TEST 1
+#endif
+
+void ActiveEdge_Test();
+void ConvexHull_Test();
+void ConvexHull_X_Test();
+void CubicBezierClip_Test();
+void CubicCoincidence_Test();
+void CubicIntersection_Test();
+void CubicParameterization_Test();
+void CubicReduceOrder_Test();
+void Inline_Tests();
+void Intersection_Tests();
+void LineCubicIntersection_Test();
+void LineIntersection_Test();
+void LineParameter_Test();
+void LineQuadraticIntersection_Test();
+void MiniSimplify_Test();
+void SimplifyAddIntersectingTs_Test();
+void SimplifyAngle_Test();
+void SimplifyDegenerate4x4TrianglesThreaded_Test(int& );
+void SimplifyFindNext_Test();
+void SimplifyFindTop_Test();
+void SimplifyNew_Test();
+void SimplifyNondegenerate4x4TrianglesThreaded_Test(int& );
+void SimplifyPolygonPaths_Test();
+void SimplifyQuadralateralPaths_Test();
+void SimplifyQuadraticPaths_Test();
+void Simplify4x4QuadralateralsThreaded_Test(int& );
+void Simplify4x4QuadraticsThreaded_Test(int& );
+void Simplify4x4RectsThreaded_Test(int& );
+void SimplifyRectangularPaths_Test();
+void QuadLineIntersectThreaded_Test(int& );
+void QuadraticBezierClip_Test();
+void QuadraticCoincidence_Test();
+void QuadraticIntersection_Test();
+void QuadraticParameterization_Test();
+void QuadraticReduceOrder_Test();
+void QuarticRoot_Test();
diff --git a/experimental/Intersection/Intersections.cpp b/experimental/Intersection/Intersections.cpp
new file mode 100644
index 0000000..3f4e8cf
--- /dev/null
+++ b/experimental/Intersection/Intersections.cpp
@@ -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.
+ */
+
+#include "DataTypes.h"
+#include "Intersections.h"
+
+void Intersections::cleanUp() {
+    assert(fCoincidentUsed);
+    assert(fUsed);
+    // find any entries in fT that could be part of the coincident range
+
+}
diff --git a/experimental/Intersection/Intersections.h b/experimental/Intersection/Intersections.h
new file mode 100644
index 0000000..c1421e3
--- /dev/null
+++ b/experimental/Intersection/Intersections.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef Intersections_DEFINE
+#define Intersections_DEFINE
+
+#include <algorithm> // for std::min
+
+class Intersections {
+public:
+    Intersections()
+        : fUsed(0)
+        , fUsed2(0)
+        , fCoincidentUsed(0)
+        , fSwap(0)
+        , fFlip(0)
+    {
+        // OPTIMIZE: don't need to be initialized in release
+        bzero(fT, sizeof(fT));
+        bzero(fCoincidentT, sizeof(fCoincidentT));
+    }
+
+    void add(double one, double two) {
+        for (int index = 0; index < fUsed; ++index) {
+            if (approximately_equal(fT[fSwap][index], one)
+                    && approximately_equal(fT[fSwap ^ 1][index], two)) {
+                return;
+            }
+        }
+        assert(fUsed < 9);
+        fT[fSwap][fUsed] = one;
+        fT[fSwap ^ 1][fUsed] = two;
+        ++fUsed;
+    }
+
+    // start if index == 0 : end if index == 1
+    void addCoincident(double one, double two) {
+        for (int index = 0; index < fCoincidentUsed; ++index) {
+            if (approximately_equal(fCoincidentT[fSwap][index], one)
+                    && approximately_equal(fCoincidentT[fSwap ^ 1][index], two)) {
+                return;
+            }
+        }
+        assert(fCoincidentUsed < 9);
+        fCoincidentT[fSwap][fCoincidentUsed] = one;
+        fCoincidentT[fSwap ^ 1][fCoincidentUsed] = two;
+        ++fCoincidentUsed;
+    }
+
+    void addCoincident(double s1, double e1, double s2, double e2) {
+        assert((fCoincidentUsed & 1) != 1);
+        for (int index = 0; index < fCoincidentUsed; index += 2) {
+            double cs1 = fCoincidentT[fSwap][index];
+            double ce1 = fCoincidentT[fSwap][index + 1];
+            bool s1in = approximately_between(cs1, s1, ce1);
+            bool e1in = approximately_between(cs1, e1, ce1);
+            double cs2 = fCoincidentT[fSwap ^ 1][index];
+            double ce2 = fCoincidentT[fSwap ^ 1][index + 1];
+            bool s2in = approximately_between(cs2, s2, ce2);
+            bool e2in = approximately_between(cs2, e2, ce2);
+            if ((s1in | e1in) & (s2in | e2in)) {
+                double lesser1 = std::min(cs1, ce1);
+                index += cs1 > ce1;
+                if (s1in < lesser1) {
+                    fCoincidentT[fSwap][index] = s1in;
+                } else if (e1in < lesser1) {
+                    fCoincidentT[fSwap][index] = e1in;
+                }
+                index ^= 1;
+                double greater1 = fCoincidentT[fSwap][index];
+                if (s1in > greater1) {
+                    fCoincidentT[fSwap][index] = s1in;
+                } else if (e1in > greater1) {
+                    fCoincidentT[fSwap][index] = e1in;
+                }
+                index &= ~1;
+                double lesser2 = std::min(cs2, ce2);
+                index += cs2 > ce2;
+                if (s2in < lesser2) {
+                    fCoincidentT[fSwap ^ 1][index] = s2in;
+                } else if (e2in < lesser2) {
+                    fCoincidentT[fSwap ^ 1][index] = e2in;
+                }
+                index ^= 1;
+                double greater2 = fCoincidentT[fSwap ^ 1][index];
+                if (s2in > greater2) {
+                    fCoincidentT[fSwap ^ 1][index] = s2in;
+                } else if (e2in > greater2) {
+                    fCoincidentT[fSwap ^ 1][index] = e2in;
+                }
+                return;
+            }
+        }
+        assert(fCoincidentUsed < 9);
+        fCoincidentT[fSwap][fCoincidentUsed] = s1;
+        fCoincidentT[fSwap ^ 1][fCoincidentUsed] = s2;
+        ++fCoincidentUsed;
+        fCoincidentT[fSwap][fCoincidentUsed] = e1;
+        fCoincidentT[fSwap ^ 1][fCoincidentUsed] = e2;
+        ++fCoincidentUsed;
+    }
+
+    // FIXME: this is necessary because curve/curve intersections are noisy
+    // remove once curve/curve intersections are improved
+    void cleanUp();
+
+    int coincidentUsed() {
+        return fCoincidentUsed;
+    }
+
+    void offset(int base, double start, double end) {
+        for (int index = base; index < fUsed; ++index) {
+            double val = fT[fSwap][index];
+            val *= end - start;
+            val += start;
+            fT[fSwap][index] = val;
+        }
+    }
+
+    void insert(double one, double two) {
+        assert(fUsed <= 1 || fT[0][0] < fT[0][1]);
+        int index;
+        for (index = 0; index < fUsed; ++index) {
+            if (approximately_equal(fT[0][index], one)
+                    && approximately_equal(fT[1][index], two)) {
+                return;
+            }
+            if (fT[0][index] > one) {
+                break;
+            }
+        }
+        assert(fUsed < 9);
+        int remaining = fUsed - index;
+        if (remaining > 0) {
+            memmove(&fT[0][index + 1], &fT[0][index], sizeof(fT[0][0]) * remaining);
+            memmove(&fT[1][index + 1], &fT[1][index], sizeof(fT[1][0]) * remaining);
+        }
+        fT[0][index] = one;
+        fT[1][index] = two;
+        ++fUsed;
+    }
+
+    void insertOne(double t, int side) {
+        int used = side ? fUsed2 : fUsed;
+        assert(used <= 1 || fT[side][0] < fT[side][1]);
+        int index;
+        for (index = 0; index < used; ++index) {
+            if (approximately_equal(fT[side][index], t)) {
+                return;
+            }
+            if (fT[side][index] > t) {
+                break;
+            }
+        }
+        assert(used < 9);
+        int remaining = used - index;
+        if (remaining > 0) {
+            memmove(&fT[side][index + 1], &fT[side][index], sizeof(fT[side][0]) * remaining);
+        }
+        fT[side][index] = t;
+        side ? ++fUsed2 : ++fUsed;
+    }
+
+    bool intersected() const {
+        return fUsed > 0;
+    }
+
+    bool insertBalanced() const {
+        return fUsed == fUsed2;
+    }
+
+    void swap() {
+        fSwap ^= 1;
+    }
+
+    bool swapped() {
+        return fSwap;
+    }
+
+    int used() {
+        return fUsed;
+    }
+
+    double fT[2][9];
+    double fCoincidentT[2][9];
+    int fUsed;
+    int fUsed2;
+    int fCoincidentUsed;
+    int fFlip;
+private:
+    int fSwap;
+};
+
+#endif
+
diff --git a/experimental/Intersection/LineCubicIntersection.cpp b/experimental/Intersection/LineCubicIntersection.cpp
new file mode 100644
index 0000000..3111ddd
--- /dev/null
+++ b/experimental/Intersection/LineCubicIntersection.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "CubicUtilities.h"
+#include "Intersections.h"
+#include "LineUtilities.h"
+
+/*
+Find the interection of a line and cubic by solving for valid t values.
+
+Analogous to line-quadratic intersection, solve line-cubic intersection by
+representing the cubic as:
+  x = a(1-t)^3 + 2b(1-t)^2t + c(1-t)t^2 + dt^3
+  y = e(1-t)^3 + 2f(1-t)^2t + g(1-t)t^2 + ht^3
+and the line as:
+  y = i*x + j  (if the line is more horizontal)
+or:
+  x = i*y + j  (if the line is more vertical)
+
+Then using Mathematica, solve for the values of t where the cubic intersects the
+line:
+
+  (in) Resultant[
+        a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - x,
+        e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - i*x - j, x]
+  (out) -e     +   j     +
+       3 e t   - 3 f t   -
+       3 e t^2 + 6 f t^2 - 3 g t^2 +
+         e t^3 - 3 f t^3 + 3 g t^3 - h t^3 +
+     i ( a     -
+       3 a t + 3 b t +
+       3 a t^2 - 6 b t^2 + 3 c t^2 -
+         a t^3 + 3 b t^3 - 3 c t^3 + d t^3 )
+
+if i goes to infinity, we can rewrite the line in terms of x. Mathematica:
+
+  (in) Resultant[
+        a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - i*y - j,
+        e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y,       y]
+  (out)  a     -   j     -
+       3 a t   + 3 b t   +
+       3 a t^2 - 6 b t^2 + 3 c t^2 -
+         a t^3 + 3 b t^3 - 3 c t^3 + d t^3 -
+     i ( e     -
+       3 e t   + 3 f t   +
+       3 e t^2 - 6 f t^2 + 3 g t^2 -
+         e t^3 + 3 f t^3 - 3 g t^3 + h t^3 )
+
+Solving this with Mathematica produces an expression with hundreds of terms;
+instead, use Numeric Solutions recipe to solve the cubic.
+
+The near-horizontal case, in terms of:  Ax^3 + Bx^2 + Cx + D == 0
+    A =   (-(-e + 3*f - 3*g + h) + i*(-a + 3*b - 3*c + d)     )
+    B = 3*(-( e - 2*f +   g    ) + i*( a - 2*b +   c    )     )
+    C = 3*(-(-e +   f          ) + i*(-a +   b          )     )
+    D =   (-( e                ) + i*( a                ) + j )
+
+The near-vertical case, in terms of:  Ax^3 + Bx^2 + Cx + D == 0
+    A =   ( (-a + 3*b - 3*c + d) - i*(-e + 3*f - 3*g + h)     )
+    B = 3*( ( a - 2*b +   c    ) - i*( e - 2*f +   g    )     )
+    C = 3*( (-a +   b          ) - i*(-e +   f          )     )
+    D =   ( ( a                ) - i*( e                ) - j )
+
+For horizontal lines:
+(in) Resultant[
+      a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - j,
+      e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, y]
+(out)  e     -   j     -
+     3 e t   + 3 f t   +
+     3 e t^2 - 6 f t^2 + 3 g t^2 -
+       e t^3 + 3 f t^3 - 3 g t^3 + h t^3
+So the cubic coefficients are:
+
+ */
+
+class LineCubicIntersections : public Intersections {
+public:
+
+LineCubicIntersections(const Cubic& c, const _Line& l, double r[3])
+    : cubic(c)
+    , line(l)
+    , range(r) {
+}
+
+int intersect() {
+    double slope;
+    double axisIntercept;
+    moreHorizontal = implicitLine(line, slope, axisIntercept);
+    double A, B, C, D;
+    coefficients(&cubic[0].x, A, B, C, D);
+    double E, F, G, H;
+    coefficients(&cubic[0].y, E, F, G, H);
+    if (moreHorizontal) {
+        A = A * slope - E;
+        B = B * slope - F;
+        C = C * slope - G;
+        D = D * slope - H + axisIntercept;
+    } else {
+        A = A - E * slope;
+        B = B - F * slope;
+        C = C - G * slope;
+        D = D - H * slope - axisIntercept;
+    }
+    return cubicRoots(A, B, C, D, range);
+}
+
+int horizontalIntersect(double axisIntercept) {
+    double A, B, C, D;
+    coefficients(&cubic[0].y, A, B, C, D);
+    D -= axisIntercept;
+    return cubicRoots(A, B, C, D, range);
+}
+
+int verticalIntersect(double axisIntercept) {
+    double A, B, C, D;
+    coefficients(&cubic[0].x, A, B, C, D);
+    D -= axisIntercept;
+    return cubicRoots(A, B, C, D, range);
+}
+
+double findLineT(double t) {
+    const double* cPtr;
+    const double* lPtr;
+    if (moreHorizontal) {
+        cPtr = &cubic[0].x;
+        lPtr = &line[0].x;
+    } else {
+        cPtr = &cubic[0].y;
+        lPtr = &line[0].y;
+    }
+    // FIXME: should fold the following in with TestUtilities.cpp xy_at_t()
+    double s = 1 - t;
+    double cubicVal = cPtr[0] * s * s * s + 3 * cPtr[2] * s * s * t
+                + 3 * cPtr[4] * s * t * t + cPtr[6] * t * t * t;
+    return (cubicVal - lPtr[0]) / (lPtr[2] - lPtr[0]);
+}
+
+private:
+
+const Cubic& cubic;
+const _Line& line;
+double* range;
+bool moreHorizontal;
+
+};
+
+int horizontalIntersect(const Cubic& cubic, double y, double tRange[3]) {
+    LineCubicIntersections c(cubic, *((_Line*) 0), tRange);
+    return c.horizontalIntersect(y);
+}
+
+int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
+        double tRange[3]) {
+    LineCubicIntersections c(cubic, *((_Line*) 0), tRange);
+    int result = c.horizontalIntersect(y);
+    for (int index = 0; index < result; ) {
+        double x, y;
+        xy_at_t(cubic, tRange[index], x, y);
+        if (x < left || x > right) {
+            if (--result > index) {
+                tRange[index] = tRange[result];
+            }
+            continue;
+        }
+        ++index;
+    }
+    return result;
+}
+
+int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
+        bool flipped, Intersections& intersections) {
+    LineCubicIntersections c(cubic, *((_Line*) 0), intersections.fT[0]);
+    int result = c.horizontalIntersect(y);
+    for (int index = 0; index < result; ) {
+        double x, y;
+        xy_at_t(cubic, intersections.fT[0][index], x, y);
+        if (x < left || x > right) {
+            if (--result > index) {
+                intersections.fT[0][index] = intersections.fT[0][result];
+            }
+            continue;
+        }
+        intersections.fT[1][index] = (x - left) / (right - left);
+        ++index;
+    }
+    if (flipped) {
+        // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
+        for (int index = 0; index < result; ++index) {
+            intersections.fT[1][index] = 1 - intersections.fT[1][index];
+        }
+    }
+    return result;
+}
+
+int verticalIntersect(const Cubic& cubic, double top, double bottom, double x,
+        bool flipped, Intersections& intersections) {
+    LineCubicIntersections c(cubic, *((_Line*) 0), intersections.fT[0]);
+    int result = c.verticalIntersect(x);
+    for (int index = 0; index < result; ) {
+        double x, y;
+        xy_at_t(cubic, intersections.fT[0][index], x, y);
+        if (y < top || y > bottom) {
+            if (--result > index) {
+                intersections.fT[1][index] = intersections.fT[0][result];
+            }
+            continue;
+        }
+        intersections.fT[0][index] = (y - top) / (bottom - top);
+        ++index;
+    }
+    if (flipped) {
+        // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
+        for (int index = 0; index < result; ++index) {
+            intersections.fT[1][index] = 1 - intersections.fT[1][index];
+        }
+    }
+    return result;
+}
+
+int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]) {
+    LineCubicIntersections c(cubic, line, cRange);
+    int roots;
+    if (approximately_equal(line[0].y, line[1].y)) {
+        roots = c.horizontalIntersect(line[0].y);
+    } else {
+        roots = c.intersect();
+    }
+    for (int index = 0; index < roots; ++index) {
+        lRange[index] = c.findLineT(cRange[index]);
+    }
+    return roots;
+}
diff --git a/experimental/Intersection/LineCubicIntersection_Test.cpp b/experimental/Intersection/LineCubicIntersection_Test.cpp
new file mode 100644
index 0000000..cc993a7
--- /dev/null
+++ b/experimental/Intersection/LineCubicIntersection_Test.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "CurveUtilities.h"
+#include "Intersection_Tests.h"
+#include "Intersections.h"
+#include "TestUtilities.h"
+
+struct lineCubic {
+    Cubic cubic;
+    _Line line;
+} lineCubicTests[] = {
+    {{{0, 0}, {0, 1}, {0, 1}, {1, 1}}, {{0, 1}, {1, 0}}}
+};
+
+size_t lineCubicTests_count = sizeof(lineCubicTests) / sizeof(lineCubicTests[0]);
+
+const int firstLineCubicIntersectionTest = 0;
+
+void LineCubicIntersection_Test() {
+    for (size_t index = firstLineCubicIntersectionTest; index < lineCubicTests_count; ++index) {
+        const Cubic& cubic = lineCubicTests[index].cubic;
+        const _Line& line = lineCubicTests[index].line;
+        Cubic reduce1;
+        _Line reduce2;
+        int order1 = reduceOrder(cubic, reduce1, kReduceOrder_NoQuadraticsAllowed);
+        int order2 = reduceOrder(line, reduce2);
+        if (order1 < 4) {
+            printf("[%d] cubic order=%d\n", (int) index, order1);
+        }
+        if (order2 < 2) {
+            printf("[%d] line order=%d\n", (int) index, order2);
+        }
+        if (order1 == 4 && order2 == 2) {
+            double range1[2], range2[2];
+            int roots = intersect(reduce1, reduce2, range1, range2);
+            for (int pt = 0; pt < roots; ++pt) {
+                double tt1 = range1[pt];
+                double tx1, ty1;
+                xy_at_t(cubic, tt1, tx1, ty1);
+                double tt2 = range2[pt];
+                double tx2, ty2;
+                xy_at_t(line, tt2, tx2, ty2);
+                if (!approximately_equal(tx1, tx2)) {
+                    printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                        __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+                }
+                if (!approximately_equal(ty1, ty2)) {
+                    printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                        __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+                }
+            }
+        }
+    }
+}
diff --git a/experimental/Intersection/LineIntersection.cpp b/experimental/Intersection/LineIntersection.cpp
new file mode 100644
index 0000000..11f42ba
--- /dev/null
+++ b/experimental/Intersection/LineIntersection.cpp
@@ -0,0 +1,325 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "Intersections.h"
+#include "LineIntersection.h"
+#include <algorithm> // used for std::swap
+
+
+/*
+   Determine the intersection point of two line segments
+   Return FALSE if the lines don't intersect
+   from: http://paulbourke.net/geometry/lineline2d/
+*/
+
+int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]) {
+    double axLen = a[1].x - a[0].x;
+    double ayLen = a[1].y - a[0].y;
+    double bxLen = b[1].x - b[0].x;
+    double byLen = b[1].y - b[0].y;
+    /* Slopes match when denom goes to zero:
+                      axLen / ayLen ==                   bxLen / byLen
+    (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen
+             byLen  * axLen         ==  ayLen          * bxLen
+             byLen  * axLen         -   ayLen          * bxLen == 0 ( == denom )
+     */
+    double denom  = byLen * axLen - ayLen * bxLen;
+    if (approximately_zero(denom)) {
+       /* See if the axis intercepts match:
+                  ay - ax * ayLen / axLen  ==          by - bx * ayLen / axLen
+         axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen)
+         axLen *  ay - ax * ayLen          == axLen *  by - bx * ayLen
+        */
+        if (approximately_equal_squared(axLen * a[0].y - ayLen * a[0].x,
+                axLen * b[0].y - ayLen * b[0].x)) {
+            const double* aPtr;
+            const double* bPtr;
+            if (fabs(axLen) > fabs(ayLen) || fabs(bxLen) > fabs(byLen)) {
+                aPtr = &a[0].x;
+                bPtr = &b[0].x;
+            } else {
+                aPtr = &a[0].y;
+                bPtr = &b[0].y;
+            }
+        #if 0 // sorting edges fails to preserve original direction
+            double aMin = aPtr[0];
+            double aMax = aPtr[2];
+            double bMin = bPtr[0];
+            double bMax = bPtr[2];
+            if (aMin > aMax) {
+                std::swap(aMin, aMax);
+            }
+            if (bMin > bMax) {
+                std::swap(bMin, bMax);
+            }
+            if (aMax < bMin || bMax < aMin) {
+                return 0;
+            }
+            if (aRange) {
+                aRange[0] = bMin <= aMin ? 0 : (bMin - aMin) / (aMax - aMin);
+                aRange[1] = bMax >= aMax ? 1 : (bMax - aMin) / (aMax - aMin);
+            }
+            int bIn = (aPtr[0] - aPtr[2]) * (bPtr[0] - bPtr[2]) < 0;
+            if (bRange) {
+                bRange[bIn] = aMin <= bMin ? 0 : (aMin - bMin) / (bMax - bMin);
+                bRange[!bIn] = aMax >= bMax ? 1 : (aMax - bMin) / (bMax - bMin);
+            }
+            return 1 + ((aRange[0] != aRange[1]) || (bRange[0] != bRange[1]));
+        #else
+            assert(aRange);
+            assert(bRange);
+            double a0 = aPtr[0];
+            double a1 = aPtr[2];
+            double b0 = bPtr[0];
+            double b1 = bPtr[2];
+            double at0 = (a0 - b0) / (a0 - a1);
+            double at1 = (a0 - b1) / (a0 - a1);
+            if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
+                return 0;
+            }
+            aRange[0] = std::max(std::min(at0, 1.0), 0.0);
+            aRange[1] = std::max(std::min(at1, 1.0), 0.0);
+            int bIn = (a0 - a1) * (b0 - b1) < 0;
+            bRange[bIn] = std::max(std::min((b0 - a0) / (b0 - b1), 1.0), 0.0);
+            bRange[!bIn] = std::max(std::min((b0 - a1) / (b0 - b1), 1.0), 0.0);
+            bool second = fabs(aRange[0] - aRange[1]) > FLT_EPSILON;
+            assert((fabs(bRange[0] - bRange[1]) <= FLT_EPSILON) ^ second);
+            return 1 + second;
+        #endif
+        }
+    }
+    double ab0y = a[0].y - b[0].y;
+    double ab0x = a[0].x - b[0].x;
+    double numerA = ab0y * bxLen - byLen * ab0x;
+    if (numerA < 0 && denom > numerA || numerA > 0 && denom < numerA) {
+        return 0;
+    }
+    double numerB = ab0y * axLen - ayLen * ab0x;
+    if (numerB < 0 && denom > numerB || numerB > 0 && denom < numerB) {
+        return 0;
+    }
+    /* Is the intersection along the the segments */
+    if (aRange) {
+        aRange[0] = numerA / denom;
+    }
+    if (bRange) {
+        bRange[0] = numerB / denom;
+    }
+    return 1;
+}
+
+int horizontalIntersect(const _Line& line, double y, double tRange[2]) {
+    double min = line[0].y;
+    double max = line[1].y;
+    if (min > max) {
+        std::swap(min, max);
+    }
+    if (min > y || max < y) {
+        return 0;
+    }
+    if (approximately_equal(min, max)) {
+        tRange[0] = 0;
+        tRange[1] = 1;
+        return 2;
+    }
+    tRange[0] = (y - line[0].y) / (line[1].y - line[0].y);
+    return 1;
+}
+
+// OPTIMIZATION  Given: dy = line[1].y - line[0].y
+// and: xIntercept / (y - line[0].y) == (line[1].x - line[0].x) / dy
+// then: xIntercept * dy == (line[1].x - line[0].x) * (y - line[0].y)
+// Assuming that dy is always > 0, the line segment intercepts if:
+//   left * dy <= xIntercept * dy <= right * dy
+// thus: left * dy <= (line[1].x - line[0].x) * (y - line[0].y) <= right * dy
+// (clever as this is, it does not give us the t value, so may be useful only
+// as a quick reject -- and maybe not then; it takes 3 muls, 3 adds, 2 cmps)
+int horizontalLineIntersect(const _Line& line, double left, double right,
+        double y, double tRange[2]) {
+    int result = horizontalIntersect(line, y, tRange);
+    if (result != 1) {
+        // FIXME: this is incorrect if result == 2
+        return result;
+    }
+    double xIntercept = line[0].x + tRange[0] * (line[1].x - line[0].x);
+    if (xIntercept > right || xIntercept < left) {
+        return 0;
+    }
+    return result;
+}
+
+int horizontalIntersect(const _Line& line, double left, double right,
+        double y, bool flipped, Intersections& intersections) {
+    int result = horizontalIntersect(line, y, intersections.fT[0]);
+    switch (result) {
+        case 0:
+            break;
+        case 1: {
+            double xIntercept = line[0].x + intersections.fT[0][0]
+                    * (line[1].x - line[0].x);
+            if (xIntercept > right || xIntercept < left) {
+                return 0;
+            }
+            intersections.fT[1][0] = (xIntercept - left) / (right - left);
+            break;
+        }
+        case 2:
+        #if 0 // sorting edges fails to preserve original direction
+            double lineL = line[0].x;
+            double lineR = line[1].x;
+            if (lineL > lineR) {
+                std::swap(lineL, lineR);
+            }
+            double overlapL = std::max(left, lineL);
+            double overlapR = std::min(right, lineR);
+            if (overlapL > overlapR) {
+                return 0;
+            }
+            if (overlapL == overlapR) {
+                result = 1;
+            }
+            intersections.fT[0][0] = (overlapL - line[0].x) / (line[1].x - line[0].x);
+            intersections.fT[1][0] = (overlapL - left) / (right - left);
+            if (result > 1) {
+                intersections.fT[0][1] = (overlapR - line[0].x) / (line[1].x - line[0].x);
+                intersections.fT[1][1] = (overlapR - left) / (right - left);
+            }
+        #else
+            double a0 = line[0].x;
+            double a1 = line[1].x;
+            double b0 = flipped ? right : left;
+            double b1 = flipped ? left : right;
+            // FIXME: share common code below
+            double at0 = (a0 - b0) / (a0 - a1);
+            double at1 = (a0 - b1) / (a0 - a1);
+            if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
+                return 0;
+            }
+            intersections.fT[0][0] = std::max(std::min(at0, 1.0), 0.0);
+            intersections.fT[0][1] = std::max(std::min(at1, 1.0), 0.0);
+            int bIn = (a0 - a1) * (b0 - b1) < 0;
+            intersections.fT[1][bIn] = std::max(std::min((b0 - a0) / (b0 - b1),
+                    1.0), 0.0);
+            intersections.fT[1][!bIn] = std::max(std::min((b0 - a1) / (b0 - b1),
+                    1.0), 0.0);
+            bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
+                    > FLT_EPSILON;
+            assert((fabs(intersections.fT[1][0] - intersections.fT[1][1])
+                    <= FLT_EPSILON) ^ second);
+            return 1 + second;
+        #endif
+            break;
+    }
+    if (flipped) {
+        // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
+        for (int index = 0; index < result; ++index) {
+            intersections.fT[1][index] = 1 - intersections.fT[1][index];
+        }
+    }
+    return result;
+}
+
+static int verticalIntersect(const _Line& line, double x, double tRange[2]) {
+    double min = line[0].x;
+    double max = line[1].x;
+    if (min > max) {
+        std::swap(min, max);
+    }
+    if (min > x || max < x) {
+        return 0;
+    }
+    if (approximately_equal(min, max)) {
+        tRange[0] = 0;
+        tRange[1] = 1;
+        return 2;
+    }
+    tRange[0] = (x - line[0].x) / (line[1].x - line[0].x);
+    return 1;
+}
+
+int verticalIntersect(const _Line& line, double top, double bottom,
+        double x, bool flipped, Intersections& intersections) {
+    int result = verticalIntersect(line, x, intersections.fT[0]);
+    switch (result) {
+        case 0:
+            break;
+        case 1: {
+            double yIntercept = line[0].y + intersections.fT[0][0]
+                    * (line[1].y - line[0].y);
+            if (yIntercept > bottom || yIntercept < top) {
+                return 0;
+            }
+            intersections.fT[1][0] = (yIntercept - top) / (bottom - top);
+            break;
+        }
+        case 2:
+        #if 0 // sorting edges fails to preserve original direction
+            double lineT = line[0].y;
+            double lineB = line[1].y;
+            if (lineT > lineB) {
+                std::swap(lineT, lineB);
+            }
+            double overlapT = std::max(top, lineT);
+            double overlapB = std::min(bottom, lineB);
+            if (overlapT > overlapB) {
+                return 0;
+            }
+            if (overlapT == overlapB) {
+                result = 1;
+            }
+            intersections.fT[0][0] = (overlapT - line[0].y) / (line[1].y - line[0].y);
+            intersections.fT[1][0] = (overlapT - top) / (bottom - top);
+            if (result > 1) {
+                intersections.fT[0][1] = (overlapB - line[0].y) / (line[1].y - line[0].y);
+                intersections.fT[1][1] = (overlapB - top) / (bottom - top);
+            }
+        #else
+            double a0 = line[0].y;
+            double a1 = line[1].y;
+            double b0 = flipped ? bottom : top;
+            double b1 = flipped ? top : bottom;
+            // FIXME: share common code above
+            double at0 = (a0 - b0) / (a0 - a1);
+            double at1 = (a0 - b1) / (a0 - a1);
+            if ((at0 < 0 && at1 < 0) || (at0 > 1 && at1 > 1)) {
+                return 0;
+            }
+            intersections.fT[0][0] = std::max(std::min(at0, 1.0), 0.0);
+            intersections.fT[0][1] = std::max(std::min(at1, 1.0), 0.0);
+            int bIn = (a0 - a1) * (b0 - b1) < 0;
+            intersections.fT[1][bIn] = std::max(std::min((b0 - a0) / (b0 - b1),
+                    1.0), 0.0);
+            intersections.fT[1][!bIn] = std::max(std::min((b0 - a1) / (b0 - b1),
+                    1.0), 0.0);
+            bool second = fabs(intersections.fT[0][0] - intersections.fT[0][1])
+                    > FLT_EPSILON;
+            assert((fabs(intersections.fT[1][0] - intersections.fT[1][1])
+                    <= FLT_EPSILON) ^ second);
+            return 1 + second;
+        #endif
+            break;
+    }
+    if (flipped) {
+        // OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
+        for (int index = 0; index < result; ++index) {
+            intersections.fT[1][index] = 1 - intersections.fT[1][index];
+        }
+    }
+    return result;
+}
+
+// from http://www.bryceboe.com/wordpress/wp-content/uploads/2006/10/intersect.py
+// 4 subs, 2 muls, 1 cmp
+static bool ccw(const _Point& A, const _Point& B, const _Point& C) {
+    return (C.y - A.y) * (B.x - A.x) > (B.y - A.y) * (C.x - A.x);
+}
+
+// 16 subs, 8 muls, 6 cmps
+bool testIntersect(const _Line& a, const _Line& b) {
+    return ccw(a[0], b[0], b[1]) != ccw(a[1], b[0], b[1])
+            && ccw(a[0], a[1], b[0]) != ccw(a[0], a[1], b[1]);
+}
diff --git a/experimental/Intersection/LineIntersection.h b/experimental/Intersection/LineIntersection.h
new file mode 100644
index 0000000..33076b6
--- /dev/null
+++ b/experimental/Intersection/LineIntersection.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 LineIntersection_DEFINE
+#define LineIntersection_DEFINE
+
+#include "DataTypes.h"
+
+int horizontalIntersect(const _Line& line, double y, double tRange[2]);
+int horizontalLineIntersect(const _Line& line, double left, double right,
+        double y, double tRange[2]);
+int verticalLineIntersect(const _Line& line, double top, double bottom,
+        double x, double tRange[2]);
+int intersect(const _Line& a, const _Line& b, double aRange[2], double bRange[2]);
+bool testIntersect(const _Line& a, const _Line& b);
+
+#endif
diff --git a/experimental/Intersection/LineIntersection_Test.cpp b/experimental/Intersection/LineIntersection_Test.cpp
new file mode 100644
index 0000000..ba15192
--- /dev/null
+++ b/experimental/Intersection/LineIntersection_Test.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveUtilities.h"
+#include "Intersection_Tests.h"
+#include "LineIntersection.h"
+
+// FIXME: add tests for intersecting, non-intersecting, degenerate, coincident
+const _Line tests[][2] = {
+    {{{0, 0}, {1, 0}}, {{1, 0}, {0, 0}}},
+    {{{0, 0}, {0, 0}}, {{0, 0}, {1, 0}}},
+    {{{0, 1}, {0, 1}}, {{0, 0}, {0, 2}}},
+    {{{0, 0}, {1, 0}}, {{0, 0}, {2, 0}}},
+    {{{1, 1}, {2, 2}}, {{0, 0}, {3, 3}}},
+    {{{166.86950047022856, 112.69654129527828}, {166.86948801592692, 112.69655741235339}},
+     {{166.86960700313026, 112.6965477747386},  {166.86925794355412, 112.69656471103423}}}
+};
+
+const size_t tests_count = sizeof(tests) / sizeof(tests[0]);
+
+const _Line noIntersect[][2] = {
+    {{{0, 0}, {1, 0}}, {{3, 0}, {2, 0}}},
+    {{{0, 0}, {0, 0}}, {{1, 0}, {2, 0}}},
+    {{{0, 1}, {0, 1}}, {{0, 3}, {0, 2}}},
+    {{{0, 0}, {1, 0}}, {{2, 0}, {3, 0}}},
+    {{{1, 1}, {2, 2}}, {{4, 4}, {3, 3}}},
+};
+
+const size_t noIntersect_count = sizeof(noIntersect) / sizeof(noIntersect[0]);
+
+static size_t firstLineIntersectionTest = 0;
+static size_t firstNoIntersectionTest = 0;
+
+void LineIntersection_Test() {
+    size_t index;
+    for (index = firstLineIntersectionTest; index < tests_count; ++index) {
+        const _Line& line1 = tests[index][0];
+        const _Line& line2 = tests[index][1];
+        double t1[2], t2[2];
+        int pts = intersect(line1, line2, t1, t2);
+        if (!pts) {
+            printf("%s [%zu] no intersection found\n", __FUNCTION__, index);
+        }
+        for (int i = 0; i < pts; ++i) {
+            _Point result1, result2;
+            xy_at_t(line1, t1[i], result1.x, result1.y);
+            xy_at_t(line2, t2[i], result2.x, result2.y);
+            if (!result1.approximatelyEqual(result2)) {
+                if (pts == 1) {
+                    printf("%s [%zu] not equal\n", __FUNCTION__, index);
+                } else {
+                    xy_at_t(line2, t2[i ^ 1], result2.x, result2.y);
+                    if (!result1.approximatelyEqual(result2)) {
+                        printf("%s [%zu] not equal\n", __FUNCTION__, index);
+                    }
+                }
+            }
+        }
+    }
+    for (index = firstNoIntersectionTest; index < noIntersect_count; ++index) {
+        const _Line& line1 = noIntersect[index][0];
+        const _Line& line2 = noIntersect[index][1];
+        double t1[2], t2[2];
+        int pts = intersect(line1, line2, t1, t2);
+        if (pts) {
+            printf("%s [%zu] no intersection expected\n", __FUNCTION__, index);
+        }
+    }
+}
diff --git a/experimental/Intersection/LineParameterization.cpp b/experimental/Intersection/LineParameterization.cpp
new file mode 100644
index 0000000..4ef6fbd
--- /dev/null
+++ b/experimental/Intersection/LineParameterization.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 "CurveIntersection.h"
+
+/* This rejects coincidence with two muls, two adds, and one cmp.
+   Coincident candidates will take another four muls and two adds, but may still
+   fail if they don't overlap. (The overlap test isn't performed here.)
+ */
+bool implicit_matches(const _Line& one, const _Line& two) {
+    _Point oneD, twoD;
+    tangent(one, oneD);
+    tangent(two, twoD);
+    /* See if the slopes match, i.e.
+                        dx1 / dy1 ==               dx2 / dy2
+          (dy1 * dy2) * dx1 / dy1 == (dy1 * dy2) * dx2 / dy2
+                 dy2  * dx1       ==  dy1        * dx2
+     */
+    if (!approximately_equal(oneD.x * twoD.y, twoD.x * oneD.y)) {
+        return false;
+    }
+    /* See if the axis intercepts match, i.e.
+               y0 - x0 * dy / dx  ==       y1 - x1 * dy / dx
+         dx * (y0 - x0 * dy / dx) == dx * (y1 - x1 * dy / dx)
+         dx *  y0 - x0 * dy       == dx *  y1 - x1 * dy
+     */
+    if (!approximately_equal(oneD.x * one[0].y - oneD.y * one[0].x,
+            oneD.x * two[0].y - oneD.y * two[0].x)) {
+        return false;
+    }
+    return true;
+}
+
+bool implicit_matches_ulps(const _Line& one, const _Line& two, int ulps) {
+    _Point oneD, twoD;
+    tangent(one, oneD);
+    tangent(two, twoD);
+    /* See if the slopes match, i.e.
+                        dx1 / dy1 ==               dx2 / dy2
+          (dy1 * dy2) * dx1 / dy1 == (dy1 * dy2) * dx2 / dy2
+                 dy2  * dx1       ==  dy1        * dx2
+     */
+    int diff = UlpsDiff((float) (oneD.x * twoD.y), (float) (twoD.x * oneD.y));
+    if (diff < 0 || diff > ulps) {
+        return false;
+    }
+    /* See if the axis intercepts match, i.e.
+               y0 - x0 * dy / dx  ==       y1 - x1 * dy / dx
+         dx * (y0 - x0 * dy / dx) == dx * (y1 - x1 * dy / dx)
+         dx *  y0 - x0 * dy       == dx *  y1 - x1 * dy
+     */
+    diff = UlpsDiff((float) (oneD.x * one[0].y - oneD.y * one[0].x),
+            (float) (oneD.x * two[0].y - oneD.y * two[0].x));
+    return diff >= 0 && diff <= ulps;
+}
+
+void tangent(const _Line& line,  _Point& result) {
+    result.x = line[0].x - line[1].x;
+    result.y = line[0].y - line[1].y;
+}
diff --git a/experimental/Intersection/LineParameters.h b/experimental/Intersection/LineParameters.h
new file mode 100644
index 0000000..fc1bcc8
--- /dev/null
+++ b/experimental/Intersection/LineParameters.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "DataTypes.h"
+
+// Sources
+// computer-aided design - volume 22 number 9 november 1990 pp 538 - 549
+// online at http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
+
+// This turns a line segment into a parameterized line, of the form
+// ax + by + c = 0
+// When a^2 + b^2 == 1, the line is normalized.
+// The distance to the line for (x, y) is d(x,y) = ax + by + c
+//
+// Note that the distances below are not necessarily normalized. To get the true
+// distance, it's necessary to either call normalize() after xxxEndPoints(), or
+// divide the result of xxxDistance() by sqrt(normalSquared())
+
+class LineParameters {
+public:
+    void cubicEndPoints(const Cubic& pts) {
+        cubicEndPoints(pts, 0, 3);
+    }
+
+    void cubicEndPoints(const Cubic& pts, int s, int e) {
+        a = approximately_pin(pts[s].y - pts[e].y);
+        b = approximately_pin(pts[e].x - pts[s].x);
+        c = pts[s].x * pts[e].y - pts[e].x * pts[s].y;
+    }
+
+    void lineEndPoints(const _Line& pts) {
+        a = approximately_pin(pts[0].y - pts[1].y);
+        b = approximately_pin(pts[1].x - pts[0].x);
+        c = pts[0].x * pts[1].y - pts[1].x * pts[0].y;
+    }
+
+    void quadEndPoints(const Quadratic& pts) {
+        quadEndPoints(pts, 0, 2);
+    }
+
+    void quadEndPoints(const Quadratic& pts, int s, int e) {
+        a = approximately_pin(pts[s].y - pts[e].y);
+        b = approximately_pin(pts[e].x - pts[s].x);
+        c = pts[s].x * pts[e].y - pts[e].x * pts[s].y;
+    }
+
+    double normalSquared() const {
+        return a * a + b * b;
+    }
+
+    bool normalize() {
+        double normal = sqrt(normalSquared());
+        if (approximately_zero(normal)) {
+            a = b = c = 0;
+            return false;
+        }
+        double reciprocal = 1 / normal;
+        a *= reciprocal;
+        b *= reciprocal;
+        c *= reciprocal;
+        return true;
+    }
+
+    void cubicDistanceY(const Cubic& pts, Cubic& distance) const {
+        double oneThird = 1 / 3.0;
+        for (int index = 0; index < 4; ++index) {
+            distance[index].x = index * oneThird;
+            distance[index].y = a * pts[index].x + b * pts[index].y + c;
+        }
+    }
+
+    void quadDistanceY(const Quadratic& pts, Quadratic& distance) const {
+        double oneHalf = 1 / 2.0;
+        for (int index = 0; index < 3; ++index) {
+            distance[index].x = index * oneHalf;
+            distance[index].y = a * pts[index].x + b * pts[index].y + c;
+        }
+    }
+
+    void controlPtDistance(const Cubic& pts, double distance[2]) const {
+        for (int index = 0; index < 2; ++index) {
+            distance[index] = a * pts[index + 1].x + b * pts[index + 1].y + c;
+        }
+    }
+
+    void controlPtDistance(const Cubic& pts, int i, int j, double distance[2]) const {
+        distance[0] = a * pts[i].x + b * pts[i].y + c;
+        distance[1] = a * pts[j].x + b * pts[j].y + c;
+    }
+
+    double controlPtDistance(const Quadratic& pts) const {
+        return a * pts[1].x + b * pts[1].y + c;
+    }
+
+    double pointDistance(const _Point& pt) const {
+        return a * pt.x + b * pt.y + c;
+    }
+
+    double dx() const {
+        return b;
+    }
+
+    double dy() const {
+        return -a;
+    }
+
+private:
+    double a;
+    double b;
+    double c;
+};
diff --git a/experimental/Intersection/LineParameteters_Test.cpp b/experimental/Intersection/LineParameteters_Test.cpp
new file mode 100644
index 0000000..e8adf33
--- /dev/null
+++ b/experimental/Intersection/LineParameteters_Test.cpp
@@ -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.
+ */
+#include "Intersection_Tests.h"
+#include "LineParameters.h"
+
+
+// tests to verify that distance calculations are coded correctly
+const Cubic tests[] = {
+    {{0, 0}, {1, 1}, {2, 2}, {0, 3}},
+    {{0, 0}, {1, 1}, {2, 2}, {3, 0}},
+    {{0, 0}, {5, 0}, {-2,4}, {3, 4}},
+    {{0, 2}, {1, 0}, {2, 0}, {3, 0}},
+    {{0, .2}, {1, 0}, {2, 0}, {3, 0}},
+    {{0, .02}, {1, 0}, {2, 0}, {3, 0}},
+    {{0, .002}, {1, 0}, {2, 0}, {3, 0}},
+    {{0, .0002}, {1, 0}, {2, 0}, {3, 0}},
+    {{0, .00002}, {1, 0}, {2, 0}, {3, 0}},
+    {{0, PointEpsilon * 2}, {1, 0}, {2, 0}, {3, 0}},
+};
+
+const double answers[][2] = {
+    {1, 2},
+    {1, 2},
+    {4, 4},
+    {1.1094003924, 0.5547001962},
+    {0.133038021, 0.06651901052},
+    {0.0133330370, 0.006666518523},
+    {0.001333333037, 0.0006666665185},
+    {0.000133333333, 6.666666652e-05},
+    {1.333333333e-05, 6.666666667e-06},
+    {1.333333333e-06, 6.666666667e-07},
+};
+
+const size_t tests_count = sizeof(tests) / sizeof(tests[0]);
+
+static size_t firstLineParameterTest = 0;
+
+void LineParameter_Test() {
+    for (size_t index = firstLineParameterTest; index < tests_count; ++index) {
+        LineParameters lineParameters;
+        const Cubic& cubic = tests[index];
+        lineParameters.cubicEndPoints(cubic);
+        double denormalizedDistance[2];
+        lineParameters.controlPtDistance(cubic, denormalizedDistance);
+        double normalSquared = lineParameters.normalSquared();
+        size_t inner;
+        for (inner = 0; inner < 2; ++inner) {
+            double distSq = denormalizedDistance[inner];
+            distSq *= distSq;
+            double answersSq = answers[index][inner];
+            answersSq *= answersSq;
+            if (approximately_equal(distSq, normalSquared * answersSq)) {
+                continue;
+            }
+            printf("%s [%d,%d] denormalizedDistance:%g != answer:%g"
+                    " distSq:%g answerSq:%g normalSquared:%g\n",
+                    __FUNCTION__, (int)index, (int)inner,
+                    denormalizedDistance[inner], answers[index][inner],
+                    distSq, answersSq, normalSquared);
+        }
+        lineParameters.normalize();
+        double normalizedDistance[2];
+        lineParameters.controlPtDistance(cubic, normalizedDistance);
+        for (inner = 0; inner < 2; ++inner) {
+            if (approximately_equal(fabs(normalizedDistance[inner]),
+                    answers[index][inner])) {
+                continue;
+            }
+            printf("%s [%d,%d] normalizedDistance:%1.10g != answer:%g\n",
+                    __FUNCTION__, (int)index, (int)inner,
+                    normalizedDistance[inner], answers[index][inner]);
+        }
+    }
+}
diff --git a/experimental/Intersection/LineQuadraticIntersection.cpp b/experimental/Intersection/LineQuadraticIntersection.cpp
new file mode 100644
index 0000000..f730250
--- /dev/null
+++ b/experimental/Intersection/LineQuadraticIntersection.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "Intersections.h"
+#include "LineUtilities.h"
+#include "QuadraticUtilities.h"
+
+/*
+Find the interection of a line and quadratic by solving for valid t values.
+
+From http://stackoverflow.com/questions/1853637/how-to-find-the-mathematical-function-defining-a-bezier-curve
+
+"A Bezier curve is a parametric function. A quadratic Bezier curve (i.e. three
+control points) can be expressed as: F(t) = A(1 - t)^2 + B(1 - t)t + Ct^2 where
+A, B and C are points and t goes from zero to one.
+
+This will give you two equations:
+
+  x = a(1 - t)^2 + b(1 - t)t + ct^2
+  y = d(1 - t)^2 + e(1 - t)t + ft^2
+
+If you add for instance the line equation (y = kx + m) to that, you'll end up
+with three equations and three unknowns (x, y and t)."
+
+Similar to above, the quadratic is represented as
+  x = a(1-t)^2 + 2b(1-t)t + ct^2
+  y = d(1-t)^2 + 2e(1-t)t + ft^2
+and the line as
+  y = g*x + h
+
+Using Mathematica, solve for the values of t where the quadratic intersects the
+line:
+
+  (in)  t1 = Resultant[a*(1 - t)^2 + 2*b*(1 - t)*t + c*t^2 - x,
+                       d*(1 - t)^2 + 2*e*(1 - t)*t  + f*t^2 - g*x - h, x]
+  (out) -d + h + 2 d t - 2 e t - d t^2 + 2 e t^2 - f t^2 +
+         g  (a - 2 a t + 2 b t + a t^2 - 2 b t^2 + c t^2)
+  (in)  Solve[t1 == 0, t]
+  (out) {
+    {t -> (-2 d + 2 e +   2 a g - 2 b g    -
+      Sqrt[(2 d - 2 e -   2 a g + 2 b g)^2 -
+          4 (-d + 2 e - f + a g - 2 b g    + c g) (-d + a g + h)]) /
+         (2 (-d + 2 e - f + a g - 2 b g    + c g))
+         },
+    {t -> (-2 d + 2 e +   2 a g - 2 b g    +
+      Sqrt[(2 d - 2 e -   2 a g + 2 b g)^2 -
+          4 (-d + 2 e - f + a g - 2 b g    + c g) (-d + a g + h)]) /
+         (2 (-d + 2 e - f + a g - 2 b g    + c g))
+         }
+        }
+
+Using the results above (when the line tends towards horizontal)
+       A =   (-(d - 2*e + f) + g*(a - 2*b + c)     )
+       B = 2*( (d -   e    ) - g*(a -   b    )     )
+       C =   (-(d          ) + g*(a          ) + h )
+
+If g goes to infinity, we can rewrite the line in terms of x.
+  x = g'*y + h'
+
+And solve accordingly in Mathematica:
+
+  (in)  t2 = Resultant[a*(1 - t)^2 + 2*b*(1 - t)*t + c*t^2 - g'*y - h',
+                       d*(1 - t)^2 + 2*e*(1 - t)*t  + f*t^2 - y, y]
+  (out)  a - h' - 2 a t + 2 b t + a t^2 - 2 b t^2 + c t^2 -
+         g'  (d - 2 d t + 2 e t + d t^2 - 2 e t^2 + f t^2)
+  (in)  Solve[t2 == 0, t]
+  (out) {
+    {t -> (2 a - 2 b -   2 d g' + 2 e g'    -
+    Sqrt[(-2 a + 2 b +   2 d g' - 2 e g')^2 -
+          4 (a - 2 b + c - d g' + 2 e g' - f g') (a - d g' - h')]) /
+         (2 (a - 2 b + c - d g' + 2 e g' - f g'))
+         },
+    {t -> (2 a - 2 b -   2 d g' + 2 e g'    +
+    Sqrt[(-2 a + 2 b +   2 d g' - 2 e g')^2 -
+          4 (a - 2 b + c - d g' + 2 e g' - f g') (a - d g' - h')])/
+         (2 (a - 2 b + c - d g' + 2 e g' - f g'))
+         }
+        }
+
+Thus, if the slope of the line tends towards vertical, we use:
+       A =   ( (a - 2*b + c) - g'*(d  - 2*e + f)      )
+       B = 2*(-(a -   b    ) + g'*(d  -   e    )      )
+       C =   ( (a          ) - g'*(d           ) - h' )
+ */
+
+
+class LineQuadraticIntersections : public Intersections {
+public:
+
+LineQuadraticIntersections(const Quadratic& q, const _Line& l, Intersections& i)
+    : quad(q)
+    , line(l)
+    , intersections(i) {
+}
+
+int intersectRay() {
+/*
+    solve by rotating line+quad so line is horizontal, then finding the roots
+    set up matrix to rotate quad to x-axis
+    |cos(a) -sin(a)|
+    |sin(a)  cos(a)|
+    note that cos(a) = A(djacent) / Hypoteneuse
+              sin(a) = O(pposite) / Hypoteneuse
+    since we are computing Ts, we can ignore hypoteneuse, the scale factor:
+    |  A     -O    |
+    |  O      A    |
+    A = line[1].x - line[0].x (adjacent side of the right triangle)
+    O = line[1].y - line[0].y (opposite side of the right triangle)
+    for each of the three points (e.g. n = 0 to 2)
+    quad[n].y' = (quad[n].y - line[0].y) * A - (quad[n].x - line[0].x) * O
+*/
+    double adj = line[1].x - line[0].x;
+    double opp = line[1].y - line[0].y;
+    double r[3];
+    for (int n = 0; n < 3; ++n) {
+        r[n] = (quad[n].y - line[0].y) * adj - (quad[n].x - line[0].x) * opp;
+    }
+    double A = r[2];
+    double B = r[1];
+    double C = r[0];
+    A += C - 2 * B; // A = a - 2*b + c
+    B -= C; // B = -(b - c)
+    return quadraticRoots(A, B, C, intersections.fT[0]);
+}
+
+int intersect() {
+    int roots = intersectRay();
+    for (int index = 0; index < roots; ) {
+        double lineT = findLineT(intersections.fT[0][index]);
+        if (lineIntersects(lineT, index, roots)) {
+            ++index;
+        }
+    }
+    return roots;
+}
+
+int horizontalIntersect(double axisIntercept) {
+    double D = quad[2].y; // f
+    double E = quad[1].y; // e
+    double F = quad[0].y; // d
+    D += F - 2 * E; // D = d - 2*e + f
+    E -= F; // E = -(d - e)
+    F -= axisIntercept;
+    return quadraticRoots(D, E, F, intersections.fT[0]);
+}
+
+int horizontalIntersect(double axisIntercept, double left, double right) {
+    int roots = horizontalIntersect(axisIntercept);
+    for (int index = 0; index < roots; ) {
+        double x;
+        xy_at_t(quad, intersections.fT[0][index], x, *(double*) NULL);
+        double lineT = (x - left) / (right - left);
+        if (lineIntersects(lineT, index, roots)) {
+            ++index;
+        }
+    }
+    return roots;
+}
+
+int verticalIntersect(double axisIntercept) {
+    double D = quad[2].x; // f
+    double E = quad[1].x; // e
+    double F = quad[0].x; // d
+    D += F - 2 * E; // D = d - 2*e + f
+    E -= F; // E = -(d - e)
+    F -= axisIntercept;
+    return quadraticRoots(D, E, F, intersections.fT[0]);
+}
+
+int verticalIntersect(double axisIntercept, double top, double bottom) {
+    int roots = verticalIntersect(axisIntercept);
+    for (int index = 0; index < roots; ) {
+        double y;
+        xy_at_t(quad, intersections.fT[0][index], *(double*) NULL, y);
+        double lineT = (y - top) / (bottom - top);
+        if (lineIntersects(lineT, index, roots)) {
+            ++index;
+        }
+    }
+    return roots;
+}
+
+protected:
+
+double findLineT(double t) {
+    double x, y;
+    xy_at_t(quad, t, x, y);
+    if (approximately_equal(x, line[0].x) && approximately_equal(y, line[0].y)) {
+        return 0;
+    }
+    if (approximately_equal(x, line[1].x) && approximately_equal(y, line[1].y)) {
+        return 1;
+    }
+    double dx = line[1].x - line[0].x;
+    double dy = line[1].y - line[0].y;
+    if (fabs(dx) > fabs(dy)) {
+        return (x - line[0].x) / dx;
+    }
+    return (y - line[0].y) / dy;
+}
+
+bool lineIntersects(double lineT, const int x, int& roots) {
+    if (!approximately_zero_or_more(lineT) || !approximately_one_or_less(lineT)) {
+        if (x < --roots) {
+            intersections.fT[0][x] = intersections.fT[0][roots];
+        }
+        return false;
+    }
+    if (approximately_less_than_zero(lineT)) {
+        lineT = 0;
+    } else if (approximately_greater_than_one(lineT)) {
+        lineT = 1;
+    }
+    intersections.fT[1][x] = lineT;
+    return true;
+}
+
+private:
+
+const Quadratic& quad;
+const _Line& line;
+Intersections& intersections;
+};
+
+// utility for pairs of coincident quads
+static double horizontalIntersect(const Quadratic& quad, const _Point& pt) {
+    Intersections intersections;
+    LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
+    int roots = q.horizontalIntersect(pt.y);
+    for (int index = 0; index < roots; ++index) {
+        double x;
+        double t = intersections.fT[0][index];
+        xy_at_t(quad, t, x, *(double*) 0);
+        if (approximately_equal(x, pt.x)) {
+            return t;
+        }
+    }
+    return -1;
+}
+
+static double verticalIntersect(const Quadratic& quad, const _Point& pt) {
+    Intersections intersections;
+    LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
+    int roots = q.verticalIntersect(pt.x);
+    for (int index = 0; index < roots; ++index) {
+        double y;
+        double t = intersections.fT[0][index];
+        xy_at_t(quad, t, *(double*) 0, y);
+        if (approximately_equal(y, pt.y)) {
+            return t;
+        }
+    }
+    return -1;
+}
+
+double axialIntersect(const Quadratic& q1, const _Point& p, bool vertical) {
+    if (vertical) {
+        return verticalIntersect(q1, p);
+    }
+    return horizontalIntersect(q1, p);
+}
+
+int horizontalIntersect(const Quadratic& quad, double left, double right,
+        double y, double tRange[2]) {
+    Intersections i;
+    LineQuadraticIntersections q(quad, *((_Line*) 0), i);
+    int result = q.horizontalIntersect(y);
+    int tCount = 0;
+    for (int index = 0; index < result; ++index) {
+        double x, y;
+        xy_at_t(quad, i.fT[0][index], x, y);
+        if (x < left || x > right) {
+            continue;
+        }
+        tRange[tCount++] = i.fT[0][index];
+    }
+    return tCount;
+}
+
+int horizontalIntersect(const Quadratic& quad, double left, double right, double y,
+        bool flipped, Intersections& intersections) {
+    LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
+    int result = q.horizontalIntersect(y, left, right);
+    if (flipped) {
+        // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
+        for (int index = 0; index < result; ++index) {
+            intersections.fT[1][index] = 1 - intersections.fT[1][index];
+        }
+    }
+    return result;
+}
+
+int verticalIntersect(const Quadratic& quad, double top, double bottom, double x,
+        bool flipped, Intersections& intersections) {
+    LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
+    int result = q.verticalIntersect(x, top, bottom);
+    if (flipped) {
+        // OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
+        for (int index = 0; index < result; ++index) {
+            intersections.fT[1][index] = 1 - intersections.fT[1][index];
+        }
+    }
+    return result;
+}
+
+int intersect(const Quadratic& quad, const _Line& line, Intersections& i) {
+    LineQuadraticIntersections q(quad, line, i);
+    return q.intersect();
+}
+
+int intersectRay(const Quadratic& quad, const _Line& line, Intersections& i) {
+    LineQuadraticIntersections q(quad, line, i);
+    return q.intersectRay();
+}
diff --git a/experimental/Intersection/LineQuadraticIntersection_Test.cpp b/experimental/Intersection/LineQuadraticIntersection_Test.cpp
new file mode 100644
index 0000000..171a778
--- /dev/null
+++ b/experimental/Intersection/LineQuadraticIntersection_Test.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 "CurveIntersection.h"
+#include "CurveUtilities.h"
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "Intersections.h"
+#include "TestUtilities.h"
+
+struct lineQuad {
+    Quadratic quad;
+    _Line line;
+    int result;
+    _Point expected[2];
+} lineQuadTests[] = {
+    //        quad                    line                  results
+    {{{1, 1}, {2, 1}, {0, 2}},  {{0, 0}, {1, 1}},  1,  {{1, 1}        }},
+    {{{0, 0}, {1, 1}, {3, 1}},  {{0, 0}, {3, 1}},  2,  {{0, 0}, {3, 1}}},
+    {{{2, 0}, {1, 1}, {2, 2}},  {{0, 0}, {0, 2}},  0                   },
+    {{{4, 0}, {0, 1}, {4, 2}},  {{3, 1}, {4, 1}},  0,                  },
+    {{{0, 0}, {0, 1}, {1, 1}},  {{0, 1}, {1, 0}},  1,  {{.25, .75}    }},
+};
+
+size_t lineQuadTests_count = sizeof(lineQuadTests) / sizeof(lineQuadTests[0]);
+
+const int firstLineQuadIntersectionTest = 0;
+
+static int doIntersect(Intersections& intersections, const Quadratic& quad, const _Line& line, bool& flipped) {
+    int result;
+    flipped = false;
+    if (line[0].x == line[1].x) {
+        double top = line[0].y;
+        double bottom = line[1].y;
+        flipped = top > bottom;
+        if (flipped) {
+            SkTSwap<double>(top, bottom);
+        }
+        result = verticalIntersect(quad, top, bottom, line[0].x, flipped, intersections);
+    } else if (line[0].y == line[1].y) {
+        double left = line[0].x;
+        double right = line[1].x;
+        flipped = left > right;
+        if (flipped) {
+            SkTSwap<double>(left, right);
+        }
+        result = horizontalIntersect(quad, left, right, line[0].y, flipped, intersections);
+    } else {
+        intersect(quad, line, intersections);
+        result = intersections.fUsed;
+    }
+    return result;
+}
+
+void LineQuadraticIntersection_Test() {
+    for (size_t index = firstLineQuadIntersectionTest; index < lineQuadTests_count; ++index) {
+        const Quadratic& quad = lineQuadTests[index].quad;
+        const _Line& line = lineQuadTests[index].line;
+        Quadratic reduce1;
+        _Line reduce2;
+        int order1 = reduceOrder(quad, reduce1);
+        int order2 = reduceOrder(line, reduce2);
+        if (order1 < 3) {
+            SkDebugf("%s [%d] quad order=%d\n", __FUNCTION__, (int) index, order1);
+            SkASSERT(0);
+        }
+        if (order2 < 2) {
+            SkDebugf("%s [%d] line order=%d\n", __FUNCTION__, (int) index, order2);
+            SkASSERT(0);
+        }
+        Intersections intersections;
+        bool flipped = false;
+        int result = doIntersect(intersections, quad, line, flipped);
+        SkASSERT(result == lineQuadTests[index].result);
+        if (!intersections.intersected()) {
+            continue;
+        }
+        for (int pt = 0; pt < result; ++pt) {
+            double tt1 = intersections.fT[0][pt];
+            SkASSERT(tt1 >= 0 && tt1 <= 1);
+            _Point t1, t2;
+            xy_at_t(quad, tt1, t1.x, t1.y);
+            double tt2 = intersections.fT[1][pt];
+            SkASSERT(tt2 >= 0 && tt2 <= 1);
+            xy_at_t(line, tt2, t2.x, t2.y);
+            if (!approximately_equal(t1.x, t2.x)) {
+                SkDebugf("%s [%d,%d] x!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n",
+                    __FUNCTION__, (int)index, pt, tt1, t1.x, t1.y, tt2, t2.x, t2.y);
+                SkASSERT(0);
+            }
+            if (!approximately_equal(t1.y, t2.y)) {
+                SkDebugf("%s [%d,%d] y!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n",
+                    __FUNCTION__, (int)index, pt, tt1, t1.x, t1.y, tt2, t2.x, t2.y);
+                SkASSERT(0);
+            }
+            if (!t1.approximatelyEqual(lineQuadTests[index].expected[0])
+                    && (lineQuadTests[index].result == 1
+                    || !t1.approximatelyEqual(lineQuadTests[index].expected[1]))) {
+                SkDebugf("%s t1=(%1.9g,%1.9g)\n", __FUNCTION__, t1.x, t1.y);
+                SkASSERT(0);
+            }
+        }
+    }
+}
+
+static void testLineIntersect(State4& state, const Quadratic& quad, const _Line& line,
+        const double x, const double y) {
+    char pathStr[1024];
+    bzero(pathStr, sizeof(pathStr));
+    char* str = pathStr;
+    str += sprintf(str, "    path.moveTo(%1.9g, %1.9g);\n", quad[0].x, quad[0].y);
+    str += sprintf(str, "    path.quadTo(%1.9g, %1.9g, %1.9g, %1.9g);\n", quad[1].x, quad[1].y, quad[2].x, quad[2].y);
+    str += sprintf(str, "    path.moveTo(%1.9g, %1.9g);\n", line[0].x, line[0].y);
+    str += sprintf(str, "    path.lineTo(%1.9g, %1.9g);\n", line[1].x, line[1].y);
+
+    Intersections intersections;
+    bool flipped = false;
+    int result = doIntersect(intersections, quad, line, flipped);
+    bool found = false;
+    for (int index = 0; index < result; ++index) {
+        double quadT = intersections.fT[0][index];
+        double quadX, quadY;
+        xy_at_t(quad, quadT, quadX, quadY);
+        double lineT = intersections.fT[1][index];
+        double lineX, lineY;
+        xy_at_t(line, lineT, lineX, lineY);
+        if (fabs(quadX - lineX) < FLT_EPSILON && fabs(quadY - lineY) < FLT_EPSILON
+                && fabs(x - lineX) < FLT_EPSILON && fabs(y - lineY) < FLT_EPSILON) {
+            found = true;
+        }
+    }
+    SkASSERT(found);
+    state.testsRun++;
+}
+
+
+// find a point on a quad by choosing a t from 0 to 1
+// create a vertical span above and below the point
+// verify that intersecting the vertical span and the quad returns t
+// verify that a vertical span starting at quad[0] intersects at t=0
+// verify that a vertical span starting at quad[2] intersects at t=1
+static void* testQuadLineIntersectMain(void* data)
+{
+    SkASSERT(data);
+    State4& state = *(State4*) data;
+    do {
+        int ax = state.a & 0x03;
+        int ay = state.a >> 2;
+        int bx = state.b & 0x03;
+        int by = state.b >> 2;
+        int cx = state.c & 0x03;
+        int cy = state.c >> 2;
+        Quadratic quad = {{ax, ay}, {bx, by}, {cx, cy}};
+        Quadratic reduced;
+        int order = reduceOrder(quad, reduced);
+        if (order < 3) {
+            continue; // skip degenerates
+        }
+        for (int tIndex = 0; tIndex <= 4; ++tIndex) {
+            double x, y;
+            xy_at_t(quad, tIndex / 4.0, x, y);
+            for (int h = -2; h <= 2; ++h) {
+                for (int v = -2; v <= 2; ++v) {
+                    if (h == v && abs(h) != 1) {
+                        continue;
+                    }
+                    _Line line = {{x - h, y - v}, {x, y}};
+                    testLineIntersect(state, quad, line, x, y);
+                    _Line line2 = {{x, y}, {x + h, y + v}};
+                    testLineIntersect(state, quad, line2, x, y);
+                    _Line line3 = {{x - h, y - v}, {x + h, y + v}};
+                    testLineIntersect(state, quad, line3, x, y);
+                }
+            }
+        }
+    } while (runNextTestSet(state));
+    return NULL;
+}
+
+void QuadLineIntersectThreaded_Test(int& testsRun)
+{
+    SkDebugf("%s\n", __FUNCTION__);
+    const char testStr[] = "testQuadLineIntersect";
+    initializeTests(testStr, sizeof(testStr));
+    int testsStart = testsRun;
+    for (int a = 0; a < 16; ++a) {
+        for (int b = 0 ; b < 16; ++b) {
+            for (int c = 0 ; c < 16; ++c) {
+                testsRun += dispatchTest4(testQuadLineIntersectMain,
+                        a, b, c, 0);
+            }
+            if (!gRunTestsInOneThread) SkDebugf(".");
+        }
+        if (!gRunTestsInOneThread) SkDebugf("%d", a);
+    }
+    testsRun += waitForCompletion();
+    SkDebugf("\n%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
+}
diff --git a/experimental/Intersection/LineUtilities.cpp b/experimental/Intersection/LineUtilities.cpp
new file mode 100644
index 0000000..fc19885
--- /dev/null
+++ b/experimental/Intersection/LineUtilities.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "LineUtilities.h"
+
+bool implicitLine(const _Line& line, double& slope, double& axisIntercept) {
+    _Point delta;
+    tangent(line, delta);
+    bool moreHorizontal = fabs(delta.x) > fabs(delta.y);
+    if (moreHorizontal) {
+        slope = delta.y / delta.x;
+        axisIntercept = line[0].y - slope * line[0].x;
+    } else {
+        slope = delta.x / delta.y;
+        axisIntercept = line[0].x - slope * line[0].y;
+    }
+    return moreHorizontal;
+}
+
+int reduceOrder(const _Line& line, _Line& reduced) {
+    reduced[0] = line[0];
+    int different = line[0] != line[1];
+    reduced[1] = line[different];
+    return 1 + different;
+}
+
+void sub_divide(const _Line& line, double t1, double t2, _Line& dst) {
+    _Point delta;
+    tangent(line, delta);
+    dst[0].x = line[0].x - t1 * delta.x;
+    dst[0].y = line[0].y - t1 * delta.y;
+    dst[1].x = line[0].x - t2 * delta.x;
+    dst[1].y = line[0].y - t2 * delta.y;
+}
+
+// may have this below somewhere else already:
+// copying here because I thought it was clever
+
+// Copyright 2001, softSurfer (www.softsurfer.com)
+// This code may be freely used and modified for any purpose
+// providing that this copyright notice is included with it.
+// SoftSurfer makes no warranty for this code, and cannot be held
+// liable for any real or imagined damage resulting from its use.
+// Users of this code must verify correctness for their application.
+
+// Assume that a class is already given for the object:
+//    Point with coordinates {float x, y;}
+//===================================================================
+
+// isLeft(): tests if a point is Left|On|Right of an infinite line.
+//    Input:  three points P0, P1, and P2
+//    Return: >0 for P2 left of the line through P0 and P1
+//            =0 for P2 on the line
+//            <0 for P2 right of the line
+//    See: the January 2001 Algorithm on Area of Triangles
+#if 0
+float isLeft( _Point P0, _Point P1, _Point P2 )
+{
+    return (float) ((P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y - P0.y));
+}
+#endif
+
+double t_at(const _Line& line, const _Point& pt) {
+    double dx = line[1].x - line[0].x;
+    double dy = line[1].y - line[0].y;
+    if (fabs(dx) > fabs(dy)) {
+        if (approximately_zero(dx)) {
+            return 0;
+        }
+        return (pt.x - line[0].x) / dx;
+    }
+    if (approximately_zero(dy)) {
+        return 0;
+    }
+    return (pt.y - line[0].y) / dy;
+}
+
+static void setMinMax(double x, int flags, double& minX, double& maxX) {
+    if (minX > x && (flags & (kFindTopMin | kFindBottomMin))) {
+        minX = x;
+    }
+    if (maxX < x && (flags & (kFindTopMax | kFindBottomMax))) {
+        maxX = x;
+    }
+}
+
+void x_at(const _Point& p1, const _Point& p2, double top, double bottom,
+        int flags, double& minX, double& maxX) {
+    if (approximately_equal(p1.y, p2.y)) {
+        // It should be OK to bail early in this case. There's another edge
+        // which shares this end point which can intersect without failing to
+        // have a slope ... maybe
+        return;
+    }
+
+    // p2.x is always greater than p1.x -- the part of points (p1, p2) are
+    // moving from the start of the cubic towards its end.
+    // if p1.y < p2.y, minX can be affected
+    // if p1.y > p2.y, maxX can be affected
+    double slope = (p2.x - p1.x) / (p2.y - p1.y);
+    int topFlags = flags & (kFindTopMin | kFindTopMax);
+    if (topFlags && (top <= p1.y && top >= p2.y
+            || top >= p1.y && top <= p2.y)) {
+        double x = p1.x + (top - p1.y) * slope;
+        setMinMax(x, topFlags, minX, maxX);
+    }
+    int bottomFlags = flags & (kFindBottomMin | kFindBottomMax);
+    if (bottomFlags && (bottom <= p1.y && bottom >= p2.y
+            || bottom >= p1.y && bottom <= p2.y)) {
+        double x = p1.x + (bottom - p1.y) * slope;
+        setMinMax(x, bottomFlags, minX, maxX);
+    }
+}
+
+void xy_at_t(const _Line& line, double t, double& x, double& y) {
+    double one_t = 1 - t;
+    if (&x) {
+        x = one_t * line[0].x + t * line[1].x;
+    }
+    if (&y) {
+        y = one_t * line[0].y + t * line[1].y;
+    }
+}
diff --git a/experimental/Intersection/LineUtilities.h b/experimental/Intersection/LineUtilities.h
new file mode 100644
index 0000000..49e6a32
--- /dev/null
+++ b/experimental/Intersection/LineUtilities.h
@@ -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 "DataTypes.h"
+
+bool implicitLine(const _Line& line, double& slope, double& axisIntercept);
+int reduceOrder(const _Line& line, _Line& reduced);
+
+double t_at(const _Line&, const _Point& );
+void xy_at_t(const _Line& , double t, double& x, double& y);
+
+enum x_at_flags {
+    kFindTopMin = 1,
+    kFindTopMax = 2,
+    kFindBottomMin = 4,
+    kFindBottomMax = 8
+};
+
+void x_at(const _Point& p1, const _Point& p2, double minY, double maxY,
+        int flags, double& tMin, double& tMax);
+
diff --git a/experimental/Intersection/MiniSimplify_Test.cpp b/experimental/Intersection/MiniSimplify_Test.cpp
new file mode 100644
index 0000000..4662381
--- /dev/null
+++ b/experimental/Intersection/MiniSimplify_Test.cpp
@@ -0,0 +1,89 @@
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "ShapeOps.h"
+
+bool gShowOriginal = true;
+
+struct curve {
+    SkPath::Verb verb;
+    SkPoint pts[4];
+};
+
+struct curve test1[] = {
+{SkPath::kQuad_Verb, {{366.608826f, 151.196014f}, {378.803101f, 136.674606f}, {398.164948f, 136.674606f}}},
+{SkPath::kLine_Verb, {{354.009216f, 208.816208f}, {393.291473f, 102.232819f}}},
+{SkPath::kQuad_Verb, {{359.978058f, 136.581512f}, {378.315979f, 136.581512f}, {388.322723f, 149.613556f}}},
+{SkPath::kQuad_Verb, {{364.390686f, 157.898193f}, {375.281769f, 136.674606f}, {396.039917f, 136.674606f}}},
+{SkPath::kLine_Verb, {{396.039917f, 136.674606f}, {350, 120}}},
+{SkPath::kDone_Verb}
+};
+
+struct curve* testSet[] = {
+    test1
+};
+
+size_t testSet_count = sizeof(testSet) / sizeof(testSet[0]);
+
+static void construct() {
+    for (size_t idx = 0; idx < testSet_count; ++idx) {
+        const curve* test = testSet[idx];
+        SkPath path;
+        bool pathComplete = false;
+        bool first = true;
+        do {
+            if (first) {
+                path.moveTo(test->pts[0].fX, test->pts[0].fY);
+                first = false;
+            } else if (test->verb != SkPath::kDone_Verb) {
+                path.lineTo(test->pts[0].fX, test->pts[0].fY);
+            }
+            switch (test->verb) {
+                case SkPath::kDone_Verb:
+                    pathComplete = true;
+                    break;
+                case SkPath::kLine_Verb:
+                    path.lineTo(test->pts[1].fX, test->pts[1].fY);
+                    break;
+                case SkPath::kQuad_Verb:
+                    path.quadTo(test->pts[1].fX, test->pts[1].fY, test->pts[2].fX, test->pts[2].fY);
+                    break;
+                case SkPath::kCubic_Verb:
+                    path.cubicTo(test->pts[1].fX, test->pts[1].fY, test->pts[2].fX, test->pts[2].fY, test->pts[3].fX, test->pts[3].fY);
+                    break;
+            }
+            test++;
+        } while (!pathComplete);
+        path.close();
+        if (gShowOriginal) {
+            showPath(path, NULL);
+            SkDebugf("simplified:\n");
+        }
+        testSimplifyx(path);
+    }
+}
+
+static void (*tests[])() = {
+    construct,
+};
+
+static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+
+static void (*firstTest)() = 0;
+static bool skipAll = false;
+
+void MiniSimplify_Test() {
+    if (skipAll) {
+        return;
+    }
+    size_t index = 0;
+    if (firstTest) {
+        while (index < testCount && tests[index] != firstTest) {
+            ++index;
+        }
+    }
+    bool firstTestComplete = false;
+    for ( ; index < testCount; ++index) {
+        (*tests[index])();
+        firstTestComplete = true;
+    }
+}
diff --git a/experimental/Intersection/Parameterization_Test.h b/experimental/Intersection/Parameterization_Test.h
new file mode 100644
index 0000000..93a27ec
--- /dev/null
+++ b/experimental/Intersection/Parameterization_Test.h
@@ -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 "DataTypes.h"
+
+// utilities used only for unit testing
+bool point_on_parameterized_curve(const Cubic& cubic, const _Point& point);
+bool point_on_parameterized_line(const _Line& line, const _Point& point);
+bool point_on_parameterized_curve(const Quadratic& quad, const _Point& point);
diff --git a/experimental/Intersection/QuadraticBezierClip.cpp b/experimental/Intersection/QuadraticBezierClip.cpp
new file mode 100644
index 0000000..6100914
--- /dev/null
+++ b/experimental/Intersection/QuadraticBezierClip.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 "CurveIntersection.h"
+#include "CurveUtilities.h"
+#include "LineParameters.h"
+#include <algorithm> // used for std::swap
+
+#define DEBUG_BEZIER_CLIP 1
+
+// return false if unable to clip (e.g., unable to create implicit line)
+// caller should subdivide, or create degenerate if the values are too small
+bool bezier_clip(const Quadratic& q1, const Quadratic& q2, double& minT, double& maxT) {
+    minT = 1;
+    maxT = 0;
+    // determine normalized implicit line equation for pt[0] to pt[3]
+    //   of the form ax + by + c = 0, where a*a + b*b == 1
+
+    // find the implicit line equation parameters
+    LineParameters endLine;
+    endLine.quadEndPoints(q1);
+    if (!endLine.normalize()) {
+        printf("line cannot be normalized: need more code here\n");
+        assert(0);
+        return false;
+    }
+
+    double distance = endLine.controlPtDistance(q1);
+
+    // find fat line
+    double top = 0;
+    double bottom = distance / 2; // http://students.cs.byu.edu/~tom/557/text/cic.pdf (7.6)
+    if (top > bottom) {
+        std::swap(top, bottom);
+    }
+
+    // compute intersecting candidate distance
+    Quadratic distance2y; // points with X of (0, 1/2, 1)
+    endLine.quadDistanceY(q2, distance2y);
+
+    int flags = 0;
+    if (approximately_lesser(distance2y[0].y, top)) {
+        flags |= kFindTopMin;
+    } else if (approximately_greater(distance2y[0].y, bottom)) {
+        flags |= kFindBottomMin;
+    } else {
+        minT = 0;
+    }
+
+    if (approximately_lesser(distance2y[2].y, top)) {
+        flags |= kFindTopMax;
+    } else if (approximately_greater(distance2y[2].y, bottom)) {
+        flags |= kFindBottomMax;
+    } else {
+        maxT = 1;
+    }
+    // Find the intersection of distance convex hull and fat line.
+    int idx = 0;
+    do {
+        int next = idx + 1;
+        if (next == 3) {
+            next = 0;
+        }
+        x_at(distance2y[idx], distance2y[next], top, bottom, flags, minT, maxT);
+        idx = next;
+    } while (idx);
+#if DEBUG_BEZIER_CLIP
+    _Rect r1, r2;
+    r1.setBounds(q1);
+    r2.setBounds(q2);
+    _Point testPt = {0.487, 0.337};
+    if (r1.contains(testPt) && r2.contains(testPt)) {
+        printf("%s q1=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+                " q2=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) minT=%1.9g maxT=%1.9g\n",
+                __FUNCTION__, q1[0].x, q1[0].y, q1[1].x, q1[1].y, q1[2].x, q1[2].y,
+                q2[0].x, q2[0].y, q2[1].x, q2[1].y, q2[2].x, q2[2].y, minT, maxT);
+    }
+#endif
+    return minT < maxT; // returns false if distance shows no intersection
+}
diff --git a/experimental/Intersection/QuadraticBezierClip_Test.cpp b/experimental/Intersection/QuadraticBezierClip_Test.cpp
new file mode 100644
index 0000000..4c6f0d7
--- /dev/null
+++ b/experimental/Intersection/QuadraticBezierClip_Test.cpp
@@ -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.
+ */
+#include "CurveIntersection.h"
+#include "Intersection_Tests.h"
+#include "QuadraticIntersection_TestData.h"
+
+static const Quadratic testSet[] = {
+    // data for oneOffTest
+    {{8.0000000000000071, 8.0000000000000071},
+     {8.7289570079366854, 8.7289570079366889},
+     {9.3914917259458743, 9.0593802763083691}},
+    {{8.0000000000000142, 8.0000000000000142},
+     {8.1250000000000107, 8.1250000000000071},
+     {8.2500000000000071, 8.2187500000000053}},
+     // data for oneAtEndTest
+    {{0.91292418204644155, 0.41931201426549197},
+     {0.70491388044579517, 0.64754305977710236},
+     {0,                   1                  }},
+    {{0.21875,             0.765625           },
+     {0.125,               0.875              },
+     {0,                   1                  }}
+};
+
+static void oneAtEndTest() {
+    const Quadratic& quad1 = testSet[2];
+    const Quadratic& quad2 = testSet[3];
+    double minT = 0;
+    double maxT = 1;
+    bezier_clip(quad1, quad2, minT, maxT);
+}
+
+
+static void oneOffTest() {
+    const Quadratic& quad1 = testSet[0];
+    const Quadratic& quad2 = testSet[1];
+    double minT = 0;
+    double maxT = 1;
+    bezier_clip(quad1, quad2, minT, maxT);
+}
+
+static void standardTestCases() {
+    for (size_t index = 0; index < quadraticTests_count; ++index) {
+        const Quadratic& quad1 = quadraticTests[index][0];
+        const Quadratic& quad2 = quadraticTests[index][1];
+        Quadratic reduce1, reduce2;
+        int order1 = reduceOrder(quad1, reduce1);
+        int order2 = reduceOrder(quad2, reduce2);
+        if (order1 < 3) {
+            printf("%s [%d] quad1 order=%d\n", __FUNCTION__, (int)index, order1);
+        }
+        if (order2 < 3) {
+            printf("%s [%d] quad2 order=%d\n", __FUNCTION__, (int)index, order2);
+        }
+        if (order1 == 3 && order2 == 3) {
+            double minT = 0;
+            double maxT = 1;
+            bezier_clip(reduce1, reduce2, minT, maxT);
+        }
+    }
+}
+
+void QuadraticBezierClip_Test() {
+    oneAtEndTest();
+    oneOffTest();
+    standardTestCases();
+}
diff --git a/experimental/Intersection/QuadraticBounds.cpp b/experimental/Intersection/QuadraticBounds.cpp
new file mode 100644
index 0000000..a86b7ed
--- /dev/null
+++ b/experimental/Intersection/QuadraticBounds.cpp
@@ -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.
+ */
+#include "CurveIntersection.h"
+#include "CurveUtilities.h"
+#include "Extrema.h"
+
+static int isBoundedByEndPoints(double a, double b, double c)
+{
+    return (a <= b && b <= c) || (a >= b && b >= c);
+}
+
+double leftMostT(const Quadratic& quad, double startT, double endT) {
+    double leftT;
+    if (findExtrema(quad[0].x, quad[1].x, quad[2].x, &leftT)
+            && startT <= leftT && leftT <= endT) {
+        return leftT;
+    }
+    _Point startPt;
+    xy_at_t(quad, startT, startPt.x, startPt.y);
+    _Point endPt;
+    xy_at_t(quad, endT, endPt.x, endPt.y);
+    return startPt.x <= endPt.x ? startT : endT;
+}
+
+void _Rect::setBounds(const Quadratic& quad) {
+    set(quad[0]);
+    add(quad[2]);
+    double tValues[2];
+    int roots = 0;
+    if (!isBoundedByEndPoints(quad[0].x, quad[1].x, quad[2].x)) {
+        roots = findExtrema(quad[0].x, quad[1].x, quad[2].x, tValues);
+    }
+    if (!isBoundedByEndPoints(quad[0].y, quad[1].y, quad[2].y)) {
+        roots += findExtrema(quad[0].y, quad[1].y, quad[2].y,
+                &tValues[roots]);
+    }
+    for (int x = 0; x < roots; ++x) {
+        _Point result;
+        xy_at_t(quad, tValues[x], result.x, result.y);
+        add(result);
+    }
+}
+
+void _Rect::setRawBounds(const Quadratic& quad) {
+    set(quad[0]);
+    for (int x = 1; x < 3; ++x) {
+        add(quad[x]);
+    }
+}
diff --git a/experimental/Intersection/QuadraticImplicit.cpp b/experimental/Intersection/QuadraticImplicit.cpp
new file mode 100644
index 0000000..268d7d3
--- /dev/null
+++ b/experimental/Intersection/QuadraticImplicit.cpp
@@ -0,0 +1,189 @@
+// Another approach is to start with the implicit form of one curve and solve
+// (seek implicit coefficients in QuadraticParameter.cpp
+// by substituting in the parametric form of the other.
+// The downside of this approach is that early rejects are difficult to come by.
+// http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html#step
+
+
+#include "CurveIntersection.h"
+#include "Intersections.h"
+#include "QuadraticParameterization.h"
+#include "QuarticRoot.h"
+#include "QuadraticUtilities.h"
+
+/* given the implicit form 0 = Ax^2 + Bxy + Cy^2 + Dx + Ey + F
+ * and given x = at^2 + bt + c  (the parameterized form)
+ *           y = dt^2 + et + f
+ * then
+ * 0 = A(at^2+bt+c)(at^2+bt+c)+B(at^2+bt+c)(dt^2+et+f)+C(dt^2+et+f)(dt^2+et+f)+D(at^2+bt+c)+E(dt^2+et+f)+F
+ */
+
+static int findRoots(const QuadImplicitForm& i, const Quadratic& q2, double roots[4]) {
+    double a, b, c;
+    set_abc(&q2[0].x, a, b, c);
+    double d, e, f;
+    set_abc(&q2[0].y, d, e, f);
+    const double t4 =     i.x2() *  a * a
+                    +     i.xy() *  a * d
+                    +     i.y2() *  d * d;
+    const double t3 = 2 * i.x2() *  a * b
+                    +     i.xy() * (a * e +     b * d)
+                    + 2 * i.y2() *  d * e;
+    const double t2 =     i.x2() * (b * b + 2 * a * c)
+                    +     i.xy() * (c * d +     b * e + a * f)
+                    +     i.y2() * (e * e + 2 * d * f)
+                    +     i.x()  *  a
+                    +     i.y()  *  d;
+    const double t1 = 2 * i.x2() *  b * c
+                    +     i.xy() * (c * e + b * f)
+                    + 2 * i.y2() *  e * f
+                    +     i.x()  *  b
+                    +     i.y()  *  e;
+    const double t0 =     i.x2() *  c * c
+                    +     i.xy() *  c * f
+                    +     i.y2() *  f * f
+                    +     i.x()  *  c
+                    +     i.y()  *  f
+                    +     i.c();
+    return quarticRoots(t4, t3, t2, t1, t0, roots);
+}
+
+static void addValidRoots(const double roots[4], const int count, const int side, Intersections& i) {
+    int index;
+    for (index = 0; index < count; ++index) {
+        if (!approximately_zero_or_more(roots[index]) || !approximately_one_or_less(roots[index])) {
+            continue;
+        }
+        double t = 1 - roots[index];
+        if (approximately_less_than_zero(t)) {
+            t = 0;
+        } else if (approximately_greater_than_one(t)) {
+            t = 1;
+        }
+        i.insertOne(t, side);
+    }
+}
+
+static bool onlyEndPtsInCommon(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
+// the idea here is to see at minimum do a quick reject by rotating all points
+// to either side of the line formed by connecting the endpoints
+// if the opposite curves points are on the line or on the other side, the
+// curves at most intersect at the endpoints
+    for (int oddMan = 0; oddMan < 3; ++oddMan) {
+        const _Point* endPt[2];
+        for (int opp = 1; opp < 3; ++opp) {
+            int end = oddMan ^ opp;
+            if (end == 3) {
+                end = opp;
+            }
+            endPt[opp - 1] = &q1[end];
+        }
+        double origX = endPt[0]->x;
+        double origY = endPt[0]->y;
+        double adj = endPt[1]->x - origX;
+        double opp = endPt[1]->y - origY;
+        double sign = (q1[oddMan].y - origY) * adj - (q1[oddMan].x - origX) * opp;
+        assert(!approximately_zero(sign));
+        for (int n = 0; n < 3; ++n) {
+            double test = (q2[n].y - origY) * adj - (q2[n].x - origX) * opp;
+            if (test * sign > 0) {
+                goto tryNextHalfPlane;
+            }
+        }
+        for (int i1 = 0; i1 < 3; i1 += 2) {
+            for (int i2 = 0; i2 < 3; i2 += 2) {
+                if (q1[i1] == q2[i2]) {
+                    i.insertOne(i1 >> 1, 0);
+                    i.insertOne(i2 >> 1, 1);
+                }
+            }
+        }
+        assert(i.fUsed < 3);
+        return true;
+tryNextHalfPlane:
+        ;
+    }
+    return false;
+}
+
+bool intersect2(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
+    // if the quads share an end point, check to see if they overlap
+
+    if (onlyEndPtsInCommon(q1, q2, i)) {
+        assert(i.insertBalanced());
+        return i.intersected();
+    }
+    QuadImplicitForm i1(q1);
+    QuadImplicitForm i2(q2);
+    if (i1.implicit_match(i2)) {
+        // FIXME: compute T values
+        // compute the intersections of the ends to find the coincident span
+        bool useVertical = fabs(q1[0].x - q1[2].x) < fabs(q1[0].y - q1[2].y);
+        double t;
+        if ((t = axialIntersect(q1, q2[0], useVertical)) >= 0) {
+            i.addCoincident(t, 0);
+        }
+        if ((t = axialIntersect(q1, q2[2], useVertical)) >= 0) {
+            i.addCoincident(t, 1);
+        }
+        useVertical = fabs(q2[0].x - q2[2].x) < fabs(q2[0].y - q2[2].y);
+        if ((t = axialIntersect(q2, q1[0], useVertical)) >= 0) {
+            i.addCoincident(0, t);
+        }
+        if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) {
+            i.addCoincident(1, t);
+        }
+        assert(i.fCoincidentUsed <= 2);
+        return i.fCoincidentUsed > 0;
+    }
+    double roots1[4], roots2[4];
+    int rootCount = findRoots(i2, q1, roots1);
+    // OPTIMIZATION: could short circuit here if all roots are < 0 or > 1
+#ifndef NDEBUG
+    int rootCount2 =
+#endif
+        findRoots(i1, q2, roots2);
+    assert(rootCount == rootCount2);
+    addValidRoots(roots1, rootCount, 0, i);
+    addValidRoots(roots2, rootCount, 1, i);
+    _Point pts[4];
+    bool matches[4];
+    int flipCheck[4];
+    int index, ndex2;
+    int flipIndex = 0;
+    for (ndex2 = 0; ndex2 < i.fUsed2; ++ndex2) {
+        xy_at_t(q2, i.fT[1][ndex2], pts[ndex2].x, pts[ndex2].y);
+        matches[ndex2] = false;
+    }
+    for (index = 0; index < i.fUsed; ) {
+        _Point xy;
+        xy_at_t(q1, i.fT[0][index], xy.x, xy.y);
+        for (ndex2 = 0; ndex2 < i.fUsed2; ++ndex2) {
+             if (approximately_equal(pts[ndex2].x, xy.x) && approximately_equal(pts[ndex2].y, xy.y)) {
+                assert(flipIndex < 4);
+                flipCheck[flipIndex++] = ndex2;
+                matches[ndex2] = true;
+                goto next;
+             }
+        }
+        if (--i.fUsed > index) {
+            memmove(&i.fT[0][index], &i.fT[0][index + 1], (i.fUsed - index) * sizeof(i.fT[0][0]));
+            continue;
+        }
+    next:
+        ++index;
+    }
+    for (ndex2 = 0; ndex2 < i.fUsed2; ) {
+        if (!matches[ndex2]) {
+             if (--i.fUsed2 > ndex2) {
+                memmove(&i.fT[1][ndex2], &i.fT[1][ndex2 + 1], (i.fUsed2 - ndex2) * sizeof(i.fT[1][0]));
+                memmove(&matches[ndex2], &matches[ndex2 + 1], (i.fUsed2 - ndex2) * sizeof(matches[0]));
+                continue;
+             }
+        }
+        ++ndex2;
+    }
+    i.fFlip = i.fUsed >= 2 && flipCheck[0] > flipCheck[1];
+    assert(i.insertBalanced());
+    return i.intersected();
+}
diff --git a/experimental/Intersection/QuadraticIntersection.cpp b/experimental/Intersection/QuadraticIntersection.cpp
new file mode 100644
index 0000000..800964d
--- /dev/null
+++ b/experimental/Intersection/QuadraticIntersection.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 "CurveIntersection.h"
+#include "Intersections.h"
+#include "IntersectionUtilities.h"
+#include "LineIntersection.h"
+#include "LineUtilities.h"
+#include "QuadraticLineSegments.h"
+#include "QuadraticUtilities.h"
+#include <algorithm> // for swap
+
+static const double tClipLimit = 0.8; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf see Multiple intersections
+
+class QuadraticIntersections {
+public:
+
+QuadraticIntersections(const Quadratic& q1, const Quadratic& q2, Intersections& i)
+    : quad1(q1)
+    , quad2(q2)
+    , intersections(i)
+    , depth(0)
+    , splits(0)
+    , coinMinT1(-1) {
+}
+
+bool intersect() {
+    double minT1, minT2, maxT1, maxT2;
+    if (!bezier_clip(quad2, quad1, minT1, maxT1)) {
+        return false;
+    }
+    if (!bezier_clip(quad1, quad2, minT2, maxT2)) {
+        return false;
+    }
+    quad1Divisions = 1 / subDivisions(quad1);
+    quad2Divisions = 1 / subDivisions(quad2);
+    int split;
+    if (maxT1 - minT1 < maxT2 - minT2) {
+        intersections.swap();
+        minT2 = 0;
+        maxT2 = 1;
+        split = maxT1 - minT1 > tClipLimit;
+    } else {
+        minT1 = 0;
+        maxT1 = 1;
+        split = (maxT2 - minT2 > tClipLimit) << 1;
+    }
+    return chop(minT1, maxT1, minT2, maxT2, split);
+}
+
+protected:
+
+bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
+    bool t1IsLine = maxT1 - minT1 <= quad1Divisions;
+    bool t2IsLine = maxT2 - minT2 <= quad2Divisions;
+    if (t1IsLine | t2IsLine) {
+        return intersectAsLine(minT1, maxT1, minT2, maxT2, t1IsLine, t2IsLine);
+    }
+    Quadratic smaller, larger;
+    // FIXME: carry last subdivide and reduceOrder result with quad
+    sub_divide(quad1, minT1, maxT1, intersections.swapped() ? larger : smaller);
+    sub_divide(quad2, minT2, maxT2, intersections.swapped() ? smaller : larger);
+    double minT, maxT;
+    if (!bezier_clip(smaller, larger, minT, maxT)) {
+        if (approximately_equal(minT, maxT)) {
+            double smallT, largeT;
+            _Point q2pt, q1pt;
+            if (intersections.swapped()) {
+                largeT = interp(minT2, maxT2, minT);
+                xy_at_t(quad2, largeT, q2pt.x, q2pt.y);
+                xy_at_t(quad1, minT1, q1pt.x, q1pt.y);
+                if (approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y)) {
+                    smallT = minT1;
+                } else {
+                    xy_at_t(quad1, maxT1, q1pt.x, q1pt.y); // FIXME: debug code
+                    assert(approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y));
+                    smallT = maxT1;
+                }
+            } else {
+                smallT = interp(minT1, maxT1, minT);
+                xy_at_t(quad1, smallT, q1pt.x, q1pt.y);
+                xy_at_t(quad2, minT2, q2pt.x, q2pt.y);
+                if (approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y)) {
+                    largeT = minT2;
+                } else {
+                    xy_at_t(quad2, maxT2, q2pt.x, q2pt.y); // FIXME: debug code
+                    assert(approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y));
+                    largeT = maxT2;
+                }
+            }
+            intersections.add(smallT, largeT);
+            return true;
+        }
+        return false;
+    }
+    int split;
+    if (intersections.swapped()) {
+        double newMinT1 = interp(minT1, maxT1, minT);
+        double newMaxT1 = interp(minT1, maxT1, maxT);
+        split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1;
+#define VERBOSE 0
+#if VERBOSE
+        printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n", __FUNCTION__, depth,
+            splits, newMinT1, newMaxT1, minT1, maxT1, split);
+#endif
+        minT1 = newMinT1;
+        maxT1 = newMaxT1;
+    } else {
+        double newMinT2 = interp(minT2, maxT2, minT);
+        double newMaxT2 = interp(minT2, maxT2, maxT);
+        split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit;
+#if VERBOSE
+        printf("%s d=%d s=%d new2=(%g,%g) old2=(%g,%g) split=%d\n", __FUNCTION__, depth,
+            splits, newMinT2, newMaxT2, minT2, maxT2, split);
+#endif
+        minT2 = newMinT2;
+        maxT2 = newMaxT2;
+    }
+    return chop(minT1, maxT1, minT2, maxT2, split);
+}
+
+bool intersectAsLine(double minT1, double maxT1, double minT2, double maxT2,
+       bool treat1AsLine, bool treat2AsLine)
+{
+    _Line line1, line2;
+    if (intersections.swapped()) {
+        std::swap(treat1AsLine, treat2AsLine);
+        std::swap(minT1, minT2);
+        std::swap(maxT1, maxT2);
+    }
+    if (coinMinT1 >= 0) {
+        bool earlyExit;
+        if ((earlyExit = coinMaxT1 == minT1)) {
+            coinMaxT1 = maxT1;
+        }
+        if (coinMaxT2 == minT2) {
+            coinMaxT2 = maxT2;
+            return true;
+        }
+        if (earlyExit) {
+            return true;
+        }
+        coinMinT1 = -1;
+    }
+    // do line/quadratic or even line/line intersection instead
+    if (treat1AsLine) {
+        xy_at_t(quad1, minT1, line1[0].x, line1[0].y);
+        xy_at_t(quad1, maxT1, line1[1].x, line1[1].y);
+    }
+    if (treat2AsLine) {
+        xy_at_t(quad2, minT2, line2[0].x, line2[0].y);
+        xy_at_t(quad2, maxT2, line2[1].x, line2[1].y);
+    }
+    int pts;
+    double smallT1, largeT1, smallT2, largeT2;
+    if (treat1AsLine & treat2AsLine) {
+        double t1[2], t2[2];
+        pts = ::intersect(line1, line2, t1, t2);
+        if (pts == 2) {
+            smallT1 = interp(minT1, maxT1, t1[0]);
+            largeT1 = interp(minT2, maxT2, t2[0]);
+            smallT2 = interp(minT1, maxT1, t1[1]);
+            largeT2 = interp(minT2, maxT2, t2[1]);
+            intersections.addCoincident(smallT1, smallT2, largeT1, largeT2);
+        } else {
+            smallT1 = interp(minT1, maxT1, t1[0]);
+            largeT1 = interp(minT2, maxT2, t2[0]);
+            intersections.add(smallT1, largeT1);
+        }
+    } else {
+        Intersections lq;
+        pts = ::intersect(treat1AsLine ? quad2 : quad1,
+                treat1AsLine ? line1 : line2, lq);
+        if (pts == 2) { // if the line and edge are coincident treat differently
+            _Point midQuad, midLine;
+            double midQuadT = (lq.fT[0][0] + lq.fT[0][1]) / 2;
+            xy_at_t(treat1AsLine ? quad2 : quad1, midQuadT, midQuad.x, midQuad.y);
+            double lineT = t_at(treat1AsLine ? line1 : line2, midQuad);
+            xy_at_t(treat1AsLine ? line1 : line2, lineT, midLine.x, midLine.y);
+            if (approximately_equal(midQuad.x, midLine.x)
+                    && approximately_equal(midQuad.y, midLine.y)) {
+                smallT1 = lq.fT[0][0];
+                largeT1 = lq.fT[1][0];
+                smallT2 = lq.fT[0][1];
+                largeT2 = lq.fT[1][1];
+                if (treat2AsLine) {
+                    smallT1 = interp(minT1, maxT1, smallT1);
+                    smallT2 = interp(minT1, maxT1, smallT2);
+                } else {
+                    largeT1 = interp(minT2, maxT2, largeT1);
+                    largeT2 = interp(minT2, maxT2, largeT2);
+                }
+                intersections.addCoincident(smallT1, smallT2, largeT1, largeT2);
+                goto setCoinMinMax;
+            }
+        }
+        for (int index = 0; index < pts; ++index) {
+            smallT1 = lq.fT[0][index];
+            largeT1 = lq.fT[1][index];
+            if (treat2AsLine) {
+                smallT1 = interp(minT1, maxT1, smallT1);
+            } else {
+                largeT1 = interp(minT2, maxT2, largeT1);
+            }
+            intersections.add(smallT1, largeT1);
+        }
+    }
+    if (pts > 0) {
+setCoinMinMax:
+        coinMinT1 = minT1;
+        coinMaxT1 = maxT1;
+        coinMinT2 = minT2;
+        coinMaxT2 = maxT2;
+    }
+    return pts > 0;
+}
+
+bool chop(double minT1, double maxT1, double minT2, double maxT2, int split) {
+    ++depth;
+    intersections.swap();
+    if (split) {
+        ++splits;
+        if (split & 2) {
+            double middle1 = (maxT1 + minT1) / 2;
+            intersect(minT1, middle1, minT2, maxT2);
+            intersect(middle1, maxT1, minT2, maxT2);
+        } else {
+            double middle2 = (maxT2 + minT2) / 2;
+            intersect(minT1, maxT1, minT2, middle2);
+            intersect(minT1, maxT1, middle2, maxT2);
+        }
+        --splits;
+        intersections.swap();
+        --depth;
+        return intersections.intersected();
+    }
+    bool result = intersect(minT1, maxT1, minT2, maxT2);
+    intersections.swap();
+    --depth;
+    return result;
+}
+
+private:
+
+const Quadratic& quad1;
+const Quadratic& quad2;
+Intersections& intersections;
+int depth;
+int splits;
+double quad1Divisions; // line segments to approximate original within error
+double quad2Divisions;
+double coinMinT1; // range of Ts where approximate line intersected curve
+double coinMaxT1;
+double coinMinT2;
+double coinMaxT2;
+};
+
+#include "LineParameters.h"
+
+static void hackToFixPartialCoincidence(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
+    // look to see if non-coincident data basically has unsortable tangents
+
+    // look to see if a point between non-coincident data is on the curve
+    int cIndex;
+    for (int uIndex = 0; uIndex < i.fUsed; ) {
+        double bestDist1 = 1;
+        double bestDist2 = 1;
+        int closest1 = -1;
+        int closest2 = -1;
+        for (cIndex = 0; cIndex < i.fCoincidentUsed; ++cIndex) {
+            double dist = fabs(i.fT[0][uIndex] - i.fCoincidentT[0][cIndex]);
+            if (bestDist1 > dist) {
+                bestDist1 = dist;
+                closest1 = cIndex;
+            }
+            dist = fabs(i.fT[1][uIndex] - i.fCoincidentT[1][cIndex]);
+            if (bestDist2 > dist) {
+                bestDist2 = dist;
+                closest2 = cIndex;
+            }
+        }
+        _Line ends;
+        _Point mid;
+        double t1 = i.fT[0][uIndex];
+        xy_at_t(q1, t1, ends[0].x, ends[0].y);
+        xy_at_t(q1, i.fCoincidentT[0][closest1], ends[1].x, ends[1].y);
+        double midT = (t1 + i.fCoincidentT[0][closest1]) / 2;
+        xy_at_t(q1, midT, mid.x, mid.y);
+        LineParameters params;
+        params.lineEndPoints(ends);
+        double midDist = params.pointDistance(mid);
+        // Note that we prefer to always measure t error, which does not scale,
+        // instead of point error, which is scale dependent. FIXME
+        if (!approximately_zero(midDist)) {
+            ++uIndex;
+            continue;
+        }
+        double t2 = i.fT[1][uIndex];
+        xy_at_t(q2, t2, ends[0].x, ends[0].y);
+        xy_at_t(q2, i.fCoincidentT[1][closest2], ends[1].x, ends[1].y);
+        midT = (t2 + i.fCoincidentT[1][closest2]) / 2;
+        xy_at_t(q2, midT, mid.x, mid.y);
+        params.lineEndPoints(ends);
+        midDist = params.pointDistance(mid);
+        if (!approximately_zero(midDist)) {
+            ++uIndex;
+            continue;
+        }
+        // if both midpoints are close to the line, lengthen coincident span
+        int cEnd = closest1 ^ 1; // assume coincidence always travels in pairs
+        if (!between(i.fCoincidentT[0][cEnd], t1, i.fCoincidentT[0][closest1])) {
+            i.fCoincidentT[0][closest1] = t1;
+        }
+        cEnd = closest2 ^ 1;
+        if (!between(i.fCoincidentT[0][cEnd], t2, i.fCoincidentT[0][closest2])) {
+            i.fCoincidentT[0][closest2] = t2;
+        }
+        int remaining = --i.fUsed - uIndex;
+        if (remaining > 0) {
+            memmove(&i.fT[0][uIndex], &i.fT[0][uIndex + 1], sizeof(i.fT[0][0]) * remaining);
+            memmove(&i.fT[1][uIndex], &i.fT[1][uIndex + 1], sizeof(i.fT[1][0]) * remaining);
+        }
+    }
+    // if coincident data is subjectively a tiny span, replace it with a single point
+    for (cIndex = 0; cIndex < i.fCoincidentUsed; ) {
+        double start1 = i.fCoincidentT[0][cIndex];
+        double end1 = i.fCoincidentT[0][cIndex + 1];
+        _Line ends1;
+        xy_at_t(q1, start1, ends1[0].x, ends1[0].y);
+        xy_at_t(q1, end1, ends1[1].x, ends1[1].y);
+        if (!approximately_equal(ends1[0].x, ends1[1].x) || approximately_equal(ends1[0].y, ends1[1].y)) {
+            cIndex += 2;
+            continue;
+        }
+        double start2 = i.fCoincidentT[1][cIndex];
+        double end2 = i.fCoincidentT[1][cIndex + 1];
+        _Line ends2;
+        xy_at_t(q2, start2, ends2[0].x, ends2[0].y);
+        xy_at_t(q2, end2, ends2[1].x, ends2[1].y);
+        // again, approximately should be used with T values, not points FIXME
+        if (!approximately_equal(ends2[0].x, ends2[1].x) || approximately_equal(ends2[0].y, ends2[1].y)) {
+            cIndex += 2;
+            continue;
+        }
+        if (approximately_less_than_zero(start1) || approximately_less_than_zero(end1)) {
+            start1 = 0;
+        } else if (approximately_greater_than_one(start1) || approximately_greater_than_one(end1)) {
+            start1 = 1;
+        } else {
+            start1 = (start1 + end1) / 2;
+        }
+        if (approximately_less_than_zero(start2) || approximately_less_than_zero(end2)) {
+            start2 = 0;
+        } else if (approximately_greater_than_one(start2) || approximately_greater_than_one(end2)) {
+            start2 = 1;
+        } else {
+            start2 = (start2 + end2) / 2;
+        }
+        i.insert(start1, start2);
+        i.fCoincidentUsed -= 2;
+        int remaining = i.fCoincidentUsed - cIndex;
+        if (remaining > 0) {
+            memmove(&i.fCoincidentT[0][cIndex], &i.fCoincidentT[0][cIndex + 2], sizeof(i.fCoincidentT[0][0]) * remaining);
+            memmove(&i.fCoincidentT[1][cIndex], &i.fCoincidentT[1][cIndex + 2], sizeof(i.fCoincidentT[1][0]) * remaining);
+        }
+    }
+}
+
+bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
+    if (implicit_matches(q1, q2)) {
+        // FIXME: compute T values
+        // compute the intersections of the ends to find the coincident span
+        bool useVertical = fabs(q1[0].x - q1[2].x) < fabs(q1[0].y - q1[2].y);
+        double t;
+        if ((t = axialIntersect(q1, q2[0], useVertical)) >= 0) {
+            i.addCoincident(t, 0);
+        }
+        if ((t = axialIntersect(q1, q2[2], useVertical)) >= 0) {
+            i.addCoincident(t, 1);
+        }
+        useVertical = fabs(q2[0].x - q2[2].x) < fabs(q2[0].y - q2[2].y);
+        if ((t = axialIntersect(q2, q1[0], useVertical)) >= 0) {
+            i.addCoincident(0, t);
+        }
+        if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) {
+            i.addCoincident(1, t);
+        }
+        assert(i.fCoincidentUsed <= 2);
+        return i.fCoincidentUsed > 0;
+    }
+    QuadraticIntersections q(q1, q2, i);
+    bool result = q.intersect();
+    // FIXME: partial coincidence detection is currently poor. For now, try
+    // to fix up the data after the fact. In the future, revisit the error
+    // term to try to avoid this kind of result in the first place.
+    if (i.fUsed && i.fCoincidentUsed) {
+        hackToFixPartialCoincidence(q1, q2, i);
+    }
+    return result;
+}
diff --git a/experimental/Intersection/QuadraticIntersection_Test.cpp b/experimental/Intersection/QuadraticIntersection_Test.cpp
new file mode 100644
index 0000000..799287d
--- /dev/null
+++ b/experimental/Intersection/QuadraticIntersection_Test.cpp
@@ -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.
+ */
+#include "CurveIntersection.h"
+#include "CurveUtilities.h"
+#include "Intersection_Tests.h"
+#include "Intersections.h"
+#include "QuadraticIntersection_TestData.h"
+#include "TestUtilities.h"
+#include "SkTypes.h"
+
+const int firstQuadIntersectionTest = 9;
+
+static void standardTestCases() {
+    for (size_t index = firstQuadIntersectionTest; index < quadraticTests_count; ++index) {
+        const Quadratic& quad1 = quadraticTests[index][0];
+        const Quadratic& quad2 = quadraticTests[index][1];
+        Quadratic reduce1, reduce2;
+        int order1 = reduceOrder(quad1, reduce1);
+        int order2 = reduceOrder(quad2, reduce2);
+        if (order1 < 3) {
+            printf("[%d] quad1 order=%d\n", (int) index, order1);
+        }
+        if (order2 < 3) {
+            printf("[%d] quad2 order=%d\n", (int) index, order2);
+        }
+        if (order1 == 3 && order2 == 3) {
+            Intersections intersections, intersections2;
+            intersect(reduce1, reduce2, intersections);
+            intersect2(reduce1, reduce2, intersections2);
+            SkASSERT(intersections.used() == intersections2.used());
+            if (intersections.intersected()) {
+                for (int pt = 0; pt < intersections.used(); ++pt) {
+                    double tt1 = intersections.fT[0][pt];
+                    double tx1, ty1;
+                    xy_at_t(quad1, tt1, tx1, ty1);
+                    double tt2 = intersections.fT[1][pt];
+                    double tx2, ty2;
+                    xy_at_t(quad2, tt2, tx2, ty2);
+                    if (!approximately_equal(tx1, tx2)) {
+                        printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                            __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+                    }
+                    if (!approximately_equal(ty1, ty2)) {
+                        printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                            __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+                    }
+                    tt1 = intersections2.fT[0][pt];
+                    SkASSERT(approximately_equal(intersections.fT[0][pt], tt1));
+                    tt2 = intersections2.fT[1][pt];
+                    SkASSERT(approximately_equal(intersections.fT[1][pt], tt2));
+                }
+            }
+        }
+    }
+}
+
+static const Quadratic testSet[] = {
+
+{{369.8543701171875, 145.66734313964844}, {382.36788940429688, 121.28203582763672}, {406.21844482421875, 121.28203582763672}},
+{{369.96469116210938, 137.96672058105469}, {383.97555541992188, 121.28203582763672}, {406.2218017578125, 121.28203582763672}},
+
+    {{369.850525, 145.675964}, {382.362915, 121.29287}, {406.211273, 121.29287}},
+    {{369.962311, 137.976044}, {383.971893, 121.29287}, {406.216125, 121.29287}},
+
+    {{400.121704, 149.468719}, {391.949493, 161.037186}, {391.949493, 181.202423}},
+    {{391.946747, 181.839218}, {391.946747, 155.62442}, {406.115479, 138.855438}},
+    {{360.048828125, 229.2578125}, {360.048828125, 224.4140625}, {362.607421875, 221.3671875}},
+    {{362.607421875, 221.3671875}, {365.166015625, 218.3203125}, {369.228515625, 218.3203125}},
+    {{8, 8}, {10, 10}, {8, -10}},
+    {{8, 8}, {12, 12}, {14, 4}},
+    {{8, 8}, {9, 9}, {10, 8}}
+};
+
+const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
+
+static void oneOffTest() {
+    for (size_t outer = 0; outer < testSetCount - 1; ++outer) {
+        for (size_t inner = outer + 1; inner < testSetCount; ++inner) {
+            const Quadratic& quad1 = testSet[outer];
+            const Quadratic& quad2 = testSet[inner];
+            double tt1, tt2;
+            Intersections intersections2;
+            intersect2(quad1, quad2, intersections2);
+            for (int pt = 0; pt < intersections2.used(); ++pt) {
+                tt1 = intersections2.fT[0][pt];
+                double tx1, ty1;
+                xy_at_t(quad1, tt1, tx1, ty1);
+                int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt;
+                tt2 = intersections2.fT[1][pt2];
+                double tx2, ty2;
+                xy_at_t(quad2, tt2, tx2, ty2);
+                if (!approximately_equal(tx1, tx2)) {
+                    SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                        __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+                    SkASSERT(0);
+                }
+                if (!approximately_equal(ty1, ty2)) {
+                    SkDebugf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n",
+                        __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2);
+                    SkASSERT(0);
+                }
+                SkDebugf("%s [%d][%d] t1=%1.9g (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
+                    outer, inner, tt1, tx1, tx2, tt2);
+            }
+        }
+    }
+}
+
+static const Quadratic coincidentTestSet[] = {
+    {{369.850525, 145.675964}, {382.362915, 121.29287}, {406.211273, 121.29287}},
+    {{369.850525, 145.675964}, {382.362915, 121.29287}, {406.211273, 121.29287}},
+    {{8, 8}, {10, 10}, {8, -10}},
+    {{8, -10}, {10, 10}, {8, 8}},
+};
+
+const size_t coincidentTestSetCount = sizeof(coincidentTestSet) / sizeof(coincidentTestSet[0]);
+
+static void coincidentTest() {
+    for (size_t testIndex = 0; testIndex < coincidentTestSetCount - 1; testIndex += 2) {
+        const Quadratic& quad1 = coincidentTestSet[testIndex];
+        const Quadratic& quad2 = coincidentTestSet[testIndex + 1];
+        Intersections intersections2;
+        intersect2(quad1, quad2, intersections2);
+        SkASSERT(intersections2.coincidentUsed() == 2);
+        for (int pt = 0; pt < intersections2.coincidentUsed(); ++pt) {
+            double tt1 = intersections2.fT[0][pt];
+            double tt2 = intersections2.fT[1][pt];
+        //    SkASSERT(approximately_equal(intersections.fT[0][pt], tt1));
+        //    SkASSERT(approximately_equal(intersections.fT[1][pt], tt2));
+        }
+    }
+}
+
+void QuadraticIntersection_Test() {
+    oneOffTest();
+    coincidentTest();
+    standardTestCases();
+}
diff --git a/experimental/Intersection/QuadraticIntersection_TestData.cpp b/experimental/Intersection/QuadraticIntersection_TestData.cpp
new file mode 100644
index 0000000..9ec585a
--- /dev/null
+++ b/experimental/Intersection/QuadraticIntersection_TestData.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 "QuadraticIntersection_TestData.h"
+
+const Quadratic quadraticLines[] = {
+    {{0, 0}, {0, 0}, {1, 0}},
+    {{0, 0}, {1, 0}, {0, 0}},
+    {{1, 0}, {0, 0}, {0, 0}},
+    {{1, 0}, {2, 0}, {3, 0}},
+    {{0, 0}, {0, 0}, {0, 1}},
+    {{0, 0}, {0, 1}, {0, 0}},
+    {{0, 1}, {0, 0}, {0, 0}},
+    {{0, 1}, {0, 2}, {0, 3}},
+    {{0, 0}, {0, 0}, {1, 1}},
+    {{0, 0}, {1, 1}, {0, 0}},
+    {{1, 1}, {0, 0}, {0, 0}},
+    {{1, 1}, {2, 2}, {3, 3}},
+    {{1, 1}, {3, 3}, {3, 3}},
+    {{1, 1}, {1, 1}, {2, 2}},
+    {{1, 1}, {2, 2}, {1, 1}},
+    {{1, 1}, {1, 1}, {3, 3}},
+    {{1, 1}, {2, 2}, {4, 4}}, // no coincident
+    {{1, 1}, {3, 3}, {4, 4}},
+    {{1, 1}, {3, 3}, {2, 2}},
+    {{1, 1}, {4, 4}, {2, 2}},
+    {{1, 1}, {4, 4}, {3, 3}},
+    {{2, 2}, {1, 1}, {3, 3}},
+    {{2, 2}, {1, 1}, {4, 4}},
+    {{2, 2}, {3, 3}, {1, 1}},
+    {{2, 2}, {3, 3}, {4, 4}},
+    {{2, 2}, {4, 4}, {1, 1}},
+    {{2, 2}, {4, 4}, {3, 3}},
+};
+
+const size_t quadraticLines_count = sizeof(quadraticLines) / sizeof(quadraticLines[0]);
+
+static const double F = PointEpsilon * 3;
+static const double H = PointEpsilon * 4;
+static const double J = PointEpsilon * 5;
+static const double K = PointEpsilon * 8; // INVESTIGATE: why are larger multiples necessary?
+
+const Quadratic quadraticModEpsilonLines[] = {
+    {{0, F}, {0, 0}, {1, 0}},
+    {{0, 0}, {1, 0}, {0, F}},
+    {{1, 0}, {0, F}, {0, 0}},
+    {{1, H}, {2, 0}, {3, 0}},
+    {{F, 0}, {0, 0}, {0, 1}},
+    {{0, 0}, {0, 1}, {F, 0}},
+    {{0, 1}, {F, 0}, {0, 0}},
+    {{H, 1}, {0, 2}, {0, 3}},
+    {{0, F}, {0, 0}, {1, 1}},
+    {{0, 0}, {1, 1}, {F, 0}},
+    {{1, 1}, {F, 0}, {0, 0}},
+    {{1, 1+J}, {2, 2}, {3, 3}},
+    {{1, 1}, {3, 3}, {3+F, 3}},
+    {{1, 1}, {1+F, 1}, {2, 2}},
+    {{1, 1}, {2, 2}, {1, 1+F}},
+    {{1, 1}, {1, 1+F}, {3, 3}},
+    {{1+H, 1}, {2, 2}, {4, 4}}, // no coincident
+    {{1, 1+K}, {3, 3}, {4, 4}},
+    {{1, 1}, {3+F, 3}, {2, 2}},
+    {{1, 1}, {4, 4+F}, {2, 2}},
+    {{1, 1}, {4, 4}, {3+F, 3}},
+    {{2, 2}, {1, 1}, {3, 3+F}},
+    {{2+F, 2}, {1, 1}, {4, 4}},
+    {{2, 2+F}, {3, 3}, {1, 1}},
+    {{2, 2}, {3+F, 3}, {4, 4}},
+    {{2, 2}, {4, 4+F}, {1, 1}},
+    {{2, 2}, {4, 4}, {3+F, 3}},
+};
+
+const size_t quadraticModEpsilonLines_count = sizeof(quadraticModEpsilonLines) / sizeof(quadraticModEpsilonLines[0]);
+
+const Quadratic quadraticTests[][2] = {
+    { // one intersection
+     {{0, 0},
+      {0, 1},
+      {1, 1}},
+     {{0, 1},
+      {0, 0},
+      {1, 0}}
+    },
+    { // four intersections
+     {{1, 0},
+      {2, 6},
+      {3, 0}},
+     {{0, 1},
+      {6, 2},
+      {0, 3}}
+    }
+};
+
+const size_t quadraticTests_count = sizeof(quadraticTests) / sizeof(quadraticTests[0]);
diff --git a/experimental/Intersection/QuadraticIntersection_TestData.h b/experimental/Intersection/QuadraticIntersection_TestData.h
new file mode 100644
index 0000000..4c95d5e
--- /dev/null
+++ b/experimental/Intersection/QuadraticIntersection_TestData.h
@@ -0,0 +1,19 @@
+/*
+ * 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 !defined(IN_TEST)
+    #define IN_TEST 1
+#endif
+
+#include "DataTypes.h"
+
+extern const Quadratic quadraticLines[];
+extern const Quadratic quadraticModEpsilonLines[];
+extern const Quadratic quadraticTests[][2];
+
+extern const size_t quadraticLines_count;
+extern const size_t quadraticModEpsilonLines_count;
+extern const size_t quadraticTests_count;
diff --git a/experimental/Intersection/QuadraticLineSegments.cpp b/experimental/Intersection/QuadraticLineSegments.cpp
new file mode 100644
index 0000000..5e26d35
--- /dev/null
+++ b/experimental/Intersection/QuadraticLineSegments.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 "QuadraticLineSegments.h"
+
+// http://cagd.cs.byu.edu/~557/text/cagd.pdf 2.7
+// A hodograph is the first derivative curve
+void hodograph(const Quadratic& quad, _Line& hodo) {
+    hodo[0].x = 2 * (quad[1].x - quad[0].x);
+    hodo[0].y = 2 * (quad[1].y - quad[0].y);
+    hodo[1].x = 2 * (quad[2].x - quad[1].x);
+    hodo[1].y = 2 * (quad[2].y - quad[1].y);
+}
+
+// A 2nd hodograph is the second derivative curve
+void secondHodograph(const Quadratic& quad, _Point& hodo2) {
+    _Line hodo;
+    hodograph(quad, hodo);
+    hodo2.x = hodo[1].x - hodo[0].x;
+    hodo2.y = hodo[1].y - hodo[0].y;
+}
+
+// The number of line segments required to approximate the quad
+// see  http://cagd.cs.byu.edu/~557/text/cagd.pdf 10.6
+double subDivisions(const Quadratic& quad) {
+    _Point hodo2;
+    secondHodograph(quad, hodo2);
+    double dist = sqrt(hodo2.x * hodo2.x + hodo2.y * hodo2.y);
+    double segments = sqrt(dist / (8 * FLT_EPSILON));
+    return segments;
+}
diff --git a/experimental/Intersection/QuadraticLineSegments.h b/experimental/Intersection/QuadraticLineSegments.h
new file mode 100644
index 0000000..640a69b
--- /dev/null
+++ b/experimental/Intersection/QuadraticLineSegments.h
@@ -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 "DataTypes.h"
+
+void hodograph(const Quadratic& , _Line& hodo);
+void secondHodograph(const Quadratic& , _Point& hodo2);
+double subDivisions(const Quadratic& );
diff --git a/experimental/Intersection/QuadraticParameterization.cpp b/experimental/Intersection/QuadraticParameterization.cpp
new file mode 100644
index 0000000..8e7f1a2
--- /dev/null
+++ b/experimental/Intersection/QuadraticParameterization.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 "CurveIntersection.h"
+#include "QuadraticParameterization.h"
+#include "QuadraticUtilities.h"
+
+/* from http://tom.cs.byu.edu/~tom/papers/cvgip84.pdf 4.1
+ *
+ * This paper proves that Syvester's method can compute the implicit form of
+ * the quadratic from the parameterized form.
+ *
+ * Given x = a*t*t + b*t + c  (the parameterized form)
+ *       y = d*t*t + e*t + f
+ *
+ * we want to find an equation of the implicit form:
+ *
+ * A*x*x + B*x*y + C*y*y + D*x + E*y + F = 0
+ *
+ * The implicit form can be expressed as a 4x4 determinant, as shown.
+ *
+ * The resultant obtained by Syvester's method is
+ *
+ * |   a   b   (c - x)     0     |
+ * |   0   a      b     (c - x)  |
+ * |   d   e   (f - y)     0     |
+ * |   0   d      e     (f - y)  |
+ *
+ * which expands to
+ *
+ * d*d*x*x + -2*a*d*x*y + a*a*y*y
+ *         + (-2*c*d*d + b*e*d - a*e*e + 2*a*f*d)*x
+ *         + (-2*f*a*a + e*b*a - d*b*b + 2*d*c*a)*y
+ *         +
+ * |   a   b   c   0   |
+ * |   0   a   b   c   | == 0.
+ * |   d   e   f   0   |
+ * |   0   d   e   f   |
+ *
+ * Expanding the constant determinant results in
+ *
+ *   | a b c |     | b c 0 |
+ * a*| e f 0 | + d*| a b c | ==
+ *   | d e f |     | d e f |
+ *
+ * a*(a*f*f + c*e*e - c*f*d - b*e*f) + d*(b*b*f + c*c*d - c*a*f - c*e*b)
+ *
+ */
+
+
+static bool straight_forward = true;
+
+QuadImplicitForm::QuadImplicitForm(const Quadratic& q) {
+    double a, b, c;
+    set_abc(&q[0].x, a, b, c);
+    double d, e, f;
+    set_abc(&q[0].y, d, e, f);
+    // compute the implicit coefficients
+    if (straight_forward) { // 42 muls, 13 adds
+        p[xx_coeff] = d * d;
+        p[xy_coeff] = -2 * a * d;
+        p[yy_coeff] = a * a;
+        p[x_coeff] = -2*c*d*d + b*e*d - a*e*e + 2*a*f*d;
+        p[y_coeff] = -2*f*a*a + e*b*a - d*b*b + 2*d*c*a;
+        p[c_coeff] = a*(a*f*f + c*e*e - c*f*d - b*e*f)
+                   + d*(b*b*f + c*c*d - c*a*f - c*e*b);
+    } else { // 26 muls, 11 adds
+        double aa = a * a;
+        double ad = a * d;
+        double dd = d * d;
+        p[xx_coeff] = dd;
+        p[xy_coeff] = -2 * ad;
+        p[yy_coeff] = aa;
+        double be = b * e;
+        double bde = be * d;
+        double cdd = c * dd;
+        double ee = e * e;
+        p[x_coeff] =  -2*cdd + bde - a*ee + 2*ad*f;
+        double aaf = aa * f;
+        double abe = a * be;
+        double ac = a * c;
+        double bb_2ac = b*b - 2*ac;
+        p[y_coeff] = -2*aaf + abe - d*bb_2ac;
+        p[c_coeff] = aaf*f + ac*ee + d*f*bb_2ac - abe*f + c*cdd - c*bde;
+    }
+}
+
+ /* Given a pair of quadratics, determine their parametric coefficients.
+  * If the scaled coefficients are nearly equal, then the part of the quadratics
+  * may be coincident.
+  * FIXME: optimization -- since comparison short-circuits on no match,
+  * lazily compute the coefficients, comparing the easiest to compute first.
+  * xx and yy first; then xy; and so on.
+  */
+bool QuadImplicitForm::implicit_match(const QuadImplicitForm& p2) const {
+    int first = 0;
+    for (int index = 0; index < coeff_count; ++index) {
+        if (approximately_zero(p[index]) && approximately_zero(p2.p[index])) {
+            first += first == index;
+            continue;
+        }
+        if (first == index) {
+            continue;
+        }
+        if (!approximately_equal(p[index] * p2.p[first],
+                p[first] * p2.p[index])) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool implicit_matches(const Quadratic& quad1, const Quadratic& quad2) {
+    QuadImplicitForm i1(quad1);  // a'xx , b'xy , c'yy , d'x , e'y , f
+    QuadImplicitForm i2(quad2);
+    return i1.implicit_match(i2);
+}
+
+static double tangent(const double* quadratic, double t) {
+    double a, b, c;
+    set_abc(quadratic, a, b, c);
+    return 2 * a * t + b;
+}
+
+void tangent(const Quadratic& quadratic, double t, _Point& result) {
+    result.x = tangent(&quadratic[0].x, t);
+    result.y = tangent(&quadratic[0].y, t);
+}
+
+
+
+// unit test to return and validate parametric coefficients
+#include "QuadraticParameterization_TestUtility.cpp"
diff --git a/experimental/Intersection/QuadraticParameterization.h b/experimental/Intersection/QuadraticParameterization.h
new file mode 100644
index 0000000..ca7d072
--- /dev/null
+++ b/experimental/Intersection/QuadraticParameterization.h
@@ -0,0 +1,27 @@
+#include "DataTypes.h"
+
+class QuadImplicitForm {
+public:
+    QuadImplicitForm(const Quadratic& q);
+    bool implicit_match(const QuadImplicitForm& two) const;
+
+    double x2() const { return p[xx_coeff]; }
+    double xy() const { return p[xy_coeff]; }
+    double y2() const { return p[yy_coeff]; }
+    double x() const { return p[x_coeff]; }
+    double y() const { return p[y_coeff]; }
+    double c() const { return p[c_coeff]; }
+
+private:
+    enum Coeffs {
+        xx_coeff,
+        xy_coeff,
+        yy_coeff,
+        x_coeff,
+        y_coeff,
+        c_coeff,
+        coeff_count
+    };
+
+    double p[coeff_count];
+};
diff --git a/experimental/Intersection/QuadraticParameterization_Test.cpp b/experimental/Intersection/QuadraticParameterization_Test.cpp
new file mode 100644
index 0000000..df239dc
--- /dev/null
+++ b/experimental/Intersection/QuadraticParameterization_Test.cpp
@@ -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.
+ */
+#include "CurveIntersection.h"
+#include "Intersection_Tests.h"
+#include "Parameterization_Test.h"
+
+const Quadratic quadratics[] = {
+    {{0, 0}, {1, 0}, {1, 1}},
+};
+
+const size_t quadratics_count = sizeof(quadratics) / sizeof(quadratics[0]);
+
+int firstQuadraticCoincidenceTest = 0;
+
+void QuadraticCoincidence_Test() {
+    // split large quadratic
+    // compare original, parts, to see if the are coincident
+    for (size_t index = firstQuadraticCoincidenceTest; index < quadratics_count; ++index) {
+        const Quadratic& test = quadratics[index];
+        QuadraticPair split;
+        chop_at(test, split, 0.5);
+        Quadratic midThird;
+        sub_divide(test, 1.0/3, 2.0/3, midThird);
+        const Quadratic* quads[] = {
+            &test, &midThird, &split.first(), &split.second()
+        };
+        size_t quadsCount = sizeof(quads) / sizeof(quads[0]);
+        for (size_t one = 0; one < quadsCount; ++one) {
+            for (size_t two = 0; two < quadsCount; ++two) {
+                for (size_t inner = 0; inner < 3; inner += 2) {
+                    if (!point_on_parameterized_curve(*quads[one], (*quads[two])[inner])) {
+                            printf("%s %zu [%zu,%zu] %zu parameterization failed\n",
+                                __FUNCTION__, index, one, two, inner);
+                    }
+                }
+                if (!implicit_matches(*quads[one], *quads[two])) {
+                    printf("%s %zu [%zu,%zu] coincidence failed\n", __FUNCTION__,
+                            index, one, two);
+                }
+            }
+        }
+    }
+}
diff --git a/experimental/Intersection/QuadraticParameterization_TestUtility.cpp b/experimental/Intersection/QuadraticParameterization_TestUtility.cpp
new file mode 100644
index 0000000..7c91eb5
--- /dev/null
+++ b/experimental/Intersection/QuadraticParameterization_TestUtility.cpp
@@ -0,0 +1,16 @@
+// included by QuadraticParameterization.cpp
+// accesses internal functions to validate parameterized coefficients
+
+#include "Parameterization_Test.h"
+
+bool point_on_parameterized_curve(const Quadratic& quad, const _Point& point) {
+    QuadImplicitForm q(quad);
+    double  xx = q.x2() * point.x * point.x;
+    double  xy = q.xy() * point.x * point.y;
+    double  yy = q.y2() * point.y * point.y;
+    double   x = q.x() * point.x;
+    double   y = q.y() * point.y;
+    double   c = q.c();
+    double sum = xx + xy + yy + x + y + c;
+    return approximately_zero(sum);
+}
diff --git a/experimental/Intersection/QuadraticReduceOrder.cpp b/experimental/Intersection/QuadraticReduceOrder.cpp
new file mode 100644
index 0000000..3904817
--- /dev/null
+++ b/experimental/Intersection/QuadraticReduceOrder.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 "CurveIntersection.h"
+#include "Extrema.h"
+#include "IntersectionUtilities.h"
+#include "LineParameters.h"
+
+static double interp_quad_coords(double a, double b, double c, double t)
+{
+    double ab = interp(a, b, t);
+    double bc = interp(b, c, t);
+    return interp(ab, bc, t);
+}
+
+static int coincident_line(const Quadratic& quad, Quadratic& reduction) {
+    reduction[0] = reduction[1] = quad[0];
+    return 1;
+}
+
+static int vertical_line(const Quadratic& quad, Quadratic& reduction) {
+    double tValue;
+    reduction[0] = quad[0];
+    reduction[1] = quad[2];
+    int smaller = reduction[1].y > reduction[0].y;
+    int larger = smaller ^ 1;
+    if (findExtrema(quad[0].y, quad[1].y, quad[2].y, &tValue)) {
+        double yExtrema = interp_quad_coords(quad[0].y, quad[1].y, quad[2].y, tValue);
+        if (reduction[smaller].y > yExtrema) {
+            reduction[smaller].y = yExtrema;
+        } else if (reduction[larger].y < yExtrema) {
+            reduction[larger].y = yExtrema;
+        }
+    }
+    return 2;
+}
+
+static int horizontal_line(const Quadratic& quad, Quadratic& reduction) {
+    double tValue;
+    reduction[0] = quad[0];
+    reduction[1] = quad[2];
+    int smaller = reduction[1].x > reduction[0].x;
+    int larger = smaller ^ 1;
+    if (findExtrema(quad[0].x, quad[1].x, quad[2].x, &tValue)) {
+        double xExtrema = interp_quad_coords(quad[0].x, quad[1].x, quad[2].x, tValue);
+        if (reduction[smaller].x > xExtrema) {
+            reduction[smaller].x = xExtrema;
+        }  else if (reduction[larger].x < xExtrema) {
+            reduction[larger].x = xExtrema;
+        }
+    }
+    return 2;
+}
+
+static int check_linear(const Quadratic& quad, Quadratic& reduction,
+        int minX, int maxX, int minY, int maxY) {
+    int startIndex = 0;
+    int endIndex = 2;
+    while (quad[startIndex].approximatelyEqual(quad[endIndex])) {
+        --endIndex;
+        if (endIndex == 0) {
+            printf("%s shouldn't get here if all four points are about equal", __FUNCTION__);
+            assert(0);
+        }
+    }
+    if (!isLinear(quad, startIndex, endIndex)) {
+        return 0;
+    }
+    // four are colinear: return line formed by outside
+    reduction[0] = quad[0];
+    reduction[1] = quad[2];
+    int sameSide;
+    bool useX = quad[maxX].x - quad[minX].x >= quad[maxY].y - quad[minY].y;
+    if (useX) {
+        sameSide = sign(quad[0].x - quad[1].x) + sign(quad[2].x - quad[1].x);
+    } else {
+        sameSide = sign(quad[0].y - quad[1].y) + sign(quad[2].y - quad[1].y);
+    }
+    if ((sameSide & 3) != 2) {
+        return 2;
+    }
+    double tValue;
+    int root;
+    if (useX) {
+        root = findExtrema(quad[0].x, quad[1].x, quad[2].x, &tValue);
+    } else {
+        root = findExtrema(quad[0].y, quad[1].y, quad[2].y, &tValue);
+    }
+    if (root) {
+        _Point extrema;
+        extrema.x = interp_quad_coords(quad[0].x, quad[1].x, quad[2].x, tValue);
+        extrema.y = interp_quad_coords(quad[0].x, quad[1].x, quad[2].x, tValue);
+        // sameSide > 0 means mid is smaller than either [0] or [2], so replace smaller
+        int replace;
+        if (useX) {
+            if (extrema.x < quad[0].x ^ extrema.x < quad[2].x) {
+                return 2;
+            }
+            replace = (extrema.x < quad[0].x | extrema.x < quad[2].x)
+                    ^ quad[0].x < quad[2].x;
+        } else {
+            if (extrema.y < quad[0].y ^ extrema.y < quad[2].y) {
+                return 2;
+            }
+            replace = (extrema.y < quad[0].y | extrema.y < quad[2].y)
+                    ^ quad[0].y < quad[2].y;
+        }
+        reduction[replace] = extrema;
+    }
+    return 2;
+}
+
+bool isLinear(const Quadratic& quad, int startIndex, int endIndex) {
+    LineParameters lineParameters;
+    lineParameters.quadEndPoints(quad, startIndex, endIndex);
+    // FIXME: maybe it's possible to avoid this and compare non-normalized
+    lineParameters.normalize();
+    double distance = lineParameters.controlPtDistance(quad);
+    return approximately_zero(distance);
+}
+
+// reduce to a quadratic or smaller
+// look for identical points
+// look for all four points in a line
+    // note that three points in a line doesn't simplify a cubic
+// look for approximation with single quadratic
+    // save approximation with multiple quadratics for later
+int reduceOrder(const Quadratic& quad, Quadratic& reduction) {
+    int index, minX, maxX, minY, maxY;
+    int minXSet, minYSet;
+    minX = maxX = minY = maxY = 0;
+    minXSet = minYSet = 0;
+    for (index = 1; index < 3; ++index) {
+        if (quad[minX].x > quad[index].x) {
+            minX = index;
+        }
+        if (quad[minY].y > quad[index].y) {
+            minY = index;
+        }
+        if (quad[maxX].x < quad[index].x) {
+            maxX = index;
+        }
+        if (quad[maxY].y < quad[index].y) {
+            maxY = index;
+        }
+    }
+    for (index = 0; index < 3; ++index) {
+        if (approximately_equal(quad[index].x, quad[minX].x)) {
+            minXSet |= 1 << index;
+        }
+        if (approximately_equal(quad[index].y, quad[minY].y)) {
+            minYSet |= 1 << index;
+        }
+    }
+    if (minXSet == 0x7) { // test for vertical line
+        if (minYSet == 0x7) { // return 1 if all four are coincident
+            return coincident_line(quad, reduction);
+        }
+        return vertical_line(quad, reduction);
+    }
+    if (minYSet == 0xF) { // test for horizontal line
+        return horizontal_line(quad, reduction);
+    }
+    int result = check_linear(quad, reduction, minX, maxX, minY, maxY);
+    if (result) {
+        return result;
+    }
+    memcpy(reduction, quad, sizeof(Quadratic));
+    return 3;
+}
diff --git a/experimental/Intersection/QuadraticReduceOrder_Test.cpp b/experimental/Intersection/QuadraticReduceOrder_Test.cpp
new file mode 100644
index 0000000..3f49b95
--- /dev/null
+++ b/experimental/Intersection/QuadraticReduceOrder_Test.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 "CurveIntersection.h"
+#include "Intersection_Tests.h"
+#include "QuadraticIntersection_TestData.h"
+#include "TestUtilities.h"
+#include "SkTypes.h"
+
+static const Quadratic testSet[] = {
+    {{1, 1}, {2, 2}, {1, 1.000003}},
+    {{1, 0}, {2, 6}, {3, 0}}
+};
+
+static const size_t testSetCount = sizeof(testSet) / sizeof(testSet[0]);
+
+
+static void oneOffTest() {
+    SkDebugf("%s FLT_EPSILON=%1.9g\n", __FUNCTION__, FLT_EPSILON);
+    for (size_t index = 0; index < testSetCount; ++index) {
+        const Quadratic& quad = testSet[index];
+        Quadratic reduce;
+        int order = reduceOrder(quad, reduce);
+        SkASSERT(order == 3);
+    }
+}
+
+static void standardTestCases() {
+    size_t index;
+    Quadratic reduce;
+    int order;
+    enum {
+        RunAll,
+        RunQuadraticLines,
+        RunQuadraticModLines,
+        RunNone
+    } run = RunAll;
+    int firstTestIndex = 0;
+#if 0
+    run = RunQuadraticLines;
+    firstTestIndex = 1;
+#endif
+    int firstQuadraticLineTest = run == RunAll ? 0 : run == RunQuadraticLines ? firstTestIndex : INT_MAX;
+    int firstQuadraticModLineTest = run == RunAll ? 0 : run == RunQuadraticModLines ? firstTestIndex : INT_MAX;
+
+    for (index = firstQuadraticLineTest; index < quadraticLines_count; ++index) {
+        const Quadratic& quad = quadraticLines[index];
+        order = reduceOrder(quad, reduce);
+        if (order != 2) {
+            printf("[%d] line quad order=%d\n", (int) index, order);
+        }
+    }
+    for (index = firstQuadraticModLineTest; index < quadraticModEpsilonLines_count; ++index) {
+        const Quadratic& quad = quadraticModEpsilonLines[index];
+        order = reduceOrder(quad, reduce);
+        if (order != 3) {
+            printf("[%d] line mod quad order=%d\n", (int) index, order);
+        }
+    }
+}
+
+void QuadraticReduceOrder_Test() {
+    oneOffTest();
+    standardTestCases();
+}
diff --git a/experimental/Intersection/QuadraticSubDivide.cpp b/experimental/Intersection/QuadraticSubDivide.cpp
new file mode 100644
index 0000000..436b5a9
--- /dev/null
+++ b/experimental/Intersection/QuadraticSubDivide.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 "CurveIntersection.h"
+#include "IntersectionUtilities.h"
+
+/*
+Given a quadratic q, t1, and t2, find a small quadratic segment.
+
+The new quadratic is defined by A, B, and C, where
+ A = c[0]*(1 - t1)*(1 - t1) + 2*c[1]*t1*(1 - t1) + c[2]*t1*t1
+ C = c[3]*(1 - t1)*(1 - t1) + 2*c[2]*t1*(1 - t1) + c[1]*t1*t1
+
+To find B, compute the point halfway between t1 and t2:
+
+q(at (t1 + t2)/2) == D
+
+Next, compute where D must be if we know the value of B:
+
+_12 = A/2 + B/2
+12_ = B/2 + C/2
+123 = A/4 + B/2 + C/4
+    = D
+
+Group the known values on one side:
+
+B   = D*2 - A/2 - C/2
+*/
+
+static double interp_quad_coords(const double* src, double t)
+{
+    double ab = interp(src[0], src[2], t);
+    double bc = interp(src[2], src[4], t);
+    double abc = interp(ab, bc, t);
+    return abc;
+}
+
+void sub_divide(const Quadratic& src, double t1, double t2, Quadratic& dst) {
+    double ax = dst[0].x = interp_quad_coords(&src[0].x, t1);
+    double ay = dst[0].y = interp_quad_coords(&src[0].y, t1);
+    double dx = interp_quad_coords(&src[0].x, (t1 + t2) / 2);
+    double dy = interp_quad_coords(&src[0].y, (t1 + t2) / 2);
+    double cx = dst[2].x = interp_quad_coords(&src[0].x, t2);
+    double cy = dst[2].y = interp_quad_coords(&src[0].y, t2);
+    /* bx = */ dst[1].x = 2*dx - (ax + cx)/2;
+    /* by = */ dst[1].y = 2*dy - (ay + cy)/2;
+}
+
+/* classic one t subdivision */
+static void interp_quad_coords(const double* src, double* dst, double t)
+{
+    double ab = interp(src[0], src[2], t);
+    double bc = interp(src[2], src[4], t);
+
+    dst[0] = src[0];
+    dst[2] = ab;
+    dst[4] = interp(ab, bc, t);
+    dst[6] = bc;
+    dst[8] = src[4];
+}
+
+void chop_at(const Quadratic& src, QuadraticPair& dst, double t)
+{
+    interp_quad_coords(&src[0].x, &dst.pts[0].x, t);
+    interp_quad_coords(&src[0].y, &dst.pts[0].y, t);
+}
diff --git a/experimental/Intersection/QuadraticUtilities.cpp b/experimental/Intersection/QuadraticUtilities.cpp
new file mode 100644
index 0000000..95be90a
--- /dev/null
+++ b/experimental/Intersection/QuadraticUtilities.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 "QuadraticUtilities.h"
+#include <math.h>
+
+/*
+
+Numeric Solutions (5.6) suggests to solve the quadratic by computing
+
+       Q = -1/2(B + sgn(B)Sqrt(B^2 - 4 A C))
+
+and using the roots
+
+      t1 = Q / A
+      t2 = C / Q
+
+*/
+
+// note: caller expects multiple results to be sorted smaller first
+// note: http://en.wikipedia.org/wiki/Loss_of_significance has an interesting
+//  analysis of the quadratic equation, suggesting why the following looks at
+//  the sign of B -- and further suggesting that the greatest loss of precision
+//  is in b squared less two a c
+int quadraticRoots(double A, double B, double C, double t[2]) {
+    B *= 2;
+    double square = B * B - 4 * A * C;
+    if (approximately_negative(square)) {
+        if (!approximately_positive(square)) {
+            return 0;
+        }
+        square = 0;
+    }
+    double squareRt = sqrt(square);
+    double Q = (B + (B < 0 ? -squareRt : squareRt)) / -2;
+    int foundRoots = 0;
+    double ratio = Q / A;
+    if (approximately_zero_or_more(ratio) && approximately_one_or_less(ratio)) {
+        if (approximately_less_than_zero(ratio)) {
+            ratio = 0;
+        } else if (approximately_greater_than_one(ratio)) {
+            ratio = 1;
+        }
+        t[0] = ratio;
+        ++foundRoots;
+    }
+    ratio = C / Q;
+    if (approximately_zero_or_more(ratio) && approximately_one_or_less(ratio)) {
+        if (approximately_less_than_zero(ratio)) {
+            ratio = 0;
+        } else if (approximately_greater_than_one(ratio)) {
+            ratio = 1;
+        }
+        if (foundRoots == 0 || !approximately_negative(ratio - t[0])) {
+            t[foundRoots++] = ratio;
+        } else if (!approximately_negative(t[0] - ratio)) {
+            t[foundRoots++] = t[0];
+            t[0] = ratio;
+        }
+    }
+    return foundRoots;
+}
+
+void dxdy_at_t(const Quadratic& quad, double t, double& x, double& y) {
+    double a = t - 1;
+    double b = 1 - 2 * t;
+    double c = t;
+    if (&x) {
+        x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
+    }
+    if (&y) {
+        y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
+    }
+}
+
+void xy_at_t(const Quadratic& quad, double t, double& x, double& y) {
+    double one_t = 1 - t;
+    double a = one_t * one_t;
+    double b = 2 * one_t * t;
+    double c = t * t;
+    if (&x) {
+        x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
+    }
+    if (&y) {
+        y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
+    }
+}
diff --git a/experimental/Intersection/QuadraticUtilities.h b/experimental/Intersection/QuadraticUtilities.h
new file mode 100644
index 0000000..5bc15ea
--- /dev/null
+++ b/experimental/Intersection/QuadraticUtilities.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.
+ */
+#include "DataTypes.h"
+
+void dxdy_at_t(const Quadratic& , double t, double& x, double& y);
+
+/* Parameterization form, given A*t*t + 2*B*t*(1-t) + C*(1-t)*(1-t)
+ *
+ * a = A - 2*B +   C
+ * b =     2*B - 2*C
+ * c =             C
+ */
+inline void set_abc(const double* quad, double& a, double& b, double& c) {
+    a = quad[0];     // a = A
+    b = 2 * quad[2]; // b =     2*B
+    c = quad[4];     // c =             C
+    b -= c;          // b =     2*B -   C
+    a -= b;          // a = A - 2*B +   C
+    b -= c;          // b =     2*B - 2*C
+}
+
+int quadraticRoots(double A, double B, double C, double t[2]);
+
+void xy_at_t(const Quadratic& , double t, double& x, double& y);
diff --git a/experimental/Intersection/QuarticRoot.cpp b/experimental/Intersection/QuarticRoot.cpp
new file mode 100644
index 0000000..f16c332
--- /dev/null
+++ b/experimental/Intersection/QuarticRoot.cpp
@@ -0,0 +1,291 @@
+// from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
+/*
+ *  Roots3And4.c
+ *
+ *  Utility functions to find cubic and quartic roots,
+ *  coefficients are passed like this:
+ *
+ *      c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0
+ *
+ *  The functions return the number of non-complex roots and
+ *  put the values into the s array.
+ *
+ *  Author:         Jochen Schwarze (schwarze@isa.de)
+ *
+ *  Jan 26, 1990    Version for Graphics Gems
+ *  Oct 11, 1990    Fixed sign problem for negative q's in SolveQuartic
+ *                  (reported by Mark Podlipec),
+ *                  Old-style function definitions,
+ *                  IsZero() as a macro
+ *  Nov 23, 1990    Some systems do not declare acos() and cbrt() in
+ *                  <math.h>, though the functions exist in the library.
+ *                  If large coefficients are used, EQN_EPS should be
+ *                  reduced considerably (e.g. to 1E-30), results will be
+ *                  correct but multiple roots might be reported more
+ *                  than once.
+ */
+
+#include    <math.h>
+#include "CubicUtilities.h"
+#include "QuarticRoot.h"
+
+const double PI = 4 * atan(1);
+
+// unlike quadraticRoots in QuadraticUtilities.cpp, this does not discard
+// real roots <= 0 or >= 1
+static int quadraticRootsX(const double A, const double B, const double C,
+        double s[2]) {
+    if (approximately_zero(A)) {
+        if (approximately_zero(B)) {
+            s[0] = 0;
+            return C == 0;
+        }
+        s[0] = -C / B;
+        return 1;
+    }
+    /* normal form: x^2 + px + q = 0 */
+    const double p = B / (2 * A);
+    const double q = C / A;
+    const double D = p * p - q;
+    if (D < 0) {
+        return 0;
+    }
+    double sqrt_D = sqrt(D);
+    if (approximately_less_than_zero(sqrt_D)) {
+        s[0] = -p;
+        return 1;
+    }
+    s[0] = sqrt_D - p;
+    s[1] = -sqrt_D - p;
+    return 2;
+}
+
+#define USE_GEMS 0
+#if USE_GEMS
+// unlike cubicRoots in CubicUtilities.cpp, this does not discard
+// real roots <= 0 or >= 1
+static int cubicRootsX(const double A, const double B, const double C,
+        const double D, double s[3]) {
+    int num;
+    /* normal form: x^3 + Ax^2 + Bx + C = 0 */
+    const double invA = 1 / A;
+    const double a = B * invA;
+    const double b = C * invA;
+    const double c = D * invA;
+    /*  substitute x = y - a/3 to eliminate quadric term:
+    x^3 +px + q = 0 */
+    const double a2 = a * a;
+    const double Q = (-a2 + b * 3) / 9;
+    const double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
+    /* use Cardano's formula */
+    const double Q3 = Q * Q * Q;
+    const double R2plusQ3 = R * R + Q3;
+    if (approximately_zero(R2plusQ3)) {
+        if (approximately_zero(R)) {/* one triple solution */
+            s[0] = 0;
+            num = 1;
+        } else { /* one single and one double solution */
+
+            double u = cube_root(-R);
+            s[0] = 2 * u;
+            s[1] = -u;
+            num = 2;
+        }
+    }
+    else if (R2plusQ3 < 0) { /* Casus irreducibilis: three real solutions */
+        const double theta = acos(-R / sqrt(-Q3)) / 3;
+        const double _2RootQ = 2 * sqrt(-Q);
+        s[0] = _2RootQ * cos(theta);
+        s[1] = -_2RootQ * cos(theta + PI / 3);
+        s[2] = -_2RootQ * cos(theta - PI / 3);
+        num = 3;
+    } else { /* one real solution */
+        const double sqrt_D = sqrt(R2plusQ3);
+        const double u = cube_root(sqrt_D - R);
+        const double v = -cube_root(sqrt_D + R);
+        s[0] = u + v;
+        num = 1;
+    }
+    /* resubstitute */
+    const double sub = a / 3;
+    for (int i = 0; i < num; ++i) {
+        s[i] -= sub;
+    }
+    return num;
+}
+#else
+
+static int cubicRootsX(double A, double B, double C, double D, double s[3]) {
+    if (approximately_zero(A)) {  // we're just a quadratic
+        return quadraticRootsX(B, C, D, s);
+    }
+    if (approximately_zero(D)) { // 0 is one root
+        int num = quadraticRootsX(A, B, C, s);
+        for (int i = 0; i < num; ++i) {
+            if (approximately_zero(s[i])) {
+                return num;
+            }
+        }
+        s[num++] = 0;
+        return num;
+    }
+    if (approximately_zero(A + B + C + D)) { // 1 is one root
+        int num = quadraticRootsX(A, A + B, -D, s);
+        for (int i = 0; i < num; ++i) {
+            if (approximately_equal(s[i], 1)) {
+                return num;
+            }
+        }
+        s[num++] = 1;
+        return num;
+    }
+    double a, b, c;
+    {
+        double invA = 1 / A;
+        a = B * invA;
+        b = C * invA;
+        c = D * invA;
+    }
+    double a2 = a * a;
+    double Q = (a2 - b * 3) / 9;
+    double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
+    double Q3 = Q * Q * Q;
+    double R2MinusQ3 = R * R - Q3;
+    double adiv3 = a / 3;
+    double r;
+    double* roots = s;
+
+    if (R2MinusQ3 > -FLT_EPSILON / 10 && R2MinusQ3 < FLT_EPSILON / 10 ) {
+        if (approximately_zero(R)) {/* one triple solution */
+            *roots++ = -adiv3;
+        } else { /* one single and one double solution */
+
+            double u = cube_root(-R);
+            *roots++ = 2 * u - adiv3;
+            *roots++ = -u - adiv3;
+        }
+    }
+    else if (R2MinusQ3 < 0)   // we have 3 real roots
+    {
+        double theta = acos(R / sqrt(Q3));
+        double neg2RootQ = -2 * sqrt(Q);
+
+        r = neg2RootQ * cos(theta / 3) - adiv3;
+        *roots++ = r;
+
+        r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
+        *roots++ = r;
+
+        r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
+        *roots++ = r;
+    }
+    else                // we have 1 real root
+    {
+        double A = fabs(R) + sqrt(R2MinusQ3);
+        A = cube_root(A);
+        if (R > 0) {
+            A = -A;
+        }
+        if (A != 0) {
+            A += Q / A;
+        }
+        r = A - adiv3;
+        *roots++ = r;
+    }
+    return (int)(roots - s);
+}
+#endif
+
+int quarticRoots(const double A, const double B, const double C, const double D,
+        const double E, double s[4]) {
+    if (approximately_zero(A)) {
+        if (approximately_zero(B)) {
+            return quadraticRootsX(C, D, E, s);
+        }
+        return cubicRootsX(B, C, D, E, s);
+    }
+    int num;
+    int i;
+    if (approximately_zero(E)) { // 0 is one root
+        num = cubicRootsX(A, B, C, D, s);
+        for (i = 0; i < num; ++i) {
+            if (approximately_zero(s[i])) {
+                return num;
+            }
+        }
+        s[num++] = 0;
+        return num;
+    }
+    if (approximately_zero(A + B + C + D + E)) { // 1 is one root
+        num = cubicRootsX(A, A + B, -(D + E), -E, s); // note that -C==A+B+D+E
+        for (i = 0; i < num; ++i) {
+            if (approximately_equal(s[i], 1)) {
+                return num;
+            }
+        }
+        s[num++] = 1;
+        return num;
+    }
+    double  u, v;
+    /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */
+    const double invA = 1 / A;
+    const double a = B * invA;
+    const double b = C * invA;
+    const double c = D * invA;
+    const double d = E * invA;
+    /*  substitute x = y - a/4 to eliminate cubic term:
+    x^4 + px^2 + qx + r = 0 */
+    const double a2 = a * a;
+    const double p = -3 * a2 / 8 + b;
+    const double q = a2 * a / 8 - a * b / 2 + c;
+    const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d;
+    if (approximately_zero(r)) {
+    /* no absolute term: y(y^3 + py + q) = 0 */
+        num = cubicRootsX(1, 0, p, q, s);
+        s[num++] = 0;
+    } else {
+        /* solve the resolvent cubic ... */
+        (void) cubicRootsX(1, -p / 2, -r, r * p / 2 - q * q / 8, s);
+        /* ... and take the one real solution ... */
+        const double z = s[0];
+        /* ... to build two quadric equations */
+        u = z * z - r;
+        v = 2 * z - p;
+        if (approximately_zero(u)) {
+            u = 0;
+        } else if (u > 0) {
+            u = sqrt(u);
+        } else {
+            return 0;
+        }
+        if (approximately_zero(v)) {
+            v = 0;
+        } else if (v > 0) {
+            v = sqrt(v);
+        } else {
+            return 0;
+        }
+        num = quadraticRootsX(1, q < 0 ? -v : v, z - u, s);
+        num += quadraticRootsX(1, q < 0 ? v : -v, z + u, s + num);
+    }
+    // eliminate duplicates
+    for (i = 0; i < num - 1; ++i) {
+        for (int j = i + 1; j < num; ) {
+            if (approximately_equal(s[i], s[j])) {
+                if (j < --num) {
+                    s[j] = s[num];
+                }
+            } else {
+                ++j;
+            }
+        }
+    }
+    /* resubstitute */
+    const double sub = a / 4;
+    for (i = 0; i < num; ++i) {
+        s[i] -= sub;
+    }
+    return num;
+}
+
+
diff --git a/experimental/Intersection/QuarticRoot.h b/experimental/Intersection/QuarticRoot.h
new file mode 100644
index 0000000..8baa842
--- /dev/null
+++ b/experimental/Intersection/QuarticRoot.h
@@ -0,0 +1,2 @@
+int quarticRoots(const double A, const double B, const double C, const double D,
+        const double E, double s[4]);
diff --git a/experimental/Intersection/QuarticRoot_Test.cpp b/experimental/Intersection/QuarticRoot_Test.cpp
new file mode 100644
index 0000000..2fe3e72
--- /dev/null
+++ b/experimental/Intersection/QuarticRoot_Test.cpp
@@ -0,0 +1,148 @@
+#include <assert.h>
+#include <math.h>
+#include "CubicUtilities.h"
+#include "Intersection_Tests.h"
+
+namespace QuarticRootTest {
+
+#include "QuarticRoot.cpp"
+
+}
+
+double mulA[] = {-3, -1, 1, 3};
+size_t mulACount = sizeof(mulA) / sizeof(mulA[0]);
+double rootB[] = {-9, -6, -3, -1, 0, 1, 3, 6, 9};
+size_t rootBCount = sizeof(rootB) / sizeof(rootB[0]);
+double rootC[] = {-8, -6, -2, -1, 0, 1, 2, 6, 8};
+size_t rootCCount = sizeof(rootC) / sizeof(rootC[0]);
+double rootD[] = {-7, -4, -1, 0, 1, 2, 5};
+size_t rootDCount = sizeof(rootD) / sizeof(rootD[0]);
+double rootE[] = {-5, -1, 0, 1, 7};
+size_t rootECount = sizeof(rootE) / sizeof(rootE[0]);
+
+static void quadraticTest() {
+    // (x - a)(x - b) == x^2 - (a + b)x + ab
+    for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) {
+        for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) {
+            for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) {
+                const double A = mulA[aIndex];
+                const double B = rootB[bIndex];
+                const double C = rootC[cIndex];
+                const double b = A * (B + C);
+                const double c = A * B * C;
+                double roots[2];
+                const int rootCount = QuarticRootTest::quadraticRootsX(A, b, c, roots);
+                const int expected = 1 + (B != C);
+                assert(rootCount == expected);
+                assert(approximately_equal(roots[0], -B)
+                        || approximately_equal(roots[0], -C));
+                if (B != C) {
+                    assert(!approximately_equal(roots[0], roots[1]));
+                    assert(approximately_equal(roots[1], -B)
+                            || approximately_equal(roots[1], -C));
+                }
+            }
+        }
+    }
+}
+
+static void cubicTest() {
+    // (x - a)(x - b)(x - c) == x^3 - (a + b + c)x^2 + (ab + bc + ac)x - abc
+    for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) {
+        for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) {
+            for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) {
+                for (size_t dIndex = 0; dIndex < rootDCount; ++dIndex) {
+                    const double A = mulA[aIndex];
+                    const double B = rootB[bIndex];
+                    const double C = rootC[cIndex];
+                    const double D = rootD[dIndex];
+                    const double b = A * (B + C + D);
+                    const double c = A * (B * C + C * D + B * D);
+                    const double d = A * B * C * D;
+                    double roots[3];
+                    const int rootCount = QuarticRootTest::cubicRootsX(A, b, c, d, roots);
+                    const int expected = 1 + (B != C) + (B != D && C != D);
+                    assert(rootCount == expected);
+                    assert(approximately_equal(roots[0], -B)
+                            || approximately_equal(roots[0], -C)
+                            || approximately_equal(roots[0], -D));
+                    if (expected > 1) {
+                        assert(!approximately_equal(roots[0], roots[1]));
+                        assert(approximately_equal(roots[1], -B)
+                                || approximately_equal(roots[1], -C)
+                                || approximately_equal(roots[1], -D));
+                        if (expected > 2) {
+                            assert(!approximately_equal(roots[0], roots[2])
+                                    && !approximately_equal(roots[1], roots[2]));
+                            assert(approximately_equal(roots[2], -B)
+                                    || approximately_equal(roots[2], -C)
+                                    || approximately_equal(roots[2], -D));
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void quarticTest() {
+    // (x - a)(x - b)(x - c)(x - d) == x^4 - (a + b + c + d)x^3
+    //   + (ab + bc + cd + ac + bd + cd)x^2 - (abc + bcd + abd + acd) * x + abcd
+    for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) {
+        for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) {
+            for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) {
+                for (size_t dIndex = 0; dIndex < rootDCount; ++dIndex) {
+                    for (size_t eIndex = 0; eIndex < rootECount; ++eIndex) {
+                        const double A = mulA[aIndex];
+                        const double B = rootB[bIndex];
+                        const double C = rootC[cIndex];
+                        const double D = rootD[dIndex];
+                        const double E = rootE[eIndex];
+                        const double b = A * (B + C + D + E);
+                        const double c = A * (B * C + C * D + B * D + B * E + C * E + D * E);
+                        const double d = A * (B * C * D + B * C * E + B * D * E + C * D * E);
+                        const double e = A * B * C * D * E;
+                        double roots[4];
+                        const int rootCount = QuarticRootTest::quarticRoots(A, b, c, d, e, roots);
+                        const int expected = 1 + (B != C) + (B != D && C != D) + (B != E && C != E && D != E);
+                        assert(rootCount == expected);
+                        assert(approximately_equal(roots[0], -B)
+                                || approximately_equal(roots[0], -C)
+                                || approximately_equal(roots[0], -D)
+                                || approximately_equal(roots[0], -E));
+                        if (expected > 1) {
+                            assert(!approximately_equal(roots[0], roots[1]));
+                            assert(approximately_equal(roots[1], -B)
+                                    || approximately_equal(roots[1], -C)
+                                    || approximately_equal(roots[1], -D)
+                                    || approximately_equal(roots[1], -E));
+                            if (expected > 2) {
+                                assert(!approximately_equal(roots[0], roots[2])
+                                        && !approximately_equal(roots[1], roots[2]));
+                                assert(approximately_equal(roots[2], -B)
+                                        || approximately_equal(roots[2], -C)
+                                        || approximately_equal(roots[2], -D)
+                                        || approximately_equal(roots[2], -E));
+                                if (expected > 3) {
+                                    assert(!approximately_equal(roots[0], roots[3])
+                                            && !approximately_equal(roots[1], roots[3])
+                                            && !approximately_equal(roots[2], roots[3]));
+                                    assert(approximately_equal(roots[3], -B)
+                                            || approximately_equal(roots[3], -C)
+                                            || approximately_equal(roots[3], -D)
+                                            || approximately_equal(roots[3], -E));
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+void QuarticRoot_Test() {
+    quadraticTest();
+    cubicTest();
+    quarticTest();
+}
diff --git a/experimental/Intersection/ShapeOps.cpp b/experimental/Intersection/ShapeOps.cpp
new file mode 100644
index 0000000..f33c8b1
--- /dev/null
+++ b/experimental/Intersection/ShapeOps.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 "Simplify.h"
+
+namespace Op {
+
+#include "Simplify.cpp"
+
+static bool windingIsActive(int winding, int spanWinding, int oppWinding,
+        const ShapeOp op) {
+    return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
+            && (!winding || !spanWinding || winding == -spanWinding);
+}
+
+static void bridgeOp(SkTDArray<Contour*>& contourList, const ShapeOp op,
+        const int aXorMask, const int bXorMask, SkPath& simple) {
+    bool firstContour = true;
+    do {
+        Segment* topStart = findTopContour(contourList);
+        if (!topStart) {
+            break;
+        }
+        // Start at the top. Above the top is outside, below is inside.
+        // follow edges to intersection by changing the index by direction.
+        int index, endIndex;
+        Segment* current = topStart->findTop(index, endIndex);
+        int contourWinding;
+        if (firstContour) {
+            contourWinding = 0;
+            firstContour = false;
+        } else {
+            int sumWinding = current->windSum(SkMin32(index, endIndex));
+            // FIXME: don't I have to adjust windSum to get contourWinding?
+            if (sumWinding == SK_MinS32) {
+                sumWinding = current->computeSum(index, endIndex);
+            }
+            if (sumWinding == SK_MinS32) {
+                contourWinding = innerContourCheck(contourList, current,
+                        index, endIndex);
+            } else {
+                contourWinding = sumWinding;
+                int spanWinding = current->spanSign(index, endIndex);
+                bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
+                if (inner) {
+                    contourWinding -= spanWinding;
+                }
+#if DEBUG_WINDING
+                SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
+                        sumWinding, spanWinding, SkSign32(index - endIndex),
+                        inner, contourWinding);
+#endif
+            }
+#if DEBUG_WINDING
+         //   SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
+            SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
+#endif
+        }
+        SkPoint lastPt;
+        int winding = contourWinding;
+        int spanWinding = current->spanSign(index, endIndex);
+        int oppWinding = current->oppSign(index, endIndex);
+        bool active = windingIsActive(winding, spanWinding, oppWinding, op);
+        SkTDArray<Span*> chaseArray;
+        bool unsortable = false;
+        do {
+        #if DEBUG_WINDING
+            SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
+                    __FUNCTION__, active ? "true" : "false",
+                    winding, spanWinding);
+        #endif
+            const SkPoint* firstPt = NULL;
+            do {
+                SkASSERT(!current->done());
+                int nextStart = index;
+                int nextEnd = endIndex;
+                Segment* next = current->findNextOp(chaseArray, active,
+                        nextStart, nextEnd, winding, spanWinding, unsortable, op,
+                        aXorMask, bXorMask);
+                if (!next) {
+                    // FIXME: if unsortable, allow partial paths to be later
+                    // assembled
+                    SkASSERT(!unsortable);
+                    if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
+                        lastPt = current->addCurveTo(index, endIndex, simple, true);
+                        SkASSERT(*firstPt == lastPt);
+                    }
+                    break;
+                }
+                if (!firstPt) {
+                    firstPt = &current->addMoveTo(index, simple, active);
+                }
+                lastPt = current->addCurveTo(index, endIndex, simple, active);
+                current = next;
+                index = nextStart;
+                endIndex = nextEnd;
+            } while (*firstPt != lastPt && (active || !current->done()));
+            if (firstPt && active) {
+        #if DEBUG_PATH_CONSTRUCTION
+                SkDebugf("%s close\n", __FUNCTION__);
+        #endif
+                simple.close();
+            }
+            current = findChase(chaseArray, index, endIndex, contourWinding);
+        #if DEBUG_ACTIVE_SPANS
+            debugShowActiveSpans(contourList);
+        #endif
+            if (!current) {
+                break;
+            }
+            int lesser = SkMin32(index, endIndex);
+            spanWinding = current->spanSign(index, endIndex);
+            winding = current->windSum(lesser);
+            bool inner = useInnerWinding(winding - spanWinding, winding);
+        #if DEBUG_WINDING
+            SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
+                    " inner=%d result=%d\n",
+                    __FUNCTION__, current->debugID(), current->t(lesser),
+                    spanWinding, winding, SkSign32(index - endIndex),
+                    useInnerWinding(winding - spanWinding, winding),
+                    inner ? winding - spanWinding : winding);
+        #endif
+            if (inner) {
+                winding -= spanWinding;
+            }
+            int oppWinding = current->oppSign(index, endIndex);
+            active = windingIsActive(winding, spanWinding, oppWinding, op);
+        } while (true);
+    } while (true);
+}
+
+} // end of Op namespace
+
+
+void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result) {
+    result.reset();
+    result.setFillType(SkPath::kEvenOdd_FillType);
+    // turn path into list of segments
+    SkTArray<Op::Contour> contours;
+    // FIXME: add self-intersecting cubics' T values to segment
+    Op::EdgeBuilder builder(one, contours);
+    const int aXorMask = builder.xorMask();
+    builder.addOperand(two);
+    const int bXorMask = builder.xorMask();
+    builder.finish();
+    SkTDArray<Op::Contour*> contourList;
+    makeContourList(contours, contourList);
+    Op::Contour** currentPtr = contourList.begin();
+    if (!currentPtr) {
+        return;
+    }
+    Op::Contour** listEnd = contourList.end();
+    // find all intersections between segments
+    do {
+        Op::Contour** nextPtr = currentPtr;
+        Op::Contour* current = *currentPtr++;
+        Op::Contour* next;
+        do {
+            next = *nextPtr++;
+        } while (addIntersectTs(current, next) && nextPtr != listEnd);
+    } while (currentPtr != listEnd);
+    // eat through coincident edges
+    coincidenceCheck(contourList);
+    fixOtherTIndex(contourList);
+    // construct closed contours
+    bridgeOp(contourList, op, aXorMask, bXorMask, result);
+}
diff --git a/experimental/Intersection/ShapeOps.h b/experimental/Intersection/ShapeOps.h
new file mode 100644
index 0000000..8ea3691
--- /dev/null
+++ b/experimental/Intersection/ShapeOps.h
@@ -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 "SkPath.h"
+
+// region-inspired approach
+void contourBounds(const SkPath& path, SkTDArray<SkRect>& boundsArray);
+void simplify(const SkPath& path, bool asFill, SkPath& simple);
+
+// contour outer edge walking approach
+#ifndef DEFINE_SHAPE_OP
+// FIXME: namespace testing doesn't allow global enums like this
+#define DEFINE_SHAPE_OP
+enum ShapeOp {
+    kDifference_Op,
+    kIntersect_Op,
+    kUnion_Op,
+    kXor_Op
+};
+
+enum ShapeOpMask {
+    kWinding_Mask = -1,
+    kNo_Mask = 0,
+    kEvenOdd_Mask = 1
+};
+#endif
+
+void operate(const SkPath& one, const SkPath& two, ShapeOp op, SkPath& result);
+void simplifyx(const SkPath& path, SkPath& simple);
+
+// FIXME: remove this section once debugging is complete
+extern const bool gRunTestsInOneThread;
+#ifdef SK_DEBUG
+extern int gDebugMaxWindSum;
+extern int gDebugMaxWindValue;
+#endif
diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp
new file mode 100644
index 0000000..dbb8988
--- /dev/null
+++ b/experimental/Intersection/Simplify.cpp
@@ -0,0 +1,4625 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Simplify.h"
+
+#undef SkASSERT
+#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
+
+// Terminology:
+// A Path contains one of more Contours
+// A Contour is made up of Segment array
+// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
+// A Verb is one of Line, Quad(ratic), or Cubic
+// A Segment contains a Span array
+// A Span is describes a portion of a Segment using starting and ending T
+// T values range from 0 to 1, where 0 is the first Point in the Segment
+// An Edge is a Segment generated from a Span
+
+// FIXME: remove once debugging is complete
+#ifdef SK_DEBUG
+int gDebugMaxWindSum = SK_MaxS32;
+int gDebugMaxWindValue = SK_MaxS32;
+#endif
+
+#define PRECISE_T_SORT 1
+
+#define DEBUG_UNUSED 0 // set to expose unused functions
+
+#if 1 // set to 1 for multiple thread -- no debugging
+
+const bool gRunTestsInOneThread = false;
+
+#define DEBUG_ACTIVE_SPANS 0
+#define DEBUG_ADD_INTERSECTING_TS 0
+#define DEBUG_ADD_T_PAIR 0
+#define DEBUG_ANGLE 0
+#define DEBUG_CONCIDENT 0
+#define DEBUG_CROSS 0
+#define DEBUG_MARK_DONE 0
+#define DEBUG_PATH_CONSTRUCTION 0
+#define DEBUG_SORT 0
+#define DEBUG_WIND_BUMP 0
+#define DEBUG_WINDING 0
+
+#else
+
+const bool gRunTestsInOneThread = true;
+
+#define DEBUG_ACTIVE_SPANS 1
+#define DEBUG_ADD_INTERSECTING_TS 1
+#define DEBUG_ADD_T_PAIR 1
+#define DEBUG_ANGLE 1
+#define DEBUG_CONCIDENT 1
+#define DEBUG_CROSS 0
+#define DEBUG_MARK_DONE 1
+#define DEBUG_PATH_CONSTRUCTION 1
+#define DEBUG_SORT 1
+#define DEBUG_WIND_BUMP 0
+#define DEBUG_WINDING 1
+
+#endif
+
+#define DEBUG_DUMP (DEBUG_ACTIVE_SPANS | DEBUG_CONCIDENT | DEBUG_SORT | DEBUG_PATH_CONSTRUCTION)
+
+#if DEBUG_DUMP
+static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
+// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
+static int gContourID;
+static int gSegmentID;
+#endif
+
+#ifndef DEBUG_TEST
+#define DEBUG_TEST 0
+#endif
+
+#define MAKE_CONST_LINE(line, pts) \
+    const _Line line = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}}
+#define MAKE_CONST_QUAD(quad, pts) \
+    const Quadratic quad = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
+            {pts[2].fX, pts[2].fY}}
+#define MAKE_CONST_CUBIC(cubic, pts) \
+    const Cubic cubic = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}, \
+            {pts[2].fX, pts[2].fY}, {pts[3].fX, pts[3].fY}}
+
+static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
+        Intersections& intersections) {
+    MAKE_CONST_LINE(aLine, a);
+    MAKE_CONST_LINE(bLine, b);
+    return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
+}
+
+static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
+        Intersections& intersections) {
+    MAKE_CONST_QUAD(aQuad, a);
+    MAKE_CONST_LINE(bLine, b);
+    return intersect(aQuad, bLine, intersections);
+}
+
+static int CubicLineIntersect(const SkPoint a[4], const SkPoint b[2],
+        Intersections& intersections) {
+    MAKE_CONST_CUBIC(aCubic, a);
+    MAKE_CONST_LINE(bLine, b);
+    return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
+}
+
+static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
+        Intersections& intersections) {
+    MAKE_CONST_QUAD(aQuad, a);
+    MAKE_CONST_QUAD(bQuad, b);
+#define TRY_QUARTIC_SOLUTION 1
+#if TRY_QUARTIC_SOLUTION
+    intersect2(aQuad, bQuad, intersections);
+#else
+    intersect(aQuad, bQuad, intersections);
+#endif
+    return intersections.fUsed ? intersections.fUsed : intersections.fCoincidentUsed;
+}
+
+static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
+        Intersections& intersections) {
+    MAKE_CONST_CUBIC(aCubic, a);
+    MAKE_CONST_CUBIC(bCubic, b);
+    intersect(aCubic, bCubic, intersections);
+    return intersections.fUsed;
+}
+
+static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
+        SkScalar y, bool flipped, Intersections& intersections) {
+    MAKE_CONST_LINE(aLine, a);
+    return horizontalIntersect(aLine, left, right, y, flipped, intersections);
+}
+
+static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
+        SkScalar y, bool flipped, Intersections& intersections) {
+    MAKE_CONST_QUAD(aQuad, a);
+    return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
+}
+
+static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
+        SkScalar y, bool flipped, Intersections& intersections) {
+    MAKE_CONST_CUBIC(aCubic, a);
+    return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
+}
+
+static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
+        SkScalar x, bool flipped, Intersections& intersections) {
+    MAKE_CONST_LINE(aLine, a);
+    return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
+}
+
+static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
+        SkScalar x, bool flipped, Intersections& intersections) {
+    MAKE_CONST_QUAD(aQuad, a);
+    return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
+}
+
+static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
+        SkScalar x, bool flipped, Intersections& intersections) {
+    MAKE_CONST_CUBIC(aCubic, a);
+    return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
+}
+
+static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
+        SkScalar , SkScalar , bool , Intersections& ) = {
+    NULL,
+    VLineIntersect,
+    VQuadIntersect,
+    VCubicIntersect
+};
+
+static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
+    MAKE_CONST_LINE(line, a);
+    double x, y;
+    xy_at_t(line, t, x, y);
+    out->fX = SkDoubleToScalar(x);
+    out->fY = SkDoubleToScalar(y);
+}
+
+static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
+    MAKE_CONST_QUAD(quad, a);
+    double x, y;
+    xy_at_t(quad, t, x, y);
+    out->fX = SkDoubleToScalar(x);
+    out->fY = SkDoubleToScalar(y);
+}
+
+static void QuadXYAtT(const SkPoint a[3], double t, _Point* out) {
+    MAKE_CONST_QUAD(quad, a);
+    xy_at_t(quad, t, out->x, out->y);
+}
+
+static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
+    MAKE_CONST_CUBIC(cubic, a);
+    double x, y;
+    xy_at_t(cubic, t, x, y);
+    out->fX = SkDoubleToScalar(x);
+    out->fY = SkDoubleToScalar(y);
+}
+
+static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
+    NULL,
+    LineXYAtT,
+    QuadXYAtT,
+    CubicXYAtT
+};
+
+static SkScalar LineXAtT(const SkPoint a[2], double t) {
+    MAKE_CONST_LINE(aLine, a);
+    double x;
+    xy_at_t(aLine, t, x, *(double*) 0);
+    return SkDoubleToScalar(x);
+}
+
+static SkScalar QuadXAtT(const SkPoint a[3], double t) {
+    MAKE_CONST_QUAD(quad, a);
+    double x;
+    xy_at_t(quad, t, x, *(double*) 0);
+    return SkDoubleToScalar(x);
+}
+
+static SkScalar CubicXAtT(const SkPoint a[4], double t) {
+    MAKE_CONST_CUBIC(cubic, a);
+    double x;
+    xy_at_t(cubic, t, x, *(double*) 0);
+    return SkDoubleToScalar(x);
+}
+
+static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
+    NULL,
+    LineXAtT,
+    QuadXAtT,
+    CubicXAtT
+};
+
+static SkScalar LineYAtT(const SkPoint a[2], double t) {
+    MAKE_CONST_LINE(aLine, a);
+    double y;
+    xy_at_t(aLine, t, *(double*) 0, y);
+    return SkDoubleToScalar(y);
+}
+
+static SkScalar QuadYAtT(const SkPoint a[3], double t) {
+    MAKE_CONST_QUAD(quad, a);
+    double y;
+    xy_at_t(quad, t, *(double*) 0, y);
+    return SkDoubleToScalar(y);
+}
+
+static SkScalar CubicYAtT(const SkPoint a[4], double t) {
+    MAKE_CONST_CUBIC(cubic, a);
+    double y;
+    xy_at_t(cubic, t, *(double*) 0, y);
+    return SkDoubleToScalar(y);
+}
+
+static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
+    NULL,
+    LineYAtT,
+    QuadYAtT,
+    CubicYAtT
+};
+
+static SkScalar LineDXAtT(const SkPoint a[2], double ) {
+    return a[1].fX - a[0].fX;
+}
+
+static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
+    MAKE_CONST_QUAD(quad, a);
+    double x;
+    dxdy_at_t(quad, t, x, *(double*) 0);
+    return SkDoubleToScalar(x);
+}
+
+static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
+    MAKE_CONST_CUBIC(cubic, a);
+    double x;
+    dxdy_at_t(cubic, t, x, *(double*) 0);
+    return SkDoubleToScalar(x);
+}
+
+static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
+    NULL,
+    LineDXAtT,
+    QuadDXAtT,
+    CubicDXAtT
+};
+
+static void LineSubDivide(const SkPoint a[2], double startT, double endT,
+        SkPoint sub[2]) {
+    MAKE_CONST_LINE(aLine, a);
+    _Line dst;
+    sub_divide(aLine, startT, endT, dst);
+    sub[0].fX = SkDoubleToScalar(dst[0].x);
+    sub[0].fY = SkDoubleToScalar(dst[0].y);
+    sub[1].fX = SkDoubleToScalar(dst[1].x);
+    sub[1].fY = SkDoubleToScalar(dst[1].y);
+}
+
+static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
+        SkPoint sub[3]) {
+    MAKE_CONST_QUAD(aQuad, a);
+    Quadratic dst;
+    sub_divide(aQuad, startT, endT, dst);
+    sub[0].fX = SkDoubleToScalar(dst[0].x);
+    sub[0].fY = SkDoubleToScalar(dst[0].y);
+    sub[1].fX = SkDoubleToScalar(dst[1].x);
+    sub[1].fY = SkDoubleToScalar(dst[1].y);
+    sub[2].fX = SkDoubleToScalar(dst[2].x);
+    sub[2].fY = SkDoubleToScalar(dst[2].y);
+}
+
+static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
+        SkPoint sub[4]) {
+    MAKE_CONST_CUBIC(aCubic, a);
+    Cubic dst;
+    sub_divide(aCubic, startT, endT, dst);
+    sub[0].fX = SkDoubleToScalar(dst[0].x);
+    sub[0].fY = SkDoubleToScalar(dst[0].y);
+    sub[1].fX = SkDoubleToScalar(dst[1].x);
+    sub[1].fY = SkDoubleToScalar(dst[1].y);
+    sub[2].fX = SkDoubleToScalar(dst[2].x);
+    sub[2].fY = SkDoubleToScalar(dst[2].y);
+    sub[3].fX = SkDoubleToScalar(dst[3].x);
+    sub[3].fY = SkDoubleToScalar(dst[3].y);
+}
+
+static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
+        SkPoint []) = {
+    NULL,
+    LineSubDivide,
+    QuadSubDivide,
+    CubicSubDivide
+};
+
+static void LineSubDivideHD(const SkPoint a[2], double startT, double endT,
+        _Line sub) {
+    MAKE_CONST_LINE(aLine, a);
+    _Line dst;
+    sub_divide(aLine, startT, endT, dst);
+    sub[0] = dst[0];
+    sub[1] = dst[1];
+}
+
+static void QuadSubDivideHD(const SkPoint a[3], double startT, double endT,
+        Quadratic sub) {
+    MAKE_CONST_QUAD(aQuad, a);
+    Quadratic dst;
+    sub_divide(aQuad, startT, endT, dst);
+    sub[0] = dst[0];
+    sub[1] = dst[1];
+    sub[2] = dst[2];
+}
+
+static void CubicSubDivideHD(const SkPoint a[4], double startT, double endT,
+        Cubic sub) {
+    MAKE_CONST_CUBIC(aCubic, a);
+    Cubic dst;
+    sub_divide(aCubic, startT, endT, dst);
+    sub[0] = dst[0];
+    sub[1] = dst[1];
+    sub[2] = dst[2];
+    sub[3] = dst[3];
+}
+
+#if DEBUG_UNUSED
+static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
+        SkRect& bounds) {
+    SkPoint dst[3];
+    QuadSubDivide(a, startT, endT, dst);
+    bounds.fLeft = bounds.fRight = dst[0].fX;
+    bounds.fTop = bounds.fBottom = dst[0].fY;
+    for (int index = 1; index < 3; ++index) {
+        bounds.growToInclude(dst[index].fX, dst[index].fY);
+    }
+}
+
+static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
+        SkRect& bounds) {
+    SkPoint dst[4];
+    CubicSubDivide(a, startT, endT, dst);
+    bounds.fLeft = bounds.fRight = dst[0].fX;
+    bounds.fTop = bounds.fBottom = dst[0].fY;
+    for (int index = 1; index < 4; ++index) {
+        bounds.growToInclude(dst[index].fX, dst[index].fY);
+    }
+}
+#endif
+
+static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
+        SkTDArray<SkPoint>& reducePts) {
+    MAKE_CONST_QUAD(aQuad, a);
+    Quadratic dst;
+    int order = reduceOrder(aQuad, dst);
+    if (order == 2) { // quad became line
+        for (int index = 0; index < order; ++index) {
+            SkPoint* pt = reducePts.append();
+            pt->fX = SkDoubleToScalar(dst[index].x);
+            pt->fY = SkDoubleToScalar(dst[index].y);
+        }
+    }
+    return (SkPath::Verb) (order - 1);
+}
+
+static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
+        SkTDArray<SkPoint>& reducePts) {
+    MAKE_CONST_CUBIC(aCubic, a);
+    Cubic dst;
+    int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
+    if (order == 2 || order == 3) { // cubic became line or quad
+        for (int index = 0; index < order; ++index) {
+            SkPoint* pt = reducePts.append();
+            pt->fX = SkDoubleToScalar(dst[index].x);
+            pt->fY = SkDoubleToScalar(dst[index].y);
+        }
+    }
+    return (SkPath::Verb) (order - 1);
+}
+
+static bool QuadIsLinear(const SkPoint a[3]) {
+    MAKE_CONST_QUAD(aQuad, a);
+    return isLinear(aQuad, 0, 2);
+}
+
+static bool CubicIsLinear(const SkPoint a[4]) {
+    MAKE_CONST_CUBIC(aCubic, a);
+    return isLinear(aCubic, 0, 3);
+}
+
+static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
+    MAKE_CONST_LINE(aLine, a);
+    double x[2];
+    xy_at_t(aLine, startT, x[0], *(double*) 0);
+    xy_at_t(aLine, endT, x[1], *(double*) 0);
+    return SkMinScalar((float) x[0], (float) x[1]);
+}
+
+static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
+    MAKE_CONST_QUAD(aQuad, a);
+    return (float) leftMostT(aQuad, startT, endT);
+}
+
+static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
+    MAKE_CONST_CUBIC(aCubic, a);
+    return (float) leftMostT(aCubic, startT, endT);
+}
+
+static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
+    NULL,
+    LineLeftMost,
+    QuadLeftMost,
+    CubicLeftMost
+};
+
+#if 0 // currently unused
+static int QuadRayIntersect(const SkPoint a[3], const SkPoint b[2],
+        Intersections& intersections) {
+    MAKE_CONST_QUAD(aQuad, a);
+    MAKE_CONST_LINE(bLine, b);
+    return intersectRay(aQuad, bLine, intersections);
+}
+#endif
+
+static int QuadRayIntersect(const SkPoint a[3], const _Line& bLine,
+        Intersections& intersections) {
+    MAKE_CONST_QUAD(aQuad, a);
+    return intersectRay(aQuad, bLine, intersections);
+}
+
+class Segment;
+
+struct Span {
+    Segment* fOther;
+    mutable SkPoint fPt; // lazily computed as needed
+    double fT;
+    double fOtherT; // value at fOther[fOtherIndex].fT
+    int fOtherIndex;  // can't be used during intersection
+    int fWindSum; // accumulated from contours surrounding this one
+    int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
+    int fWindValueOpp; // opposite value, if any (for binary ops with coincidence)
+    bool fDone; // if set, this span to next higher T has been processed
+    bool fUnsortableStart; // set when start is part of an unsortable pair
+    bool fUnsortableEnd; // set when end is part of an unsortable pair
+};
+
+// sorting angles
+// given angles of {dx dy ddx ddy dddx dddy} sort them
+class Angle {
+public:
+    // FIXME: this is bogus for quads and cubics
+    // if the quads and cubics' line from end pt to ctrl pt are coincident,
+    // there's no obvious way to determine the curve ordering from the
+    // derivatives alone. In particular, if one quadratic's coincident tangent
+    // is longer than the other curve, the final control point can place the
+    // longer curve on either side of the shorter one.
+    // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
+    // may provide some help, but nothing has been figured out yet.
+
+ //   start here
+    /*(
+    for quads and cubics, set up a parameterized line (e.g. LineParameters )
+    for points [0] to [1]. See if point [2] is on that line, or on one side
+    or the other. If it both quads' end points are on the same side, choose
+    the shorter tangent. If the tangents are equal, choose the better second
+    tangent angle
+
+    maybe I could set up LineParameters lazily
+    */
+    bool operator<(const Angle& rh) const {
+        double y = dy();
+        double ry = rh.dy();
+        if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ?
+            return y < 0;
+        }
+        double x = dx();
+        double rx = rh.dx();
+        if (y == 0 && ry == 0 && x * rx < 0) {
+            return x < rx;
+        }
+        double x_ry = x * ry;
+        double rx_y = rx * y;
+        double cmp = x_ry - rx_y;
+        if (!approximately_zero(cmp)) {
+            return cmp < 0;
+        }
+        if (approximately_zero(x_ry) && approximately_zero(rx_y)
+                && !approximately_zero_squared(cmp)) {
+            return cmp < 0;
+        }
+        // at this point, the initial tangent line is coincident
+        if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) || !approximately_zero(rh.fSide))) {
+            // FIXME: running demo will trigger this assertion
+            // (don't know if commenting out will trigger further assertion or not)
+            // commenting it out allows demo to run in release, though
+     //       SkASSERT(fSide != rh.fSide);
+            return fSide < rh.fSide;
+        }
+        // see if either curve can be lengthened and try the tangent compare again
+        if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely identical
+                && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecting
+            Angle longer = *this;
+            Angle rhLonger = rh;
+            if (longer.lengthen() | rhLonger.lengthen()) {
+                return longer < rhLonger;
+            }
+            // what if we extend in the other direction?
+            longer = *this;
+            rhLonger = rh;
+            if (longer.reverseLengthen() | rhLonger.reverseLengthen()) {
+                return longer < rhLonger;
+            }
+        }
+        SkASSERT(fVerb == SkPath::kQuad_Verb); // worry about cubics later
+        SkASSERT(rh.fVerb == SkPath::kQuad_Verb);
+        // FIXME: until I can think of something better, project a ray from the
+        // end of the shorter tangent to midway between the end points
+        // through both curves and use the resulting angle to sort
+        // FIXME: some of this setup can be moved to set() if it works, or cached if it's expensive
+        double len = fTangent1.normalSquared();
+        double rlen = rh.fTangent1.normalSquared();
+        _Line ray;
+        Intersections i, ri;
+        int roots, rroots;
+        bool flip = false;
+        do {
+            const Quadratic& q = (len < rlen) ^ flip ? fQ : rh.fQ;
+            double midX = (q[0].x + q[2].x) / 2;
+            double midY = (q[0].y + q[2].y) / 2;
+            ray[0] = q[1];
+            ray[1].x = midX;
+            ray[1].y = midY;
+            SkASSERT(ray[0] != ray[1]);
+            roots = QuadRayIntersect(fPts, ray, i);
+            rroots = QuadRayIntersect(rh.fPts, ray, ri);
+        } while ((roots == 0 || rroots == 0) && (flip ^= true));
+        if (roots == 0 || rroots == 0) {
+            // FIXME: we don't have a solution in this case. The interim solution
+            // is to mark the edges as unsortable, exclude them from this and
+            // future computations, and allow the returned path to be fragmented
+            fUnsortable = true;
+            rh.fUnsortable = true;
+            return this < &rh; // even with no solution, return a stable sort
+        }
+        _Point loc;
+        double best = SK_ScalarInfinity;
+        double dx, dy, dist;
+        int index;
+        for (index = 0; index < roots; ++index) {
+            QuadXYAtT(fPts, i.fT[0][index], &loc);
+            dx = loc.x - ray[0].x;
+            dy = loc.y - ray[0].y;
+            dist = dx * dx + dy * dy;
+            if (best > dist) {
+                best = dist;
+            }
+        }
+        for (index = 0; index < rroots; ++index) {
+            QuadXYAtT(rh.fPts, ri.fT[0][index], &loc);
+            dx = loc.x - ray[0].x;
+            dy = loc.y - ray[0].y;
+            dist = dx * dx + dy * dy;
+            if (best > dist) {
+                return fSide < 0;
+            }
+        }
+        return fSide > 0;
+    }
+
+    double dx() const {
+        return fTangent1.dx();
+    }
+
+    double dy() const {
+        return fTangent1.dy();
+    }
+
+    int end() const {
+        return fEnd;
+    }
+
+    bool isHorizontal() const {
+        return dy() == 0 && fVerb == SkPath::kLine_Verb;
+    }
+
+    bool lengthen() {
+        int newEnd = fEnd;
+        if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
+            fEnd = newEnd;
+            setSpans();
+            return true;
+        }
+        return false;
+    }
+
+    bool reverseLengthen() {
+        if (fReversed) {
+            return false;
+        }
+        int newEnd = fStart;
+        if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) {
+            fEnd = newEnd;
+            fReversed = true;
+            setSpans();
+            return true;
+        }
+        return false;
+    }
+
+    void set(const SkPoint* orig, SkPath::Verb verb, const Segment* segment,
+            int start, int end, const SkTDArray<Span>& spans) {
+        fSegment = segment;
+        fStart = start;
+        fEnd = end;
+        fPts = orig;
+        fVerb = verb;
+        fSpans = &spans;
+        fReversed = false;
+        fUnsortable = false;
+        setSpans();
+    }
+
+    void setSpans() {
+        double startT = (*fSpans)[fStart].fT;
+        double endT = (*fSpans)[fEnd].fT;
+        switch (fVerb) {
+        case SkPath::kLine_Verb:
+            _Line l;
+            LineSubDivideHD(fPts, startT, endT, l);
+            // OPTIMIZATION: for pure line compares, we never need fTangent1.c
+            fTangent1.lineEndPoints(l);
+            fSide = 0;
+            break;
+        case SkPath::kQuad_Verb:
+            QuadSubDivideHD(fPts, startT, endT, fQ);
+            fTangent1.quadEndPoints(fQ, 0, 1);
+            fSide = -fTangent1.pointDistance(fQ[2]); // not normalized -- compare sign only
+            break;
+        case SkPath::kCubic_Verb:
+            Cubic c;
+            CubicSubDivideHD(fPts, startT, endT, c);
+            fTangent1.cubicEndPoints(c, 0, 1);
+            fSide = -fTangent1.pointDistance(c[2]); // not normalized -- compare sign only
+            break;
+        default:
+            SkASSERT(0);
+        }
+    }
+
+    Segment* segment() const {
+        return const_cast<Segment*>(fSegment);
+    }
+
+    int sign() const {
+        return SkSign32(fStart - fEnd);
+    }
+
+    const SkTDArray<Span>* spans() const {
+        return fSpans;
+    }
+
+    int start() const {
+        return fStart;
+    }
+    
+    bool unsortable() const {
+        return fUnsortable;
+    }
+
+#if DEBUG_ANGLE
+    const SkPoint* pts() const {
+        return fPts;
+    }
+
+    SkPath::Verb verb() const {
+        return fVerb;
+    }
+
+    void debugShow(const SkPoint& a) const {
+        SkDebugf("    d=(%1.9g,%1.9g) side=%1.9g\n", dx(), dy(), fSide);
+    }
+#endif
+
+private:
+    const SkPoint* fPts;
+    Quadratic fQ;
+    SkPath::Verb fVerb;
+    double fSide;
+    LineParameters fTangent1;
+    const SkTDArray<Span>* fSpans;
+    const Segment* fSegment;
+    int fStart;
+    int fEnd;
+    bool fReversed;
+    mutable bool fUnsortable; // this alone is editable by the less than operator
+};
+
+// Bounds, unlike Rect, does not consider a line to be empty.
+struct Bounds : public SkRect {
+    static bool Intersects(const Bounds& a, const Bounds& b) {
+        return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
+                a.fTop <= b.fBottom && b.fTop <= a.fBottom;
+    }
+
+    void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
+        if (left < fLeft) {
+            fLeft = left;
+        }
+        if (top < fTop) {
+            fTop = top;
+        }
+        if (right > fRight) {
+            fRight = right;
+        }
+        if (bottom > fBottom) {
+            fBottom = bottom;
+        }
+    }
+
+    void add(const Bounds& toAdd) {
+        add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
+    }
+
+    bool isEmpty() {
+        return fLeft > fRight || fTop > fBottom
+                || (fLeft == fRight && fTop == fBottom)
+                || isnan(fLeft) || isnan(fRight)
+                || isnan(fTop) || isnan(fBottom);
+    }
+
+    void setCubicBounds(const SkPoint a[4]) {
+        _Rect dRect;
+        MAKE_CONST_CUBIC(cubic, a);
+        dRect.setBounds(cubic);
+        set((float) dRect.left, (float) dRect.top, (float) dRect.right,
+                (float) dRect.bottom);
+    }
+
+    void setQuadBounds(const SkPoint a[3]) {
+        MAKE_CONST_QUAD(quad, a);
+        _Rect dRect;
+        dRect.setBounds(quad);
+        set((float) dRect.left, (float) dRect.top, (float) dRect.right,
+                (float) dRect.bottom);
+    }
+};
+
+static bool useInnerWinding(int outerWinding, int innerWinding) {
+    SkASSERT(outerWinding != innerWinding);
+    int absOut = abs(outerWinding);
+    int absIn = abs(innerWinding);
+    bool result = absOut == absIn ? outerWinding < 0 : absOut < absIn;
+    if (outerWinding * innerWinding < 0) {
+#if DEBUG_WINDING
+        SkDebugf("%s outer=%d inner=%d result=%s\n", __FUNCTION__,
+                outerWinding, innerWinding, result ? "true" : "false");
+#endif
+    }
+    return result;
+}
+
+static const bool opLookup[][2][2] = {
+    //     ==0             !=0
+    //  b      a        b      a
+    {{true , false}, {false, true }}, // a - b
+    {{false, false}, {true , true }}, // a & b
+    {{true , true }, {false, false}}, // a | b
+    {{true , true }, {true , true }}, // a ^ b
+};
+
+static bool activeOp(bool angleIsOp, int otherNonZero, ShapeOp op) {
+    return opLookup[op][otherNonZero][angleIsOp];
+}
+
+class Segment {
+public:
+    Segment() {
+#if DEBUG_DUMP
+        fID = ++gSegmentID;
+#endif
+    }
+
+    bool activeAngle(int index, int& done, SkTDArray<Angle>& angles) const {
+        if (activeAngleInner(index, done, angles)) {
+            return true;
+        }
+        double referenceT = fTs[index].fT;
+        int lesser = index;
+        while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
+            if (activeAngleOther(lesser, done, angles)) {
+                return true;
+            }
+        }
+        do {
+            if (activeAngleOther(index, done, angles)) {
+                return true;
+            }
+        } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
+        return false;
+    }
+
+    bool activeAngleOther(int index, int& done, SkTDArray<Angle>& angles) const {
+        Span* span = &fTs[index];
+        Segment* other = span->fOther;
+        int oIndex = span->fOtherIndex;
+        return other->activeAngleInner(oIndex, done, angles);
+    }
+
+    bool activeAngleInner(int index, int& done, SkTDArray<Angle>& angles) const {
+        int next = nextSpan(index, 1);
+        if (next > 0) {
+            const Span& upSpan = fTs[index];
+            if (upSpan.fWindValue) {
+                addAngle(angles, index, next);
+                if (upSpan.fDone) {
+                    done++;
+                } else if (upSpan.fWindSum != SK_MinS32) {
+                    return true;
+                }
+            }
+        }
+        int prev = nextSpan(index, -1);
+        // edge leading into junction
+        if (prev >= 0) {
+            const Span& downSpan = fTs[prev];
+            if (downSpan.fWindValue) {
+                addAngle(angles, index, prev);
+                if (downSpan.fDone) {
+                    done++;
+                 } else if (downSpan.fWindSum != SK_MinS32) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    SkScalar activeTop() const {
+        SkASSERT(!done());
+        int count = fTs.count();
+        SkScalar result = SK_ScalarMax;
+        bool lastDone = true;
+        for (int index = 0; index < count; ++index) {
+            bool done = fTs[index].fDone;
+            if (!done || !lastDone) {
+                SkScalar y = yAtT(index);
+                if (result > y) {
+                    result = y;
+                }
+            }
+            lastDone = done;
+        }
+        SkASSERT(result < SK_ScalarMax);
+        return result;
+    }
+
+    void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
+        SkASSERT(start != end);
+        Angle* angle = angles.append();
+#if DEBUG_ANGLE
+        if (angles.count() > 1) {
+            SkPoint angle0Pt, newPt;
+            (*SegmentXYAtT[angles[0].verb()])(angles[0].pts(),
+                    (*angles[0].spans())[angles[0].start()].fT, &angle0Pt);
+            (*SegmentXYAtT[fVerb])(fPts, fTs[start].fT, &newPt);
+            SkASSERT(approximately_equal(angle0Pt.fX, newPt.fX));
+            SkASSERT(approximately_equal(angle0Pt.fY, newPt.fY));
+        }
+#endif
+        angle->set(fPts, fVerb, this, start, end, fTs);
+    }
+
+    void addCancelOutsides(double tStart, double oStart, Segment& other,
+            double oEnd) {
+        int tIndex = -1;
+        int tCount = fTs.count();
+        int oIndex = -1;
+        int oCount = other.fTs.count();
+        do {
+            ++tIndex;
+        } while (!approximately_negative(tStart - fTs[tIndex].fT) && tIndex < tCount);
+        int tIndexStart = tIndex;
+        do {
+            ++oIndex;
+        } while (!approximately_negative(oStart - other.fTs[oIndex].fT) && oIndex < oCount);
+        int oIndexStart = oIndex;
+        double nextT;
+        do {
+            nextT = fTs[++tIndex].fT;
+        } while (nextT < 1 && approximately_negative(nextT - tStart));
+        double oNextT;
+        do {
+            oNextT = other.fTs[++oIndex].fT;
+        } while (oNextT < 1 && approximately_negative(oNextT - oStart));
+        // at this point, spans before and after are at:
+        //  fTs[tIndexStart - 1], fTs[tIndexStart], fTs[tIndex]
+        // if tIndexStart == 0, no prior span
+        // if nextT == 1, no following span
+
+        // advance the span with zero winding
+        // if the following span exists (not past the end, non-zero winding)
+        // connect the two edges
+        if (!fTs[tIndexStart].fWindValue) {
+            if (tIndexStart > 0 && fTs[tIndexStart - 1].fWindValue) {
+    #if DEBUG_CONCIDENT
+                SkDebugf("%s 1 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+                        __FUNCTION__, fID, other.fID, tIndexStart - 1,
+                        fTs[tIndexStart].fT, xyAtT(tIndexStart).fX,
+                        xyAtT(tIndexStart).fY);
+    #endif
+                addTPair(fTs[tIndexStart].fT, other, other.fTs[oIndex].fT, false);
+            }
+            if (nextT < 1 && fTs[tIndex].fWindValue) {
+    #if DEBUG_CONCIDENT
+                SkDebugf("%s 2 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+                        __FUNCTION__, fID, other.fID, tIndex,
+                        fTs[tIndex].fT, xyAtT(tIndex).fX,
+                        xyAtT(tIndex).fY);
+    #endif
+                addTPair(fTs[tIndex].fT, other, other.fTs[oIndexStart].fT, false);
+            }
+        } else {
+            SkASSERT(!other.fTs[oIndexStart].fWindValue);
+            if (oIndexStart > 0 && other.fTs[oIndexStart - 1].fWindValue) {
+    #if DEBUG_CONCIDENT
+                SkDebugf("%s 3 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+                        __FUNCTION__, fID, other.fID, oIndexStart - 1,
+                        other.fTs[oIndexStart].fT, other.xyAtT(oIndexStart).fX,
+                        other.xyAtT(oIndexStart).fY);
+                other.debugAddTPair(other.fTs[oIndexStart].fT, *this, fTs[tIndex].fT);
+    #endif
+            }
+            if (oNextT < 1 && other.fTs[oIndex].fWindValue) {
+    #if DEBUG_CONCIDENT
+                SkDebugf("%s 4 this=%d other=%d t [%d] %1.9g (%1.9g,%1.9g)\n",
+                        __FUNCTION__, fID, other.fID, oIndex,
+                        other.fTs[oIndex].fT, other.xyAtT(oIndex).fX,
+                        other.xyAtT(oIndex).fY);
+                other.debugAddTPair(other.fTs[oIndex].fT, *this, fTs[tIndexStart].fT);
+    #endif
+            }
+        }
+    }
+
+    void addCoinOutsides(const SkTDArray<double>& outsideTs, Segment& other,
+            double oEnd) {
+        // walk this to outsideTs[0]
+        // walk other to outsideTs[1]
+        // if either is > 0, add a pointer to the other, copying adjacent winding
+        int tIndex = -1;
+        int oIndex = -1;
+        double tStart = outsideTs[0];
+        double oStart = outsideTs[1];
+        do {
+            ++tIndex;
+        } while (!approximately_negative(tStart - fTs[tIndex].fT));
+        do {
+            ++oIndex;
+        } while (!approximately_negative(oStart - other.fTs[oIndex].fT));
+        if (tIndex > 0 || oIndex > 0) {
+            addTPair(tStart, other, oStart, false);
+        }
+        tStart = fTs[tIndex].fT;
+        oStart = other.fTs[oIndex].fT;
+        do {
+            double nextT;
+            do {
+                nextT = fTs[++tIndex].fT;
+            } while (approximately_negative(nextT - tStart));
+            tStart = nextT;
+            do {
+                nextT = other.fTs[++oIndex].fT;
+            } while (approximately_negative(nextT - oStart));
+            oStart = nextT;
+            if (tStart == 1 && oStart == 1) {
+                break;
+            }
+            addTPair(tStart, other, oStart, false);
+        } while (tStart < 1 && oStart < 1 && !approximately_negative(oEnd - oStart));
+    }
+
+    void addCubic(const SkPoint pts[4], bool operand) {
+        init(pts, SkPath::kCubic_Verb, operand);
+        fBounds.setCubicBounds(pts);
+    }
+
+    // FIXME: this needs to defer add for aligned consecutive line segments
+    SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
+        SkPoint edge[4];
+        // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
+        (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
+        if (active) {
+    #if DEBUG_PATH_CONSTRUCTION
+            SkDebugf("path.%sTo(%1.9g,%1.9g",
+                    kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
+            if (fVerb > 1) {
+                SkDebugf(", %1.9g,%1.9g", edge[2].fX, edge[2].fY);
+            }
+            if (fVerb > 2) {
+                SkDebugf(", %1.9g,%1.9g", edge[3].fX, edge[3].fY);
+            }
+            SkDebugf(");\n");
+    #endif
+            switch (fVerb) {
+                case SkPath::kLine_Verb:
+                    path.lineTo(edge[1].fX, edge[1].fY);
+                    break;
+                case SkPath::kQuad_Verb:
+                    path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
+                    break;
+                case SkPath::kCubic_Verb:
+                    path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
+                            edge[3].fX, edge[3].fY);
+                    break;
+                default:
+                    SkASSERT(0);
+            }
+        }
+        return edge[fVerb];
+    }
+
+    void addLine(const SkPoint pts[2], bool operand) {
+        init(pts, SkPath::kLine_Verb, operand);
+        fBounds.set(pts, 2);
+    }
+
+    const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
+        const SkPoint& pt = xyAtT(tIndex);
+        if (active) {
+    #if DEBUG_PATH_CONSTRUCTION
+            SkDebugf("path.moveTo(%1.9g, %1.9g);\n", pt.fX, pt.fY);
+    #endif
+            path.moveTo(pt.fX, pt.fY);
+        }
+        return pt;
+    }
+
+    // add 2 to edge or out of range values to get T extremes
+    void addOtherT(int index, double otherT, int otherIndex) {
+        Span& span = fTs[index];
+        span.fOtherT = otherT;
+        span.fOtherIndex = otherIndex;
+    }
+
+    void addQuad(const SkPoint pts[3], bool operand) {
+        init(pts, SkPath::kQuad_Verb, operand);
+        fBounds.setQuadBounds(pts);
+    }
+
+    // Defer all coincident edge processing until
+    // after normal intersections have been computed
+
+// no need to be tricky; insert in normal T order
+// resolve overlapping ts when considering coincidence later
+
+    // add non-coincident intersection. Resulting edges are sorted in T.
+    int addT(double newT, Segment* other) {
+        // FIXME: in the pathological case where there is a ton of intercepts,
+        //  binary search?
+        int insertedAt = -1;
+        size_t tCount = fTs.count();
+        // FIXME: only do this pinning here (e.g. this is done also in quad/line intersect)
+        if (approximately_less_than_zero(newT)) {
+            newT = 0;
+        }
+        if (approximately_greater_than_one(newT)) {
+            newT = 1;
+        }
+        for (size_t index = 0; index < tCount; ++index) {
+            // OPTIMIZATION: if there are three or more identical Ts, then
+            // the fourth and following could be further insertion-sorted so
+            // that all the edges are clockwise or counterclockwise.
+            // This could later limit segment tests to the two adjacent
+            // neighbors, although it doesn't help with determining which
+            // circular direction to go in.
+            if (newT < fTs[index].fT) {
+                insertedAt = index;
+                break;
+            }
+        }
+        Span* span;
+        if (insertedAt >= 0) {
+            span = fTs.insert(insertedAt);
+        } else {
+            insertedAt = tCount;
+            span = fTs.append();
+        }
+        span->fT = newT;
+        span->fOther = other;
+        span->fPt.fX = SK_ScalarNaN;
+        span->fWindSum = SK_MinS32;
+        span->fWindValue = 1;
+        span->fWindValueOpp = 0;
+        if ((span->fDone = newT == 1)) {
+            ++fDoneSpans;
+        }
+        span->fUnsortableStart = false;
+        span->fUnsortableEnd = false;
+        return insertedAt;
+    }
+
+    // set spans from start to end to decrement by one
+    // note this walks other backwards
+    // FIMXE: there's probably an edge case that can be constructed where
+    // two span in one segment are separated by float epsilon on one span but
+    // not the other, if one segment is very small. For this
+    // case the counts asserted below may or may not be enough to separate the
+    // spans. Even if the counts work out, what if the spans aren't correctly
+    // sorted? It feels better in such a case to match the span's other span
+    // pointer since both coincident segments must contain the same spans.
+    void addTCancel(double startT, double endT, Segment& other,
+            double oStartT, double oEndT) {
+        SkASSERT(!approximately_negative(endT - startT));
+        SkASSERT(!approximately_negative(oEndT - oStartT));
+        bool binary = fOperand != other.fOperand;
+        int index = 0;
+        while (!approximately_negative(startT - fTs[index].fT)) {
+            ++index;
+        }
+        int oIndex = other.fTs.count();
+        while (approximately_positive(other.fTs[--oIndex].fT - oEndT))
+            ;
+        double tRatio = (oEndT - oStartT) / (endT - startT);
+        Span* test = &fTs[index];
+        Span* oTest = &other.fTs[oIndex];
+        SkTDArray<double> outsideTs;
+        SkTDArray<double> oOutsideTs;
+        do {
+            bool decrement = test->fWindValue && oTest->fWindValue;
+            bool track = test->fWindValue || oTest->fWindValue;
+            double testT = test->fT;
+            double oTestT = oTest->fT;
+            Span* span = test;
+            do {
+                if (decrement) {
+                    if (binary) {
+                        --(span->fWindValueOpp);
+                    } else {
+                        decrementSpan(span);
+                    }
+                } else if (track && span->fT < 1 && oTestT < 1) {
+                    TrackOutside(outsideTs, span->fT, oTestT);
+                }
+                span = &fTs[++index];
+            } while (approximately_negative(span->fT - testT));
+            Span* oSpan = oTest;
+            double otherTMatchStart = oEndT - (span->fT - startT) * tRatio;
+            double otherTMatchEnd = oEndT - (test->fT - startT) * tRatio;
+            SkDEBUGCODE(int originalWindValue = oSpan->fWindValue);
+            while (approximately_negative(otherTMatchStart - oSpan->fT)
+                    && !approximately_negative(otherTMatchEnd - oSpan->fT)) {
+        #ifdef SK_DEBUG
+                SkASSERT(originalWindValue == oSpan->fWindValue);
+        #endif
+                if (decrement) {
+                    other.decrementSpan(oSpan);
+                } else if (track && oSpan->fT < 1 && testT < 1) {
+                    TrackOutside(oOutsideTs, oSpan->fT, testT);
+                }
+                if (!oIndex) {
+                    break;
+                }
+                oSpan = &other.fTs[--oIndex];
+            }
+            test = span;
+            oTest = oSpan;
+        } while (!approximately_negative(endT - test->fT));
+        SkASSERT(!oIndex || approximately_negative(oTest->fT - oStartT));
+        // FIXME: determine if canceled edges need outside ts added
+        if (!done() && outsideTs.count()) {
+            double tStart = outsideTs[0];
+            double oStart = outsideTs[1];
+            addCancelOutsides(tStart, oStart, other, oEndT);
+            int count = outsideTs.count();
+            if (count > 2) {
+                double tStart = outsideTs[count - 2];
+                double oStart = outsideTs[count - 1];
+                addCancelOutsides(tStart, oStart, other, oEndT);
+            }
+        }
+        if (!other.done() && oOutsideTs.count()) {
+            double tStart = oOutsideTs[0];
+            double oStart = oOutsideTs[1];
+            other.addCancelOutsides(tStart, oStart, *this, endT);
+        }
+    }
+
+    // set spans from start to end to increment the greater by one and decrement
+    // the lesser
+    void addTCoincident(const bool isXor, double startT, double endT,
+            Segment& other, double oStartT, double oEndT) {
+        SkASSERT(!approximately_negative(endT - startT));
+        SkASSERT(!approximately_negative(oEndT - oStartT));
+        bool binary = fOperand != other.fOperand;
+        int index = 0;
+        while (!approximately_negative(startT - fTs[index].fT)) {
+            ++index;
+        }
+        int oIndex = 0;
+        while (!approximately_negative(oStartT - other.fTs[oIndex].fT)) {
+            ++oIndex;
+        }
+        double tRatio = (oEndT - oStartT) / (endT - startT);
+        Span* test = &fTs[index];
+        Span* oTest = &other.fTs[oIndex];
+        SkTDArray<double> outsideTs;
+        SkTDArray<double> xOutsideTs;
+        SkTDArray<double> oOutsideTs;
+        SkTDArray<double> oxOutsideTs;
+        do {
+            bool transfer = test->fWindValue && oTest->fWindValue;
+            bool decrementThis = (test->fWindValue < oTest->fWindValue) & !isXor;
+            bool decrementOther = (test->fWindValue >= oTest->fWindValue) & !isXor;
+            Span* end = test;
+            double startT = end->fT;
+            int startIndex = index;
+            double oStartT = oTest->fT;
+            int oStartIndex = oIndex;
+            do {
+                if (transfer) {
+                    if (decrementOther) {
+                #ifdef SK_DEBUG
+                        SkASSERT(abs(end->fWindValue) < gDebugMaxWindValue);
+                #endif
+                        if (binary) {
+                            ++(end->fWindValueOpp);
+                        } else {
+                            ++(end->fWindValue);
+                        }
+                    } else if (decrementSpan(end)) {
+                        TrackOutside(outsideTs, end->fT, oStartT);
+                    }
+                } else if (oTest->fWindValue) {
+                    SkASSERT(!decrementOther);
+                    if (startIndex > 0 && fTs[startIndex - 1].fWindValue) {
+                        TrackOutside(xOutsideTs, end->fT, oStartT);
+                    }
+                }
+                end = &fTs[++index];
+            } while (approximately_negative(end->fT - test->fT));
+        // because of the order in which coincidences are resolved, this and other
+        // may not have the same intermediate points. Compute the corresponding
+        // intermediate T values (using this as the master, other as the follower)
+        // and walk other conditionally -- hoping that it catches up in the end
+            double otherTMatch = (test->fT - startT) * tRatio + oStartT;
+            Span* oEnd = oTest;
+            while (!approximately_negative(oEndT - oEnd->fT) && approximately_negative(oEnd->fT - otherTMatch)) {
+                if (transfer) {
+                    if (decrementThis) {
+                 #ifdef SK_DEBUG
+                       SkASSERT(abs(oEnd->fWindValue) < gDebugMaxWindValue);
+                #endif
+                        if (binary) {
+                            ++(oEnd->fWindValueOpp);
+                        } else {
+                            ++(oEnd->fWindValue);
+                        }
+                    } else if (other.decrementSpan(oEnd)) {
+                        TrackOutside(oOutsideTs, oEnd->fT, startT);
+                    }
+                } else if (test->fWindValue) {
+                    SkASSERT(!decrementOther);
+                    if (oStartIndex > 0 && other.fTs[oStartIndex - 1].fWindValue) {
+                        SkASSERT(0); // track for later?
+                    }
+                }
+                oEnd = &other.fTs[++oIndex];
+            }
+            test = end;
+            oTest = oEnd;
+        } while (!approximately_negative(endT - test->fT));
+        SkASSERT(approximately_negative(oTest->fT - oEndT));
+        SkASSERT(approximately_negative(oEndT - oTest->fT));
+        if (!done()) {
+            if (outsideTs.count()) {
+                addCoinOutsides(outsideTs, other, oEndT);
+            }
+            if (xOutsideTs.count()) {
+                addCoinOutsides(xOutsideTs, other, oEndT);
+            }
+        }
+        if (!other.done() && oOutsideTs.count()) {
+            other.addCoinOutsides(oOutsideTs, *this, endT);
+        }
+    }
+
+    // FIXME: this doesn't prevent the same span from being added twice
+    // fix in caller, assert here?
+    void addTPair(double t, Segment& other, double otherT, bool borrowWind) {
+        int tCount = fTs.count();
+        for (int tIndex = 0; tIndex < tCount; ++tIndex) {
+            const Span& span = fTs[tIndex];
+            if (!approximately_negative(span.fT - t)) {
+                break;
+            }
+            if (approximately_negative(span.fT - t) && span.fOther == &other
+                    && approximately_equal(span.fOtherT, otherT)) {
+#if DEBUG_ADD_T_PAIR
+                SkDebugf("%s addTPair duplicate this=%d %1.9g other=%d %1.9g\n",
+                        __FUNCTION__, fID, t, other.fID, otherT);
+#endif
+                return;
+            }
+        }
+#if DEBUG_ADD_T_PAIR
+        SkDebugf("%s addTPair this=%d %1.9g other=%d %1.9g\n",
+                __FUNCTION__, fID, t, other.fID, otherT);
+#endif
+        int insertedAt = addT(t, &other);
+        int otherInsertedAt = other.addT(otherT, this);
+        addOtherT(insertedAt, otherT, otherInsertedAt);
+        other.addOtherT(otherInsertedAt, t, insertedAt);
+        matchWindingValue(insertedAt, t, borrowWind);
+        other.matchWindingValue(otherInsertedAt, otherT, borrowWind);
+    }
+
+    void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
+        // add edge leading into junction
+        if (fTs[SkMin32(end, start)].fWindValue > 0) {
+            addAngle(angles, end, start);
+        }
+        // add edge leading away from junction
+        int step = SkSign32(end - start);
+        int tIndex = nextExactSpan(end, step);
+        if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
+            addAngle(angles, end, tIndex);
+        }
+    }
+
+    const Bounds& bounds() const {
+        return fBounds;
+    }
+
+    void buildAngles(int index, SkTDArray<Angle>& angles) const {
+        double referenceT = fTs[index].fT;
+        int lesser = index;
+    #if PRECISE_T_SORT
+        while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+            buildAnglesInner(lesser, angles);
+        }
+        do {
+            buildAnglesInner(index, angles);
+        } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+    #else
+        while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
+            buildAnglesInner(lesser, angles);
+        }
+        do {
+            buildAnglesInner(index, angles);
+        } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
+    #endif
+    }
+
+    void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
+        Span* span = &fTs[index];
+        Segment* other = span->fOther;
+    // if there is only one live crossing, and no coincidence, continue
+    // in the same direction
+    // if there is coincidence, the only choice may be to reverse direction
+        // find edge on either side of intersection
+        int oIndex = span->fOtherIndex;
+        // if done == -1, prior span has already been processed
+        int step = 1;
+    #if PRECISE_T_SORT
+        int next = other->nextExactSpan(oIndex, step);
+    #else
+        int next = other->nextSpan(oIndex, step);
+    #endif
+       if (next < 0) {
+            step = -step;
+    #if PRECISE_T_SORT
+            next = other->nextExactSpan(oIndex, step);
+    #else
+            next = other->nextSpan(oIndex, step);
+    #endif
+        }
+        // add candidate into and away from junction
+        other->addTwoAngles(next, oIndex, angles);
+    }
+
+    // figure out if the segment's ascending T goes clockwise or not
+    // not enough context to write this as shown
+    // instead, add all segments meeting at the top
+    // sort them using buildAngleList
+    // find the first in the sort
+    // see if ascendingT goes to top
+    bool clockwise(int /* tIndex */) const {
+        SkASSERT(0); // incomplete
+        return false;
+    }
+
+    // FIXME may not need this at all
+    // FIXME once I figure out the logic, merge this and too close to call
+    // NOTE not sure if tiny triangles can ever form at the edge, so until we
+    // see one, only worry about triangles that happen away from 0 and 1
+    void collapseTriangles(bool isXor) {
+        if (fTs.count() < 3) { // require t=0, x, 1 at minimum
+            return;
+        }
+        int lastIndex = 1;
+        double lastT;
+        while (approximately_less_than_zero((lastT = fTs[lastIndex].fT))) {
+            ++lastIndex;
+        }
+        if (approximately_greater_than_one(lastT)) {
+            return;
+        }
+        int matchIndex = lastIndex;
+        do {
+            Span& match = fTs[++matchIndex];
+            double matchT = match.fT;
+            if (approximately_greater_than_one(matchT)) {
+                return;
+            }
+            if (matchT == lastT) {
+                goto nextSpan;
+            }
+            if (approximately_negative(matchT - lastT)) {
+                Span& last = fTs[lastIndex];
+                Segment* lOther = last.fOther;
+                double lT = last.fOtherT;
+                if (approximately_less_than_zero(lT) || approximately_greater_than_one(lT)) {
+                    goto nextSpan;
+                }
+                Segment* mOther = match.fOther;
+                double mT = match.fOtherT;
+                if (approximately_less_than_zero(mT) || approximately_greater_than_one(mT)) {
+                    goto nextSpan;
+                }
+                // add new point to connect adjacent spurs
+                int count = lOther->fTs.count();
+                for (int index = 0; index < count; ++index) {
+                    Span& span = lOther->fTs[index];
+                    if (span.fOther == mOther && approximately_zero(span.fOtherT - mT)) {
+                        goto nextSpan;
+                    }
+                }
+                mOther->addTPair(mT, *lOther, lT, false);
+                // FIXME ? this could go on to detect that spans on mOther, lOther are now coincident
+            }
+    nextSpan:
+            lastIndex = matchIndex;
+            lastT = matchT;
+        } while (true);
+    }
+
+    int computeSum(int startIndex, int endIndex) {
+        SkTDArray<Angle> angles;
+        addTwoAngles(startIndex, endIndex, angles);
+        buildAngles(endIndex, angles);
+        // OPTIMIZATION: check all angles to see if any have computed wind sum
+        // before sorting (early exit if none)
+        SkTDArray<Angle*> sorted;
+        bool sortable = SortAngles(angles, sorted);
+#if DEBUG_SORT
+        sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
+#endif
+        if (!sortable) {
+            return SK_MinS32;
+        }
+        int angleCount = angles.count();
+        const Angle* angle;
+        const Segment* base;
+        int winding;
+        int firstIndex = 0;
+        do {
+            angle = sorted[firstIndex];
+            base = angle->segment();
+            winding = base->windSum(angle);
+            if (winding != SK_MinS32) {
+                break;
+            }
+            if (++firstIndex == angleCount) {
+                return SK_MinS32;
+            }
+        } while (true);
+        // turn winding into contourWinding
+        int spanWinding = base->spanSign(angle);
+        bool inner = useInnerWinding(winding + spanWinding, winding);
+    #if DEBUG_WINDING
+        SkDebugf("%s spanWinding=%d winding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
+            spanWinding, winding, angle->sign(), inner,
+            inner ? winding + spanWinding : winding);
+    #endif
+        if (inner) {
+            winding += spanWinding;
+        }
+    #if DEBUG_SORT
+        base->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
+    #endif
+        int nextIndex = firstIndex + 1;
+        int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
+        winding -= base->spanSign(angle);
+        do {
+            if (nextIndex == angleCount) {
+                nextIndex = 0;
+            }
+            angle = sorted[nextIndex];
+            Segment* segment = angle->segment();
+            int maxWinding = winding;
+            winding -= segment->spanSign(angle);
+            if (segment->windSum(angle) == SK_MinS32) {
+                if (useInnerWinding(maxWinding, winding)) {
+                    maxWinding = winding;
+                }
+                segment->markAndChaseWinding(angle, maxWinding);
+            }
+        } while (++nextIndex != lastIndex);
+        return windSum(SkMin32(startIndex, endIndex));
+    }
+
+    int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
+        int bestT = -1;
+        SkScalar top = bounds().fTop;
+        SkScalar bottom = bounds().fBottom;
+        int end = 0;
+        do {
+            int start = end;
+            end = nextSpan(start, 1);
+            if (fTs[start].fWindValue == 0) {
+                continue;
+            }
+            SkPoint edge[4];
+            double startT = fTs[start].fT;
+            double endT = fTs[end].fT;
+            (*SegmentSubDivide[fVerb])(fPts, startT, endT, edge);
+            // intersect ray starting at basePt with edge
+            Intersections intersections;
+            // FIXME: always use original and limit results to T values within
+            // start t and end t.
+            // OPTIMIZE: use specialty function that intersects ray with curve,
+            // returning t values only for curve (we don't care about t on ray)
+            int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
+                    false, intersections);
+            if (pts == 0) {
+                continue;
+            }
+            if (pts > 1 && fVerb == SkPath::kLine_Verb) {
+            // if the intersection is edge on, wait for another one
+                continue;
+            }
+            for (int index = 0; index < pts; ++index) {
+                SkPoint pt;
+                double foundT = intersections.fT[0][index];
+                double testT = startT + (endT - startT) * foundT;
+                (*SegmentXYAtT[fVerb])(fPts, testT, &pt);
+                if (bestY < pt.fY && pt.fY < basePt.fY) {
+                    if (fVerb > SkPath::kLine_Verb
+                            && !approximately_less_than_zero(foundT)
+                            && !approximately_greater_than_one(foundT)) {
+                        SkScalar dx = (*SegmentDXAtT[fVerb])(fPts, testT);
+                        if (approximately_zero(dx)) {
+                            continue;
+                        }
+                    }
+                    bestY = pt.fY;
+                    bestT = foundT < 1 ? start : end;
+                    hitT = testT;
+                }
+            }
+        } while (fTs[end].fT != 1);
+        return bestT;
+    }
+
+    bool crossedSpanHalves(const SkPoint& basePt, bool leftHalf, bool rightHalf) {
+        // if a segment is connected to this one, consider it crossing
+        int tIndex;
+        if (fPts[0].fX == basePt.fX) {
+            tIndex = 0;
+            do {
+                const Span& sSpan = fTs[tIndex];
+                const Segment* sOther = sSpan.fOther;
+                if (!sOther->fTs[sSpan.fOtherIndex].fWindValue) {
+                    continue;
+                }
+                if (leftHalf ? sOther->fBounds.fLeft < basePt.fX
+                        : sOther->fBounds.fRight > basePt.fX) {
+                    return true;
+                }
+            } while (fTs[++tIndex].fT == 0);
+        }
+        if (fPts[fVerb].fX == basePt.fX) {
+            tIndex = fTs.count() - 1;
+            do {
+                const Span& eSpan = fTs[tIndex];
+                const Segment* eOther = eSpan.fOther;
+                if (!eOther->fTs[eSpan.fOtherIndex].fWindValue) {
+                    continue;
+                }
+                if (leftHalf ? eOther->fBounds.fLeft < basePt.fX
+                        : eOther->fBounds.fRight > basePt.fX) {
+                    return true;
+                }
+            } while (fTs[--tIndex].fT == 1);
+        }
+        return false;
+    }
+
+    bool decrementSpan(Span* span) {
+        SkASSERT(span->fWindValue > 0);
+        if (--(span->fWindValue) == 0) {
+            span->fDone = true;
+            ++fDoneSpans;
+            return true;
+        }
+        return false;
+    }
+
+    bool done() const {
+        SkASSERT(fDoneSpans <= fTs.count());
+        return fDoneSpans == fTs.count();
+    }
+
+    bool done(const Angle& angle) const {
+        int start = angle.start();
+        int end = angle.end();
+        const Span& mSpan = fTs[SkMin32(start, end)];
+        return mSpan.fDone;
+    }
+
+    Segment* findNextOp(SkTDArray<Span*>& chase, bool active,
+            int& nextStart, int& nextEnd, int& winding, int& spanWinding, 
+            bool& unsortable, ShapeOp op,
+            const int aXorMask, const int bXorMask) {
+        const int startIndex = nextStart;
+        const int endIndex = nextEnd;
+        int outerWinding = winding;
+        int innerWinding = winding + spanWinding;
+    #if DEBUG_WINDING
+        SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
+                __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
+    #endif
+        if (useInnerWinding(outerWinding, innerWinding)) {
+            outerWinding = innerWinding;
+        }
+        SkASSERT(startIndex != endIndex);
+        int count = fTs.count();
+        SkASSERT(startIndex < endIndex ? startIndex < count - 1
+                : startIndex > 0);
+        int step = SkSign32(endIndex - startIndex);
+    #if PRECISE_T_SORT
+        int end = nextExactSpan(startIndex, step);
+    #else
+        int end = nextSpan(startIndex, step);
+    #endif
+        SkASSERT(end >= 0);
+        Span* endSpan = &fTs[end];
+        Segment* other;
+        if (isSimple(end)) {
+        // mark the smaller of startIndex, endIndex done, and all adjacent
+        // spans with the same T value (but not 'other' spans)
+    #if DEBUG_WINDING
+            SkDebugf("%s simple\n", __FUNCTION__);
+    #endif
+            markDone(SkMin32(startIndex, endIndex), outerWinding);
+            other = endSpan->fOther;
+            nextStart = endSpan->fOtherIndex;
+            double startT = other->fTs[nextStart].fT;
+            nextEnd = nextStart;
+            do {
+                nextEnd += step;
+            }
+    #if PRECISE_T_SORT
+            while (precisely_zero(startT - other->fTs[nextEnd].fT));
+    #else
+            while (approximately_zero(startT - other->fTs[nextEnd].fT));
+    #endif
+            SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
+            return other;
+        }
+        // more than one viable candidate -- measure angles to find best
+        SkTDArray<Angle> angles;
+        SkASSERT(startIndex - endIndex != 0);
+        SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+        addTwoAngles(startIndex, end, angles);
+        buildAngles(end, angles);
+        SkTDArray<Angle*> sorted;
+        bool sortable = SortAngles(angles, sorted);
+        int angleCount = angles.count();
+        int firstIndex = findStartingEdge(sorted, startIndex, end);
+        SkASSERT(firstIndex >= 0);
+    #if DEBUG_SORT
+        debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
+    #endif
+        if (!sortable) {
+            unsortable = true;
+            return NULL;
+        }
+        SkASSERT(sorted[firstIndex]->segment() == this);
+    #if DEBUG_WINDING
+        SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
+    #endif
+        int aSumWinding = winding;
+        int bSumWinding = winding;
+        bool angleIsOp = sorted[firstIndex]->segment()->operand();
+        int angleSpan = spanSign(sorted[firstIndex]);
+        if (angleIsOp) {
+            bSumWinding -= angleSpan;
+        } else {
+            aSumWinding -= angleSpan;
+        }
+        int nextIndex = firstIndex + 1;
+        int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
+        const Angle* foundAngle = NULL;
+        // FIXME: found done logic probably fails if there are more than 4
+        // sorted angles. It should bias towards the first and last undone
+        // edges -- but not sure that it won't choose a middle (incorrect)
+        // edge if one is undone
+        bool foundDone = false;
+        bool foundDone2 = false;
+        // iterate through the angle, and compute everyone's winding
+        bool altFlipped = false;
+        bool foundFlipped = false;
+        int foundMax = SK_MinS32;
+        int foundSum = SK_MinS32;
+        Segment* nextSegment;
+        int lastNonZeroSum = winding;
+        do {
+            if (nextIndex == angleCount) {
+                nextIndex = 0;
+            }
+            const Angle* nextAngle = sorted[nextIndex];
+            nextSegment = nextAngle->segment();
+            angleIsOp = nextSegment->operand();
+            int sumWinding = angleIsOp ? bSumWinding : aSumWinding;
+            int maxWinding = sumWinding;
+            if (sumWinding) {
+                lastNonZeroSum = sumWinding;
+            }
+            sumWinding -= nextSegment->spanSign(nextAngle);
+            int xorMask = nextSegment->operand() ? bXorMask : aXorMask;
+            bool otherNonZero;
+            if (angleIsOp) {
+                bSumWinding = sumWinding;
+                otherNonZero = aSumWinding & aXorMask;
+            } else {
+                aSumWinding = sumWinding;
+                otherNonZero = bSumWinding & bXorMask;
+            }
+            altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
+    #if 0 &&  DEBUG_WINDING
+            SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
+            SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
+                    nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
+    #endif
+
+            if (!(sumWinding & xorMask) && activeOp(angleIsOp, otherNonZero, op)) {
+                if (!active) {
+                    markDone(SkMin32(startIndex, endIndex), outerWinding);
+                    // FIXME: seems like a bug that this isn't calling userInnerWinding
+                    nextSegment->markWinding(SkMin32(nextAngle->start(),
+                                nextAngle->end()), maxWinding);
+    #if DEBUG_WINDING
+                    SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
+    #endif
+                    return NULL;
+                }
+                if (!foundAngle || foundDone) {
+                    foundAngle = nextAngle;
+                    foundDone = nextSegment->done(*nextAngle);
+                    foundFlipped = altFlipped;
+                    foundMax = maxWinding;
+                }
+                continue;
+            }
+            if (!(maxWinding & xorMask) && (!foundAngle || foundDone2)
+                    && activeOp(angleIsOp, otherNonZero, op)) {
+        #if DEBUG_WINDING
+                if (foundAngle && foundDone2) {
+                    SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
+                }
+        #endif
+                foundAngle = nextAngle;
+                foundDone2 = nextSegment->done(*nextAngle);
+                foundFlipped = altFlipped;
+                foundSum = sumWinding;
+            }
+            if (nextSegment->done()) {
+                continue;
+            }
+            // if the winding is non-zero, nextAngle does not connect to
+            // current chain. If we haven't done so already, mark the angle
+            // as done, record the winding value, and mark connected unambiguous
+            // segments as well.
+            if (nextSegment->windSum(nextAngle) == SK_MinS32) {
+                if (useInnerWinding(maxWinding, sumWinding)) {
+                    maxWinding = sumWinding;
+                }
+                Span* last;
+                if (foundAngle) {
+                    last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
+                } else {
+                    last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
+                }
+                if (last) {
+                    *chase.append() = last;
+                }
+            }
+        } while (++nextIndex != lastIndex);
+        markDone(SkMin32(startIndex, endIndex), outerWinding);
+        if (!foundAngle) {
+            return NULL;
+        }
+        nextStart = foundAngle->start();
+        nextEnd = foundAngle->end();
+        nextSegment = foundAngle->segment();
+        int flipped = foundFlipped ? -1 : 1;
+        spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
+                SkMin32(nextStart, nextEnd));
+        if (winding) {
+    #if DEBUG_WINDING
+            SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
+            if (foundSum == SK_MinS32) {
+                SkDebugf("?");
+            } else {
+                SkDebugf("%d", foundSum);
+            }
+            SkDebugf(" foundMax=");
+            if (foundMax == SK_MinS32) {
+                SkDebugf("?");
+            } else {
+                SkDebugf("%d", foundMax);
+            }
+            SkDebugf("\n");
+     #endif
+            winding = foundSum;
+        }
+    #if DEBUG_WINDING
+        SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
+    #endif
+        return nextSegment;
+    }
+
+    // so the span needs to contain the pairing info found here
+    // this should include the winding computed for the edge, and
+    //  what edge it connects to, and whether it is discarded
+    //  (maybe discarded == abs(winding) > 1) ?
+    // only need derivatives for duration of sorting, add a new struct
+    // for pairings, remove extra spans that have zero length and
+    //  reference an unused other
+    // for coincident, the last span on the other may be marked done
+    //  (always?)
+
+    // if loop is exhausted, contour may be closed.
+    // FIXME: pass in close point so we can check for closure
+
+    // given a segment, and a sense of where 'inside' is, return the next
+    // segment. If this segment has an intersection, or ends in multiple
+    // segments, find the mate that continues the outside.
+    // note that if there are multiples, but no coincidence, we can limit
+    // choices to connections in the correct direction
+
+    // mark found segments as done
+
+    // start is the index of the beginning T of this edge
+    // it is guaranteed to have an end which describes a non-zero length (?)
+    // winding -1 means ccw, 1 means cw
+    Segment* findNextWinding(SkTDArray<Span*>& chase, bool active,
+            int& nextStart, int& nextEnd, int& winding, int& spanWinding,
+            bool& unsortable) {
+        const int startIndex = nextStart;
+        const int endIndex = nextEnd;
+        int outerWinding = winding;
+        int innerWinding = winding + spanWinding;
+    #if DEBUG_WINDING
+        SkDebugf("%s winding=%d spanWinding=%d outerWinding=%d innerWinding=%d\n",
+                __FUNCTION__, winding, spanWinding, outerWinding, innerWinding);
+    #endif
+        if (useInnerWinding(outerWinding, innerWinding)) {
+            outerWinding = innerWinding;
+        }
+        SkASSERT(startIndex != endIndex);
+        int count = fTs.count();
+        SkASSERT(startIndex < endIndex ? startIndex < count - 1
+                : startIndex > 0);
+        int step = SkSign32(endIndex - startIndex);
+    #if PRECISE_T_SORT
+        int end = nextExactSpan(startIndex, step);
+    #else
+        int end = nextSpan(startIndex, step);
+    #endif
+        SkASSERT(end >= 0);
+        Span* endSpan = &fTs[end];
+        Segment* other;
+        if (isSimple(end)) {
+        // mark the smaller of startIndex, endIndex done, and all adjacent
+        // spans with the same T value (but not 'other' spans)
+    #if DEBUG_WINDING
+            SkDebugf("%s simple\n", __FUNCTION__);
+    #endif
+            markDone(SkMin32(startIndex, endIndex), outerWinding);
+            other = endSpan->fOther;
+            nextStart = endSpan->fOtherIndex;
+            double startT = other->fTs[nextStart].fT;
+            nextEnd = nextStart;
+            do {
+                nextEnd += step;
+            }
+    #if PRECISE_T_SORT
+             while (precisely_zero(startT - other->fTs[nextEnd].fT));
+    #else
+             while (approximately_zero(startT - other->fTs[nextEnd].fT));
+    #endif
+            SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
+            return other;
+        }
+        // more than one viable candidate -- measure angles to find best
+        SkTDArray<Angle> angles;
+        SkASSERT(startIndex - endIndex != 0);
+        SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+        addTwoAngles(startIndex, end, angles);
+        buildAngles(end, angles);
+        SkTDArray<Angle*> sorted;
+        bool sortable = SortAngles(angles, sorted);
+        int angleCount = angles.count();
+        int firstIndex = findStartingEdge(sorted, startIndex, end);
+        SkASSERT(firstIndex >= 0);
+    #if DEBUG_SORT
+        debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
+    #endif
+        if (!sortable) {
+            unsortable = true;
+            return NULL;
+        }
+        SkASSERT(sorted[firstIndex]->segment() == this);
+    #if DEBUG_WINDING
+        SkDebugf("%s [%d] sign=%d\n", __FUNCTION__, firstIndex, sorted[firstIndex]->sign());
+    #endif
+        int sumWinding = winding - spanSign(sorted[firstIndex]);
+        int nextIndex = firstIndex + 1;
+        int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
+        const Angle* foundAngle = NULL;
+        // FIXME: found done logic probably fails if there are more than 4
+        // sorted angles. It should bias towards the first and last undone
+        // edges -- but not sure that it won't choose a middle (incorrect)
+        // edge if one is undone
+        bool foundDone = false;
+        bool foundDone2 = false;
+        // iterate through the angle, and compute everyone's winding
+        bool altFlipped = false;
+        bool foundFlipped = false;
+        int foundMax = SK_MinS32;
+        int foundSum = SK_MinS32;
+        Segment* nextSegment;
+        int lastNonZeroSum = winding;
+        do {
+            if (nextIndex == angleCount) {
+                nextIndex = 0;
+            }
+            const Angle* nextAngle = sorted[nextIndex];
+            int maxWinding = sumWinding;
+            if (sumWinding) {
+                lastNonZeroSum = sumWinding;
+            }
+            nextSegment = nextAngle->segment();
+            sumWinding -= nextSegment->spanSign(nextAngle);
+            altFlipped ^= lastNonZeroSum * sumWinding < 0; // flip if different signs
+    #if 0 && DEBUG_WINDING
+            SkASSERT(abs(sumWinding) <= gDebugMaxWindSum);
+            SkDebugf("%s [%d] maxWinding=%d sumWinding=%d sign=%d altFlipped=%d\n", __FUNCTION__,
+                    nextIndex, maxWinding, sumWinding, nextAngle->sign(), altFlipped);
+    #endif
+           if (!sumWinding) {
+                if (!active) {
+                    markDone(SkMin32(startIndex, endIndex), outerWinding);
+                    // FIXME: seems like a bug that this isn't calling userInnerWinding
+                    nextSegment->markWinding(SkMin32(nextAngle->start(),
+                                nextAngle->end()), maxWinding);
+    #if DEBUG_WINDING
+                    SkDebugf("%s [%d] inactive\n", __FUNCTION__, nextIndex);
+    #endif
+                    return NULL;
+                }
+                if (!foundAngle || foundDone) {
+                    foundAngle = nextAngle;
+                    foundDone = nextSegment->done(*nextAngle);
+                    foundFlipped = altFlipped;
+                    foundMax = maxWinding;
+                }
+                continue;
+            }
+            if (!maxWinding && (!foundAngle || foundDone2)) {
+        #if DEBUG_WINDING
+                if (foundAngle && foundDone2) {
+                    SkDebugf("%s [%d] !foundAngle && foundDone2\n", __FUNCTION__, nextIndex);
+                }
+        #endif
+                foundAngle = nextAngle;
+                foundDone2 = nextSegment->done(*nextAngle);
+                foundFlipped = altFlipped;
+                foundSum = sumWinding;
+            }
+            if (nextSegment->done()) {
+                continue;
+            }
+            // if the winding is non-zero, nextAngle does not connect to
+            // current chain. If we haven't done so already, mark the angle
+            // as done, record the winding value, and mark connected unambiguous
+            // segments as well.
+            if (nextSegment->windSum(nextAngle) == SK_MinS32) {
+                if (useInnerWinding(maxWinding, sumWinding)) {
+                    maxWinding = sumWinding;
+                }
+                Span* last;
+                if (foundAngle) {
+                    last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
+                } else {
+                    last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
+                }
+                if (last) {
+                    *chase.append() = last;
+                }
+            }
+        } while (++nextIndex != lastIndex);
+        markDone(SkMin32(startIndex, endIndex), outerWinding);
+        if (!foundAngle) {
+            return NULL;
+        }
+        nextStart = foundAngle->start();
+        nextEnd = foundAngle->end();
+        nextSegment = foundAngle->segment();
+        int flipped = foundFlipped ? -1 : 1;
+        spanWinding = SkSign32(spanWinding) * flipped * nextSegment->windValue(
+                SkMin32(nextStart, nextEnd));
+        if (winding) {
+    #if DEBUG_WINDING
+            SkDebugf("%s ---6 winding=%d foundSum=", __FUNCTION__, winding);
+            if (foundSum == SK_MinS32) {
+                SkDebugf("?");
+            } else {
+                SkDebugf("%d", foundSum);
+            }
+            SkDebugf(" foundMax=");
+            if (foundMax == SK_MinS32) {
+                SkDebugf("?");
+            } else {
+                SkDebugf("%d", foundMax);
+            }
+            SkDebugf("\n");
+     #endif
+            winding = foundSum;
+        }
+    #if DEBUG_WINDING
+        SkDebugf("%s spanWinding=%d flipped=%d\n", __FUNCTION__, spanWinding, flipped);
+    #endif
+        return nextSegment;
+    }
+
+    Segment* findNextXor(int& nextStart, int& nextEnd, bool& unsortable) {
+        const int startIndex = nextStart;
+        const int endIndex = nextEnd;
+        SkASSERT(startIndex != endIndex);
+        int count = fTs.count();
+        SkASSERT(startIndex < endIndex ? startIndex < count - 1
+                : startIndex > 0);
+        int step = SkSign32(endIndex - startIndex);
+    #if PRECISE_T_SORT
+        int end = nextExactSpan(startIndex, step);
+    #else
+        int end = nextSpan(startIndex, step);
+    #endif
+        SkASSERT(end >= 0);
+        Span* endSpan = &fTs[end];
+        Segment* other;
+        markDone(SkMin32(startIndex, endIndex), 1);
+        if (isSimple(end)) {
+    #if DEBUG_WINDING
+            SkDebugf("%s simple\n", __FUNCTION__);
+    #endif
+            other = endSpan->fOther;
+            nextStart = endSpan->fOtherIndex;
+            double startT = other->fTs[nextStart].fT;
+            SkDEBUGCODE(bool firstLoop = true;)
+            if ((approximately_less_than_zero(startT) && step < 0)
+                    || (approximately_greater_than_one(startT) && step > 0)) {
+                step = -step;
+                SkDEBUGCODE(firstLoop = false;)
+            }
+            do {
+                nextEnd = nextStart;
+                do {
+                    nextEnd += step;
+                }
+    #if PRECISE_T_SORT
+                 while (precisely_zero(startT - other->fTs[nextEnd].fT));
+    #else
+                 while (approximately_zero(startT - other->fTs[nextEnd].fT));
+    #endif
+                if (other->fTs[SkMin32(nextStart, nextEnd)].fWindValue) {
+                    break;
+                }
+ #ifdef SK_DEBUG
+                SkASSERT(firstLoop);
+ #endif
+                SkDEBUGCODE(firstLoop = false;)
+                step = -step;
+            } while (true);
+            SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
+            return other;
+        }
+        SkTDArray<Angle> angles;
+        SkASSERT(startIndex - endIndex != 0);
+        SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
+        addTwoAngles(startIndex, end, angles);
+        buildAngles(end, angles);
+        SkTDArray<Angle*> sorted;
+        bool sortable = SortAngles(angles, sorted);
+        int angleCount = angles.count();
+        int firstIndex = findStartingEdge(sorted, startIndex, end);
+        SkASSERT(firstIndex >= 0);
+    #if DEBUG_SORT
+        debugShowSort(__FUNCTION__, sorted, firstIndex, 0);
+    #endif
+        if (!sortable) {
+            unsortable = true;
+            return NULL;
+        }
+        SkASSERT(sorted[firstIndex]->segment() == this);
+        int nextIndex = firstIndex + 1;
+        int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
+        const Angle* nextAngle;
+        Segment* nextSegment;
+        do {
+            if (nextIndex == angleCount) {
+                nextIndex = 0;
+            }
+            nextAngle = sorted[nextIndex];
+            nextSegment = nextAngle->segment();
+            if (!nextSegment->done(*nextAngle)) {
+                break;
+            }
+            if (++nextIndex == lastIndex) {
+                return NULL;
+            }
+        } while (true);
+        nextStart = nextAngle->start();
+        nextEnd = nextAngle->end();
+        return nextSegment;
+    }
+
+    int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
+        int angleCount = sorted.count();
+        int firstIndex = -1;
+        for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
+            const Angle* angle = sorted[angleIndex];
+            if (angle->segment() == this && angle->start() == end &&
+                    angle->end() == start) {
+                firstIndex = angleIndex;
+                break;
+            }
+        }
+        return firstIndex;
+    }
+
+    // FIXME: this is tricky code; needs its own unit test
+    void findTooCloseToCall(bool isXor) {
+        int count = fTs.count();
+        if (count < 3) { // require t=0, x, 1 at minimum
+            return;
+        }
+        int matchIndex = 0;
+        int moCount;
+        Span* match;
+        Segment* mOther;
+        do {
+            match = &fTs[matchIndex];
+            mOther = match->fOther;
+            // FIXME: allow quads, cubics to be near coincident?
+            if (mOther->fVerb == SkPath::kLine_Verb) {
+                moCount = mOther->fTs.count();
+                if (moCount >= 3) {
+                    break;
+                }
+            }
+            if (++matchIndex >= count) {
+                return;
+            }
+        } while (true); // require t=0, x, 1 at minimum
+        // OPTIMIZATION: defer matchPt until qualifying toCount is found?
+        const SkPoint* matchPt = &xyAtT(match);
+        // look for a pair of nearby T values that map to the same (x,y) value
+        // if found, see if the pair of other segments share a common point. If
+        // so, the span from here to there is coincident.
+        for (int index = matchIndex + 1; index < count; ++index) {
+            Span* test = &fTs[index];
+            if (test->fDone) {
+                continue;
+            }
+            Segment* tOther = test->fOther;
+            if (tOther->fVerb != SkPath::kLine_Verb) {
+                continue; // FIXME: allow quads, cubics to be near coincident?
+            }
+            int toCount = tOther->fTs.count();
+            if (toCount < 3) { // require t=0, x, 1 at minimum
+                continue;
+            }
+            const SkPoint* testPt = &xyAtT(test);
+            if (*matchPt != *testPt) {
+                matchIndex = index;
+                moCount = toCount;
+                match = test;
+                mOther = tOther;
+                matchPt = testPt;
+                continue;
+            }
+            int moStart = -1;
+            int moEnd = -1;
+            double moStartT, moEndT;
+            for (int moIndex = 0; moIndex < moCount; ++moIndex) {
+                Span& moSpan = mOther->fTs[moIndex];
+                if (moSpan.fDone) {
+                    continue;
+                }
+                if (moSpan.fOther == this) {
+                    if (moSpan.fOtherT == match->fT) {
+                        moStart = moIndex;
+                        moStartT = moSpan.fT;
+                    }
+                    continue;
+                }
+                if (moSpan.fOther == tOther) {
+                    if (tOther->fTs[moSpan.fOtherIndex].fWindValue == 0) {
+                        moStart = -1;
+                        break;
+                    }
+                    SkASSERT(moEnd == -1);
+                    moEnd = moIndex;
+                    moEndT = moSpan.fT;
+                }
+            }
+            if (moStart < 0 || moEnd < 0) {
+                continue;
+            }
+            // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
+            if (approximately_equal(moStartT, moEndT)) {
+                continue;
+            }
+            int toStart = -1;
+            int toEnd = -1;
+            double toStartT, toEndT;
+            for (int toIndex = 0; toIndex < toCount; ++toIndex) {
+                Span& toSpan = tOther->fTs[toIndex];
+                if (toSpan.fDone) {
+                    continue;
+                }
+                if (toSpan.fOther == this) {
+                    if (toSpan.fOtherT == test->fT) {
+                        toStart = toIndex;
+                        toStartT = toSpan.fT;
+                    }
+                    continue;
+                }
+                if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
+                    if (mOther->fTs[toSpan.fOtherIndex].fWindValue == 0) {
+                        moStart = -1;
+                        break;
+                    }
+                    SkASSERT(toEnd == -1);
+                    toEnd = toIndex;
+                    toEndT = toSpan.fT;
+                }
+            }
+            // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
+            if (toStart <= 0 || toEnd <= 0) {
+                continue;
+            }
+            if (approximately_equal(toStartT, toEndT)) {
+                continue;
+            }
+            // test to see if the segment between there and here is linear
+            if (!mOther->isLinear(moStart, moEnd)
+                    || !tOther->isLinear(toStart, toEnd)) {
+                continue;
+            }
+            bool flipped = (moStart - moEnd) * (toStart - toEnd) < 1;
+            if (flipped) {
+                mOther->addTCancel(moStartT, moEndT, *tOther, toEndT, toStartT);
+            } else {
+                // FIXME: this is bogus for multiple ops
+                // the xorMask needs to be accumulated from the union of the two
+                // edges -- which means that the segment must have its own copy of the mask
+                mOther->addTCoincident(isXor, moStartT, moEndT, *tOther, toStartT, toEndT);
+            }
+        }
+    }
+
+ //   start here;
+    // either: 
+    // a) mark spans with either end unsortable as done, or
+    // b) rewrite findTop / findTopSegment / findTopContour to iterate further
+    //    when encountering an unsortable span
+
+    // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
+    // and use more concise logic like the old edge walker code?
+    // FIXME: this needs to deal with coincident edges
+    Segment* findTop(int& tIndex, int& endIndex) {
+        // iterate through T intersections and return topmost
+        // topmost tangent from y-min to first pt is closer to horizontal
+        SkASSERT(!done());
+        int firstT;
+        int lastT;
+        SkPoint topPt;
+        topPt.fY = SK_ScalarMax;
+        int count = fTs.count();
+        // see if either end is not done since we want smaller Y of the pair
+        bool lastDone = true;
+        bool lastUnsortableEnd;
+        for (int index = 0; index < count; ++index) {
+            const Span& span = fTs[index];
+            if ((!span.fDone && !span.fUnsortableStart) || (!lastDone && !lastUnsortableEnd)) {
+                const SkPoint& intercept = xyAtT(&span);
+                if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
+                        && topPt.fX > intercept.fX)) {
+                    topPt = intercept;
+                    firstT = lastT = index;
+                } else if (topPt == intercept) {
+                    lastT = index;
+                }
+            }
+            lastDone = span.fDone;
+            lastUnsortableEnd = span.fUnsortableEnd;
+        }
+        // sort the edges to find the leftmost
+        int step = 1;
+        int end = nextSpan(firstT, step);
+        if (end == -1) {
+            step = -1;
+            end = nextSpan(firstT, step);
+            SkASSERT(end != -1);
+        }
+        // if the topmost T is not on end, or is three-way or more, find left
+        // look for left-ness from tLeft to firstT (matching y of other)
+        SkTDArray<Angle> angles;
+        SkASSERT(firstT - end != 0);
+        addTwoAngles(end, firstT, angles);
+        buildAngles(firstT, angles);
+        SkTDArray<Angle*> sorted;
+        (void) SortAngles(angles, sorted);
+    #if DEBUG_SORT
+        sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
+    #endif
+        // skip edges that have already been processed
+        firstT = -1;
+        Segment* leftSegment;
+        do {
+            const Angle* angle = sorted[++firstT];
+            if (angle->unsortable()) {
+                // FIXME: if all angles are unsortable, find next topmost
+                SkASSERT(firstT < angles.count() - 1);
+                continue;
+            }
+            leftSegment = angle->segment();
+            tIndex = angle->end();
+            endIndex = angle->start();
+        } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
+        return leftSegment;
+    }
+
+    // FIXME: not crazy about this
+    // when the intersections are performed, the other index is into an
+    // incomplete array. as the array grows, the indices become incorrect
+    // while the following fixes the indices up again, it isn't smart about
+    // skipping segments whose indices are already correct
+    // assuming we leave the code that wrote the index in the first place
+    void fixOtherTIndex() {
+        int iCount = fTs.count();
+        for (int i = 0; i < iCount; ++i) {
+            Span& iSpan = fTs[i];
+            double oT = iSpan.fOtherT;
+            Segment* other = iSpan.fOther;
+            int oCount = other->fTs.count();
+            for (int o = 0; o < oCount; ++o) {
+                Span& oSpan = other->fTs[o];
+                if (oT == oSpan.fT && this == oSpan.fOther) {
+                    iSpan.fOtherIndex = o;
+                    break;
+                }
+            }
+        }
+    }
+
+    // OPTIMIZATION: uses tail recursion. Unwise?
+    Span* innerChaseDone(int index, int step, int winding) {
+        int end = nextSpan(index, step);
+        SkASSERT(end >= 0);
+        if (multipleSpans(end)) {
+            return &fTs[end];
+        }
+        const Span& endSpan = fTs[end];
+        Segment* other = endSpan.fOther;
+        index = endSpan.fOtherIndex;
+        int otherEnd = other->nextSpan(index, step);
+        Span* last = other->innerChaseDone(index, step, winding);
+        other->markDone(SkMin32(index, otherEnd), winding);
+        return last;
+    }
+
+    Span* innerChaseWinding(int index, int step, int winding) {
+        int end = nextSpan(index, step);
+        SkASSERT(end >= 0);
+        if (multipleSpans(end)) {
+            return &fTs[end];
+        }
+        const Span& endSpan = fTs[end];
+        Segment* other = endSpan.fOther;
+        index = endSpan.fOtherIndex;
+        int otherEnd = other->nextSpan(index, step);
+        int min = SkMin32(index, otherEnd);
+        if (other->fTs[min].fWindSum != SK_MinS32) {
+            SkASSERT(other->fTs[min].fWindSum == winding);
+            return NULL;
+        }
+        Span* last = other->innerChaseWinding(index, step, winding);
+        other->markWinding(min, winding);
+        return last;
+    }
+
+    void init(const SkPoint pts[], SkPath::Verb verb, bool operand) {
+        fDoneSpans = 0;
+        fOperand = operand;
+        fPts = pts;
+        fVerb = verb;
+    }
+
+    bool intersected() const {
+        return fTs.count() > 0;
+    }
+
+    bool isConnected(int startIndex, int endIndex) const {
+        return fTs[startIndex].fWindSum != SK_MinS32
+                || fTs[endIndex].fWindSum != SK_MinS32;
+    }
+
+    bool isHorizontal() const {
+        return fBounds.fTop == fBounds.fBottom;
+    }
+
+    bool isLinear(int start, int end) const {
+        if (fVerb == SkPath::kLine_Verb) {
+            return true;
+        }
+        if (fVerb == SkPath::kQuad_Verb) {
+            SkPoint qPart[3];
+            QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
+            return QuadIsLinear(qPart);
+        } else {
+            SkASSERT(fVerb == SkPath::kCubic_Verb);
+            SkPoint cPart[4];
+            CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
+            return CubicIsLinear(cPart);
+        }
+    }
+
+    // OPTIMIZE: successive calls could start were the last leaves off
+    // or calls could specialize to walk forwards or backwards
+    bool isMissing(double startT) const {
+        size_t tCount = fTs.count();
+        for (size_t index = 0; index < tCount; ++index) {
+            if (approximately_zero(startT - fTs[index].fT)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    bool isSimple(int end) const {
+        int count = fTs.count();
+        if (count == 2) {
+            return true;
+        }
+        double t = fTs[end].fT;
+        if (approximately_less_than_zero(t)) {
+            return !approximately_less_than_zero(fTs[1].fT);
+        }
+        if (approximately_greater_than_one(t)) {
+            return !approximately_greater_than_one(fTs[count - 2].fT);
+        }
+        return false;
+    }
+
+    bool isVertical() const {
+        return fBounds.fLeft == fBounds.fRight;
+    }
+
+    SkScalar leftMost(int start, int end) const {
+        return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
+    }
+
+    // this span is excluded by the winding rule -- chase the ends
+    // as long as they are unambiguous to mark connections as done
+    // and give them the same winding value
+    Span* markAndChaseDone(const Angle* angle, int winding) {
+        int index = angle->start();
+        int endIndex = angle->end();
+        int step = SkSign32(endIndex - index);
+        Span* last = innerChaseDone(index, step, winding);
+        markDone(SkMin32(index, endIndex), winding);
+        return last;
+    }
+
+    Span* markAndChaseWinding(const Angle* angle, int winding) {
+        int index = angle->start();
+        int endIndex = angle->end();
+        int min = SkMin32(index, endIndex);
+        int step = SkSign32(endIndex - index);
+        Span* last = innerChaseWinding(index, step, winding);
+        markWinding(min, winding);
+        return last;
+    }
+
+    // FIXME: this should also mark spans with equal (x,y)
+    // This may be called when the segment is already marked done. While this
+    // wastes time, it shouldn't do any more than spin through the T spans.
+    // OPTIMIZATION: abort on first done found (assuming that this code is
+    // always called to mark segments done).
+    void markDone(int index, int winding) {
+      //  SkASSERT(!done());
+        SkASSERT(winding);
+        double referenceT = fTs[index].fT;
+        int lesser = index;
+    #if PRECISE_T_SORT
+        while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+            markOneDone(__FUNCTION__, lesser, winding);
+        }
+        do {
+            markOneDone(__FUNCTION__, index, winding);
+        } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+    #else
+        while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
+            markOneDone(__FUNCTION__, lesser, winding);
+        }
+        do {
+            markOneDone(__FUNCTION__, index, winding);
+        } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
+    #endif
+    }
+
+    void markOneDone(const char* funName, int tIndex, int winding) {
+        Span* span = markOneWinding(funName, tIndex, winding);
+        if (!span) {
+            return;
+        }
+        span->fDone = true;
+        fDoneSpans++;
+    }
+
+    Span* markOneWinding(const char* funName, int tIndex, int winding) {
+        Span& span = fTs[tIndex];
+        if (span.fDone) {
+            return NULL;
+        }
+    #if DEBUG_MARK_DONE
+        debugShowNewWinding(funName, span, winding);
+    #endif
+        SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
+   #ifdef SK_DEBUG
+        SkASSERT(abs(winding) <= gDebugMaxWindSum);
+   #endif
+        span.fWindSum = winding;
+        return &span;
+    }
+
+    void markWinding(int index, int winding) {
+    //    SkASSERT(!done());
+        SkASSERT(winding);
+        double referenceT = fTs[index].fT;
+        int lesser = index;
+    #if PRECISE_T_SORT
+        while (--lesser >= 0 && precisely_negative(referenceT - fTs[lesser].fT)) {
+            markOneWinding(__FUNCTION__, lesser, winding);
+        }
+        do {
+            markOneWinding(__FUNCTION__, index, winding);
+       } while (++index < fTs.count() && precisely_negative(fTs[index].fT - referenceT));
+    #else
+        while (--lesser >= 0 && approximately_negative(referenceT - fTs[lesser].fT)) {
+            markOneWinding(__FUNCTION__, lesser, winding);
+        }
+        do {
+            markOneWinding(__FUNCTION__, index, winding);
+       } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
+    #endif
+    }
+
+    void matchWindingValue(int tIndex, double t, bool borrowWind) {
+        int nextDoorWind = SK_MaxS32;
+        if (tIndex > 0) {
+            const Span& below = fTs[tIndex - 1];
+            if (approximately_negative(t - below.fT)) {
+                nextDoorWind = below.fWindValue;
+            }
+        }
+        if (nextDoorWind == SK_MaxS32 && tIndex + 1 < fTs.count()) {
+            const Span& above = fTs[tIndex + 1];
+            if (approximately_negative(above.fT - t)) {
+                nextDoorWind = above.fWindValue;
+            }
+        }
+        if (nextDoorWind == SK_MaxS32 && borrowWind && tIndex > 0 && t < 1) {
+            const Span& below = fTs[tIndex - 1];
+            nextDoorWind = below.fWindValue;
+        }
+        if (nextDoorWind != SK_MaxS32) {
+            Span& newSpan = fTs[tIndex];
+            newSpan.fWindValue = nextDoorWind;
+            if (!nextDoorWind) {
+                newSpan.fDone = true;
+                ++fDoneSpans;
+            }
+        }
+    }
+
+    // return span if when chasing, two or more radiating spans are not done
+    // OPTIMIZATION: ? multiple spans is detected when there is only one valid
+    // candidate and the remaining spans have windValue == 0 (canceled by
+    // coincidence). The coincident edges could either be removed altogether,
+    // or this code could be more complicated in detecting this case. Worth it?
+    bool multipleSpans(int end) const {
+        return end > 0 && end < fTs.count() - 1;
+    }
+
+    // This has callers for two different situations: one establishes the end
+    // of the current span, and one establishes the beginning of the next span
+    // (thus the name). When this is looking for the end of the current span,
+    // coincidence is found when the beginning Ts contain -step and the end
+    // contains step. When it is looking for the beginning of the next, the
+    // first Ts found can be ignored and the last Ts should contain -step.
+    // OPTIMIZATION: probably should split into two functions
+    int nextSpan(int from, int step) const {
+        const Span& fromSpan = fTs[from];
+        int count = fTs.count();
+        int to = from;
+        while (step > 0 ? ++to < count : --to >= 0) {
+            const Span& span = fTs[to];
+            if (approximately_zero(span.fT - fromSpan.fT)) {
+                continue;
+            }
+            return to;
+        }
+        return -1;
+    }
+
+#if PRECISE_T_SORT
+    // FIXME
+    // this returns at any difference in T, vs. a preset minimum. It may be
+    // that all callers to nextSpan should use this instead.
+    int nextExactSpan(int from, int step) const {
+        const Span& fromSpan = fTs[from];
+        int count = fTs.count();
+        int to = from;
+        while (step > 0 ? ++to < count : --to >= 0) {
+            const Span& span = fTs[to];
+            if (precisely_zero(span.fT - fromSpan.fT)) {
+                continue;
+            }
+            return to;
+        }
+        return -1;
+    }
+#endif
+
+    bool operand() const {
+        return fOperand;
+    }
+
+    int oppSign(int startIndex, int endIndex) const {
+        int result = startIndex < endIndex ? -fTs[startIndex].fWindValueOpp :
+                fTs[endIndex].fWindValueOpp;
+#if DEBUG_WIND_BUMP
+        SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
+#endif
+        return result;
+    }
+
+    const SkPoint* pts() const {
+        return fPts;
+    }
+
+    void reset() {
+        init(NULL, (SkPath::Verb) -1, false);
+        fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
+        fTs.reset();
+    }
+
+    static bool SortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
+        int angleCount = angles.count();
+        int angleIndex;
+        angleList.setReserve(angleCount);
+        for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
+            *angleList.append() = &angles[angleIndex];
+        }
+        QSort<Angle>(angleList.begin(), angleList.end() - 1);
+        bool result = true;
+        for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
+            Angle& angle = angles[angleIndex];
+            if (angle.unsortable()) {
+                // so that it is available for early exclusion in findTop and others
+                const SkTDArray<Span>* spans = angle.spans();
+                Span* span = const_cast<Span*>(&(*spans)[angle.start()]);
+                if (angle.start() < angle.end()) {
+                    span->fUnsortableStart = true;
+                } else {
+                    --span;
+                    span->fUnsortableEnd = true;
+                }
+                result = false;
+            }
+        }
+        return result;
+    }
+
+    // OPTIMIZATION: mark as debugging only if used solely by tests
+    const Span& span(int tIndex) const {
+        return fTs[tIndex];
+    }
+
+    int spanSign(const Angle* angle) const {
+        SkASSERT(angle->segment() == this);
+        return spanSign(angle->start(), angle->end());
+    }
+
+    int spanSign(int startIndex, int endIndex) const {
+        int result = startIndex < endIndex ? -fTs[startIndex].fWindValue :
+                fTs[endIndex].fWindValue;
+#if DEBUG_WIND_BUMP
+        SkDebugf("%s spanSign=%d\n", __FUNCTION__, result);
+#endif
+        return result;
+    }
+
+    // OPTIMIZATION: mark as debugging only if used solely by tests
+    double t(int tIndex) const {
+        return fTs[tIndex].fT;
+    }
+
+    static void TrackOutside(SkTDArray<double>& outsideTs, double end,
+            double start) {
+        int outCount = outsideTs.count();
+        if (outCount == 0 || !approximately_negative(end - outsideTs[outCount - 2])) {
+            *outsideTs.append() = end;
+            *outsideTs.append() = start;
+        }
+    }
+
+    void undoneSpan(int& start, int& end) {
+        size_t tCount = fTs.count();
+        size_t index;
+        for (index = 0; index < tCount; ++index) {
+            if (!fTs[index].fDone) {
+                break;
+            }
+        }
+        SkASSERT(index < tCount - 1);
+        start = index;
+        double startT = fTs[index].fT;
+        while (approximately_negative(fTs[++index].fT - startT))
+            SkASSERT(index < tCount);
+        SkASSERT(index < tCount);
+        end = index;
+    }
+
+    void updatePts(const SkPoint pts[]) {
+        fPts = pts;
+    }
+
+    SkPath::Verb verb() const {
+        return fVerb;
+    }
+
+    int windSum(int tIndex) const {
+        return fTs[tIndex].fWindSum;
+    }
+
+    int windSum(const Angle* angle) const {
+        int start = angle->start();
+        int end = angle->end();
+        int index = SkMin32(start, end);
+        return windSum(index);
+    }
+
+    int windValue(int tIndex) const {
+        return fTs[tIndex].fWindValue;
+    }
+
+    int windValue(const Angle* angle) const {
+        int start = angle->start();
+        int end = angle->end();
+        int index = SkMin32(start, end);
+        return windValue(index);
+    }
+
+    SkScalar xAtT(const Span* span) const {
+        return xyAtT(span).fX;
+    }
+
+    const SkPoint& xyAtT(int index) const {
+        return xyAtT(&fTs[index]);
+    }
+
+    const SkPoint& xyAtT(const Span* span) const {
+        if (SkScalarIsNaN(span->fPt.fX)) {
+            if (span->fT == 0) {
+                span->fPt = fPts[0];
+            } else if (span->fT == 1) {
+                span->fPt = fPts[fVerb];
+            } else {
+                (*SegmentXYAtT[fVerb])(fPts, span->fT, &span->fPt);
+            }
+        }
+        return span->fPt;
+    }
+
+    SkScalar yAtT(int index) const {
+        return yAtT(&fTs[index]);
+    }
+
+    SkScalar yAtT(const Span* span) const {
+        return xyAtT(span).fY;
+    }
+
+#if DEBUG_DUMP
+    void dump() const {
+        const char className[] = "Segment";
+        const int tab = 4;
+        for (int i = 0; i < fTs.count(); ++i) {
+            SkPoint out;
+            (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
+            SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
+                    " otherT=%1.9g windSum=%d\n",
+                    tab + sizeof(className), className, fID,
+                    kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
+                    fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
+        }
+        SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
+                tab + sizeof(className), className, fID,
+                fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
+    }
+#endif
+
+#if DEBUG_CONCIDENT
+    // assert if pair has not already been added
+     void debugAddTPair(double t, const Segment& other, double otherT) const {
+        for (int i = 0; i < fTs.count(); ++i) {
+            if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
+                return;
+            }
+        }
+        SkASSERT(0);
+     }
+#endif
+
+#if DEBUG_DUMP
+    int debugID() const {
+        return fID;
+    }
+#endif
+
+#if DEBUG_WINDING
+    void debugShowSums() const {
+        SkDebugf("%s id=%d (%1.9g,%1.9g %1.9g,%1.9g)", __FUNCTION__, fID,
+            fPts[0].fX, fPts[0].fY, fPts[fVerb].fX, fPts[fVerb].fY);
+        for (int i = 0; i < fTs.count(); ++i) {
+            const Span& span = fTs[i];
+            SkDebugf(" [t=%1.3g %1.9g,%1.9g w=", span.fT, xAtT(&span), yAtT(&span));
+            if (span.fWindSum == SK_MinS32) {
+                SkDebugf("?");
+            } else {
+                SkDebugf("%d", span.fWindSum);
+            }
+            SkDebugf("]");
+        }
+        SkDebugf("\n");
+    }
+#endif
+
+#if DEBUG_CONCIDENT
+    void debugShowTs() const {
+        SkDebugf("%s id=%d", __FUNCTION__, fID);
+        for (int i = 0; i < fTs.count(); ++i) {
+            SkDebugf(" [o=%d t=%1.3g %1.9g,%1.9g w=%d]", fTs[i].fOther->fID,
+                    fTs[i].fT, xAtT(&fTs[i]), yAtT(&fTs[i]), fTs[i].fWindValue);
+        }
+        SkDebugf("\n");
+    }
+#endif
+
+#if DEBUG_ACTIVE_SPANS
+    void debugShowActiveSpans() const {
+        if (done()) {
+            return;
+        }
+        for (int i = 0; i < fTs.count(); ++i) {
+            if (fTs[i].fDone) {
+                continue;
+            }
+            SkDebugf("%s id=%d", __FUNCTION__, fID);
+            SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
+            for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
+                SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
+            }
+            const Span* span = &fTs[i];
+            SkDebugf(") t=%1.9g (%1.9g,%1.9g)", fTs[i].fT,
+                     xAtT(span), yAtT(span));
+            const Segment* other = fTs[i].fOther;
+            SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
+                    other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
+            if (fTs[i].fWindSum == SK_MinS32) {
+                SkDebugf("?");
+            } else {
+                SkDebugf("%d", fTs[i].fWindSum);
+            }
+            SkDebugf(" windValue=%d\n", fTs[i].fWindValue);
+        }
+    }
+
+    // This isn't useful yet -- but leaving it in for now in case i think of something
+    // to use it for
+    void validateActiveSpans() const {
+        if (done()) {
+            return;
+        }
+        int tCount = fTs.count();
+        for (int index = 0; index < tCount; ++index) {
+            if (fTs[index].fDone) {
+                continue;
+            }
+            // count number of connections which are not done
+            int first = index;
+            double baseT = fTs[index].fT;
+            while (first > 0 && approximately_equal(fTs[first - 1].fT, baseT)) {
+                --first;
+            }
+            int last = index;
+            while (last < tCount - 1 && approximately_equal(fTs[last + 1].fT, baseT)) {
+                ++last;
+            }
+            int connections = 0;
+            connections += first > 0 && !fTs[first - 1].fDone;
+            for (int test = first; test <= last; ++test) {
+                connections += !fTs[test].fDone;
+                const Segment* other = fTs[test].fOther;
+                int oIndex = fTs[test].fOtherIndex;
+                connections += !other->fTs[oIndex].fDone;
+                connections += oIndex > 0 && !other->fTs[oIndex - 1].fDone;
+            }
+      //      SkASSERT(!(connections & 1));
+        }
+    }
+#endif
+
+#if DEBUG_MARK_DONE
+    void debugShowNewWinding(const char* fun, const Span& span, int winding) {
+        const SkPoint& pt = xyAtT(&span);
+        SkDebugf("%s id=%d", fun, fID);
+        SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
+        for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
+            SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
+        }
+        SkDebugf(") t=%1.9g (%1.9g,%1.9g) newWindSum=%d windSum=",
+                span.fT, pt.fX, pt.fY, winding);
+        if (span.fWindSum == SK_MinS32) {
+            SkDebugf("?");
+        } else {
+            SkDebugf("%d", span.fWindSum);
+        }
+        SkDebugf(" windValue=%d\n", span.fWindValue);
+    }
+#endif
+
+#if DEBUG_SORT
+    void debugShowSort(const char* fun, const SkTDArray<Angle*>& angles, int first,
+            const int contourWinding) const {
+        SkASSERT(angles[first]->segment() == this);
+        SkASSERT(angles.count() > 1);
+        int lastSum = contourWinding;
+        int windSum = lastSum - spanSign(angles[first]);
+        SkDebugf("%s %s contourWinding=%d sign=%d\n", fun, __FUNCTION__,
+                contourWinding, spanSign(angles[first]));
+        int index = first;
+        bool firstTime = true;
+        do {
+            const Angle& angle = *angles[index];
+            const Segment& segment = *angle.segment();
+            int start = angle.start();
+            int end = angle.end();
+            const Span& sSpan = segment.fTs[start];
+            const Span& eSpan = segment.fTs[end];
+            const Span& mSpan = segment.fTs[SkMin32(start, end)];
+            if (!firstTime) {
+                lastSum = windSum;
+                windSum -= segment.spanSign(&angle);
+            }
+            SkDebugf("%s [%d] %s id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)"
+                    " sign=%d windValue=%d winding: %d->%d (max=%d) done=%d\n",
+                    __FUNCTION__, index, angle.unsortable() ? "*** UNSORTABLE ***" : "",
+                    segment.fID, kLVerbStr[segment.fVerb],
+                    start, segment.xAtT(&sSpan),
+                    segment.yAtT(&sSpan), end, segment.xAtT(&eSpan),
+                    segment.yAtT(&eSpan), angle.sign(), mSpan.fWindValue,
+                    lastSum, windSum, useInnerWinding(lastSum, windSum)
+                    ? windSum : lastSum, mSpan.fDone);
+#if false && DEBUG_ANGLE
+            angle.debugShow(segment.xyAtT(&sSpan));
+#endif
+            ++index;
+            if (index == angles.count()) {
+                index = 0;
+            }
+            if (firstTime) {
+                firstTime = false;
+            }
+        } while (index != first);
+    }
+#endif
+
+#if DEBUG_WINDING
+    bool debugVerifyWinding(int start, int end, int winding) const {
+        const Span& span = fTs[SkMin32(start, end)];
+        int spanWinding = span.fWindSum;
+        if (spanWinding == SK_MinS32) {
+            return true;
+        }
+        int spanSign = SkSign32(start - end);
+        int signedVal = spanSign * span.fWindValue;
+        if (signedVal < 0) {
+            spanWinding -= signedVal;
+        }
+        return span.fWindSum == winding;
+    }
+#endif
+
+private:
+    const SkPoint* fPts;
+    SkPath::Verb fVerb;
+    Bounds fBounds;
+    SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
+    int fDoneSpans; // quick check that segment is finished
+    bool fOperand;
+#if DEBUG_DUMP
+    int fID;
+#endif
+};
+
+class Contour;
+
+struct Coincidence {
+    Contour* fContours[2];
+    int fSegments[2];
+    double fTs[2][2];
+    bool fXor;
+};
+
+class Contour {
+public:
+    Contour() {
+        reset();
+#if DEBUG_DUMP
+        fID = ++gContourID;
+#endif
+    }
+
+    bool operator<(const Contour& rh) const {
+        return fBounds.fTop == rh.fBounds.fTop
+                ? fBounds.fLeft < rh.fBounds.fLeft
+                : fBounds.fTop < rh.fBounds.fTop;
+    }
+
+    void addCoincident(int index, Contour* other, int otherIndex,
+            const Intersections& ts, bool swap) {
+        Coincidence& coincidence = *fCoincidences.append();
+        coincidence.fContours[0] = this;
+        coincidence.fContours[1] = other;
+        coincidence.fSegments[0] = index;
+        coincidence.fSegments[1] = otherIndex;
+        if (fSegments[index].verb() == SkPath::kLine_Verb &&
+                other->fSegments[otherIndex].verb() == SkPath::kLine_Verb) {
+            // FIXME: coincident lines use legacy Ts instead of coincident Ts
+            coincidence.fTs[swap][0] = ts.fT[0][0];
+            coincidence.fTs[swap][1] = ts.fT[0][1];
+            coincidence.fTs[!swap][0] = ts.fT[1][0];
+            coincidence.fTs[!swap][1] = ts.fT[1][1];
+        } else if (fSegments[index].verb() == SkPath::kQuad_Verb &&
+                other->fSegments[otherIndex].verb() == SkPath::kQuad_Verb) {
+            coincidence.fTs[swap][0] = ts.fCoincidentT[0][0];
+            coincidence.fTs[swap][1] = ts.fCoincidentT[0][1];
+            coincidence.fTs[!swap][0] = ts.fCoincidentT[1][0];
+            coincidence.fTs[!swap][1] = ts.fCoincidentT[1][1];
+        }
+        coincidence.fXor = fOperand == other->fOperand ? fXor : true;
+    }
+
+    void addCross(const Contour* crosser) {
+#ifdef DEBUG_CROSS
+        for (int index = 0; index < fCrosses.count(); ++index) {
+            SkASSERT(fCrosses[index] != crosser);
+        }
+#endif
+        *fCrosses.append() = crosser;
+    }
+
+    void addCubic(const SkPoint pts[4]) {
+        fSegments.push_back().addCubic(pts, fOperand);
+        fContainsCurves = true;
+    }
+
+    int addLine(const SkPoint pts[2]) {
+        fSegments.push_back().addLine(pts, fOperand);
+        return fSegments.count();
+    }
+
+    void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
+        fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
+    }
+
+    int addQuad(const SkPoint pts[3]) {
+        fSegments.push_back().addQuad(pts, fOperand);
+        fContainsCurves = true;
+        return fSegments.count();
+    }
+
+    int addT(int segIndex, double newT, Contour* other, int otherIndex) {
+        containsIntercepts();
+        return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
+    }
+
+    const Bounds& bounds() const {
+        return fBounds;
+    }
+
+    void collapseTriangles() {
+        int segmentCount = fSegments.count();
+        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+            fSegments[sIndex].collapseTriangles(fXor);
+        }
+    }
+
+    void complete() {
+        setBounds();
+        fContainsIntercepts = false;
+    }
+
+    void containsIntercepts() {
+        fContainsIntercepts = true;
+    }
+
+    const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
+            int &tIndex, double& hitT) {
+        int segmentCount = fSegments.count();
+        const Segment* bestSegment = NULL;
+        for (int test = 0; test < segmentCount; ++test) {
+            Segment* testSegment = &fSegments[test];
+            const SkRect& bounds = testSegment->bounds();
+            if (bounds.fBottom <= bestY) {
+                continue;
+            }
+            if (bounds.fTop >= basePt.fY) {
+                continue;
+            }
+            if (bounds.fLeft > basePt.fX) {
+                continue;
+            }
+            if (bounds.fRight < basePt.fX) {
+                continue;
+            }
+            if (bounds.fLeft == bounds.fRight) {
+                continue;
+            }
+     #if 0
+            bool leftHalf = bounds.fLeft == basePt.fX;
+            bool rightHalf = bounds.fRight == basePt.fX;
+            if ((leftHalf || rightHalf) && !testSegment->crossedSpanHalves(
+                    basePt, leftHalf, rightHalf)) {
+                continue;
+            }
+     #endif
+            double testHitT;
+            int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
+            if (testT >= 0) {
+                bestSegment = testSegment;
+                tIndex = testT;
+                hitT = testHitT;
+            }
+        }
+        return bestSegment;
+    }
+
+    bool crosses(const Contour* crosser) const {
+        for (int index = 0; index < fCrosses.count(); ++index) {
+            if (fCrosses[index] == crosser) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void findTooCloseToCall() {
+        int segmentCount = fSegments.count();
+        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+            fSegments[sIndex].findTooCloseToCall(fXor);
+        }
+    }
+
+    void fixOtherTIndex() {
+        int segmentCount = fSegments.count();
+        for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
+            fSegments[sIndex].fixOtherTIndex();
+        }
+    }
+
+    void reset() {
+        fSegments.reset();
+        fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
+        fContainsCurves = fContainsIntercepts = false;
+    }
+
+    // FIXME: for binary ops, need to keep both ops winding contributions separately
+    // in edge array
+    void resolveCoincidence() {
+        int count = fCoincidences.count();
+        for (int index = 0; index < count; ++index) {
+            Coincidence& coincidence = fCoincidences[index];
+            Contour* thisContour = coincidence.fContours[0];
+            Contour* otherContour = coincidence.fContours[1];
+            int thisIndex = coincidence.fSegments[0];
+            int otherIndex = coincidence.fSegments[1];
+            Segment& thisOne = thisContour->fSegments[thisIndex];
+            Segment& other = otherContour->fSegments[otherIndex];
+        #if DEBUG_CONCIDENT
+            thisOne.debugShowTs();
+            other.debugShowTs();
+        #endif
+            double startT = coincidence.fTs[0][0];
+            double endT = coincidence.fTs[0][1];
+            bool opposite = false;
+            if (startT > endT) {
+                SkTSwap<double>(startT, endT);
+                opposite ^= true;
+            }
+            SkASSERT(!approximately_negative(endT - startT));
+            double oStartT = coincidence.fTs[1][0];
+            double oEndT = coincidence.fTs[1][1];
+            if (oStartT > oEndT) {
+                SkTSwap<double>(oStartT, oEndT);
+                opposite ^= true;
+            }
+            SkASSERT(!approximately_negative(oEndT - oStartT));
+            if (opposite) {
+                        // make sure startT and endT have t entries
+                SkASSERT(opposite);
+                if (startT > 0 || oEndT < 1
+                        || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
+                    thisOne.addTPair(startT, other, oEndT, true);
+                }
+                if (oStartT > 0 || endT < 1
+                        || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
+                    other.addTPair(oStartT, thisOne, endT, true);
+                }
+                thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
+            } else {
+                if (startT > 0 || oStartT > 0
+                        || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
+                    thisOne.addTPair(startT, other, oStartT, true);
+                }
+                if (endT < 1 || oEndT < 1
+                        || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
+                    other.addTPair(oEndT, thisOne, endT, true);
+                }
+                thisOne.addTCoincident(coincidence.fXor, startT, endT, other, oStartT, oEndT);
+            }
+        #if DEBUG_CONCIDENT
+            thisOne.debugShowTs();
+            other.debugShowTs();
+        #endif
+        }
+    }
+
+    const SkTArray<Segment>& segments() {
+        return fSegments;
+    }
+
+    void setOperand(bool isOp) {
+        fOperand = isOp;
+    }
+
+    void setXor(bool isXor) {
+        fXor = isXor;
+    }
+
+    // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
+    // we need to sort and walk edges in y, but that on the surface opens the
+    // same can of worms as before. But then, this is a rough sort based on
+    // segments' top, and not a true sort, so it could be ameniable to regular
+    // sorting instead of linear searching. Still feel like I'm missing something
+    Segment* topSegment(SkScalar& bestY) {
+        int segmentCount = fSegments.count();
+        SkASSERT(segmentCount > 0);
+        int best = -1;
+        Segment* bestSegment = NULL;
+        while (++best < segmentCount) {
+            Segment* testSegment = &fSegments[best];
+            if (testSegment->done()) {
+                continue;
+            }
+            bestSegment = testSegment;
+            break;
+        }
+        if (!bestSegment) {
+            return NULL;
+        }
+        SkScalar bestTop = bestSegment->activeTop();
+        for (int test = best + 1; test < segmentCount; ++test) {
+            Segment* testSegment = &fSegments[test];
+            if (testSegment->done()) {
+                continue;
+            }
+            if (testSegment->bounds().fTop > bestTop) {
+                continue;
+            }
+            SkScalar testTop = testSegment->activeTop();
+            if (bestTop > testTop) {
+                bestTop = testTop;
+                bestSegment = testSegment;
+            }
+        }
+        bestY = bestTop;
+        return bestSegment;
+    }
+
+    Segment* undoneSegment(int& start, int& end) {
+        int segmentCount = fSegments.count();
+        for (int test = 0; test < segmentCount; ++test) {
+            Segment* testSegment = &fSegments[test];
+            if (testSegment->done()) {
+                continue;
+            }
+            testSegment->undoneSpan(start, end);
+            return testSegment;
+        }
+        return NULL;
+    }
+
+    int updateSegment(int index, const SkPoint* pts) {
+        Segment& segment = fSegments[index];
+        segment.updatePts(pts);
+        return segment.verb() + 1;
+    }
+
+#if DEBUG_TEST
+    SkTArray<Segment>& debugSegments() {
+        return fSegments;
+    }
+#endif
+
+#if DEBUG_DUMP
+    void dump() {
+        int i;
+        const char className[] = "Contour";
+        const int tab = 4;
+        SkDebugf("%s %p (contour=%d)\n", className, this, fID);
+        for (i = 0; i < fSegments.count(); ++i) {
+            SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
+                    className, i);
+            fSegments[i].dump();
+        }
+        SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
+                tab + sizeof(className), className,
+                fBounds.fLeft, fBounds.fTop,
+                fBounds.fRight, fBounds.fBottom);
+        SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
+                className, fContainsIntercepts);
+        SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
+                className, fContainsCurves);
+    }
+#endif
+
+#if DEBUG_ACTIVE_SPANS
+    void debugShowActiveSpans() {
+        for (int index = 0; index < fSegments.count(); ++index) {
+            fSegments[index].debugShowActiveSpans();
+        }
+    }
+
+    void validateActiveSpans() {
+        for (int index = 0; index < fSegments.count(); ++index) {
+            fSegments[index].validateActiveSpans();
+        }
+    }
+#endif
+
+protected:
+    void setBounds() {
+        int count = fSegments.count();
+        if (count == 0) {
+            SkDebugf("%s empty contour\n", __FUNCTION__);
+            SkASSERT(0);
+            // FIXME: delete empty contour?
+            return;
+        }
+        fBounds = fSegments.front().bounds();
+        for (int index = 1; index < count; ++index) {
+            fBounds.add(fSegments[index].bounds());
+        }
+    }
+
+private:
+    SkTArray<Segment> fSegments;
+    SkTDArray<Coincidence> fCoincidences;
+    SkTDArray<const Contour*> fCrosses;
+    Bounds fBounds;
+    bool fContainsIntercepts;
+    bool fContainsCurves;
+    bool fOperand; // true for the second argument to a binary operator
+    bool fXor;
+#if DEBUG_DUMP
+    int fID;
+#endif
+};
+
+class EdgeBuilder {
+public:
+
+EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
+    : fPath(&path)
+    , fContours(contours)
+    , fCurrentContour(NULL)
+    , fOperand(false)
+{
+    fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
+#if DEBUG_DUMP
+    gContourID = 0;
+    gSegmentID = 0;
+#endif
+    fSecondHalf = preFetch();
+}
+
+void addOperand(const SkPath& path) {
+    fPath = &path;
+    fXorMask = (path.getFillType() & 1) ? kEvenOdd_Mask : kWinding_Mask;
+    preFetch();
+}
+
+void finish() {
+    walk();
+    complete();
+    if (fCurrentContour && !fCurrentContour->segments().count()) {
+        fContours.pop_back();
+    }
+    // correct pointers in contours since fReducePts may have moved as it grew
+    int cIndex = 0;
+    int extraCount = fExtra.count();
+    SkASSERT(extraCount == 0 || fExtra[0] == -1);
+    int eIndex = 0;
+    int rIndex = 0;
+    while (++eIndex < extraCount) {
+        int offset = fExtra[eIndex];
+        if (offset < 0) {
+            ++cIndex;
+            continue;
+        }
+        fCurrentContour = &fContours[cIndex];
+        rIndex += fCurrentContour->updateSegment(offset - 1,
+                &fReducePts[rIndex]);
+    }
+    fExtra.reset(); // we're done with this
+}
+
+ShapeOpMask xorMask() const {
+    return fXorMask;
+}
+
+protected:
+
+void complete() {
+    if (fCurrentContour && fCurrentContour->segments().count()) {
+        fCurrentContour->complete();
+        fCurrentContour = NULL;
+    }
+}
+
+// FIXME:remove once we can access path pts directly
+int preFetch() {
+    SkPath::RawIter iter(*fPath); // FIXME: access path directly when allowed
+    SkPoint pts[4];
+    SkPath::Verb verb;
+    do {
+        verb = iter.next(pts);
+        *fPathVerbs.append() = verb;
+        if (verb == SkPath::kMove_Verb) {
+            *fPathPts.append() = pts[0];
+        } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
+            fPathPts.append(verb, &pts[1]);
+        }
+    } while (verb != SkPath::kDone_Verb);
+    return fPathVerbs.count();
+}
+
+void walk() {
+    SkPath::Verb reducedVerb;
+    uint8_t* verbPtr = fPathVerbs.begin();
+    uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
+    const SkPoint* pointsPtr = fPathPts.begin();
+    const SkPoint* finalCurveStart = NULL;
+    const SkPoint* finalCurveEnd = NULL;
+    SkPath::Verb verb;
+    while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                complete();
+                if (!fCurrentContour) {
+                    fCurrentContour = fContours.push_back_n(1);
+                    fCurrentContour->setOperand(fOperand);
+                    fCurrentContour->setXor(fXorMask == kEvenOdd_Mask);
+                    *fExtra.append() = -1; // start new contour
+                }
+                finalCurveEnd = pointsPtr++;
+                continue;
+            case SkPath::kLine_Verb:
+                // skip degenerate points
+                if (pointsPtr[-1].fX != pointsPtr[0].fX
+                        || pointsPtr[-1].fY != pointsPtr[0].fY) {
+                    fCurrentContour->addLine(&pointsPtr[-1]);
+                }
+                break;
+            case SkPath::kQuad_Verb:
+
+                reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
+                if (reducedVerb == 0) {
+                    break; // skip degenerate points
+                }
+                if (reducedVerb == 1) {
+                    *fExtra.append() =
+                            fCurrentContour->addLine(fReducePts.end() - 2);
+                    break;
+                }
+                fCurrentContour->addQuad(&pointsPtr[-1]);
+                break;
+            case SkPath::kCubic_Verb:
+                reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
+                if (reducedVerb == 0) {
+                    break; // skip degenerate points
+                }
+                if (reducedVerb == 1) {
+                    *fExtra.append() =
+                            fCurrentContour->addLine(fReducePts.end() - 2);
+                    break;
+                }
+                if (reducedVerb == 2) {
+                    *fExtra.append() =
+                            fCurrentContour->addQuad(fReducePts.end() - 3);
+                    break;
+                }
+                fCurrentContour->addCubic(&pointsPtr[-1]);
+                break;
+            case SkPath::kClose_Verb:
+                SkASSERT(fCurrentContour);
+                if (finalCurveStart && finalCurveEnd
+                        && *finalCurveStart != *finalCurveEnd) {
+                    *fReducePts.append() = *finalCurveStart;
+                    *fReducePts.append() = *finalCurveEnd;
+                    *fExtra.append() =
+                            fCurrentContour->addLine(fReducePts.end() - 2);
+                }
+                complete();
+                continue;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return;
+        }
+        finalCurveStart = &pointsPtr[verb - 1];
+        pointsPtr += verb;
+        SkASSERT(fCurrentContour);
+        if (verbPtr == endOfFirstHalf) {
+            fOperand = true;
+        }
+    }
+}
+
+private:
+    const SkPath* fPath;
+    SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
+    SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
+    Contour* fCurrentContour;
+    SkTArray<Contour>& fContours;
+    SkTDArray<SkPoint> fReducePts; // segments created on the fly
+    SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
+    ShapeOpMask fXorMask;
+    int fSecondHalf;
+    bool fOperand;
+};
+
+class Work {
+public:
+    enum SegmentType {
+        kHorizontalLine_Segment = -1,
+        kVerticalLine_Segment = 0,
+        kLine_Segment = SkPath::kLine_Verb,
+        kQuad_Segment = SkPath::kQuad_Verb,
+        kCubic_Segment = SkPath::kCubic_Verb,
+    };
+
+    void addCoincident(Work& other, const Intersections& ts, bool swap) {
+        fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
+    }
+
+    // FIXME: does it make sense to write otherIndex now if we're going to
+    // fix it up later?
+    void addOtherT(int index, double otherT, int otherIndex) {
+        fContour->addOtherT(fIndex, index, otherT, otherIndex);
+    }
+
+    // Avoid collapsing t values that are close to the same since
+    // we walk ts to describe consecutive intersections. Since a pair of ts can
+    // be nearly equal, any problems caused by this should be taken care
+    // of later.
+    // On the edge or out of range values are negative; add 2 to get end
+    int addT(double newT, const Work& other) {
+        return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
+    }
+
+    bool advance() {
+        return ++fIndex < fLast;
+    }
+
+    SkScalar bottom() const {
+        return bounds().fBottom;
+    }
+
+    const Bounds& bounds() const {
+        return fContour->segments()[fIndex].bounds();
+    }
+
+    const SkPoint* cubic() const {
+        return fCubic;
+    }
+
+    void init(Contour* contour) {
+        fContour = contour;
+        fIndex = 0;
+        fLast = contour->segments().count();
+    }
+
+    bool isAdjacent(const Work& next) {
+        return fContour == next.fContour && fIndex + 1 == next.fIndex;
+    }
+
+    bool isFirstLast(const Work& next) {
+        return fContour == next.fContour && fIndex == 0
+                && next.fIndex == fLast - 1;
+    }
+
+    SkScalar left() const {
+        return bounds().fLeft;
+    }
+
+    void promoteToCubic() {
+        fCubic[0] = pts()[0];
+        fCubic[2] = pts()[1];
+        fCubic[3] = pts()[2];
+        fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
+        fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
+        fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
+        fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
+    }
+
+    const SkPoint* pts() const {
+        return fContour->segments()[fIndex].pts();
+    }
+
+    SkScalar right() const {
+        return bounds().fRight;
+    }
+
+    ptrdiff_t segmentIndex() const {
+        return fIndex;
+    }
+
+    SegmentType segmentType() const {
+        const Segment& segment = fContour->segments()[fIndex];
+        SegmentType type = (SegmentType) segment.verb();
+        if (type != kLine_Segment) {
+            return type;
+        }
+        if (segment.isHorizontal()) {
+            return kHorizontalLine_Segment;
+        }
+        if (segment.isVertical()) {
+            return kVerticalLine_Segment;
+        }
+        return kLine_Segment;
+    }
+
+    bool startAfter(const Work& after) {
+        fIndex = after.fIndex;
+        return advance();
+    }
+
+    SkScalar top() const {
+        return bounds().fTop;
+    }
+
+    SkPath::Verb verb() const {
+        return fContour->segments()[fIndex].verb();
+    }
+
+    SkScalar x() const {
+        return bounds().fLeft;
+    }
+
+    bool xFlipped() const {
+        return x() != pts()[0].fX;
+    }
+
+    SkScalar y() const {
+        return bounds().fTop;
+    }
+
+    bool yFlipped() const {
+        return y() != pts()[0].fY;
+    }
+
+protected:
+    Contour* fContour;
+    SkPoint fCubic[4];
+    int fIndex;
+    int fLast;
+};
+
+#if DEBUG_ADD_INTERSECTING_TS
+static void debugShowLineIntersection(int pts, const Work& wt,
+        const Work& wn, const double wtTs[2], const double wnTs[2]) {
+    if (!pts) {
+        SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
+                __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
+                wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
+                wn.pts()[1].fX, wn.pts()[1].fY);
+        return;
+    }
+    SkPoint wtOutPt, wnOutPt;
+    LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
+    LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
+    SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
+            __FUNCTION__,
+            wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
+            wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
+    if (pts == 2) {
+        SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
+    }
+    SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
+            wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
+            wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
+    if (pts == 2) {
+        SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
+    }
+    SkDebugf("\n");
+}
+
+static void debugShowQuadIntersection(int pts, const Work& wt,
+        const Work& wn, const double wtTs[2], const double wnTs[2]) {
+    if (!pts) {
+        SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
+                " (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)\n",
+                __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
+                wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
+                wn.pts()[0].fX, wn.pts()[0].fY, wn.pts()[1].fX, wn.pts()[1].fY,
+                wt.pts()[2].fX, wt.pts()[2].fY );
+        return;
+    }
+    SkPoint wtOutPt, wnOutPt;
+    QuadXYAtT(wt.pts(), wtTs[0], &wtOutPt);
+    QuadXYAtT(wn.pts(), wnTs[0], &wnOutPt);
+    SkDebugf("%s wtTs[0]=%1.9g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
+            __FUNCTION__,
+            wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
+            wt.pts()[1].fX, wt.pts()[1].fY, wt.pts()[2].fX, wt.pts()[2].fY,
+            wtOutPt.fX, wtOutPt.fY);
+    if (pts == 2) {
+        SkDebugf(" wtTs[1]=%1.9g", wtTs[1]);
+    }
+    SkDebugf(" wnTs[0]=%g (%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g)",
+            wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
+            wn.pts()[1].fX, wn.pts()[1].fY, wn.pts()[2].fX, wn.pts()[2].fY,
+            wnOutPt.fX, wnOutPt.fY);
+    if (pts == 2) {
+        SkDebugf(" wnTs[1]=%1.9g", wnTs[1]);
+    }
+    SkDebugf("\n");
+}
+#else
+static void debugShowLineIntersection(int , const Work& ,
+        const Work& , const double [2], const double [2]) {
+}
+
+static void debugShowQuadIntersection(int , const Work& ,
+        const Work& , const double [2], const double [2]) {
+}
+#endif
+
+static bool addIntersectTs(Contour* test, Contour* next) {
+
+    if (test != next) {
+        if (test->bounds().fBottom < next->bounds().fTop) {
+            return false;
+        }
+        if (!Bounds::Intersects(test->bounds(), next->bounds())) {
+            return true;
+        }
+    }
+    Work wt;
+    wt.init(test);
+    bool foundCommonContour = test == next;
+    do {
+        Work wn;
+        wn.init(next);
+        if (test == next && !wn.startAfter(wt)) {
+            continue;
+        }
+        do {
+            if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
+                continue;
+            }
+            int pts;
+            Intersections ts;
+            bool swap = false;
+            switch (wt.segmentType()) {
+                case Work::kHorizontalLine_Segment:
+                    swap = true;
+                    switch (wn.segmentType()) {
+                        case Work::kHorizontalLine_Segment:
+                        case Work::kVerticalLine_Segment:
+                        case Work::kLine_Segment: {
+                            pts = HLineIntersect(wn.pts(), wt.left(),
+                                    wt.right(), wt.y(), wt.xFlipped(), ts);
+                            debugShowLineIntersection(pts, wt, wn,
+                                    ts.fT[1], ts.fT[0]);
+                            break;
+                        }
+                        case Work::kQuad_Segment: {
+                            pts = HQuadIntersect(wn.pts(), wt.left(),
+                                    wt.right(), wt.y(), wt.xFlipped(), ts);
+                            break;
+                        }
+                        case Work::kCubic_Segment: {
+                            pts = HCubicIntersect(wn.pts(), wt.left(),
+                                    wt.right(), wt.y(), wt.xFlipped(), ts);
+                            break;
+                        }
+                        default:
+                            SkASSERT(0);
+                    }
+                    break;
+                case Work::kVerticalLine_Segment:
+                    swap = true;
+                    switch (wn.segmentType()) {
+                        case Work::kHorizontalLine_Segment:
+                        case Work::kVerticalLine_Segment:
+                        case Work::kLine_Segment: {
+                            pts = VLineIntersect(wn.pts(), wt.top(),
+                                    wt.bottom(), wt.x(), wt.yFlipped(), ts);
+                            debugShowLineIntersection(pts, wt, wn,
+                                    ts.fT[1], ts.fT[0]);
+                            break;
+                        }
+                        case Work::kQuad_Segment: {
+                            pts = VQuadIntersect(wn.pts(), wt.top(),
+                                    wt.bottom(), wt.x(), wt.yFlipped(), ts);
+                            break;
+                        }
+                        case Work::kCubic_Segment: {
+                            pts = VCubicIntersect(wn.pts(), wt.top(),
+                                    wt.bottom(), wt.x(), wt.yFlipped(), ts);
+                            break;
+                        }
+                        default:
+                            SkASSERT(0);
+                    }
+                    break;
+                case Work::kLine_Segment:
+                    switch (wn.segmentType()) {
+                        case Work::kHorizontalLine_Segment:
+                            pts = HLineIntersect(wt.pts(), wn.left(),
+                                    wn.right(), wn.y(), wn.xFlipped(), ts);
+                            debugShowLineIntersection(pts, wt, wn,
+                                    ts.fT[1], ts.fT[0]);
+                            break;
+                        case Work::kVerticalLine_Segment:
+                            pts = VLineIntersect(wt.pts(), wn.top(),
+                                    wn.bottom(), wn.x(), wn.yFlipped(), ts);
+                            debugShowLineIntersection(pts, wt, wn,
+                                    ts.fT[1], ts.fT[0]);
+                            break;
+                        case Work::kLine_Segment: {
+                            pts = LineIntersect(wt.pts(), wn.pts(), ts);
+                            debugShowLineIntersection(pts, wt, wn,
+                                    ts.fT[1], ts.fT[0]);
+                            break;
+                        }
+                        case Work::kQuad_Segment: {
+                            swap = true;
+                            pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
+                            break;
+                        }
+                        case Work::kCubic_Segment: {
+                            swap = true;
+                            pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
+                            break;
+                        }
+                        default:
+                            SkASSERT(0);
+                    }
+                    break;
+                case Work::kQuad_Segment:
+                    switch (wn.segmentType()) {
+                        case Work::kHorizontalLine_Segment:
+                            pts = HQuadIntersect(wt.pts(), wn.left(),
+                                    wn.right(), wn.y(), wn.xFlipped(), ts);
+                            break;
+                        case Work::kVerticalLine_Segment:
+                            pts = VQuadIntersect(wt.pts(), wn.top(),
+                                    wn.bottom(), wn.x(), wn.yFlipped(), ts);
+                            break;
+                        case Work::kLine_Segment: {
+                            pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
+                            break;
+                        }
+                        case Work::kQuad_Segment: {
+                            pts = QuadIntersect(wt.pts(), wn.pts(), ts);
+                            debugShowQuadIntersection(pts, wt, wn,
+                                    ts.fT[1], ts.fT[0]);
+                            break;
+                        }
+                        case Work::kCubic_Segment: {
+                            wt.promoteToCubic();
+                            pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
+                            break;
+                        }
+                        default:
+                            SkASSERT(0);
+                    }
+                    break;
+                case Work::kCubic_Segment:
+                    switch (wn.segmentType()) {
+                        case Work::kHorizontalLine_Segment:
+                            pts = HCubicIntersect(wt.pts(), wn.left(),
+                                    wn.right(), wn.y(), wn.xFlipped(), ts);
+                            break;
+                        case Work::kVerticalLine_Segment:
+                            pts = VCubicIntersect(wt.pts(), wn.top(),
+                                    wn.bottom(), wn.x(), wn.yFlipped(), ts);
+                            break;
+                        case Work::kLine_Segment: {
+                            pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
+                            break;
+                        }
+                        case Work::kQuad_Segment: {
+                            wn.promoteToCubic();
+                            pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
+                            break;
+                        }
+                        case Work::kCubic_Segment: {
+                            pts = CubicIntersect(wt.pts(), wn.pts(), ts);
+                            break;
+                        }
+                        default:
+                            SkASSERT(0);
+                    }
+                    break;
+                default:
+                    SkASSERT(0);
+            }
+            if (!foundCommonContour && pts > 0) {
+                test->addCross(next);
+                next->addCross(test);
+                foundCommonContour = true;
+            }
+            // in addition to recording T values, record matching segment
+            if (pts == 2) {
+                if (wn.segmentType() <= Work::kLine_Segment
+                        && wt.segmentType() <= Work::kLine_Segment) {
+                    wt.addCoincident(wn, ts, swap);
+                    continue;
+                }
+                if (wn.segmentType() == Work::kQuad_Segment
+                        && wt.segmentType() == Work::kQuad_Segment
+                        && ts.coincidentUsed() == 2) {
+                    wt.addCoincident(wn, ts, swap);
+                    continue;
+                }
+
+            }
+            int pt2 = 0;
+            int pt2inc = 1;
+            if (ts.fFlip) {
+                pt2 = pts - 1;
+                pt2inc = -1;
+            }
+            for (int pt = 0; pt < pts; ++pt) {
+                SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
+                SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
+                int testTAt = wt.addT(ts.fT[swap][pt], wn);
+                int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
+                wt.addOtherT(testTAt, ts.fT[!swap][pt ^ ts.fFlip], nextTAt);
+                wn.addOtherT(nextTAt, ts.fT[swap][pt ^ ts.fFlip], testTAt);
+                pt2 += pt2inc;
+            }
+        } while (wn.advance());
+    } while (wt.advance());
+    return true;
+}
+
+// resolve any coincident pairs found while intersecting, and
+// see if coincidence is formed by clipping non-concident segments
+static void coincidenceCheck(SkTDArray<Contour*>& contourList) {
+    int contourCount = contourList.count();
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        Contour* contour = contourList[cIndex];
+        contour->resolveCoincidence();
+    }
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        Contour* contour = contourList[cIndex];
+        contour->findTooCloseToCall();
+    }
+#if 0
+    // OPTIMIZATION: this check could be folded in with findTooClose -- maybe
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        Contour* contour = contourList[cIndex];
+        contour->collapseTriangles();
+    }
+#endif
+}
+
+// project a ray from the top of the contour up and see if it hits anything
+// note: when we compute line intersections, we keep track of whether
+// two contours touch, so we need only look at contours not touching this one.
+// OPTIMIZATION: sort contourList vertically to avoid linear walk
+static int innerContourCheck(SkTDArray<Contour*>& contourList,
+        const Segment* current, int index, int endIndex) {
+    const SkPoint& basePt = current->xyAtT(endIndex);
+    int contourCount = contourList.count();
+    SkScalar bestY = SK_ScalarMin;
+    const Segment* test = NULL;
+    int tIndex;
+    double tHit;
+ //   bool checkCrosses = true;
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        Contour* contour = contourList[cTest];
+        if (basePt.fY < contour->bounds().fTop) {
+            continue;
+        }
+        if (bestY > contour->bounds().fBottom) {
+            continue;
+        }
+#if 0
+        // even though the contours crossed, if spans cancel through concidence,
+        // the contours may be not have any span links to chase, and the current
+        // segment may be isolated. Detect this by seeing if current has
+        // uninitialized wind sums. If so, project a ray instead of relying on
+        // previously found intersections.
+        if (baseContour == contour) {
+            continue;
+        }
+        if (checkCrosses && baseContour->crosses(contour)) {
+            if (current->isConnected(index, endIndex)) {
+                continue;
+            }
+            checkCrosses = false;
+        }
+#endif
+        const Segment* next = contour->crossedSegment(basePt, bestY, tIndex, tHit);
+        if (next) {
+            test = next;
+        }
+    }
+    if (!test) {
+        return 0;
+    }
+    int winding, windValue;
+    // If the ray hit the end of a span, we need to construct the wheel of
+    // angles to find the span closest to the ray -- even if there are just
+    // two spokes on the wheel.
+    const Angle* angle = NULL;
+    if (approximately_zero(tHit - test->t(tIndex))) {
+        SkTDArray<Angle> angles;
+        int end = test->nextSpan(tIndex, 1);
+        if (end < 0) {
+            end = test->nextSpan(tIndex, -1);
+        }
+        test->addTwoAngles(end, tIndex, angles);
+        SkASSERT(angles.count() > 0);
+        if (angles[0].segment()->yAtT(angles[0].start()) >= basePt.fY) {
+#if DEBUG_SORT
+            SkDebugf("%s early return\n", __FUNCTION__);
+#endif
+            return 0;
+        }
+        test->buildAngles(tIndex, angles);
+        SkTDArray<Angle*> sorted;
+        // OPTIMIZATION: call a sort that, if base point is the leftmost,
+        // returns the first counterclockwise hour before 6 o'clock,
+        // or if the base point is rightmost, returns the first clockwise
+        // hour after 6 o'clock
+        (void) Segment::SortAngles(angles, sorted);
+#if DEBUG_SORT
+        sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
+#endif
+        // walk the sorted angle fan to find the lowest angle
+        // above the base point. Currently, the first angle in the sorted array
+        // is 12 noon or an earlier hour (the next counterclockwise)
+        int count = sorted.count();
+        int left = -1;
+        int mid = -1;
+        int right = -1;
+        bool baseMatches = test->yAtT(tIndex) == basePt.fY;
+        for (int index = 0; index < count; ++index) {
+            angle = sorted[index];
+            if (angle->unsortable()) {
+                continue;
+            }
+            if (baseMatches && angle->isHorizontal()) {
+                continue;
+            }
+            double indexDx = angle->dx();
+            test = angle->segment();
+            if (test->verb() > SkPath::kLine_Verb && approximately_zero(indexDx)) {
+                const SkPoint* pts = test->pts();
+                indexDx = pts[2].fX - pts[1].fX - indexDx;
+            }
+            if (indexDx < 0) {
+                left = index;
+            } else if (indexDx > 0) {
+                right = index;
+                int previous = index - 1;
+                if (previous < 0) {
+                    previous = count - 1;
+                }
+                const Angle* prev = sorted[previous];
+                if (prev->dy() >= 0 && prev->dx() > 0 && angle->dy() < 0) {
+#if DEBUG_SORT
+                    SkDebugf("%s use prev\n", __FUNCTION__);
+#endif
+                    right = previous;
+                }
+                break;
+            } else {
+                mid = index;
+            }
+        }
+        if (left < 0 && right < 0) {
+            left = mid;
+        }
+        SkASSERT(left >= 0 || right >= 0);
+        if (left < 0) {
+            left = right;
+        } else if (left >= 0 && mid >= 0 && right >= 0
+                && sorted[mid]->sign() == sorted[right]->sign()) {
+            left = right;
+        }
+        angle = sorted[left];
+        test = angle->segment();
+        winding = test->windSum(angle);
+        SkASSERT(winding != SK_MinS32);
+        windValue = test->windValue(angle);
+#if DEBUG_WINDING
+        SkDebugf("%s angle winding=%d windValue=%d sign=%d\n", __FUNCTION__, winding,
+                windValue, angle->sign());
+#endif
+    } else {
+        winding = test->windSum(tIndex);
+        SkASSERT(winding != SK_MinS32);
+        windValue = test->windValue(tIndex);
+#if DEBUG_WINDING
+        SkDebugf("%s single winding=%d windValue=%d\n", __FUNCTION__, winding,
+                windValue);
+#endif
+    }
+    // see if a + change in T results in a +/- change in X (compute x'(T))
+    SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
+    if (test->verb() > SkPath::kLine_Verb && approximately_zero(dx)) {
+        const SkPoint* pts = test->pts();
+        dx = pts[2].fX - pts[1].fX - dx;
+    }
+#if DEBUG_WINDING
+    SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
+#endif
+    SkASSERT(dx != 0);
+    if (winding * dx > 0) { // if same signs, result is negative
+        winding += dx > 0 ? -windValue : windValue;
+#if DEBUG_WINDING
+        SkDebugf("%s final winding=%d\n", __FUNCTION__, winding);
+#endif
+    }
+    return winding;
+}
+
+// OPTIMIZATION: not crazy about linear search here to find top active y.
+// seems like we should break down and do the sort, or maybe sort each
+// contours' segments?
+// Once the segment array is built, there's no reason I can think of not to
+// sort it in Y. hmmm
+// FIXME: return the contour found to pass to inner contour check
+static Segment* findTopContour(SkTDArray<Contour*>& contourList) {
+    int contourCount = contourList.count();
+    int cIndex = 0;
+    Segment* topStart;
+    SkScalar bestY = SK_ScalarMax;
+    Contour* contour;
+    do {
+        contour = contourList[cIndex];
+        topStart = contour->topSegment(bestY);
+    } while (!topStart && ++cIndex < contourCount);
+    if (!topStart) {
+        return NULL;
+    }
+    while (++cIndex < contourCount) {
+        contour = contourList[cIndex];
+        if (bestY < contour->bounds().fTop) {
+            continue;
+        }
+        SkScalar testY = SK_ScalarMax;
+        Segment* test = contour->topSegment(testY);
+        if (!test || bestY <= testY) {
+            continue;
+        }
+        topStart = test;
+        bestY = testY;
+    }
+    return topStart;
+}
+
+static Segment* findUndone(SkTDArray<Contour*>& contourList, int& start, int& end) {
+    int contourCount = contourList.count();
+    Segment* result;
+    for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
+        Contour* contour = contourList[cIndex];
+        result = contour->undoneSegment(start, end);
+        if (result) {
+            return result;
+        }
+    }
+    return NULL;
+}
+
+
+
+static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex,
+        int contourWinding) {
+    while (chase.count()) {
+        Span* span = chase[chase.count() - 1];
+        const Span& backPtr = span->fOther->span(span->fOtherIndex);
+        Segment* segment = backPtr.fOther;
+        tIndex = backPtr.fOtherIndex;
+        SkTDArray<Angle> angles;
+        int done = 0;
+        if (segment->activeAngle(tIndex, done, angles)) {
+            Angle* last = angles.end() - 1;
+            tIndex = last->start();
+            endIndex = last->end();
+            return last->segment();
+        }
+        if (done == angles.count()) {
+            chase.pop(&span);
+            continue;
+        }
+        SkTDArray<Angle*> sorted;
+        bool sortable = Segment::SortAngles(angles, sorted);
+#if DEBUG_SORT
+        sorted[0]->segment()->debugShowSort(__FUNCTION__, sorted, 0, 0);
+#endif
+        if (!sortable) {
+            chase.pop(&span);
+            continue;
+        }
+        // find first angle, initialize winding to computed fWindSum
+        int firstIndex = -1;
+        const Angle* angle;
+        int winding;
+        do {
+            angle = sorted[++firstIndex];
+            segment = angle->segment();
+            winding = segment->windSum(angle);
+        } while (winding == SK_MinS32);
+        int spanWinding = segment->spanSign(angle->start(), angle->end());
+    #if DEBUG_WINDING
+        SkDebugf("%s winding=%d spanWinding=%d contourWinding=%d\n",
+                __FUNCTION__, winding, spanWinding, contourWinding);
+    #endif
+        // turn swinding into contourWinding
+        if (spanWinding * winding < 0) {
+            winding += spanWinding;
+        }
+    #if DEBUG_SORT
+        segment->debugShowSort(__FUNCTION__, sorted, firstIndex, winding);
+    #endif
+        // we care about first sign and whether wind sum indicates this
+        // edge is inside or outside. Maybe need to pass span winding
+        // or first winding or something into this function?
+        // advance to first undone angle, then return it and winding
+        // (to set whether edges are active or not)
+        int nextIndex = firstIndex + 1;
+        int angleCount = sorted.count();
+        int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
+        angle = sorted[firstIndex];
+        winding -= angle->segment()->spanSign(angle);
+        do {
+            SkASSERT(nextIndex != firstIndex);
+            if (nextIndex == angleCount) {
+                nextIndex = 0;
+            }
+            angle = sorted[nextIndex];
+            segment = angle->segment();
+            int maxWinding = winding;
+            winding -= segment->spanSign(angle);
+    #if DEBUG_SORT
+            SkDebugf("%s id=%d maxWinding=%d winding=%d sign=%d\n", __FUNCTION__,
+                    segment->debugID(), maxWinding, winding, angle->sign());
+    #endif
+            tIndex = angle->start();
+            endIndex = angle->end();
+            int lesser = SkMin32(tIndex, endIndex);
+            const Span& nextSpan = segment->span(lesser);
+            if (!nextSpan.fDone) {
+#if 1
+            // FIXME: this be wrong. assign startWinding if edge is in
+            // same direction. If the direction is opposite, winding to
+            // assign is flipped sign or +/- 1?
+                if (useInnerWinding(maxWinding, winding)) {
+                    maxWinding = winding;
+                }
+                segment->markWinding(lesser, maxWinding);
+#endif
+                break;
+            }
+        } while (++nextIndex != lastIndex);
+        return segment;
+    }
+    return NULL;
+}
+
+#if DEBUG_ACTIVE_SPANS
+static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
+    int index;
+    for (index = 0; index < contourList.count(); ++ index) {
+        contourList[index]->debugShowActiveSpans();
+    }
+    for (index = 0; index < contourList.count(); ++ index) {
+        contourList[index]->validateActiveSpans();
+    }
+}
+#endif
+
+static bool windingIsActive(int winding, int spanWinding) {
+    return winding * spanWinding <= 0 && abs(winding) <= abs(spanWinding)
+            && (!winding || !spanWinding || winding == -spanWinding);
+}
+
+// Each segment may have an inside or an outside. Segments contained within
+// winding may have insides on either side, and form a contour that should be
+// ignored. Segments that are coincident with opposing direction segments may
+// have outsides on either side, and should also disappear.
+// 'Normal' segments will have one inside and one outside. Subsequent connections
+// when winding should follow the intersection direction. If more than one edge
+// is an option, choose first edge that continues the inside.
+    // since we start with leftmost top edge, we'll traverse through a
+    // smaller angle counterclockwise to get to the next edge.
+// returns true if all edges were processed
+static bool bridgeWinding(SkTDArray<Contour*>& contourList, SkPath& simple) {
+    bool firstContour = true;
+    bool unsortable = false;
+    do {
+        Segment* topStart = findTopContour(contourList);
+        if (!topStart) {
+            break;
+        }
+        // Start at the top. Above the top is outside, below is inside.
+        // follow edges to intersection by changing the index by direction.
+        int index, endIndex;
+        Segment* current = topStart->findTop(index, endIndex);
+        int contourWinding;
+        if (firstContour) {
+            contourWinding = 0;
+            firstContour = false;
+        } else {
+            int sumWinding = current->windSum(SkMin32(index, endIndex));
+            // FIXME: don't I have to adjust windSum to get contourWinding?
+            if (sumWinding == SK_MinS32) {
+                sumWinding = current->computeSum(index, endIndex);
+            }
+            if (sumWinding == SK_MinS32) {
+                contourWinding = innerContourCheck(contourList, current,
+                        index, endIndex);
+            } else {
+                contourWinding = sumWinding;
+                int spanWinding = current->spanSign(index, endIndex);
+                bool inner = useInnerWinding(sumWinding - spanWinding, sumWinding);
+                if (inner) {
+                    contourWinding -= spanWinding;
+                }
+#if DEBUG_WINDING
+                SkDebugf("%s sumWinding=%d spanWinding=%d sign=%d inner=%d result=%d\n", __FUNCTION__,
+                        sumWinding, spanWinding, SkSign32(index - endIndex),
+                        inner, contourWinding);
+#endif
+            }
+#if DEBUG_WINDING
+         //   SkASSERT(current->debugVerifyWinding(index, endIndex, contourWinding));
+            SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
+#endif
+        }
+        SkPoint lastPt;
+        int winding = contourWinding;
+        int spanWinding = current->spanSign(index, endIndex);
+        // FIXME: needs work. While it works in limited situations, it does
+        // not always compute winding correctly. Active should be removed and instead
+        // the initial winding should be correctly passed in so that if the
+        // inner contour is wound the same way, it never finds an accumulated
+        // winding of zero. Inside 'find next', we need to look for transitions
+        // other than zero when resolving sorted angles.
+        bool active = windingIsActive(winding, spanWinding);
+        SkTDArray<Span*> chaseArray;
+        do {
+        #if DEBUG_WINDING
+            SkDebugf("%s active=%s winding=%d spanWinding=%d\n",
+                    __FUNCTION__, active ? "true" : "false",
+                    winding, spanWinding);
+        #endif
+            const SkPoint* firstPt = NULL;
+            do {
+                SkASSERT(unsortable || !current->done());
+                int nextStart = index;
+                int nextEnd = endIndex;
+                Segment* next = current->findNextWinding(chaseArray, active,
+                        nextStart, nextEnd, winding, spanWinding, unsortable);
+                if (!next) {
+                    if (active && firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
+                        lastPt = current->addCurveTo(index, endIndex, simple, true);
+                        SkASSERT(*firstPt == lastPt);
+                    }
+                    break;
+                }
+                if (!firstPt) {
+                    firstPt = &current->addMoveTo(index, simple, active);
+                }
+                lastPt = current->addCurveTo(index, endIndex, simple, active);
+                current = next;
+                index = nextStart;
+                endIndex = nextEnd;
+            } while (*firstPt != lastPt && (active || !current->done()));
+            if (firstPt && active) {
+        #if DEBUG_PATH_CONSTRUCTION
+                SkDebugf("path.close();\n");
+        #endif
+                simple.close();
+            }
+            current = findChase(chaseArray, index, endIndex, contourWinding);
+        #if DEBUG_ACTIVE_SPANS
+            debugShowActiveSpans(contourList);
+        #endif
+            if (!current) {
+                break;
+            }
+            int lesser = SkMin32(index, endIndex);
+            spanWinding = current->spanSign(index, endIndex);
+            winding = current->windSum(lesser);
+            bool inner = useInnerWinding(winding - spanWinding, winding);
+        #if DEBUG_WINDING
+            SkDebugf("%s id=%d t=%1.9g spanWinding=%d winding=%d sign=%d"
+                    " inner=%d result=%d\n",
+                    __FUNCTION__, current->debugID(), current->t(lesser),
+                    spanWinding, winding, SkSign32(index - endIndex),
+                    useInnerWinding(winding - spanWinding, winding),
+                    inner ? winding - spanWinding : winding);
+        #endif
+            if (inner) {
+                winding -= spanWinding;
+            }
+            active = windingIsActive(winding, spanWinding);
+        } while (true);
+    } while (true);
+    return !unsortable;
+}
+
+// returns true if all edges were processed
+static bool bridgeXor(SkTDArray<Contour*>& contourList, SkPath& simple) {
+    Segment* current;
+    int start, end;
+    bool unsortable = false;
+    while ((current = findUndone(contourList, start, end))) {
+        const SkPoint* firstPt = NULL;
+        SkPoint lastPt;
+        do {
+            SkASSERT(unsortable || !current->done());
+            int nextStart = start;
+            int nextEnd = end;
+            Segment* next = current->findNextXor(nextStart, nextEnd, unsortable);
+            if (!next) {
+                if (firstPt && current->verb() != SkPath::kLine_Verb && *firstPt != lastPt) {
+                    lastPt = current->addCurveTo(start, end, simple, true);
+                    SkASSERT(*firstPt == lastPt);
+                }
+                break;
+            }
+            if (!firstPt) {
+                firstPt = &current->addMoveTo(start, simple, true);
+            }
+            lastPt = current->addCurveTo(start, end, simple, true);
+            current = next;
+            start = nextStart;
+            end = nextEnd;
+        } while (*firstPt != lastPt);
+        if (firstPt) {
+    #if DEBUG_PATH_CONSTRUCTION
+            SkDebugf("%s close\n", __FUNCTION__);
+    #endif
+            simple.close();
+        }
+    #if DEBUG_ACTIVE_SPANS
+        debugShowActiveSpans(contourList);
+    #endif
+    }
+    return !unsortable;
+}
+
+static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
+    int contourCount = contourList.count();
+    for (int cTest = 0; cTest < contourCount; ++cTest) {
+        Contour* contour = contourList[cTest];
+        contour->fixOtherTIndex();
+    }
+}
+
+static void makeContourList(SkTArray<Contour>& contours,
+        SkTDArray<Contour*>& list) {
+    int count = contours.count();
+    if (count == 0) {
+        return;
+    }
+    for (int index = 0; index < count; ++index) {
+        *list.append() = &contours[index];
+    }
+    QSort<Contour>(list.begin(), list.end() - 1);
+}
+
+static void assemble(SkPath& simple) {
+    // TODO: find the non-closed paths and connect them together
+    SkASSERT(0);
+}
+
+void simplifyx(const SkPath& path, SkPath& simple) {
+    // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
+    simple.reset();
+    simple.setFillType(SkPath::kEvenOdd_FillType);
+
+    // turn path into list of segments
+    SkTArray<Contour> contours;
+    // FIXME: add self-intersecting cubics' T values to segment
+    EdgeBuilder builder(path, contours);
+    builder.finish();
+    SkTDArray<Contour*> contourList;
+    makeContourList(contours, contourList);
+    Contour** currentPtr = contourList.begin();
+    if (!currentPtr) {
+        return;
+    }
+    Contour** listEnd = contourList.end();
+    // find all intersections between segments
+    do {
+        Contour** nextPtr = currentPtr;
+        Contour* current = *currentPtr++;
+        Contour* next;
+        do {
+            next = *nextPtr++;
+        } while (addIntersectTs(current, next) && nextPtr != listEnd);
+    } while (currentPtr != listEnd);
+    // eat through coincident edges
+    coincidenceCheck(contourList);
+    fixOtherTIndex(contourList);
+    // construct closed contours
+    if (builder.xorMask() == kWinding_Mask
+                ? !bridgeWinding(contourList, simple)
+                : !bridgeXor(contourList, simple)) 
+    { // if some edges could not be resolved, assemble remaining fragments
+        assemble(simple);
+    }
+}
+
diff --git a/experimental/Intersection/Simplify.h b/experimental/Intersection/Simplify.h
new file mode 100644
index 0000000..a0b936a
--- /dev/null
+++ b/experimental/Intersection/Simplify.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "CurveIntersection.h"
+#include "CurveUtilities.h"
+#include "Intersections.h"
+#include "LineIntersection.h"
+#include "LineParameters.h"
+#include "SkPath.h"
+#include "SkRect.h"
+#include "SkTArray.h"
+#include "SkTDArray.h"
+#include "ShapeOps.h"
+#include "TSearch.h"
+#include <algorithm> // used for std::min
+
diff --git a/experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp b/experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp
new file mode 100644
index 0000000..ca96ea6
--- /dev/null
+++ b/experimental/Intersection/SimplifyAddIntersectingTs_Test.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 "Simplify.h"
+
+namespace SimplifyAddIntersectingTsTest {
+
+#include "Simplify.cpp"
+
+} // end of SimplifyAddIntersectingTsTest namespace
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Intersection_Tests.h"
+
+static const SkPoint lines[][2] = {
+    {{ 1,  1}, { 1,  1}},   // degenerate
+    {{ 1,  1}, { 4,  1}},   // horizontal
+    {{ 4,  1}, { 9,  1}},
+    {{ 2,  1}, { 3,  1}},
+    {{ 2,  1}, { 6,  1}},
+    {{ 5,  1}, { 9,  1}},
+    {{ 1,  1}, { 1,  4}},   // vertical
+    {{ 1,  2}, { 1,  3}},
+    {{ 1,  2}, { 1,  6}},
+    {{ 1,  5}, { 1,  9}},
+    {{ 1,  1}, { 3,  3}},   // diagonal
+    {{ 2,  2}, { 4,  4}},
+    {{ 2,  4}, { 4,  2}},
+};
+
+static const size_t lineCount = sizeof(lines) / sizeof(lines[0]);
+
+static const SkPoint quads[][3] = {
+    {{ 1,  1}, { 1,  1}, { 1,  1}},   // degenerate
+    {{ 1,  1}, { 4,  1}, { 5,  1}},   // line
+    {{ 1,  1}, { 4,  1}, { 4,  4}},   // curve
+};
+
+static const size_t quadCount = sizeof(quads) / sizeof(quads[0]);
+
+static const SkPoint cubics[][4] = {
+    {{ 1,  1}, { 1,  1}, { 1,  1}, { 1,  1}},   // degenerate
+    {{ 1,  1}, { 4,  1}, { 5,  1}, { 6,  1}},   // line
+    {{ 1,  1}, { 3,  1}, { 4,  2}, { 4,  4}},   // curve
+};
+
+static const size_t cubicCount = sizeof(cubics) / sizeof(cubics[0]);
+static const size_t testCount = lineCount + quadCount + cubicCount;
+
+static SkPath::Verb setPath(size_t outer, SkPath& path, const SkPoint*& pts1) {
+    SkPath::Verb c1Type;
+    if (outer < lineCount) {
+        path.moveTo(lines[outer][0].fX, lines[outer][0].fY);
+        path.lineTo(lines[outer][1].fX, lines[outer][1].fY);
+        c1Type = SkPath::kLine_Verb;
+        pts1 = lines[outer];
+    } else {
+        outer -= lineCount;
+        if (outer < quadCount) {
+        path.moveTo(quads[outer][0].fX, quads[outer][0].fY);
+        path.quadTo(quads[outer][1].fX, quads[outer][1].fY,
+                quads[outer][2].fX, quads[outer][2].fY);
+            c1Type = SkPath::kQuad_Verb;
+            pts1 = quads[outer];
+        } else {
+            outer -= quadCount;
+            path.moveTo(cubics[outer][0].fX, cubics[outer][0].fY);
+            path.cubicTo(cubics[outer][1].fX, cubics[outer][1].fY,
+                    cubics[outer][2].fX, cubics[outer][2].fY,
+                    cubics[outer][3].fX, cubics[outer][3].fY);
+            c1Type = SkPath::kCubic_Verb;
+            pts1 = cubics[outer];
+        }
+    }
+    return c1Type;
+}
+
+static void testPath(const SkPath& path, const SkPoint* pts1, SkPath::Verb c1Type,
+        const SkPoint* pts2, SkPath::Verb c2Type) {
+    SkTArray<SimplifyAddIntersectingTsTest::Contour> contour;
+    SimplifyAddIntersectingTsTest::EdgeBuilder builder(path, contour);
+    if (contour.count() < 2) {
+        return;
+    }
+    SimplifyAddIntersectingTsTest::Contour& c1 = contour[0];
+    SimplifyAddIntersectingTsTest::Contour& c2 = contour[1];
+    addIntersectTs(&c1, &c2);
+#if DEBUG_DUMP
+    bool c1Intersected = c1.segments()[0].intersected();
+    // bool c2Intersected = c2.fSegments[0].intersected();
+    SkDebugf("%s %s (%1.9g,%1.9g %1.9g,%1.9g) %s %s (%1.9g,%1.9g %1.9g,%1.9g)\n",
+            __FUNCTION__, SimplifyAddIntersectingTsTest::kLVerbStr[c1Type],
+            pts1[0].fX, pts1[0].fY,
+            pts1[c1Type].fX, pts1[c1Type].fY,
+            c1Intersected ? "intersects" : "does not intersect",
+            SimplifyAddIntersectingTsTest::kLVerbStr[c2Type],
+            pts2[0].fX, pts2[0].fY,
+            pts2[c2Type].fX, pts2[c2Type].fY);
+    if (c1Intersected) {
+        c1.dump();
+        c2.dump();
+    }
+#endif
+}
+
+static const size_t firstO = 6;
+static const size_t firstI = 1;
+
+void SimplifyAddIntersectingTs_Test() {
+    const SkPoint* pts1, * pts2;
+    if (firstO > 0 || firstI > 0) {
+        SkPath path;
+        SkPath::Verb c1Type = setPath(firstO, path, pts1);
+        SkPath path2(path);
+        SkPath::Verb c2Type = setPath(firstI, path2, pts2);
+        testPath(path2, pts1, c1Type, pts2, c2Type);
+    }
+    for (size_t o = 0; o < testCount; ++o) {
+        SkPath path;
+        SkPath::Verb c1Type = setPath(o, path, pts1);
+        for (size_t i = 0; i < testCount; ++i) {
+            SkPath path2(path);
+            SkPath::Verb c2Type = setPath(i, path2, pts2);
+            testPath(path2, pts1, c1Type, pts2, c2Type);
+        }
+    }
+}
+
diff --git a/experimental/Intersection/SimplifyAngle_Test.cpp b/experimental/Intersection/SimplifyAngle_Test.cpp
new file mode 100644
index 0000000..9b25851
--- /dev/null
+++ b/experimental/Intersection/SimplifyAngle_Test.cpp
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Simplify.h"
+
+namespace SimplifyAngleTest {
+
+#include "Simplify.cpp"
+
+} // end of SimplifyAngleTest namespace
+
+#include "Intersection_Tests.h"
+
+static const SkPoint lines[][2] = {
+    { { 10,  10}, { 10,  20} },
+    { { 10,  10}, { 20,  10} },
+    { { 10,  10}, {-20,  10} },
+    { { 10,  10}, { 10, -20} },
+    { { 10,  10}, { 20,  20} },
+    { { 10,  10}, {-20, -20} },
+    { { 10,  10}, {-20,  40} },
+    { { 10,  10}, { 40, -20} }
+};
+
+static const size_t lineCount = sizeof(lines) / sizeof(lines[0]);
+
+static const SkPoint quads[][3] = {
+    {{ 1,  1}, { 2,  2}, { 1,  3}}, // 0
+    {{ 1,  1}, { 3,  3}, { 1,  5}}, // 1
+    {{ 1,  1}, { 4,  4}, { 1,  7}}, // 2
+    {{ 1,  1}, { 5,  5}, { 9,  9}}, // 3
+    {{ 1,  1}, { 4,  4}, { 7,  1}}, // 4
+    {{ 1,  1}, { 3,  3}, { 5,  1}}, // 5
+    {{ 1,  1}, { 2,  2}, { 3,  1}}, // 6
+};
+
+static const size_t quadCount = sizeof(quads) / sizeof(quads[0]);
+
+static const SkPoint cubics[][4] = {
+    {{ 1,  1}, { 2,  2}, { 2,  3}, { 1,  4}},
+    {{ 1,  1}, { 3,  3}, { 3,  5}, { 1,  7}},
+    {{ 1,  1}, { 4,  4}, { 4,  7}, { 1,  10}},
+    {{ 1,  1}, { 5,  5}, { 8,  8}, { 9,  9}},
+    {{ 1,  1}, { 4,  4}, { 7,  4}, { 10, 1}},
+    {{ 1,  1}, { 3,  3}, { 5,  3}, { 7,  1}},
+    {{ 1,  1}, { 2,  2}, { 3,  2}, { 4,  1}},
+};
+
+static const size_t cubicCount = sizeof(cubics) / sizeof(cubics[0]);
+
+struct segment {
+    SkPath::Verb verb;
+    SkPoint pts[4];
+};
+
+static const segment segmentTest1[] = {
+    {SkPath::kLine_Verb, {{2, 1}, {1, 0}        }},
+    {SkPath::kQuad_Verb, {{2, 1}, {1, 0}, {0, 0}}},
+    {SkPath::kQuad_Verb, {{2, 1}, {3, 2}, {2, 3}}},
+    {SkPath::kLine_Verb, {{2, 1}, {3, 2}        }},
+    {SkPath::kMove_Verb                          }
+};
+
+static const segment segmentTest2[] = {
+    {SkPath::kLine_Verb, {{1, 0}, {0, 0}        }},
+    {SkPath::kQuad_Verb, {{1, 0}, {1.89897954f, 0.898979485f}, {2.39387703f, 1.59591794f}}},
+    {SkPath::kLine_Verb, {{1, 0}, {3, 2}        }},
+    {SkPath::kMove_Verb                          }
+};
+
+static const segment segmentTest3[] = {
+    {SkPath::kQuad_Verb, {{0, 0}, {2, 0}, {0, 1}}},
+    {SkPath::kQuad_Verb, {{0, 0}, {1, 0}, {0, 1}}},
+    {SkPath::kMove_Verb                          }
+};
+
+static const segment* segmentTests[] = {
+    segmentTest3,
+    segmentTest2,
+    segmentTest1,
+};
+
+static const size_t segmentTestCount = sizeof(segmentTests) / sizeof(segmentTests[0]);
+
+static void testSegments(bool testFlat) {
+    for (size_t testIndex = 0; testIndex < segmentTestCount; ++testIndex) {
+        const segment* segPtr = segmentTests[testIndex];
+        SimplifyAngleTest::Angle lesser, greater;
+        int index = 0;
+        do {
+            int next = index + 1;
+            SkTDArray<SimplifyAngleTest::Span> dummy;
+            lesser.set(segPtr[index].pts, segPtr[index].verb, NULL, index, next, dummy);
+            if (segPtr[next].verb == SkPath::kMove_Verb) {
+                break;
+            }
+            greater.set(segPtr[next].pts, segPtr[next].verb, NULL, index, next, dummy);
+            bool result = lesser < greater;
+            SkASSERT(result);
+            index = next;
+        } while (true);
+    }
+}
+
+static void testLines(bool testFlat) {
+    // create angles in a circle
+    SkTDArray<SimplifyAngleTest::Angle> angles;
+    SkTDArray<SimplifyAngleTest::Angle* > angleList;
+    SkTDArray<double> arcTans;
+    size_t x;
+    for (x = 0; x < lineCount; ++x) {
+        SimplifyAngleTest::Angle* angle = angles.append();
+        SkTDArray<SimplifyAngleTest::Span> dummy;
+        angle->set(lines[x], SkPath::kLine_Verb, 0, x, x + 1, dummy);
+        double arcTan = atan2(lines[x][0].fX - lines[x][1].fX,
+                lines[x][0].fY - lines[x][1].fY);
+        arcTans.push(arcTan);
+    }
+    for (x = 0; x < lineCount; ++x) {
+        angleList.push(&angles[x]);
+    }
+    QSort<SimplifyAngleTest::Angle>(angleList.begin(), angleList.end() - 1);
+    bool first = true;
+    bool wrap = false;
+    double base, last;
+    for (size_t x = 0; x < lineCount; ++x) {
+        const SimplifyAngleTest::Angle* angle = angleList[x];
+        int span = angle->start();
+//        SkDebugf("%s [%d] %1.9g (%1.9g,%1.9g %1.9g,%1.9g)\n", __FUNCTION__,
+//                span, arcTans[span], lines[span][0].fX, lines[span][0].fY,
+//                lines[span][1].fX, lines[span][1].fY);
+        if (first) {
+            base = last = arcTans[span];
+            first = false;
+            continue;
+        }
+        if (last < arcTans[span]) {
+            last = arcTans[span];
+            continue;
+        }
+        if (!wrap) {
+            if (base < arcTans[span]) {
+                SkDebugf("%s !wrap [%d] %g\n", __FUNCTION__, span, arcTans[span]);
+                SkASSERT(0);
+            }
+            last = arcTans[span];
+            wrap = true;
+            continue;
+        }
+        SkDebugf("%s wrap [%d] %g\n", __FUNCTION__, span, arcTans[span]);
+        SkASSERT(0);
+    }
+}
+
+static void testQuads(bool testFlat) {
+    SkTDArray<SimplifyAngleTest::Angle> angles;
+    SkTDArray<SimplifyAngleTest::Angle* > angleList;
+    size_t x;
+    for (x = 0; x < quadCount; ++x) {
+        SimplifyAngleTest::Angle* angle = angles.append();
+        SkTDArray<SimplifyAngleTest::Span> dummy;
+        angle->set(quads[x], SkPath::kQuad_Verb, 0, x, x + 1, dummy);
+   }
+    for (x = 0; x < quadCount; ++x) {
+        angleList.push(&angles[x]);
+    }
+    QSort<SimplifyAngleTest::Angle>(angleList.begin(), angleList.end() - 1);
+    for (size_t x = 0; x < quadCount; ++x) {
+        *angleList[x] < *angleList[x + 1];
+        SkASSERT(x == quadCount - 1 || *angleList[x] < *angleList[x + 1]);
+        const SimplifyAngleTest::Angle* angle = angleList[x];
+        if ((int) x != angle->start()) {
+            SkDebugf("%s [%d] [%d]\n", __FUNCTION__, x, angle->start());
+            SkASSERT(0);
+        }
+    }
+}
+
+static void testCubics(bool testFlat) {
+    SkTDArray<SimplifyAngleTest::Angle> angles;
+    SkTDArray<SimplifyAngleTest::Angle* > angleList;
+    for (size_t x = 0; x < cubicCount; ++x) {
+        SimplifyAngleTest::Angle* angle = angles.append();
+            SkTDArray<SimplifyAngleTest::Span> dummy;
+        angle->set(cubics[x], SkPath::kCubic_Verb, 0, x, x + 1, dummy);
+        angleList.push(angle);
+    }
+    QSort<SimplifyAngleTest::Angle>(angleList.begin(), angleList.end() - 1);
+    for (size_t x = 0; x < cubicCount; ++x) {
+        const SimplifyAngleTest::Angle* angle = angleList[x];
+        if ((int) x != angle->start()) {
+            SkDebugf("%s [%d] [%d]\n", __FUNCTION__, x, angle->start());
+            SkASSERT(0);
+        }
+    }
+}
+
+struct segmentWithT {
+    SkPath::Verb verb;
+    SkPoint pts[4];
+    double ts[2];
+};
+
+
+static const segmentWithT oneOffTest1[] = {
+    {SkPath::kQuad_Verb, {{391.653534f, 183.286819f}, {391.653534f, 157.724487f}, {405.469604f, 141.372879f}},
+        {0.62346946335026932, 0.62344389027237135}},
+    {SkPath::kQuad_Verb, {{399.365234f, 171.695801f}, {399.365234f, 140.337967f}, {375.976227f, 140.337967f}},
+        {0.31638302676995866, 0.31637992418411398}},
+    {SkPath::kMove_Verb }
+};
+
+static const segmentWithT oneOffTest2[] = {
+    {SkPath::kQuad_Verb, {{399.070374f, 151.722f}, {391.101532f, 163.002533f}, {391.101532f, 182.665863f}},
+        {0.13793711854916513, 0.13790171160614006}},
+    {SkPath::kQuad_Verb, {{391.653534f, 183.286819f}, {391.653534f, 157.724487f}, {405.469604f, 141.372879f}},
+        {0.62344389027237135, 0.62346946335026932}},
+    {SkPath::kMove_Verb }
+};
+
+static const segmentWithT oneOffTest3[] = {
+    {SkPath::kQuad_Verb, {{399.365234f, 171.695801f}, {399.365234f, 140.337967f}, {375.976227f, 140.337967f}},
+        {0.31637992418411398, 0.31638302676995866, }},
+    {SkPath::kQuad_Verb, {{399.070374f, 151.722f}, {391.101532f, 163.002533f}, {391.101532f, 182.665863f}},
+        {0.13790171160614006, 0.13793711854916513}},
+    {SkPath::kMove_Verb }
+};
+
+static const segmentWithT* oneOffTests[] = {
+    oneOffTest1,
+    oneOffTest2,
+    oneOffTest3,
+};
+
+static const size_t oneOffTestCount = sizeof(oneOffTests) / sizeof(oneOffTests[0]);
+
+static void oneOffTest(bool testFlat) {
+    for (size_t testIndex = 0; testIndex < oneOffTestCount; ++testIndex) {
+        const segmentWithT* segPtr = oneOffTests[testIndex];
+        SimplifyAngleTest::Angle lesser, greater;
+        int index = 0;
+        do {
+            int next = index + 1;
+            SkTDArray<SimplifyAngleTest::Span> dummy; // FIXME
+            lesser.set(segPtr[index].pts, segPtr[index].verb, 0, index, next, dummy); // FIXME: segPtr[index].ts[0], segPtr[index].ts[1]);
+            if (segPtr[next].verb == SkPath::kMove_Verb) {
+                break;
+            }
+            greater.set(segPtr[next].pts, segPtr[next].verb, 0, index, next, dummy); // FIXME: segPtr[next].ts[0], segPtr[next].ts[1]);
+            bool result = lesser < greater;
+            SkASSERT(result);
+            index = next;
+        } while (true);
+    }
+    SkDebugf("%s finished\n", __FUNCTION__);
+}
+
+static void (*tests[])(bool) = {
+    oneOffTest,
+    testSegments,
+    testLines,
+    testQuads,
+    testCubics
+};
+
+static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+
+static void (*firstTest)(bool) = 0;
+static bool skipAll = false;
+
+void SimplifyAngle_Test() {
+    if (skipAll) {
+        return;
+    }
+    size_t index = 0;
+    if (firstTest) {
+        while (index < testCount && tests[index] != firstTest) {
+            ++index;
+        }
+    }
+    bool firstTestComplete = false;
+    for ( ; index < testCount; ++index) {
+        (*tests[index])(false); // set to true to exercise setFlat
+        firstTestComplete = true;
+    }
+}
diff --git a/experimental/Intersection/SimplifyFindNext_Test.cpp b/experimental/Intersection/SimplifyFindNext_Test.cpp
new file mode 100644
index 0000000..b6f5d1e
--- /dev/null
+++ b/experimental/Intersection/SimplifyFindNext_Test.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#define DEBUG_TEST 1
+
+#include "Simplify.h"
+
+namespace SimplifyFindNextTest {
+
+#include "Simplify.cpp"
+
+} // end of SimplifyFindNextTest namespace
+
+#include "Intersection_Tests.h"
+
+static const SimplifyFindNextTest::Segment* testCommon(
+        int contourWinding, int spanWinding, int startIndex, int endIndex,
+        SkTArray<SimplifyFindNextTest::Contour>& contours) {
+    SkTDArray<SimplifyFindNextTest::Contour*> contourList;
+    makeContourList(contours, contourList);
+    addIntersectTs(contourList[0], contourList[0]);
+    if (contours.count() > 1) {
+        SkASSERT(contours.count() == 2);
+        addIntersectTs(contourList[0], contourList[1]);
+        addIntersectTs(contourList[1], contourList[1]);
+    }
+    fixOtherTIndex(contourList);
+    SimplifyFindNextTest::Segment& segment = contours[0].debugSegments()[0];
+    SkPoint pts[2];
+    pts[0] = segment.xyAtT(&segment.span(endIndex));
+    int nextStart = startIndex;
+    int nextEnd = endIndex;
+    SkTDArray<SimplifyFindNextTest::Span*> chaseArray;
+    bool unsortable = false;
+    SimplifyFindNextTest::Segment* next = segment.findNextWinding(chaseArray,
+            true, nextStart, nextEnd, contourWinding, spanWinding,
+            unsortable);
+    pts[1] = next->xyAtT(&next->span(nextStart));
+    SkASSERT(pts[0] == pts[1]);
+    return next;
+}
+
+static void test(const SkPath& path) {
+    SkTArray<SimplifyFindNextTest::Contour> contours;
+    SimplifyFindNextTest::EdgeBuilder builder(path, contours);
+    int contourWinding = 0;
+    int spanWinding = 1;
+    int start = 0;
+    int end = 1;
+    testCommon(contourWinding, spanWinding, start, end, contours);
+}
+
+static void test(const SkPath& path, int start, int end) {
+    SkTArray<SimplifyFindNextTest::Contour> contours;
+    SimplifyFindNextTest::EdgeBuilder builder(path, contours);
+    int contourWinding = 0;
+    int spanWinding = 1;
+    testCommon(contourWinding, spanWinding, start, end, contours);
+}
+
+static void testLine1() {
+    SkPath path;
+    path.moveTo(2,0);
+    path.lineTo(1,1);
+    path.lineTo(0,0);
+    path.close();
+    test(path);
+}
+
+static void addInnerCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(4,1);
+    path.lineTo(2,1);
+    path.close();
+}
+
+#if DEBUG_UNUSED
+static void addInnerCCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(2,1);
+    path.lineTo(4,1);
+    path.close();
+}
+#endif
+
+static void addOuterCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(6,2);
+    path.lineTo(0,2);
+    path.close();
+}
+
+#if DEBUG_UNUSED
+static void addOuterCCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(0,2);
+    path.lineTo(6,2);
+    path.close();
+}
+#endif
+
+static void testLine2() {
+    SkPath path;
+    addInnerCWTriangle(path);
+    addOuterCWTriangle(path);
+    test(path, 0, 3);
+}
+
+static void testLine3() {
+    SkPath path;
+    addInnerCWTriangle(path);
+    addOuterCWTriangle(path);
+    test(path, 3, 0);
+}
+
+static void testLine4() {
+    SkPath path;
+    addInnerCWTriangle(path);
+    addOuterCWTriangle(path);
+    test(path, 3, 2);
+}
+
+static void (*tests[])() = {
+    testLine1,
+    testLine2,
+    testLine3,
+    testLine4,
+};
+
+static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+
+static void (*firstTest)() = 0;
+static bool skipAll = false;
+
+void SimplifyFindNext_Test() {
+    if (skipAll) {
+        return;
+    }
+    size_t index = 0;
+    if (firstTest) {
+        while (index < testCount && tests[index] != firstTest) {
+            ++index;
+        }
+    }
+    bool firstTestComplete = false;
+    for ( ; index < testCount; ++index) {
+        (*tests[index])();
+        firstTestComplete = true;
+    }
+}
diff --git a/experimental/Intersection/SimplifyFindTop_Test.cpp b/experimental/Intersection/SimplifyFindTop_Test.cpp
new file mode 100644
index 0000000..0c9c2e0
--- /dev/null
+++ b/experimental/Intersection/SimplifyFindTop_Test.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 "Simplify.h"
+
+namespace SimplifyFindTopTest {
+
+#include "Simplify.cpp"
+
+} // end of SimplifyFindTopTest namespace
+
+#include "Intersection_Tests.h"
+
+static const SimplifyFindTopTest::Segment* testCommon(
+        SkTArray<SimplifyFindTopTest::Contour>& contours,
+        int& index, int& end) {
+    SkTDArray<SimplifyFindTopTest::Contour*> contourList;
+    makeContourList(contours, contourList);
+    addIntersectTs(contourList[0], contourList[0]);
+    if (contours.count() > 1) {
+        SkASSERT(contours.count() == 2);
+        addIntersectTs(contourList[0], contourList[1]);
+        addIntersectTs(contourList[1], contourList[1]);
+    }
+    fixOtherTIndex(contourList);
+    SimplifyFindTopTest::Segment* topStart = findTopContour(contourList);
+    const SimplifyFindTopTest::Segment* topSegment = topStart->findTop(index,
+            end);
+    return topSegment;
+}
+
+static void test(const SkPath& path) {
+    SkTArray<SimplifyFindTopTest::Contour> contours;
+    SimplifyFindTopTest::EdgeBuilder builder(path, contours);
+    int index, end;
+    testCommon(contours, index, end);
+    SkASSERT(index + 1 == end);
+}
+
+static void test(const SkPath& path, SkScalar x1, SkScalar y1,
+        SkScalar x2, SkScalar y2) {
+    SkTArray<SimplifyFindTopTest::Contour> contours;
+    SimplifyFindTopTest::EdgeBuilder builder(path, contours);
+    int index, end;
+    const SimplifyFindTopTest::Segment* topSegment =
+            testCommon(contours, index, end);
+    SkPoint pts[2];
+    double firstT = topSegment->t(index);
+    pts[0] = topSegment->xyAtT(&topSegment->span(index));
+    int direction = index < end ? 1 : -1;
+    do {
+        index += direction;
+        double nextT = topSegment->t(index);
+        if (nextT == firstT) {
+            continue;
+        }
+        pts[1] = topSegment->xyAtT(&topSegment->span(index));
+        if (pts[0] != pts[1]) {
+            break;
+        }
+    } while (true);
+    SkASSERT(pts[0].fX == x1);
+    SkASSERT(pts[0].fY == y1);
+    SkASSERT(pts[1].fX == x2);
+    SkASSERT(pts[1].fY == y2);
+}
+
+static void testLine1() {
+    SkPath path;
+    path.moveTo(2,0);
+    path.lineTo(1,1);
+    path.lineTo(0,0);
+    path.close();
+    test(path);
+}
+
+static void addInnerCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(4,1);
+    path.lineTo(2,1);
+    path.close();
+}
+
+static void addInnerCCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(2,1);
+    path.lineTo(4,1);
+    path.close();
+}
+
+static void addOuterCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(6,2);
+    path.lineTo(0,2);
+    path.close();
+}
+
+static void addOuterCCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(0,2);
+    path.lineTo(6,2);
+    path.close();
+}
+
+static void testLine2() {
+    SkPath path;
+    addInnerCWTriangle(path);
+    addOuterCWTriangle(path);
+    test(path, 0, 2, 3, 0);
+}
+
+static void testLine3() {
+    SkPath path;
+    addOuterCWTriangle(path);
+    addInnerCWTriangle(path);
+    test(path, 0, 2, 3, 0);
+}
+
+static void testLine4() {
+    SkPath path;
+    addInnerCCWTriangle(path);
+    addOuterCWTriangle(path);
+    test(path, 0, 2, 3, 0);
+}
+
+static void testLine5() {
+    SkPath path;
+    addOuterCWTriangle(path);
+    addInnerCCWTriangle(path);
+    test(path, 0, 2, 3, 0);
+}
+
+static void testLine6() {
+    SkPath path;
+    addInnerCWTriangle(path);
+    addOuterCCWTriangle(path);
+    test(path, 0, 2, 3, 0);
+}
+
+static void testLine7() {
+    SkPath path;
+    addOuterCCWTriangle(path);
+    addInnerCWTriangle(path);
+    test(path, 0, 2, 3, 0);
+}
+
+static void testLine8() {
+    SkPath path;
+    addInnerCCWTriangle(path);
+    addOuterCCWTriangle(path);
+    test(path, 0, 2, 3, 0);
+}
+
+static void testLine9() {
+    SkPath path;
+    addOuterCCWTriangle(path);
+    addInnerCCWTriangle(path);
+    test(path, 0, 2, 3, 0);
+}
+
+static void testQuads() {
+    SkPath path;
+    path.moveTo(2,0);
+    path.quadTo(1,1, 0,0);
+    path.close();
+    test(path);
+}
+
+static void testCubics() {
+    SkPath path;
+    path.moveTo(2,0);
+    path.cubicTo(2,3, 1,1, 0,0);
+    path.close();
+    test(path);
+}
+
+static void (*tests[])() = {
+    testLine1,
+    testLine2,
+    testLine3,
+    testLine4,
+    testLine5,
+    testLine6,
+    testLine7,
+    testLine8,
+    testLine9,
+    testQuads,
+    testCubics
+};
+
+static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+
+static void (*firstTest)() = 0;
+static bool skipAll = false;
+
+void SimplifyFindTop_Test() {
+    if (skipAll) {
+        return;
+    }
+    size_t index = 0;
+    if (firstTest) {
+        while (index < testCount && tests[index] != firstTest) {
+            ++index;
+        }
+    }
+    bool firstTestComplete = false;
+    for ( ; index < testCount; ++index) {
+        (*tests[index])();
+        firstTestComplete = true;
+    }
+}
diff --git a/experimental/Intersection/SimplifyNew_Test.cpp b/experimental/Intersection/SimplifyNew_Test.cpp
new file mode 100644
index 0000000..9841176
--- /dev/null
+++ b/experimental/Intersection/SimplifyNew_Test.cpp
@@ -0,0 +1,3158 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "ShapeOps.h"
+
+#define TEST(name) { name, #name }
+
+static void testLine1() {
+    SkPath path;
+    path.moveTo(2,0);
+    path.lineTo(1,1);
+    path.lineTo(0,0);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine1x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(2,0);
+    path.lineTo(1,1);
+    path.lineTo(0,0);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void addInnerCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(4,1);
+    path.lineTo(2,1);
+    path.close();
+}
+
+static void addInnerCCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(2,1);
+    path.lineTo(4,1);
+    path.close();
+}
+
+static void addOuterCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(6,2);
+    path.lineTo(0,2);
+    path.close();
+}
+
+static void addOuterCCWTriangle(SkPath& path) {
+    path.moveTo(3,0);
+    path.lineTo(0,2);
+    path.lineTo(6,2);
+    path.close();
+}
+
+static void testLine2() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addInnerCWTriangle(path);
+    addOuterCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine2x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addInnerCWTriangle(path);
+    addOuterCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine3() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addInnerCCWTriangle(path);
+    addOuterCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine3x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addInnerCCWTriangle(path);
+    addOuterCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine3a() {
+    SkPath path;
+    addInnerCWTriangle(path);
+    addOuterCCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine3ax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addInnerCWTriangle(path);
+    addOuterCCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine3b() {
+    SkPath path;
+    addInnerCCWTriangle(path);
+    addOuterCCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine3bx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addInnerCCWTriangle(path);
+    addOuterCCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine4() {
+    SkPath path;
+    addOuterCCWTriangle(path);
+    addOuterCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine4x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addOuterCCWTriangle(path);
+    addOuterCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine5() {
+    SkPath path;
+    addOuterCWTriangle(path);
+    addOuterCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine5x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addOuterCWTriangle(path);
+    addOuterCWTriangle(path);
+    testSimplifyx(path);
+}
+
+static void testLine6() {
+    SkPath path;
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(2,0);
+    path.lineTo(6,0);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine6x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(2,0);
+    path.lineTo(6,0);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine7() {
+    SkPath path;
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(6,0);
+    path.lineTo(2,0);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine7x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(6,0);
+    path.lineTo(2,0);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine7a() {
+    SkPath path;
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.lineTo(2,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine7ax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.lineTo(2,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine7b() {
+    SkPath path;
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.close();
+    path.moveTo(6,0);
+    path.lineTo(2,0);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine7bx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.close();
+    path.moveTo(6,0);
+    path.lineTo(2,0);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine8() {
+    SkPath path;
+    path.moveTo(0,4);
+    path.lineTo(4,4);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(2,4);
+    path.lineTo(6,4);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine8x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0,4);
+    path.lineTo(4,4);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(2,4);
+    path.lineTo(6,4);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine9() {
+    SkPath path;
+    path.moveTo(0,4);
+    path.lineTo(4,4);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(6,4);
+    path.lineTo(2,4);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine9x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0,4);
+    path.lineTo(4,4);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(6,4);
+    path.lineTo(2,4);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine10() {
+    SkPath path;
+    path.moveTo(0,4);
+    path.lineTo(4,4);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(2,1);
+    path.lineTo(3,4);
+    path.lineTo(6,1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine10x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0,4);
+    path.lineTo(4,4);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(2,1);
+    path.lineTo(3,4);
+    path.lineTo(6,1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine10a() {
+    SkPath path;
+    path.moveTo(0,4);
+    path.lineTo(8,4);
+    path.lineTo(4,0);
+    path.close();
+    path.moveTo(2,2);
+    path.lineTo(3,3);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine10ax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0,4);
+    path.lineTo(8,4);
+    path.lineTo(4,0);
+    path.close();
+    path.moveTo(2,2);
+    path.lineTo(3,3);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void addCWContainer(SkPath& path) {
+    path.moveTo(6,4);
+    path.lineTo(0,4);
+    path.lineTo(3,1);
+    path.close();
+}
+
+static void addCCWContainer(SkPath& path) {
+    path.moveTo(0,4);
+    path.lineTo(6,4);
+    path.lineTo(3,1);
+    path.close();
+}
+
+static void addCWContents(SkPath& path) {
+    path.moveTo(2,3);
+    path.lineTo(3,2);
+    path.lineTo(4,3);
+    path.close();
+}
+
+static void addCCWContents(SkPath& path) {
+    path.moveTo(3,2);
+    path.lineTo(2,3);
+    path.lineTo(4,3);
+    path.close();
+}
+
+static void testLine11() {
+    SkPath path;
+    addCWContainer(path);
+    addCWContents(path);
+    testSimplifyx(path);
+}
+
+static void testLine11x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addCWContainer(path);
+    addCWContents(path);
+    testSimplifyx(path);
+}
+
+static void testLine12() {
+    SkPath path;
+    addCCWContainer(path);
+    addCWContents(path);
+    testSimplifyx(path);
+}
+
+static void testLine12x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addCCWContainer(path);
+    addCWContents(path);
+    testSimplifyx(path);
+}
+
+static void testLine13() {
+    SkPath path;
+    addCWContainer(path);
+    addCCWContents(path);
+    testSimplifyx(path);
+}
+
+static void testLine13x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addCWContainer(path);
+    addCCWContents(path);
+    testSimplifyx(path);
+}
+
+static void testLine14() {
+    SkPath path;
+    addCCWContainer(path);
+    addCCWContents(path);
+    testSimplifyx(path);
+}
+
+static void testLine14x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    addCCWContainer(path);
+    addCCWContents(path);
+    testSimplifyx(path);
+}
+
+static void testLine15() {
+    SkPath path;
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine15x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine16() {
+    SkPath path;
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 4, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine16x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 4, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine17() {
+    SkPath path;
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine17x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine18() {
+    SkPath path;
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 4, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine18x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 4, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine19() {
+    SkPath path;
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 16, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine19x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 16, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine20() {
+    SkPath path;
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 12, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine20x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 12, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine21() {
+    SkPath path;
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 16, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine21x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 16, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine22() {
+    SkPath path;
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine22x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine23() {
+    SkPath path;
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine23x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine24a() {
+    SkPath path;
+    path.moveTo(2,0);
+    path.lineTo(4,4);
+    path.lineTo(0,4);
+    path.close();
+    path.moveTo(2,0);
+    path.lineTo(1,2);
+    path.lineTo(2,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine24ax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(2,0);
+    path.lineTo(4,4);
+    path.lineTo(0,4);
+    path.close();
+    path.moveTo(2,0);
+    path.lineTo(1,2);
+    path.lineTo(2,2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine24() {
+    SkPath path;
+    path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine24x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine25() {
+    SkPath path;
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine25x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine26() {
+    SkPath path;
+    path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 12, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine26x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 12, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine27() {
+    SkPath path;
+    path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 8, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine27x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 8, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine28() {
+    SkPath path;
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine28x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine29() {
+    SkPath path;
+    path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 12, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine29x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 12, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine30() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 4, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine30x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 4, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine31() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 4, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine31x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 4, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine32() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine32x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine33() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine33x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine34() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine34x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine35() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 0, 18, 18, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine35x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 0, 18, 18, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine36() {
+    SkPath path;
+    path.addRect(0, 10, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 12, 18, 18, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine36x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 10, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 12, 18, 18, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine37() {
+    SkPath path;
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(18, 24, 30, 30, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine37x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(18, 24, 30, 30, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine38() {
+    SkPath path;
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(6, 12, 18, 18, (SkPath::Direction) 0);
+    path.addRect(12, 12, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine38x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(6, 12, 18, 18, (SkPath::Direction) 0);
+    path.addRect(12, 12, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine40() {
+    SkPath path;
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 18, 24, 24, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine40x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 18, 24, 24, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine41() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(18, 24, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine41x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(18, 24, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine42() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(8, 16, 17, 17, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine42x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(8, 16, 17, 17, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine43() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 24, 18, 18, (SkPath::Direction) 0);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine43x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 24, 18, 18, (SkPath::Direction) 0);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine44() {
+    SkPath path;
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 32, 27, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine44x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 32, 27, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine45() {
+    SkPath path;
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 32, 33, 36, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine45x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 32, 33, 36, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine46() {
+    SkPath path;
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 0, 36, 36, (SkPath::Direction) 0);
+    path.addRect(24, 32, 33, 36, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine46x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 0, 36, 36, (SkPath::Direction) 0);
+    path.addRect(24, 32, 33, 36, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine47() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine47x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine48() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine48x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine49() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine49x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine50() {
+    SkPath path;
+    path.addRect(10, 30, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine50x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 30, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine51() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine51x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine52() {
+    SkPath path;
+    path.addRect(0, 30, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 20, 18, 30, (SkPath::Direction) 0);
+    path.addRect(32, 0, 36, 41, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine52x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 30, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 20, 18, 30, (SkPath::Direction) 0);
+    path.addRect(32, 0, 36, 41, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine53() {
+    SkPath path;
+    path.addRect(10, 30, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 20, 24, 30, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine53x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 30, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 20, 24, 30, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine54() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 0, 18, 18, (SkPath::Direction) 0);
+    path.addRect(8, 4, 17, 17, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine54x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 0, 18, 18, (SkPath::Direction) 0);
+    path.addRect(8, 4, 17, 17, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine55() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 6, 18, 18, (SkPath::Direction) 0);
+    path.addRect(4, 4, 13, 13, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine55x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 6, 18, 18, (SkPath::Direction) 0);
+    path.addRect(4, 4, 13, 13, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine56() {
+    SkPath path;
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(18, 20, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine56x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(18, 20, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine57() {
+    SkPath path;
+    path.addRect(20, 0, 40, 40, (SkPath::Direction) 0);
+    path.addRect(20, 0, 30, 40, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine57x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(20, 0, 40, 40, (SkPath::Direction) 0);
+    path.addRect(20, 0, 30, 40, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine58() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 1);
+    path.addRect(0, 12, 9, 9, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine58x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 1);
+    path.addRect(0, 12, 9, 9, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine59() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 6, 18, 18, (SkPath::Direction) 1);
+    path.addRect(4, 4, 13, 13, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine59x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 6, 18, 18, (SkPath::Direction) 1);
+    path.addRect(4, 4, 13, 13, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine60() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 12, 18, 18, (SkPath::Direction) 1);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine60x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 12, 18, 18, (SkPath::Direction) 1);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine61() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(12, 0, 24, 24, (SkPath::Direction) 1);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine61x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(12, 0, 24, 24, (SkPath::Direction) 1);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine62() {
+    SkPath path;
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine62x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine63() {
+    SkPath path;
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 10, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 1);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine63x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 10, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 1);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine64() {
+    SkPath path;
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 6, 30, 30, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine64x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 6, 30, 30, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine65() {
+    SkPath path;
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 0, 36, 36, (SkPath::Direction) 0);
+    path.addRect(32, 6, 36, 41, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine65x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 0, 36, 36, (SkPath::Direction) 0);
+    path.addRect(32, 6, 36, 41, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine66() {
+    SkPath path;
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 30, 20, 20, (SkPath::Direction) 0);
+    path.addRect(12, 20, 24, 30, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine66x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 30, 20, 20, (SkPath::Direction) 0);
+    path.addRect(12, 20, 24, 30, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine67() {
+    SkPath path;
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 0);
+    path.addRect(32, 0, 36, 41, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine67x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 0);
+    path.addRect(32, 0, 36, 41, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68a() {
+    SkPath path;
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 0);
+    path.addRect(1, 2, 4, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68ax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 0);
+    path.addRect(1, 2, 4, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68b() {
+    SkPath path;
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68bx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68c() {
+    SkPath path;
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 0);
+    path.addRect(1, 2, 4, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68cx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 0);
+    path.addRect(1, 2, 4, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68d() {
+    SkPath path;
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 4, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68dx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 4, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68e() {
+    SkPath path;
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68ex() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68f() {
+    SkPath path;
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68fx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68g() {
+    SkPath path;
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68gx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68h() {
+    SkPath path;
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine68hx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine69() {
+    SkPath path;
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine69x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine70() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 24, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine70x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 24, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine71() {
+    SkPath path;
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(12, 0, 24, 24, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine71x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(12, 0, 24, 24, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine72() {
+    SkPath path;
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(6, 20, 18, 30, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine72x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(6, 20, 18, 30, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine73() {
+    SkPath path;
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 40, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine73x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 40, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine74() {
+    SkPath path;
+    path.addRect(20, 30, 40, 40, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 1);
+    path.addRect(32, 24, 36, 41, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine74x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(20, 30, 40, 40, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 1);
+    path.addRect(32, 24, 36, 41, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine75() {
+    SkPath path;
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 1);
+    path.addRect(18, 0, 30, 30, (SkPath::Direction) 1);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine75x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 1);
+    path.addRect(18, 0, 30, 30, (SkPath::Direction) 1);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine76() {
+    SkPath path;
+    path.addRect(36, 0, 66, 60, (SkPath::Direction) 0);
+    path.addRect(10, 20, 40, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 1);
+    path.addRect(32, 6, 36, 41, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine76x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(36, 0, 66, 60, (SkPath::Direction) 0);
+    path.addRect(10, 20, 40, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 1);
+    path.addRect(32, 6, 36, 41, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine77() {
+    SkPath path;
+    path.addRect(20, 0, 40, 40, (SkPath::Direction) 0);
+    path.addRect(24, 6, 36, 36, (SkPath::Direction) 1);
+    path.addRect(24, 32, 33, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine77x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(20, 0, 40, 40, (SkPath::Direction) 0);
+    path.addRect(24, 6, 36, 36, (SkPath::Direction) 1);
+    path.addRect(24, 32, 33, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine78() {
+    SkPath path;
+    path.addRect(0, 0, 30, 60, (SkPath::Direction) 0);
+    path.addRect(10, 20, 30, 30, (SkPath::Direction) 1);
+    path.addRect(18, 20, 30, 30, (SkPath::Direction) 1);
+    path.addRect(32, 0, 36, 41, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine78x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 30, 60, (SkPath::Direction) 0);
+    path.addRect(10, 20, 30, 30, (SkPath::Direction) 1);
+    path.addRect(18, 20, 30, 30, (SkPath::Direction) 1);
+    path.addRect(32, 0, 36, 41, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine79() {
+    SkPath path;
+    path.addRect(0, 36, 60, 30, (SkPath::Direction) 0);
+    path.addRect(10, 30, 40, 30, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 1);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine79x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 36, 60, 30, (SkPath::Direction) 0);
+    path.addRect(10, 30, 40, 30, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 1);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testDegenerate1() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(2, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 0);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testDegenerate1x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(2, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 0);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testDegenerate2() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testDegenerate2x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testDegenerate3() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(2, 0);
+    path.lineTo(1, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(3, 0);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testDegenerate3x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(2, 0);
+    path.lineTo(1, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(3, 0);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testDegenerate4() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.lineTo(1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testDegenerate4x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.lineTo(1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testNondegenerate1() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(1, 1);
+    path.lineTo(2, 1);
+    path.lineTo(1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testNondegenerate1x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(1, 1);
+    path.lineTo(2, 1);
+    path.lineTo(1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testNondegenerate2() {
+    SkPath path;
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 2);
+    path.lineTo(0, 3);
+    path.lineTo(1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testNondegenerate2x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 2);
+    path.lineTo(0, 3);
+    path.lineTo(1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testNondegenerate3() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(1, 1);
+    path.lineTo(0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testNondegenerate3x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(1, 1);
+    path.lineTo(0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testNondegenerate4() {
+    SkPath path;
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 2);
+    path.lineTo(0, 3);
+    path.lineTo(1, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testNondegenerate4x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 2);
+    path.lineTo(0, 3);
+    path.lineTo(1, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral5() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 2);
+    path.lineTo(3, 2);
+    path.lineTo(3, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral5x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 2);
+    path.lineTo(3, 2);
+    path.lineTo(3, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral6() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.lineTo(0, 2);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral6x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.lineTo(0, 2);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testFauxQuadralateral6() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.lineTo(1 + 1.0f/3, 2.0f/3);
+    path.close();
+    path.moveTo(1 + 1.0f/3, 2.0f/3);
+    path.lineTo(0, 2);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testFauxQuadralateral6x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.lineTo(1 + 1.0f/3, 2.0f/3);
+    path.close();
+    path.moveTo(1 + 1.0f/3, 2.0f/3);
+    path.lineTo(0, 2);
+    path.lineTo(2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testFauxQuadralateral6a() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(0, 6);
+    path.lineTo(6, 6);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testFauxQuadralateral6ax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(0, 6);
+    path.lineTo(6, 6);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testFauxQuadralateral6b() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(6, 6);
+    path.lineTo(0, 6);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testFauxQuadralateral6bx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(6, 6);
+    path.lineTo(0, 6);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testFauxQuadralateral6c() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(3, 3);
+    path.lineTo(3, 0);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(0, 6);
+    path.lineTo(6, 6);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testFauxQuadralateral6cx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(3, 3);
+    path.lineTo(3, 0);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(0, 6);
+    path.lineTo(6, 6);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testFauxQuadralateral6d() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(3, 3);
+    path.lineTo(3, 0);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(6, 6);
+    path.lineTo(0, 6);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testFauxQuadralateral6dx() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(3, 3);
+    path.lineTo(3, 0);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(6, 6);
+    path.lineTo(0, 6);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral6a() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(0, 6);
+    path.lineTo(6, 6);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral6ax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(0, 6);
+    path.lineTo(6, 6);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral7() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.lineTo(2, 2);
+    path.lineTo(1, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral7x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.lineTo(2, 2);
+    path.lineTo(1, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral8() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(3, 1);
+    path.lineTo(1, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(2, 1);
+    path.lineTo(0, 2);
+    path.lineTo(3, 2);
+    path.lineTo(2, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral8x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(3, 1);
+    path.lineTo(1, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(2, 1);
+    path.lineTo(0, 2);
+    path.lineTo(3, 2);
+    path.lineTo(2, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral9() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 2);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(1, 1);
+    path.lineTo(2, 1);
+    path.lineTo(1, 3);
+    path.lineTo(2, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadralateral9x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 2);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(1, 1);
+    path.lineTo(2, 1);
+    path.lineTo(1, 3);
+    path.lineTo(2, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testLine1ax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 0, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+}
+
+static void testLine2ax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine3aax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 30, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 20, 30, 30, (SkPath::Direction) 1);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testLine4ax() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 30, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 1);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+    testSimplifyx(path);
+}
+
+static void testQuadratic1() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(1, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic1x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(1, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic2() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(3, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic2x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(3, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic3() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic3x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic4() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic4x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic5() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic6() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 0, 0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic7() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(3, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(3, 0, 1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic8() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic9() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(3, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(1, 2, 3, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic14() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(3, 2, 3, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic15() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.quadTo(1, 1, 0, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic17x() {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 3, 1);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(3, 1, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic18() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic19() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 0, 0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic20() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic21() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testIntersect1() {
+    SkPath one, two, result;
+    one.addRect(0, 0, 6, 6, (SkPath::Direction) 0);
+    two.addRect(3, 3, 9, 9, (SkPath::Direction) 0);
+    operate(one, two, kIntersect_Op, result);
+    SkASSERT(result.countPoints() == 4);
+    SkASSERT(result.countVerbs() == 6); // move, 4 lines, close
+    SkRect bounds = result.getBounds();
+    SkASSERT(bounds.fLeft == 3);
+    SkASSERT(bounds.fTop == 3);
+    SkASSERT(bounds.fRight == 6);
+    SkASSERT(bounds.fBottom == 6);
+}
+
+static void testUnion1() {
+    SkPath one, two, result;
+    one.addRect(0, 0, 6, 6, (SkPath::Direction) 0);
+    two.addRect(3, 3, 9, 9, (SkPath::Direction) 0);
+    operate(one, two, kIntersect_Op, result);
+    SkASSERT(result.countPoints() == 8);
+    SkASSERT(result.countVerbs() == 10); // move, 8 lines, close
+    SkRect bounds = result.getBounds();
+    SkASSERT(bounds.fLeft == 0);
+    SkASSERT(bounds.fTop == 0);
+    SkASSERT(bounds.fRight == 9);
+    SkASSERT(bounds.fBottom == 9);
+}
+
+static void testDiff1() {
+    SkPath one, two, result;
+    one.addRect(0, 0, 6, 6, (SkPath::Direction) 0);
+    two.addRect(3, 3, 9, 9, (SkPath::Direction) 0);
+    operate(one, two, kIntersect_Op, result);
+    SkASSERT(result.countPoints() == 6);
+    SkASSERT(result.countVerbs() == 8); // move, 8 lines, close
+    SkRect bounds = result.getBounds();
+    SkASSERT(bounds.fLeft == 0);
+    SkASSERT(bounds.fTop == 0);
+    SkASSERT(bounds.fRight == 6);
+    SkASSERT(bounds.fBottom == 6);
+}
+
+static void testXor1() {
+    SkPath one, two, result;
+    one.addRect(0, 0, 6, 6, (SkPath::Direction) 0);
+    two.addRect(3, 3, 9, 9, (SkPath::Direction) 0);
+    operate(one, two, kIntersect_Op, result);
+    SkASSERT(result.countPoints() == 10);
+    SkASSERT(result.countVerbs() == 12); // move, 8 lines, close
+    SkRect bounds = result.getBounds();
+    SkASSERT(bounds.fLeft == 0);
+    SkASSERT(bounds.fTop == 0);
+    SkASSERT(bounds.fRight == 12);
+    SkASSERT(bounds.fBottom == 12);
+}
+
+static void testQuadratic22() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 1, 2, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic23() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 2, 1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic24() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(2, 0, 0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic25() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic26() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic27() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic28() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 2);
+    path.quadTo(1, 2, 0, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic29() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 2, 1);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic30() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 2);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic31() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 2);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 1, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic32() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 2, 3);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(3, 1, 0, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic33() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(2, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.quadTo(2, 1, 2, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic34() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(2, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.quadTo(2, 1, 1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic35() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(3, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic36() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(2, 1, 2, 3);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(3, 1);
+    path.lineTo(1, 2);
+    path.quadTo(3, 2, 1, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic37() {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.quadTo(0, 2, 1, 2);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(3, 1);
+    path.quadTo(0, 2, 1, 2);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void testQuadratic38() {
+    SkPath path;
+    path.moveTo(1, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 2);
+    path.quadTo(2, 2, 1, 3);
+    path.close();
+    testSimplifyx(path);
+}
+
+static void (*firstTest)() = testQuadratic7;
+
+static struct {
+    void (*fun)();
+    const char* str;
+} tests[] = {
+    TEST(testQuadratic38),
+    TEST(testQuadratic37),
+    TEST(testQuadratic36),
+    TEST(testQuadratic35),
+    TEST(testQuadratic34),
+    TEST(testQuadratic33),
+    TEST(testQuadratic32),
+    TEST(testQuadratic31),
+    TEST(testQuadratic30),
+    TEST(testQuadratic29),
+    TEST(testQuadratic28),
+    TEST(testQuadratic27),
+    TEST(testQuadratic26),
+    TEST(testQuadratic25),
+    TEST(testQuadratic24),
+    TEST(testQuadratic23),
+    TEST(testQuadratic22),
+    TEST(testQuadratic21),
+    TEST(testQuadratic20),
+    TEST(testQuadratic19),
+    TEST(testQuadratic18),
+    TEST(testQuadratic17x),
+    TEST(testQuadratic15),
+    TEST(testQuadratic14),
+    TEST(testQuadratic9),
+    TEST(testQuadratic8),
+    TEST(testQuadratic7),
+    TEST(testQuadratic6),
+    TEST(testQuadratic5),
+    TEST(testQuadratic4x),
+    TEST(testQuadratic3x),
+    TEST(testQuadratic2x),
+    TEST(testQuadratic1x),
+    TEST(testQuadratic4),
+    TEST(testQuadratic3),
+    TEST(testQuadratic2),
+    TEST(testQuadratic1),
+    TEST(testLine4ax),
+    TEST(testLine3aax),
+    TEST(testLine2ax),
+    TEST(testLine1ax),
+    TEST(testQuadralateral9x),
+    TEST(testQuadralateral8x),
+    TEST(testQuadralateral7x),
+    TEST(testQuadralateral6x),
+    TEST(testQuadralateral6ax),
+    TEST(testQuadralateral9),
+    TEST(testQuadralateral8),
+    TEST(testQuadralateral7),
+    TEST(testQuadralateral6),
+    TEST(testQuadralateral6a),
+    TEST(testFauxQuadralateral6dx),
+    TEST(testFauxQuadralateral6cx),
+    TEST(testFauxQuadralateral6bx),
+    TEST(testFauxQuadralateral6ax),
+    TEST(testFauxQuadralateral6x),
+    TEST(testFauxQuadralateral6d),
+    TEST(testFauxQuadralateral6c),
+    TEST(testFauxQuadralateral6b),
+    TEST(testFauxQuadralateral6a),
+    TEST(testFauxQuadralateral6),
+    TEST(testQuadralateral5x),
+    TEST(testQuadralateral5),
+    TEST(testNondegenerate4x),
+    TEST(testNondegenerate3x),
+    TEST(testNondegenerate2x),
+    TEST(testNondegenerate1x),
+    TEST(testNondegenerate4),
+    TEST(testNondegenerate3),
+    TEST(testNondegenerate2),
+    TEST(testNondegenerate1),
+    TEST(testDegenerate4x),
+    TEST(testDegenerate3x),
+    TEST(testDegenerate2x),
+    TEST(testDegenerate1x),
+    TEST(testDegenerate4),
+    TEST(testDegenerate3),
+    TEST(testDegenerate2),
+    TEST(testDegenerate1),
+    TEST(testLine79x),
+    TEST(testLine78x),
+    TEST(testLine77x),
+    TEST(testLine76x),
+    TEST(testLine75x),
+    TEST(testLine74x),
+    TEST(testLine73x),
+    TEST(testLine72x),
+    TEST(testLine71x),
+    TEST(testLine70x),
+    TEST(testLine69x),
+    TEST(testLine68hx),
+    TEST(testLine68gx),
+    TEST(testLine68fx),
+    TEST(testLine68ex),
+    TEST(testLine68dx),
+    TEST(testLine68cx),
+    TEST(testLine68bx),
+    TEST(testLine68ax),
+    TEST(testLine67x),
+    TEST(testLine66x),
+    TEST(testLine65x),
+    TEST(testLine64x),
+    TEST(testLine63x),
+    TEST(testLine62x),
+    TEST(testLine61x),
+    TEST(testLine60x),
+    TEST(testLine59x),
+    TEST(testLine58x),
+    TEST(testLine57x),
+    TEST(testLine56x),
+    TEST(testLine55x),
+    TEST(testLine54x),
+    TEST(testLine53x),
+    TEST(testLine52x),
+    TEST(testLine51x),
+    TEST(testLine50x),
+    TEST(testLine49x),
+    TEST(testLine48x),
+    TEST(testLine47x),
+    TEST(testLine46x),
+    TEST(testLine45x),
+    TEST(testLine44x),
+    TEST(testLine43x),
+    TEST(testLine42x),
+    TEST(testLine41x),
+    TEST(testLine40x),
+    TEST(testLine38x),
+    TEST(testLine37x),
+    TEST(testLine36x),
+    TEST(testLine35x),
+    TEST(testLine34x),
+    TEST(testLine33x),
+    TEST(testLine32x),
+    TEST(testLine31x),
+    TEST(testLine30x),
+    TEST(testLine29x),
+    TEST(testLine28x),
+    TEST(testLine27x),
+    TEST(testLine26x),
+    TEST(testLine25x),
+    TEST(testLine24ax),
+    TEST(testLine24x),
+    TEST(testLine23x),
+    TEST(testLine22x),
+    TEST(testLine21x),
+    TEST(testLine20x),
+    TEST(testLine19x),
+    TEST(testLine18x),
+    TEST(testLine17x),
+    TEST(testLine16x),
+    TEST(testLine15x),
+    TEST(testLine14x),
+    TEST(testLine13x),
+    TEST(testLine12x),
+    TEST(testLine11x),
+    TEST(testLine10ax),
+    TEST(testLine10x),
+    TEST(testLine9x),
+    TEST(testLine8x),
+    TEST(testLine7bx),
+    TEST(testLine7ax),
+    TEST(testLine7x),
+    TEST(testLine6x),
+    TEST(testLine5x),
+    TEST(testLine4x),
+    TEST(testLine3bx),
+    TEST(testLine3ax),
+    TEST(testLine3x),
+    TEST(testLine2x),
+    TEST(testLine1x),
+    TEST(testLine79),
+    TEST(testLine78),
+    TEST(testLine77),
+    TEST(testLine76),
+    TEST(testLine75),
+    TEST(testLine74),
+    TEST(testLine73),
+    TEST(testLine72),
+    TEST(testLine71),
+    TEST(testLine70),
+    TEST(testLine69),
+    TEST(testLine68h),
+    TEST(testLine68g),
+    TEST(testLine68f),
+    TEST(testLine68e),
+    TEST(testLine68d),
+    TEST(testLine68c),
+    TEST(testLine68b),
+    TEST(testLine68a),
+    TEST(testLine67),
+    TEST(testLine66),
+    TEST(testLine65),
+    TEST(testLine64),
+    TEST(testLine63),
+    TEST(testLine62),
+    TEST(testLine61),
+    TEST(testLine60),
+    TEST(testLine59),
+    TEST(testLine58),
+    TEST(testLine57),
+    TEST(testLine56),
+    TEST(testLine55),
+    TEST(testLine54),
+    TEST(testLine53),
+    TEST(testLine52),
+    TEST(testLine51),
+    TEST(testLine50),
+    TEST(testLine49),
+    TEST(testLine48),
+    TEST(testLine47),
+    TEST(testLine46),
+    TEST(testLine45),
+    TEST(testLine44),
+    TEST(testLine43),
+    TEST(testLine42),
+    TEST(testLine41),
+    TEST(testLine40),
+    TEST(testLine38),
+    TEST(testLine37),
+    TEST(testLine36),
+    TEST(testLine35),
+    TEST(testLine34),
+    TEST(testLine33),
+    TEST(testLine32),
+    TEST(testLine31),
+    TEST(testLine30),
+    TEST(testLine29),
+    TEST(testLine28),
+    TEST(testLine27),
+    TEST(testLine26),
+    TEST(testLine25),
+    TEST(testLine24a),
+    TEST(testLine24),
+    TEST(testLine23),
+    TEST(testLine22),
+    TEST(testLine21),
+    TEST(testLine20),
+    TEST(testLine19),
+    TEST(testLine18),
+    TEST(testLine17),
+    TEST(testLine16),
+    TEST(testLine15),
+    TEST(testLine14),
+    TEST(testLine13),
+    TEST(testLine12),
+    TEST(testLine11),
+    TEST(testLine10a),
+    TEST(testLine10),
+    TEST(testLine9),
+    TEST(testLine8),
+    TEST(testLine7b),
+    TEST(testLine7a),
+    TEST(testLine7),
+    TEST(testLine6),
+    TEST(testLine5),
+    TEST(testLine4),
+    TEST(testLine3b),
+    TEST(testLine3a),
+    TEST(testLine3),
+    TEST(testLine2),
+    TEST(testLine1),
+};
+
+static const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+
+static struct {
+    void (*fun)();
+    const char* str;
+} subTests[] = {
+    TEST(testXor1),
+    TEST(testDiff1),
+    TEST(testUnion1),
+    TEST(testIntersect1),
+};
+
+static const size_t subTestCount = sizeof(subTests) / sizeof(subTests[0]);
+
+static bool skipAll = false;
+static bool runSubTests = false;
+static bool runReverse = false;
+
+void SimplifyNew_Test() {
+    if (skipAll) {
+        return;
+    }
+#ifdef SK_DEBUG
+    gDebugMaxWindSum = 4;
+    gDebugMaxWindValue = 4;
+    size_t index;
+#endif
+    if (runSubTests) {
+        index = subTestCount - 1;
+        do {
+            SkDebugf("  %s [%s]\n", __FUNCTION__, subTests[index].str);
+            (*subTests[index].fun)();
+        } while (index--);
+    }
+    index = testCount - 1;
+    if (firstTest) {
+        while (index > 0 && tests[index].fun != firstTest) {
+            --index;
+        }
+        SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
+        (*tests[index].fun)();
+    }
+    index = runReverse ? testCount - 1 : 0;
+    size_t last = runReverse ? 0 : testCount - 1;
+    bool firstTestComplete = false;
+    do {
+        SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
+        (*tests[index].fun)();
+        firstTestComplete = true;
+        if (index == last) {
+            break;
+        }
+        index += runReverse ? -1 : 1;
+    } while (true);
+#ifdef SK_DEBUG
+    gDebugMaxWindSum = SK_MaxS32;
+    gDebugMaxWindValue = SK_MaxS32;
+#endif
+}
diff --git a/experimental/Intersection/SimplifyRect4x4_Test.cpp b/experimental/Intersection/SimplifyRect4x4_Test.cpp
new file mode 100644
index 0000000..cdd43dd
--- /dev/null
+++ b/experimental/Intersection/SimplifyRect4x4_Test.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 "EdgeWalker_Test.h"
+#include "Intersection_Tests.h"
+#include "ShapeOps.h"
+
+// four rects, of four sizes
+// for 3 smaller sizes, tall, wide
+    // top upper mid lower bottom aligned (3 bits, 5 values)
+    // same with x (3 bits, 5 values)
+// not included, square, tall, wide (2 bits)
+// cw or ccw (1 bit)
+
+static void* testSimplify4x4RectsMain(void* data)
+{
+    SkASSERT(data);
+    State4& state = *(State4*) data;
+    char pathStr[1024]; // gdb: set print elements 400
+    bzero(pathStr, sizeof(pathStr));
+    do {
+        int aShape = state.a & 0x03;
+        int aCW = state.a >> 2;
+        int bShape = state.b & 0x03;
+        int bCW = state.b >> 2;
+        int cShape = state.c & 0x03;
+        int cCW = state.c >> 2;
+        int dShape = state.d & 0x03;
+        int dCW = state.d >> 2;
+        for (int aXAlign = 0 ; aXAlign < 5; ++aXAlign) {
+        for (int aYAlign = 0 ; aYAlign < 5; ++aYAlign)      {
+        for (int bXAlign = 0 ; bXAlign < 5; ++bXAlign)          {
+        for (int bYAlign = 0 ; bYAlign < 5; ++bYAlign)              {
+        for (int cXAlign = 0 ; cXAlign < 5; ++cXAlign)                  {
+        for (int cYAlign = 0 ; cYAlign < 5; ++cYAlign)                      {
+        for (int dXAlign = 0 ; dXAlign < 5; ++dXAlign)                          {
+        for (int dYAlign = 0 ; dYAlign < 5; ++dYAlign)                              {
+            SkPath path, out;
+            char* str = pathStr;
+            path.setFillType(SkPath::kWinding_FillType);
+            int l, t, r, b;
+            if (aShape) {
+                switch (aShape) {
+                    case 1: // square
+                        l =  0; r = 60;
+                        t =  0; b = 60;
+                        aXAlign = 5;
+                        aYAlign = 5;
+                        break;
+                    case 2:
+                        l =  aXAlign * 12;
+                        r =  l + 30;
+                        t =  0; b = 60;
+                        aYAlign = 5;
+                        break;
+                    case 3:
+                        l =  0; r = 60;
+                        t =  aYAlign * 12;
+                        b =  l + 30;
+                        aXAlign = 5;
+                        break;
+                }
+                path.addRect(l, t, r, b, (SkPath::Direction) aCW);
+                str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
+                        " (SkPath::Direction) %d);\n", l, t, r, b, aCW);
+            } else {
+                aXAlign = 5;
+                aYAlign = 5;
+            }
+            if (bShape) {
+                switch (bShape) {
+                    case 1: // square
+                        l =  bXAlign * 10;
+                        r =  l + 20;
+                        t =  bYAlign * 10;
+                        b =  l + 20;
+                        break;
+                    case 2:
+                        l =  bXAlign * 10;
+                        r =  l + 20;
+                        t =  10; b = 40;
+                        bYAlign = 5;
+                        break;
+                    case 3:
+                        l =  10; r = 40;
+                        t =  bYAlign * 10;
+                        b =  l + 20;
+                        bXAlign = 5;
+                        break;
+                }
+                path.addRect(l, t, r, b, (SkPath::Direction) bCW);
+                str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
+                        " (SkPath::Direction) %d);\n", l, t, r, b, bCW);
+            } else {
+                bXAlign = 5;
+                bYAlign = 5;
+            }
+            if (cShape) {
+                switch (cShape) {
+                    case 1: // square
+                        l =  cXAlign * 6;
+                        r =  l + 12;
+                        t =  cYAlign * 6;
+                        b =  l + 12;
+                        break;
+                    case 2:
+                        l =  cXAlign * 6;
+                        r =  l + 12;
+                        t =  20; b = 30;
+                        cYAlign = 5;
+                        break;
+                    case 3:
+                        l =  20; r = 30;
+                        t =  cYAlign * 6;
+                        b =  l + 20;
+                        cXAlign = 5;
+                        break;
+                }
+                path.addRect(l, t, r, b, (SkPath::Direction) cCW);
+                str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
+                        " (SkPath::Direction) %d);\n", l, t, r, b, cCW);
+            } else {
+                cXAlign = 5;
+                cYAlign = 5;
+            }
+            if (dShape) {
+                switch (dShape) {
+                    case 1: // square
+                        l =  dXAlign * 4;
+                        r =  l + 9;
+                        t =  dYAlign * 4;
+                        b =  l + 9;
+                        break;
+                    case 2:
+                        l =  dXAlign * 6;
+                        r =  l + 9;
+                        t =  32; b = 36;
+                        dYAlign = 5;
+                        break;
+                    case 3:
+                        l =  32; r = 36;
+                        t =  dYAlign * 6;
+                        b =  l + 9;
+                        dXAlign = 5;
+                        break;
+                }
+                path.addRect(l, t, r, b, (SkPath::Direction) dCW);
+                str += sprintf(str, "    path.addRect(%d, %d, %d, %d,"
+                        " (SkPath::Direction) %d);\n", l, t, r, b, dCW);
+            } else {
+                dXAlign = 5;
+                dYAlign = 5;
+            }
+            path.close();
+            outputProgress(state, pathStr, SkPath::kWinding_FillType);
+            testSimplifyx(path, false, out, state, pathStr);
+            state.testsRun++;
+            outputProgress(state, pathStr, SkPath::kEvenOdd_FillType);
+            testSimplifyx(path, true, out, state, pathStr);
+            state.testsRun++;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    } while (runNextTestSet(state));
+    return NULL;
+}
+
+void Simplify4x4RectsThreaded_Test(int& testsRun)
+{
+    SkDebugf("%s\n", __FUNCTION__);
+#ifdef SK_DEBUG
+    gDebugMaxWindSum = 4;
+    gDebugMaxWindValue = 4;
+#endif
+    const char testLineStr[] = "testLine";
+    initializeTests(testLineStr, sizeof(testLineStr));
+    int testsStart = testsRun;
+    for (int a = 0; a < 8; ++a) { // outermost
+        for (int b = a ; b < 8; ++b) {
+            for (int c = b ; c < 8; ++c) {
+                for (int d = c; d < 8; ++d) {
+                    testsRun += dispatchTest4(testSimplify4x4RectsMain, a, b, c, d);
+                }
+                if (!gRunTestsInOneThread) SkDebugf(".");
+            }
+            if (!gRunTestsInOneThread) SkDebugf("%d", b);
+        }
+        if (!gRunTestsInOneThread) SkDebugf("\n%d", a);
+    }
+    testsRun += waitForCompletion();
+    SkDebugf("%s tests=%d total=%d\n", __FUNCTION__, testsRun - testsStart, testsRun);
+}
+
diff --git a/experimental/Intersection/SkAntiEdge.cpp b/experimental/Intersection/SkAntiEdge.cpp
new file mode 100644
index 0000000..eb7dc34
--- /dev/null
+++ b/experimental/Intersection/SkAntiEdge.cpp
@@ -0,0 +1,1082 @@
+/*
+ *  SkAntiEdge.cpp
+ *  core
+ *
+ *  Created by Cary Clark on 5/6/11.
+ *  Copyright 2011 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#include "SkAntiEdge.h"
+#include "SkPoint.h"
+
+void SkAntiEdge::pointOnLine(SkFixed x, SkFixed y) {
+    float x0 = SkFixedToFloat(x);
+    float y0 = SkFixedToFloat(y);
+    float x1 = SkFixedToFloat(fFirstX);
+    float y1 = SkFixedToFloat(fFirstY);
+    float x2 = SkFixedToFloat(fLastX);
+    float y2 = SkFixedToFloat(fLastY);
+    float numer = (x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1);
+    float denom = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
+    double dist = fabs(numer) / sqrt(denom);
+    SkAssertResult(dist < 0.01);
+}
+
+void SkAntiEdge::pointInLine(SkFixed x, SkFixed y) {
+    if (y == SK_MaxS32) {
+        return;
+    }
+    pointOnLine(x, y);
+    SkAssertResult(y >= fFirstY && y <= fLastY);
+}
+
+void SkAntiEdge::validate() {
+    pointOnLine(fWalkX, fY);
+    pointOnLine(fX, fWalkY);
+}
+
+bool SkAntiEdge::setLine(const SkPoint& p0, const SkPoint& p1) {
+    fFirstY = SkScalarToFixed(p0.fY);
+    fLastY = SkScalarToFixed(p1.fY);
+    if (fFirstY == fLastY) {
+        return false;
+    }
+    fFirstX = SkScalarToFixed(p0.fX);
+    fLastX = SkScalarToFixed(p1.fX);
+    if (fFirstY > fLastY) {
+        SkTSwap(fFirstX, fLastX);
+        SkTSwap(fFirstY, fLastY);
+        fWinding = -1;
+    } else {
+        fWinding = 1;
+    }
+    SkFixed dx = fLastX - fFirstX;
+    fDXFlipped = dx < 0;
+    SkFixed dy = fLastY - fFirstY;
+    fDX = SkFixedDiv(dx, dy);
+    fDY = dx == 0 ? SK_MaxS32 : SkFixedDiv(dy, SkFixedAbs(dx));
+    fLink = NULL;
+    fLinkSet = false;
+    return true;
+}
+
+void SkAntiEdge::calcLine() {
+    SkFixed yStartFrac = SkFixedFraction(fFirstY);
+    if (fDXFlipped) {
+        SkFixed vert = SK_Fixed1 - yStartFrac; // distance from y start to x-axis
+        fX0 = fFirstX + SkFixedMul(fDX, vert);
+        SkFixed backupX = fFirstX + SkFixedMul(vert, fDX); // x cell to back up to
+        SkFixed cellX = SkIntToFixed(SkFixedFloor(backupX));
+        SkFixed endX = SkIntToFixed(SkFixedFloor(fLastX));
+        if (cellX < endX) {
+            cellX = endX;
+        }
+        SkFixed distX = fFirstX - cellX; // to y-axis
+        fY0 = fFirstY + SkFixedMul(fDY, distX);
+        SkFixed rowBottom = SkIntToFixed(SkFixedCeil(fFirstY + 1));
+        if (fLastY > rowBottom) {
+            fPartialY = 0;
+            fX = fX0;
+            fY = rowBottom;
+        } else {
+            fPartialY = SkFixedFraction(fLastY);
+            fX = fLastX;
+            fY = fLastY;
+        }
+    } else {
+        fPartialY = yStartFrac;
+        fX0 = fFirstX - SkFixedMul(fDX, yStartFrac);
+        fY0 = fFirstY;
+        if (fDY != SK_MaxS32) {
+            SkFixed xStartFrac = SkFixedFraction(fFirstX);
+            fY0 -= SkFixedMul(fDY, xStartFrac);
+        }
+        fX = fFirstX;
+        fY = fFirstY;
+    }
+    fWalkX = fX;
+    fWalkY = fY;
+    fFinished = false;
+}
+
+static SkFixed SkFixedAddPin(SkFixed a, SkFixed b) {
+    SkFixed result = a + b;
+    if (((a ^ ~b) & (a ^ result)) >= 0) { // one positive, one negative
+        return result;                    //  or all three same sign
+    }
+    return a < 0 ? -SK_FixedMax : SK_FixedMax;
+}
+
+// edge is increasing in x and y
+uint16_t SkAntiEdge::advanceX(SkFixed left) {
+    validate();
+    SkFixed x = SkFixedAddPin(fX0, fDX);
+    SkFixed wy = SkIntToFixed(SkFixedFloor(fWalkY + SK_Fixed1));
+    pointOnLine(x, wy);
+    SkFixed partial = SK_Fixed1 - fPartialY;
+    SkFixed bottomPartial = wy - fLastY;
+    if (bottomPartial > 0) {
+        partial -= bottomPartial;
+    }
+    if (x > fLastX) {
+        x = fLastX;
+        wy = fLastY;
+    }
+    uint16_t coverage;
+    if (left >= x) {
+        fFinished = true;
+        coverage = partial - 1; // walker is to the right of edge
+    } else {
+        SkFixed y = SkFixedAddPin(fY0, fDY);
+        SkFixed wx = SkIntToFixed(SkFixedFloor(fWalkX + SK_Fixed1));
+        if (fDY != SK_MaxS32) {
+            pointOnLine(wx, y);
+        }
+        if (y > fLastY) {
+            y = fLastY;
+            wx = fLastX;
+        }
+        bool topCorner = fWalkX <= fX;
+        bool bottomCorner = x <= wx;
+        bool halfPlane = !(topCorner ^ bottomCorner);
+        if (halfPlane) {
+            if (x - SkIntToFixed(SkFixedFloor(fX)) <= SK_Fixed1) {
+                coverage = ~((fX + x) >> 1); // avg of fx, fx+dx
+                fFinished = true;
+                if (x >= left + SK_Fixed1) {
+                    fWalkX = wx;
+                    fY = fY0 = y;
+                }
+            } else {
+                SkAssertResult(y - SkIntToFixed(SkFixedFloor(fY)) <= SK_Fixed1);
+                coverage = ((fY + y) >> 1);
+                fFinished = y == fLastY;
+                fWalkX = wx;
+                fY = fY0 = y;
+            }
+            coverage = coverage * partial >> 16;
+        } else if (topCorner) {
+            SkFixed xDiff = wx - fX;
+            SkAssertResult(xDiff >= 0);
+            SkAssertResult(xDiff <= SK_Fixed1);
+            SkFixed yDiff = y - fWalkY;
+            // This may be a very small negative number if error accumulates
+            // FIXME: for now, try setting it to zero in that case.
+            if (yDiff < 0) {
+                fX = fX0 = SkIntToFixed(SkFixedCeil(fX));
+                yDiff = 0;
+            }
+            SkAssertResult(yDiff >= 0);
+            SkAssertResult(yDiff <= SK_Fixed1);
+            int xCoverage = xDiff >> 1; // throw away 1 bit so multiply
+            int yCoverage = yDiff >> 1; //  stays in range
+            int triangle = xCoverage * yCoverage; // 30 bits
+            SkFixed bottomPartial = y - fLastY;
+            fFinished = bottomPartial >= 0;
+            if (fFinished) {
+                yCoverage = bottomPartial >> 1;
+                xCoverage = (wx - fLastX) >> 1;
+                triangle -= xCoverage * yCoverage;
+            }
+            coverage = triangle >> 15;
+            fWalkX = wx;
+            fY = fY0 = y;
+        } else {
+            SkAssertResult(bottomCorner);
+            SkFixed xDiff = x - fWalkX;
+            SkAssertResult(xDiff >= 0);
+            SkAssertResult(xDiff <= SK_Fixed1);
+            SkFixed yDiff = wy - fY;
+            SkAssertResult(yDiff >= 0);
+            SkAssertResult(yDiff <= SK_Fixed1);
+            int xCoverage = xDiff >> 1; // throw away 1 bit so multiply
+            int yCoverage = yDiff >> 1; //  stays in range
+            int triangle = xCoverage * yCoverage >> 15;
+            coverage = partial - 1 - triangle;
+            fFinished = true;
+        }
+    }
+    validate();
+    return coverage;
+}
+
+// edge is increasing in x, but decreasing in y
+uint16_t SkAntiEdge::advanceFlippedX(SkFixed left) {
+    validate();
+    SkFixed x = SkFixedAddPin(fX0, -fDX);
+    SkFixed wy = SkIntToFixed(SkFixedFloor(fWalkY - 1));
+    pointOnLine(x, wy);
+    SkFixed partial = fPartialY ? fPartialY : SK_Fixed1;
+    SkFixed topPartial = fFirstY - wy;
+    if (topPartial > 0) {
+        partial -= topPartial;
+    }
+    if (x > fFirstX) {
+        x = fFirstX;
+        wy = fFirstY;
+    }
+    uint16_t coverage;
+    if (left >= x) {
+        fFinished = true;
+        coverage = partial - 1; // walker is to the right of edge
+    } else {
+        SkFixed y = SkFixedAddPin(fY0, -fDY);
+        SkFixed wx = SkIntToFixed(SkFixedFloor(fWalkX + SK_Fixed1));
+        pointOnLine(wx, y);
+        if (y < fFirstY) {
+            y = fFirstY;
+            wx = fFirstX;
+        }
+        bool bottomCorner = fWalkX <= fX;
+        bool topCorner = x <= wx;
+        bool halfPlane = !(topCorner ^ bottomCorner);
+        if (halfPlane) {
+            if (x - SkIntToFixed(SkFixedFloor(fX)) <= SK_Fixed1) {
+                coverage = ~((fX + x) >> 1); // avg of fx, fx+dx
+                fFinished = true;
+            } else {
+                SkAssertResult(y - SkIntToFixed(SkFixedFloor(fY)) <= SK_Fixed1);
+                coverage = ~((fY + y) >> 1);
+                fFinished = y == fY;
+                fWalkX = wx;
+                fY = fY0 = y;
+            }
+            coverage = coverage * partial >> 16;
+        } else if (bottomCorner) {
+            SkFixed xDiff = wx - fX;
+            SkAssertResult(xDiff >= 0);
+            SkAssertResult(xDiff <= SK_Fixed1);
+            SkFixed yDiff = fWalkY - y;
+            SkAssertResult(yDiff >= 0);
+            SkAssertResult(yDiff <= SK_Fixed1);
+            int xCoverage = xDiff >> 1; // throw away 1 bit so multiply
+            int yCoverage = yDiff >> 1; //  stays in range
+            int triangle = xCoverage * yCoverage; // 30 bits
+            SkFixed bottomPartial = fFirstY - y;
+            fFinished = bottomPartial >= 0;
+            if (fFinished) {
+                yCoverage = bottomPartial >> 1;
+                xCoverage = (wx - fFirstX) >> 1;
+                triangle -= xCoverage * yCoverage;
+            }
+            coverage = triangle >> 15;
+            fWalkX = wx;
+            fY = fY0 = y;
+        } else {
+            SkAssertResult(topCorner);
+            SkFixed xDiff = x - fWalkX;
+            SkAssertResult(xDiff >= 0);
+            SkAssertResult(xDiff <= SK_Fixed1);
+            SkFixed yDiff = fY - wy;
+            SkAssertResult(yDiff >= 0);
+            SkAssertResult(yDiff <= SK_Fixed1);
+            int xCoverage = xDiff >> 1; // throw away 1 bit so multiply
+            int yCoverage = yDiff >> 1; //  stays in range
+            int triangle = xCoverage * yCoverage >> 15;
+            coverage = partial - 1 - triangle;
+            fFinished = true;
+        }
+    }
+    validate();
+    return coverage;
+}
+
+void SkAntiEdge::advanceY(SkFixed top) {
+    validate();
+    fX0 = SkFixedAddPin(fX0, fDX);
+    fPartialY = 0;
+    if (fDXFlipped) {
+        if (fX0 < fLastX) {
+            fWalkX = fX = fLastX;
+        } else {
+            fWalkX = fX = fX0;
+        }
+        SkFixed bottom = top + SK_Fixed1;
+        if (bottom > fLastY) {
+            bottom = fLastY;
+        }
+        SkFixed vert = bottom - fFirstY; // distance from y start to x-axis
+        SkFixed backupX = fFirstX + SkFixedMul(vert, fDX); // x cell to back up to
+        SkFixed distX = fFirstX - SkIntToFixed(SkFixedFloor(backupX)); // to y-axis
+        fY0 = fFirstY + SkFixedMul(fDY, distX);
+
+        fY = top + SK_Fixed1;
+        if (fY > fLastY) {
+            fY = fLastY;
+        }
+        if (fLastY < top + SK_Fixed1) {
+            fPartialY = SkFixedFraction(fLastY);
+        }
+    } else {
+        if (fX0 > fLastX) {
+            fX0 = fLastX;
+        }
+        fX = fX0;
+    }
+    fWalkY = SkIntToFixed(SkFixedFloor(fWalkY + SK_Fixed1));
+    if (fWalkY > fLastY) {
+        fWalkY = fLastY;
+    }
+    validate();
+    fFinished = false;
+}
+
+int SkAntiEdgeBuilder::build(const SkPoint pts[], int count) {
+    SkAntiEdge* edge = fEdges.append();
+    for (int index = 0; index < count; ++index) {
+        if (edge->setLine(pts[index], pts[(index + 1) % count])) {
+            edge = fEdges.append();
+        }
+    }
+    int result = fEdges.count();
+    fEdges.setCount(--result);
+    if (result > 0) {
+        sk_bzero(&fHeadEdge, sizeof(fHeadEdge));
+        sk_bzero(&fTailEdge, sizeof(fTailEdge));
+        for (int index = 0; index < result; ++index) {
+            *fList.append() = &fEdges[index];
+        }
+    }
+    return result;
+}
+
+void SkAntiEdgeBuilder::calc() {
+    for (SkAntiEdge* active = fEdges.begin(); active != fEdges.end(); ++active) {
+        active->calcLine();
+    }
+    // compute winding sum for edges
+    SkAntiEdge* first = fHeadEdge.fNext;
+    SkAntiEdge* active;
+    SkAntiEdge* listTop = first;
+    for (active = first; active != &fTailEdge; active = active->fNext) {
+        active->fWindingSum = active->fWinding;
+        while (listTop->fLastY < active->fFirstY) {
+            listTop = listTop->fNext;
+        }
+        for (SkAntiEdge* check = listTop; check->fFirstY <= active->fFirstY; check = check->fNext) {
+            if (check == active) {
+                continue;
+            }
+            if (check->fLastY <= active->fFirstY) {
+                continue;
+            }
+            if (check->fFirstX > active->fFirstX) {
+                continue;
+            }
+            if (check->fFirstX == active->fFirstX && check->fDX > active->fDX) {
+                continue;
+            }
+            active->fWindingSum += check->fWinding;
+        }
+    }
+}
+
+extern "C" {
+    static int edge_compare(const void* a, const void* b) {
+        const SkAntiEdge* edgea = *(const SkAntiEdge**)a;
+        const SkAntiEdge* edgeb = *(const SkAntiEdge**)b;
+
+        int valuea = edgea->fFirstY;
+        int valueb = edgeb->fFirstY;
+
+        if (valuea == valueb) {
+            valuea = edgea->fFirstX;
+            valueb = edgeb->fFirstX;
+        }
+
+        if (valuea == valueb) {
+            valuea = edgea->fDX;
+            valueb = edgeb->fDX;
+        }
+
+        return valuea - valueb;
+    }
+}
+
+void SkAntiEdgeBuilder::sort(SkTDArray<SkAntiEdge*>& listOfEdges) {
+    SkAntiEdge** list = listOfEdges.begin();
+    int count = listOfEdges.count();
+    qsort(list, count, sizeof(SkAntiEdge*), edge_compare);
+
+    // link the edges in sorted order
+    for (int i = 1; i < count; i++) {
+        list[i - 1]->fNext = list[i];
+        list[i]->fPrev = list[i - 1];
+    }
+}
+
+#define kEDGE_HEAD_XY    SK_MinS32
+#define kEDGE_TAIL_XY    SK_MaxS32
+
+void SkAntiEdgeBuilder::sort() {
+    sort(fList);
+    SkAntiEdge* last = fList.end()[-1];
+    fHeadEdge.fNext = fList[0];
+    fHeadEdge.fFirstX = fHeadEdge.fFirstY = fHeadEdge.fWalkY = fHeadEdge.fLastY = kEDGE_HEAD_XY;
+    fList[0]->fPrev = &fHeadEdge;
+
+    fTailEdge.fPrev = last;
+    fTailEdge.fFirstX = fTailEdge.fFirstY = fTailEdge.fWalkY = fTailEdge.fLastY = kEDGE_TAIL_XY;
+    last->fNext = &fTailEdge;
+}
+
+static inline void remove_edge(SkAntiEdge* edge) {
+    edge->fPrev->fNext = edge->fNext;
+    edge->fNext->fPrev = edge->fPrev;
+}
+
+static inline void swap_edges(SkAntiEdge* prev, SkAntiEdge* 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(SkAntiEdge* edge SkDECLAREPARAM(int, y)) {
+    SkFixed x = edge->fFirstX;
+
+    for (;;) {
+        SkAntiEdge* 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 && SkFixedFloor(prev->fWalkY - prev->fDXFlipped) <= y + 1);
+
+        if (prev->fFirstX <= x) {
+            break;
+        }
+        swap_edges(prev, edge);
+    }
+}
+
+static void insert_new_edges(SkAntiEdge* newEdge, SkFixed curr_y) {
+    int y = SkFixedFloor(curr_y);
+    if (SkFixedFloor(newEdge->fWalkY - newEdge->fDXFlipped) < y) {
+        return;
+    }
+    while (SkFixedFloor(newEdge->fWalkY - newEdge->fDXFlipped) == y) {
+        SkAntiEdge* next = newEdge->fNext;
+        backward_insert_edge_based_on_x(newEdge  SkPARAM(y));
+        newEdge = next;
+    }
+}
+
+static int find_active_edges(int y, SkAntiEdge** activeLeft,
+                             SkAntiEdge** activeLast) {
+    SkAntiEdge* first = *activeLeft;
+    SkFixed bottom = first->fLastY;
+    SkAntiEdge* active = first->fNext;
+    first->fLinkSet = false;
+    SkFixed yLimit = SkIntToFixed(y + 1); // limiting pixel edge
+    for ( ; active->fWalkY != kEDGE_TAIL_XY; active = active->fNext) {
+        active->fLinkSet = false;
+        if (yLimit <= active->fWalkY - active->fDXFlipped) {
+            break;
+        }
+        if ((*activeLeft)->fWalkX > active->fWalkX) {
+            *activeLeft = active;
+        }
+        if (bottom > active->fLastY) {
+            bottom = active->fLastY;
+        }
+    }
+    *activeLast = active;
+    return SkFixedCeil(bottom);
+}
+
+// All edges are oriented to increase in y. Link edges with common tops and
+// bottoms so the links can share their winding sum.
+void SkAntiEdgeBuilder::link() {
+    SkAntiEdge* tail = fEdges.end();
+    // look for links forwards and backwards
+    SkAntiEdge* prev = fEdges.begin();
+    SkAntiEdge* active;
+    for (active = prev + 1; active != tail; ++active) {
+        if (prev->fWinding == active->fWinding) {
+            if (prev->fLastX == active->fFirstX && prev->fLastY == active->fFirstY) {
+                prev->fLink = active;
+                active->fLinkSet = true;
+            } else if (active->fLastX == prev->fFirstX && active->fLastY == prev->fFirstY) {
+                active->fLink = prev;
+                prev->fLinkSet = true;
+            }
+        }
+        prev = active;
+    }
+    // look for stragglers
+    prev = fEdges.begin() - 1;
+    do {
+        do {
+            if (++prev == tail) {
+                return;
+            }
+        } while (prev->fLinkSet || NULL != prev->fLink);
+        for (active = prev + 1; active != tail; ++active) {
+            if (active->fLinkSet || NULL != active->fLink) {
+                continue;
+            }
+            if (prev->fWinding != active->fWinding) {
+                continue;
+            }
+            if (prev->fLastX == active->fFirstX && prev->fLastY == active->fFirstY) {
+                prev->fLink = active;
+                active->fLinkSet = true;
+                break;
+            }
+            if (active->fLastX == prev->fFirstX && active->fLastY == prev->fFirstY) {
+                active->fLink = prev;
+                prev->fLinkSet = true;
+                break;
+            }
+        }
+    } while (true);
+}
+
+void SkAntiEdgeBuilder::split(SkAntiEdge* edge, SkFixed y) {
+    SkPoint upperPoint = {edge->fFirstX, edge->fFirstY};
+    SkPoint midPoint = {edge->fFirstX + SkMulDiv(y - edge->fFirstY,
+            edge->fLastX - edge->fFirstX, edge->fLastY - edge->fFirstY), y};
+    SkPoint lowerPoint = {edge->fLastX, edge->fLastY};
+    int8_t winding = edge->fWinding;
+    edge->setLine(upperPoint, midPoint);
+    edge->fWinding = winding;
+    SkAntiEdge* lower = fEdges.append();
+    lower->setLine(midPoint, lowerPoint);
+    lower->fWinding = winding;
+    insert_new_edges(lower, y);
+}
+
+// An edge computes pixel coverage by considering the integral winding value
+// to its left. If an edge is enclosed by fractional winding, split it.
+// FIXME: This is also a good time to find crossing edges and split them, too.
+void SkAntiEdgeBuilder::split() {
+    // create a new set of edges that describe the whole link
+    SkTDArray<SkAntiEdge> links;
+    SkAntiEdge* first = fHeadEdge.fNext;
+    SkAntiEdge* active;
+    for (active = first; active != &fTailEdge; active = active->fNext) {
+        if (active->fLinkSet || NULL == active->fLink) {
+            continue;
+        }
+        SkAntiEdge* link = links.append();
+        link->fFirstX = active->fFirstX;
+        link->fFirstY = active->fFirstY;
+        SkAntiEdge* linkEnd;
+        SkAntiEdge* next = active;
+        do {
+            linkEnd = next;
+            next = next->fLink;
+        } while (NULL != next);
+        link->fLastX = linkEnd->fLastX;
+        link->fLastY = linkEnd->fLastY;
+    }
+    // create a list of all edges, links and singletons
+    SkTDArray<SkAntiEdge*> list;
+    for (active = links.begin(); active != links.end(); ++active) {
+        *list.append() = active;
+    }
+    for (active = first; active != &fTailEdge; active = active->fNext) {
+        if (!active->fLinkSet && NULL == active->fLink) {
+            SkAntiEdge* link = links.append();
+            link->fFirstX = active->fFirstX;
+            link->fFirstY = active->fFirstY;
+            link->fLastX = active->fLastX;
+            link->fLastY = active->fLastY;
+            *list.append() = link;
+        }
+    }
+    SkAntiEdge tail;
+    tail.fFirstY = tail.fLastY = kEDGE_TAIL_XY;
+    *list.append() = &tail;
+    sort(list);
+    // walk the list, splitting edges partially occluded on the left
+    SkAntiEdge* listTop = list[0];
+    for (active = first; active != &fTailEdge; active = active->fNext) {
+        while (listTop->fLastY < active->fFirstY) {
+            listTop = listTop->fNext;
+        }
+        for (SkAntiEdge* check = listTop; check->fFirstY < active->fLastY; check = check->fNext) {
+            if (check->fFirstX > active->fFirstX) {
+                continue;
+            }
+            if (check->fFirstX == active->fFirstX && check->fDX > active->fDX) {
+                continue;
+            }
+            if (check->fFirstY > active->fFirstY) {
+                split(active, check->fFirstY);
+            }
+            if (check->fLastY < active->fLastY) {
+                split(active, check->fLastY);
+            }
+        }
+    }
+}
+
+static inline uint8_t coverage_to_8(int coverage) {
+    uint16_t x = coverage < 0 ? 0 : coverage > 0xFFFF ? 0xFFFF : coverage;
+    // for values 0x7FFF and smaller, add (0x7F - high byte) and trunc
+    // for values 0x8000 and larger, subtract (high byte - 0x80) and trunc
+    return (x + 0x7f + (x >> 15) - (x >> 8)) >> 8;
+}
+
+void SkAntiEdgeBuilder::walk(uint8_t* result, int rowBytes, int height) {
+    SkAntiEdge* first = fHeadEdge.fNext;
+    SkFixed top = first->fWalkY - first->fDXFlipped;
+    int y = SkFixedFloor(top);
+    do {
+        SkAntiEdge* activeLeft = first;
+        SkAntiEdge* activeLast, * active;
+        int yLast = find_active_edges(y, &activeLeft, &activeLast);
+        while (y < yLast) {
+            SkAssertResult(y >= 0);
+            SkAssertResult(y < height);
+            SkFixed left = activeLeft->fWalkX;
+            int x = SkFixedFloor(left);
+            uint8_t* resultPtr = &result[y * rowBytes + x];
+            bool finished;
+            do {
+                left = SkIntToFixed(x);
+                SkAssertResult(x >= 0);
+              //  SkAssertResult(x < pixelCol);
+                if (x >= rowBytes) { // FIXME: cumulative error in fX += fDX
+                    break;           // fails to set fFinished early enough
+                }                    // see test 6 (dy<dx)
+                finished = true;
+                int coverage = 0;
+                for (active = first; active != activeLast; active = active->fNext) {
+                    if (left + SK_Fixed1 <= active->fX) {
+                        finished = false;
+                        continue; // walker is to the left of edge
+                    }
+                    int cover = active->fDXFlipped ?
+                        active->advanceFlippedX(left) : active->advanceX(left);
+                    if (0 == active->fWindingSum) {
+                        cover = -cover;
+                    }
+                    coverage += cover;
+                    finished &= active->fFinished;
+                }
+                uint8_t old = *resultPtr;
+                uint8_t pix = coverage_to_8(coverage);
+                uint8_t blend = old > pix ? old : pix;
+                *resultPtr++ = blend;
+                ++x;
+            } while (!finished);
+            ++y;
+            top = SkIntToFixed(y);
+            SkFixed topLimit = top + SK_Fixed1;
+            SkFixed xSort = -SK_FixedMax;
+            for (active = first; active != activeLast; active = active->fNext) {
+                if (xSort > active->fX || topLimit > active->fLastY) {
+                    yLast = y; // recompute bottom after all Ys are advanced
+                }
+                xSort = active->fX;
+                if (active->fWalkY < active->fLastY) {
+                    active->advanceY(top);
+                }
+            }
+            for (active = first; active != activeLast; ) {
+                SkAntiEdge* next = active->fNext;
+                if (top >= active->fLastY) {
+                    remove_edge(active);
+                }
+                active = next;
+            }
+            first = fHeadEdge.fNext;
+        }
+        SkAntiEdge* prev = activeLast->fPrev;
+        if (prev != &fHeadEdge) {
+            insert_new_edges(prev, top);
+            first = fHeadEdge.fNext;
+        }
+    } while (first->fWalkY < kEDGE_TAIL_XY);
+}
+
+void SkAntiEdgeBuilder::process(const SkPoint* points, int ptCount,
+        uint8_t* result, int pixelCol, int pixelRow) {
+    if (ptCount < 3) {
+        return;
+    }
+    int count = build(points, ptCount);
+    if (count == 0) {
+        return;
+    }
+    SkAssertResult(count > 1);
+    link();
+    sort();
+    split();
+    calc();
+    walk(result, pixelCol, pixelRow);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+int test3by3_test;
+
+// input is a rectangle
+static void test_3_by_3() {
+    const int pixelRow = 3;
+    const int pixelCol = 3;
+    const int ptCount = 4;
+    const int pixelCount = pixelRow * pixelCol;
+    const SkPoint tests[][ptCount] = {
+        {{2.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 2.0f}, {2.0f, 2.0f}}, // 0: full rect
+        {{2.5f, 1.0f}, {1.5f, 1.0f}, {1.5f, 2.0f}, {2.5f, 2.0f}}, // 1: y edge
+        {{2.0f, 1.5f}, {1.0f, 1.5f}, {1.0f, 2.5f}, {2.0f, 2.5f}}, // 2: x edge
+        {{2.5f, 1.5f}, {1.5f, 1.5f}, {1.5f, 2.5f}, {2.5f, 2.5f}}, // 3: x/y edge
+        {{2.8f, 0.2f}, {0.2f, 0.2f}, {0.2f, 2.8f}, {2.8f, 2.8f}}, // 4: large
+        {{1.8f, 1.2f}, {1.2f, 1.2f}, {1.2f, 1.8f}, {1.8f, 1.8f}}, // 5: small
+        {{0.0f, 0.0f}, {0.0f, 1.0f}, {3.0f, 2.0f}, {3.0f, 1.0f}}, // 6: dy<dx
+        {{3.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, 2.0f}, {3.0f, 1.0f}}, // 7: dy<-dx
+        {{1.0f, 0.0f}, {0.0f, 0.0f}, {1.0f, 3.0f}, {2.0f, 3.0f}}, // 8: dy>dx
+        {{2.0f, 0.0f}, {1.0f, 0.0f}, {0.0f, 3.0f}, {1.0f, 3.0f}}, // 9: dy>-dx
+        {{0.5f, 0.5f}, {0.5f, 1.5f}, {2.5f, 2.5f}, {2.5f, 1.5f}}, // 10: dy<dx 2
+        {{2.5f, 0.5f}, {0.5f, 1.5f}, {0.5f, 2.5f}, {2.5f, 1.5f}}, // 11: dy<-dx 2
+        {{0.0f, 0.0f}, {2.0f, 0.0f}, {2.0f, 2.0f}, {0.0f, 2.0f}}, // 12: 2x2
+        {{0.0f, 0.0f}, {3.0f, 0.0f}, {3.0f, 3.0f}, {0.0f, 3.0f}}, // 13: 3x3
+        {{1.75f, 0.25f}, {2.75f, 1.25f}, {1.25f, 2.75f}, {0.25f, 1.75f}}, // 14
+        {{2.25f, 0.25f}, {2.75f, 0.75f}, {0.75f, 2.75f}, {0.25f, 2.25f}}, // 15
+        {{0.25f, 0.75f}, {0.75f, 0.25f}, {2.75f, 2.25f}, {2.25f, 2.75f}}, // 16
+        {{1.25f, 0.50f}, {1.75f, 0.25f}, {2.75f, 2.25f}, {2.25f, 2.50f}}, // 17
+        {{1.00f, 0.75f}, {2.00f, 0.50f}, {2.00f, 1.50f}, {1.00f, 1.75f}}, // 18
+        {{1.00f, 0.50f}, {2.00f, 0.75f}, {2.00f, 1.75f}, {1.00f, 1.50f}}, // 19
+        {{1.00f, 0.75f}, {1.00f, 1.75f}, {2.00f, 1.50f}, {2.00f, 0.50f}}, // 20
+        {{1.00f, 0.50f}, {1.00f, 1.50f}, {2.00f, 1.75f}, {2.00f, 0.75f}}, // 21
+    };
+    const uint8_t results[][pixelCount] = {
+        {0x00, 0x00, 0x00, // 0: 1 pixel rect
+         0x00, 0xFF, 0x00,
+         0x00, 0x00, 0x00},
+        {0x00, 0x00, 0x00, // 1: y edge
+         0x00, 0x7F, 0x80,
+         0x00, 0x00, 0x00},
+        {0x00, 0x00, 0x00, // 2: x edge
+         0x00, 0x7F, 0x00,
+         0x00, 0x7F, 0x00},
+        {0x00, 0x00, 0x00, // 3: x/y edge
+         0x00, 0x40, 0x40,
+         0x00, 0x40, 0x40},
+        {0xA3, 0xCC, 0xA3, // 4: large
+         0xCC, 0xFF, 0xCC,
+         0xA3, 0xCC, 0xA3},
+        {0x00, 0x00, 0x00, // 5: small
+         0x00, 0x5C, 0x00,
+         0x00, 0x00, 0x00},
+        {0xD5, 0x80, 0x2B, // 6: dy<dx
+         0x2A, 0x7F, 0xD4,
+         0x00, 0x00, 0x00},
+        {0x2B, 0x80, 0xD5, // 7: dy<-dx
+         0xD4, 0x7F, 0x2A,
+         0x00, 0x00, 0x00},
+        {0xD5, 0x2A, 0x00, // 8: dy>dx
+         0x80, 0x7F, 0x00,
+         0x2B, 0xD4, 0x00},
+        {0x2A, 0xD5, 0x00, // 9: dy>-dx
+         0x7F, 0x80, 0x00,
+         0xD4, 0x2B, 0x00},
+        {0x30, 0x10, 0x00, // 10: dy<dx 2
+         0x50, 0xDF, 0x50,
+         0x00, 0x10, 0x30},
+        {0x00, 0x10, 0x30, // 11: dy<-dx 2
+         0x50, 0xDF, 0x50,
+         0x30, 0x10, 0x00},
+        {0xFF, 0xFF, 0x00, // 12: 2x2
+         0xFF, 0xFF, 0x00,
+         0x00, 0x00, 0x00},
+        {0xFF, 0xFF, 0xFF, // 13: 3x3
+         0xFF, 0xFF, 0xFF,
+         0xFF, 0xFF, 0xFF},
+        {0x00, 0x70, 0x20, // 14
+         0x70, 0xFF, 0x70,
+         0x20, 0x70, 0x00},
+        {0x00, 0x20, 0x60, // 15
+         0x20, 0xBF, 0x20,
+         0x60, 0x20, 0x00},
+        {0x60, 0x20, 0x00, // 16
+         0x20, 0xBF, 0x20,
+         0x00, 0x20, 0x60},
+        {0x00, 0x60, 0x04, // 17
+         0x00, 0x40, 0x60,
+         0x00, 0x00, 0x3C},
+        {0x00, 0x60, 0x00, // 18
+         0x00, 0x9F, 0x00,
+         0x00, 0x00, 0x00},
+        {0x00, 0x60, 0x00, // 19
+         0x00, 0x9F, 0x00,
+         0x00, 0x00, 0x00},
+        {0x00, 0x60, 0x00, // 20
+         0x00, 0x9F, 0x00,
+         0x00, 0x00, 0x00},
+        {0x00, 0x60, 0x00, // 21
+         0x00, 0x9F, 0x00,
+         0x00, 0x00, 0x00},
+    };
+    const int testCount = sizeof(tests) / sizeof(tests[0]);
+    SkAssertResult(testCount == sizeof(results) / sizeof(results[0]));
+    int testFirst = test3by3_test < 0 ? 0 : test3by3_test;
+    int testLast = test3by3_test < 0 ? testCount : test3by3_test + 1;
+    for (int testIndex = testFirst; testIndex < testLast; ++testIndex) {
+        uint8_t result[pixelRow][pixelCol];
+        sk_bzero(result, sizeof(result));
+        const SkPoint* rect = tests[testIndex];
+        SkAntiEdgeBuilder builder;
+        builder.process(rect, ptCount, result[0], pixelCol, pixelRow);
+        SkAssertResult(memcmp(results[testIndex], result[0], pixelCount) == 0);
+    }
+}
+
+// input has arbitrary number of points
+static void test_arbitrary_3_by_3() {
+    const int pixelRow = 3;
+    const int pixelCol = 3;
+    const int pixelCount = pixelRow * pixelCol;
+    const SkPoint t1[] = { {1,1}, {2,1}, {2,1.5f}, {1,1.5f}, {1,2}, {2,2},
+        {2,1.5f}, {1,1.5f}, {1,1} };
+    const SkPoint* tests[] = { t1 };
+    size_t testPts[] = { sizeof(t1) / sizeof(t1[0]) };
+    const uint8_t results[][pixelCount] = {
+        {0x00, 0x00, 0x00, // 0: 1 pixel rect
+         0x00, 0xFF, 0x00,
+         0x00, 0x00, 0x00},
+    };
+    const int testCount = sizeof(tests) / sizeof(tests[0]);
+    SkAssertResult(testCount == sizeof(results) / sizeof(results[0]));
+    int testFirst = test3by3_test < 0 ? 0 : test3by3_test;
+    int testLast = test3by3_test < 0 ? testCount : test3by3_test + 1;
+    for (int testIndex = testFirst; testIndex < testLast; ++testIndex) {
+        uint8_t result[pixelRow][pixelCol];
+        sk_bzero(result, sizeof(result));
+        const SkPoint* pts = tests[testIndex];
+        size_t ptCount = testPts[testIndex];
+        SkAntiEdgeBuilder builder;
+        builder.process(pts, ptCount, result[0], pixelCol, pixelRow);
+        SkAssertResult(memcmp(results[testIndex], result[0], pixelCount) == 0);
+    }
+}
+
+#include "SkRect.h"
+#include "SkPath.h"
+
+int testsweep_test;
+
+static void create_sweep(uint8_t* result, int pixelRow, int pixelCol, SkScalar rectWidth) {
+    const int ptCount = 4;
+    SkRect refRect = {pixelCol / 2 - rectWidth / 2, 5,
+                      pixelCol / 2 + rectWidth / 2, pixelRow / 2 - 5};
+    SkPath refPath;
+    refPath.addRect(refRect);
+    SkScalar angleFirst = testsweep_test < 0 ? 0 : testsweep_test;
+    SkScalar angleLast = testsweep_test < 0 ? 360 : testsweep_test + 1;
+    for (SkScalar angle = angleFirst; angle < angleLast; angle += 12) {
+        SkPath rotPath;
+        SkMatrix matrix;
+        matrix.setRotate(angle, SkIntToScalar(pixelCol) / 2,
+            SkIntToScalar(pixelRow) / 2);
+        refPath.transform(matrix, &rotPath);
+        SkPoint rect[ptCount], temp[2];
+        SkPath::Iter iter(rotPath, false);
+        int index = 0;
+        for (;;) {
+            SkPath::Verb verb = iter.next(temp);
+            if (verb == SkPath::kMove_Verb) {
+                continue;
+            }
+            if (verb == SkPath::kClose_Verb) {
+                break;
+            }
+            SkAssertResult(SkPath::kLine_Verb == verb);
+            rect[index++] = temp[0];
+        }
+        SkAntiEdgeBuilder builder;
+        builder.process(rect, ptCount, result, pixelCol, pixelRow);
+    }
+}
+
+static void create_horz(uint8_t* result, int pixelRow, int pixelCol) {
+    const int ptCount = 4;
+    for (SkScalar x = 0; x < 100; x += 5) {
+        SkPoint rect[ptCount];
+        rect[0].fX = 0;     rect[0].fY = x;
+        rect[1].fX = 100;   rect[1].fY = x;
+        rect[2].fX = 100;   rect[2].fY = x + x / 50;
+        rect[3].fX = 0;     rect[3].fY = x + x / 50;
+        SkAntiEdgeBuilder builder;
+        builder.process(rect, ptCount, result, pixelCol, pixelRow);
+    }
+}
+
+static void create_vert(uint8_t* result, int pixelRow, int pixelCol) {
+    const int ptCount = 4;
+    for (SkScalar x = 0; x < 100; x += 5) {
+        SkPoint rect[ptCount];
+        rect[0].fY = 0;     rect[0].fX = x;
+        rect[1].fY = 100;   rect[1].fX = x;
+        rect[2].fY = 100;   rect[2].fX = x + x / 50;
+        rect[3].fY = 0;     rect[3].fX = x + x / 50;
+        SkAntiEdgeBuilder builder;
+        builder.process(rect, ptCount, result, pixelCol, pixelRow);
+    }
+}
+
+static void create_angle(uint8_t* result, int pixelRow, int pixelCol, SkScalar angle) {
+    const int ptCount = 4;
+    SkRect refRect = {25, 25, 125, 125};
+    SkPath refPath;
+    for (SkScalar x = 30; x < 125; x += 5) {
+        refRect.fTop = x;
+        refRect.fBottom = x + (x - 25) / 50;
+        refPath.addRect(refRect);
+    }
+    SkPath rotPath;
+    SkMatrix matrix;
+    matrix.setRotate(angle, 75, 75);
+    refPath.transform(matrix, &rotPath);
+    SkPath::Iter iter(rotPath, false);
+    for (SkScalar x = 30; x < 125; x += 5) {
+        SkPoint rect[ptCount], temp[2];
+        int index = 0;
+        for (;;) {
+            SkPath::Verb verb = iter.next(temp);
+            if (verb == SkPath::kMove_Verb) {
+                continue;
+            }
+            if (verb == SkPath::kClose_Verb) {
+                break;
+            }
+            SkAssertResult(SkPath::kLine_Verb == verb);
+            rect[index++] = temp[0];
+        }
+    //    if ((x == 30 || x == 75) && angle == 12) continue;
+        SkAntiEdgeBuilder builder;
+        builder.process(rect, ptCount, result, pixelCol, pixelRow);
+    }
+}
+
+static void test_sweep() {
+    const int pixelRow = 100;
+    const int pixelCol = 100;
+    uint8_t result[pixelRow][pixelCol];
+    sk_bzero(result, sizeof(result));
+    create_sweep(result[0], pixelRow, pixelCol, 1);
+}
+
+static void test_horz() {
+    const int pixelRow = 100;
+    const int pixelCol = 100;
+    uint8_t result[pixelRow][pixelCol];
+    sk_bzero(result, sizeof(result));
+    create_horz(result[0], pixelRow, pixelCol);
+}
+
+static void test_vert() {
+    const int pixelRow = 100;
+    const int pixelCol = 100;
+    uint8_t result[pixelRow][pixelCol];
+    sk_bzero(result, sizeof(result));
+    create_vert(result[0], pixelRow, pixelCol);
+}
+
+static void test_angle(SkScalar angle) {
+    const int pixelRow = 150;
+    const int pixelCol = 150;
+    uint8_t result[pixelRow][pixelCol];
+    sk_bzero(result, sizeof(result));
+    create_angle(result[0], pixelRow, pixelCol, angle);
+}
+
+#include "SkBitmap.h"
+
+void CreateSweep(SkBitmap* sweep, SkScalar rectWidth) {
+    const int pixelRow = 100;
+    const int pixelCol = 100;
+    sweep->setConfig(SkBitmap::kA8_Config, pixelCol, pixelRow);
+    sweep->allocPixels();
+    sweep->eraseColor(0);
+    sweep->lockPixels();
+    void* pixels = sweep->getPixels();
+    create_sweep((uint8_t*) pixels, pixelRow, pixelCol, rectWidth);
+    sweep->unlockPixels();
+}
+
+void CreateHorz(SkBitmap* sweep) {
+    const int pixelRow = 100;
+    const int pixelCol = 100;
+    sweep->setConfig(SkBitmap::kA8_Config, pixelCol, pixelRow);
+    sweep->allocPixels();
+    sweep->eraseColor(0);
+    sweep->lockPixels();
+    void* pixels = sweep->getPixels();
+    create_horz((uint8_t*) pixels, pixelRow, pixelCol);
+    sweep->unlockPixels();
+}
+
+void CreateVert(SkBitmap* sweep) {
+    const int pixelRow = 100;
+    const int pixelCol = 100;
+    sweep->setConfig(SkBitmap::kA8_Config, pixelCol, pixelRow);
+    sweep->allocPixels();
+    sweep->eraseColor(0);
+    sweep->lockPixels();
+    void* pixels = sweep->getPixels();
+    create_vert((uint8_t*) pixels, pixelRow, pixelCol);
+    sweep->unlockPixels();
+}
+
+void CreateAngle(SkBitmap* sweep, SkScalar angle) {
+    const int pixelRow = 150;
+    const int pixelCol = 150;
+    sweep->setConfig(SkBitmap::kA8_Config, pixelCol, pixelRow);
+    sweep->allocPixels();
+    sweep->eraseColor(0);
+    sweep->lockPixels();
+    void* pixels = sweep->getPixels();
+    create_angle((uint8_t*) pixels, pixelRow, pixelCol, angle);
+    sweep->unlockPixels();
+}
+
+#include "SkCanvas.h"
+
+static void testPng() {
+    SkBitmap device;
+    device.setConfig(SkBitmap::kARGB_8888_Config, 4, 4);
+    device.allocPixels();
+    device.eraseColor(0xFFFFFFFF);
+
+    SkCanvas canvas(device);
+    canvas.drawARGB(167, 0, 0, 0);
+
+    device.lockPixels();
+    unsigned char* pixels = (unsigned char*) device.getPixels();
+    SkDebugf("%02x%02x%02x%02x", pixels[3], pixels[2], pixels[1], pixels[0]);
+}
+
+void SkAntiEdge_Test() {
+    testPng();
+    test_arbitrary_3_by_3();
+    test_angle(12);
+#if 0
+    test3by3_test = 18;
+#else
+    test3by3_test = -1;
+#endif
+#if 0
+    testsweep_test = 7 * 12;
+#else
+    testsweep_test = -1;
+#endif
+    if (testsweep_test == -1) {
+        test_3_by_3();
+    }
+    test_sweep();
+    test_horz();
+    test_vert();
+}
+
diff --git a/experimental/Intersection/SkAntiEdge.h b/experimental/Intersection/SkAntiEdge.h
new file mode 100644
index 0000000..a85c518
--- /dev/null
+++ b/experimental/Intersection/SkAntiEdge.h
@@ -0,0 +1,79 @@
+/*
+ *  SkAntiEdge.h
+ *  core
+ *
+ *  Created by Cary Clark on 5/6/11.
+ *  Copyright 2011 __MyCompanyName__. All rights reserved.
+ *
+ */
+
+#ifndef SkAntiEdge_DEFINED
+#define SkAntiEdge_DEFINED
+
+#include "SkFixed.h"
+#include "SkTDArray.h"
+
+struct SkBitmap;
+struct SkPoint;
+
+struct SkAntiEdge {
+    SkAntiEdge* fNext; // list in walking order (y, then x, then diag)
+    SkAntiEdge* fPrev; // reverse in walking order
+    SkAntiEdge* fLink; // list in connected order, top to bottom
+
+    SkFixed     fFirstX; // starting X
+    SkFixed     fFirstY; // starting Y
+    SkFixed     fLastX; // ending X
+    SkFixed     fLastY; // ending Y
+    SkFixed     fX0; // computed edge current value (may be off end)
+    SkFixed     fY0;
+    SkFixed     fX; // edge current value (always on edge)
+    SkFixed     fY;
+    SkFixed     fDX; // change in X per unit step in Y
+    SkFixed     fDY; // change in Y per unit step in X
+    SkFixed     fWalkX; // unit step position (integer after initial step)
+    SkFixed     fWalkY;
+    uint16_t    fPartialY; // initial partial coverage in Y (0 .. SkFixed1]
+    int16_t     fWindingSum; // winding including contributions to the left
+    int8_t      fWinding; // 1 or -1 (could be 2 bits)
+    bool        fFinished : 1;
+    unsigned    fDXFlipped : 1; // used as bool and to adjust calculations (0/1)
+    bool        fLinkSet : 1; // set if edge has been attached to another edge
+
+    void calcLine();
+    bool setLine(const SkPoint& p0, const SkPoint& p1);
+    uint16_t advanceX(SkFixed left);
+    uint16_t advanceFlippedX(SkFixed left);
+    void advanceY(SkFixed top);
+// FIXME: mark DEBUG
+    void pointInLine(SkFixed x, SkFixed y);
+    void pointOnLine(SkFixed x, SkFixed y);
+    void validate();
+};
+
+class SkAntiEdgeBuilder {
+public:
+void process(const SkPoint* points, int ptCount,
+        uint8_t* result, int pixelCol, int pixelRow);
+private:
+    int build(const SkPoint pts[], int count);
+    void calc();
+    void link();
+    void sort();
+    void sort(SkTDArray<SkAntiEdge*>&);
+    void split();
+    void split(SkAntiEdge* edge, SkFixed y);
+    void walk(uint8_t* result, int rowBytes, int height);
+    SkAntiEdge fHeadEdge;
+    SkAntiEdge fTailEdge;
+    SkTDArray<SkAntiEdge> fEdges;
+    SkTDArray<SkAntiEdge*> fList;
+};
+
+void SkAntiEdge_Test();
+void CreateSweep(SkBitmap* , float width);
+void CreateHorz(SkBitmap* );
+void CreateVert(SkBitmap* );
+void CreateAngle(SkBitmap* sweep, float angle);
+
+#endif
diff --git a/experimental/Intersection/TSearch.h b/experimental/Intersection/TSearch.h
new file mode 100644
index 0000000..010e69f
--- /dev/null
+++ b/experimental/Intersection/TSearch.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef TSearch_DEFINED
+#define TSearch_DEFINED
+
+#include "SkTypes.h"
+
+// FIXME: Move this templated version into SKTSearch.h
+
+template <typename T>
+static T** QSort_Partition(T** left, T** right, T** pivot)
+{
+    T* pivotValue = *pivot;
+    SkTSwap(*pivot, *right);
+    T** newPivot = left;
+    while (left < right) {
+        if (**left < *pivotValue) {
+            SkTSwap(*left, *newPivot);
+            newPivot += 1;
+        }
+        left += 1;
+    }
+    SkTSwap(*newPivot, *right);
+    return newPivot;
+}
+
+template <typename T>
+void QSort(T** left, T** right)
+{
+    if (left >= right) {
+        return;
+    }
+    T** pivot = left + (right - left >> 1);
+    pivot = QSort_Partition(left, right, pivot);
+    QSort(left, pivot - 1);
+    QSort(pivot + 1, right);
+}
+
+template <typename S, typename T>
+static T* QSort_Partition(S& context, T* left, T* right, T* pivot,
+        bool (*lessThan)(S&, const T, const T))
+{
+    T pivotValue = *pivot;
+    SkTSwap(*pivot, *right);
+    T* newPivot = left;
+    while (left < right) {
+        if (lessThan(context, *left, pivotValue)) {
+            SkTSwap(*left, *newPivot);
+            newPivot += 1;
+        }
+        left += 1;
+    }
+    SkTSwap(*newPivot, *right);
+    return newPivot;
+}
+
+template <typename S, typename T>
+void QSort(S& context, T* left, T* right,
+        bool (*lessThan)(S& , const T, const T))
+{
+    if (left >= right) {
+        return;
+    }
+    T* pivot = left + (right - left >> 1);
+    pivot = QSort_Partition(context, left, right, pivot, lessThan);
+    QSort(context, left, pivot - 1, lessThan);
+    QSort(context, pivot + 1, right, lessThan);
+}
+
+#endif
diff --git a/experimental/Intersection/TestUtilities.cpp b/experimental/Intersection/TestUtilities.cpp
new file mode 100644
index 0000000..95ed1e0
--- /dev/null
+++ b/experimental/Intersection/TestUtilities.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 "CurveIntersection.h"
+#include "TestUtilities.h"
+
+void quad_to_cubic(const Quadratic& quad, Cubic& cubic) {
+    cubic[0] = quad[0];
+    cubic[1].x = quad[0].x / 3 + quad[1].x * 2 / 3;
+    cubic[1].y = quad[0].y / 3 + quad[1].y * 2 / 3;
+    cubic[2].x = quad[2].x / 3 + quad[1].x * 2 / 3;
+    cubic[2].y = quad[2].y / 3 + quad[1].y * 2 / 3;
+    cubic[3] = quad[2];
+}
+
+static bool tiny(const Cubic& cubic) {
+    int index, minX, maxX, minY, maxY;
+    minX = maxX = minY = maxY = 0;
+    for (index = 1; index < 4; ++index) {
+        if (cubic[minX].x > cubic[index].x) {
+            minX = index;
+        }
+        if (cubic[minY].y > cubic[index].y) {
+            minY = index;
+        }
+        if (cubic[maxX].x < cubic[index].x) {
+            maxX = index;
+        }
+        if (cubic[maxY].y < cubic[index].y) {
+            maxY = index;
+        }
+    }
+    return     approximately_equal(cubic[maxX].x, cubic[minX].x)
+            && approximately_equal(cubic[maxY].y, cubic[minY].y);
+}
+
+void find_tight_bounds(const Cubic& cubic, _Rect& bounds) {
+    CubicPair cubicPair;
+    chop_at(cubic, cubicPair, 0.5);
+    if (!tiny(cubicPair.first()) && !controls_inside(cubicPair.first())) {
+        find_tight_bounds(cubicPair.first(), bounds);
+    } else {
+        bounds.add(cubicPair.first()[0]);
+        bounds.add(cubicPair.first()[3]);
+    }
+    if (!tiny(cubicPair.second()) && !controls_inside(cubicPair.second())) {
+        find_tight_bounds(cubicPair.second(), bounds);
+    } else {
+        bounds.add(cubicPair.second()[0]);
+        bounds.add(cubicPair.second()[3]);
+    }
+}
+
+bool controls_inside(const Cubic& cubic) {
+    return
+        ((cubic[0].x <= cubic[1].x && cubic[0].x <= cubic[2].x && cubic[1].x <= cubic[3].x && cubic[2].x <= cubic[3].x)
+     ||  (cubic[0].x >= cubic[1].x && cubic[0].x >= cubic[2].x && cubic[1].x >= cubic[3].x && cubic[2].x >= cubic[3].x))
+     && ((cubic[0].y <= cubic[1].y && cubic[0].y <= cubic[2].y && cubic[1].y <= cubic[3].y && cubic[2].y <= cubic[3].y)
+     ||  (cubic[0].y >= cubic[1].y && cubic[0].y >= cubic[2].y && cubic[1].y >= cubic[3].y && cubic[2].x >= cubic[3].y));
+}
+
diff --git a/experimental/Intersection/TestUtilities.h b/experimental/Intersection/TestUtilities.h
new file mode 100644
index 0000000..2bce308
--- /dev/null
+++ b/experimental/Intersection/TestUtilities.h
@@ -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 "DataTypes.h"
+
+bool controls_inside(const Cubic& );
+void find_tight_bounds(const Cubic& , _Rect& );
+void quad_to_cubic(const Quadratic& , Cubic& );
diff --git a/experimental/Intersection/bc.htm b/experimental/Intersection/bc.htm
new file mode 100644
index 0000000..15601de
--- /dev/null
+++ b/experimental/Intersection/bc.htm
@@ -0,0 +1,454 @@
+<!-- bezier clip visualizer -->
+<html>
+<head>
+<div style="height:0">
+
+<div id="clip1">
+(gdb) p smaller
+$2 = {{
+    x = 0.91292418204644155, 
+    y = 0.41931201426549197
+  }, {
+    x = 0.70491388044579517, 
+    y = 0.64754305977710236
+  }, {
+    x = 0, 
+    y = 1
+  }}
+(gdb) p larger
+$3 = {{
+    x = 0.21875, 
+    y = 0.765625
+  }, {
+    x = 0.125, 
+    y = 0.875
+  }, {
+    x = 0, 
+    y = 1
+  }}
+(gdb) p distance2y
+$1 = {{
+    x = 0, 
+    y = 0.080355482722450078
+  }, {
+    x = 0.5, 
+    y = 0.038383741101172597
+  }, {
+    x = 1, 
+    y = 0
+  }}
+</div>
+
+<div id="quad21a">
+bezier_clip q1=(0,0 1,0 0,2) q2=(0.5,0.25 0.5,0.5 0,1) minT=0 maxT=1
+</div>
+<div id="quad21b">
+bezier_clip q1=(0.5,0.25 0.5,0.375 0.375,0.5625) q2=(0,0 1,0 0,2) minT=0.3 maxT=0.78125
+</div>
+<div id="quad21c">
+bezier_clip q1=(0.42,0.18 0.6125,0.46875 0.341796875,1.22070312) q2=(0.5,0.25 0.5,0.375 0.375,0.5625) minT=0 maxT=0.926710098
+</div>
+<div id="quad21d">
+bezier_clip q1=(0.5,0.25 0.5,0.307919381 0.473162762,0.379257381) q2=(0.42,0.18 0.6125,0.46875 0.341796875,1.22070312) minT=0.187231244 maxT=0.729263299
+</div>
+<div id="quad21e">
+bezier_clip q1=(0.475846194,0.304363878 0.53317904,0.507883959 0.454423387,0.847492538) q2=(0.5,0.25 0.5,0.307919381 0.473162762,0.379257381) minT=0 maxT=1
+</div>
+<div id="quad21f">
+bezier_clip q1=(0.493290691,0.311274036 0.486581381,0.343588381 0.473162762,0.379257381) q2=(0.475846194,0.304363878 0.53317904,0.507883959 0.454423387,0.847492538) minT=0.0828748517 maxT=0.150086861
+</div>
+
+<div id="quad21g">
+(gdb) p smaller
+$1 = {{
+    x = 0.48441440743366754, 
+    y = 0.33903196011243797
+  }, {
+    x = 0.48750982503868118, 
+    y = 0.35346899178071778
+  }, {
+    x = 0.48999046908865357, 
+    y = 0.368520797004039
+  }}
+(gdb) p larger
+$2 = {{
+    x = 0.49329069058425024, 
+    y = 0.31127403581536672
+  }, {
+    x = 0.48658138116850047, 
+    y = 0.34358838107698753
+  }, {
+    x = 0.47316276233700094, 
+    y = 0.37925738104648321
+  }}
+</div>
+
+<div id="quad36">
+(gdb) p fQ
+$2 = {{
+    x = 1.8883839294261275, 
+    y = 2.1108590606904345
+  }, {
+    x = 1.888463903363252, 
+    y = 2.1111576060205435
+  }, {
+    x = 1.8885438199983176, 
+    y = 2.1114561800016824
+  }}
+(gdb) p rh.fQ
+$3 = {{
+    x = 1.8883839294260976, 
+    y = 2.1108590606903377
+  }, {
+    x = 1.8886366953645748, 
+    y = 2.1109850143489544
+  }, {
+    x = 1.8888888888888888, 
+    y = 2.1111111111111112
+  }}
+(gdb) 
+</div>
+
+<div id="quad37">
+ {{x = 360.048828125, y = 229.2578125}, {x = 360.048828125, y = 224.4140625}, {x = 362.607421875, y = 221.3671875}}
+ {{x = 362.607421875, y = 221.3671875}, {x = 365.166015625, y = 218.3203125}, {x = 369.228515625, y = 218.3203125}}
+</div>
+
+<div id="quad38">
+$2 = {{fX = 369.969421, fY = 137.94809}, {fX = 383.982849, fY = 121.260353}, {fX = 406.233154, fY = 121.260353}}
+$4 = {{fX = 406.232788, fY = 121.260353}, {fX = 409.441956, fY = 121.260353}, {fX = 412.972046, fY = 121.795212}}
+</div>
+
+<div id="quad39">
+{{x = 406.233154296875, y = 121.26035308837891}, {x = 406.23153587045397, y = 121.26035308837891}, {x = 406.22991748761177, y = 121.26035317666889}}, 
+{{x = 406.23295158013377, y = 121.26035308872596}, {x = 406.2328698329315, y = 121.26035308837889}, {x = 406.2327880859375, y = 121.26035308837891}},
+</div>
+
+</div>
+
+<script type="text/javascript">
+
+var testDivs = [
+    quad39,
+    quad38,
+    quad37,
+    quad36,
+    quad21g,
+    quad21a,
+    quad21b,
+    quad21c,
+    quad21d,
+    quad21e,
+    quad21f,
+    clip1,
+];
+
+var scale, columns, rows, xStart, yStart;
+
+var ticks = 10;
+var at_x = 13 + 0.5;
+var at_y = 13 + 0.5;
+var init_decimal_places = 1; // make this 3 to show more precision
+var decimal_places;
+var tests = [];
+var testTitles = [];
+var testIndex = 0;
+var ctx;
+var fat1 = true;
+var fat2 = false;
+var ctl1 = true;
+var ctl2 = false;
+var ctlPts1 = true;
+var ctlPts2 = false;
+var minScale = 1;
+var subscale = 1;
+
+function parse(test, title) {
+    var curveStrs = test.split("{{");
+    if (curveStrs.length == 1)
+        curveStrs = test.split("=(");
+    var pattern = /[a-z$=]?-?\d+\.*\d*/g;
+    var curves = [];
+    for (var c in curveStrs) {
+        var curveStr = curveStrs[c];
+        var points = curveStr.match(pattern);
+        var pts = [];
+        for (var wd in points) {
+            var num = parseFloat(points[wd]);
+            if (isNaN(num)) continue;
+            pts.push(num);
+        }
+        if (pts.length > 0)
+            curves.push(pts);
+    }
+    if (curves.length >= 2) {
+        tests.push(curves);
+        testTitles.push(title);
+    }
+}
+
+function init(test) {
+    var canvas = document.getElementById('canvas');
+    if (!canvas.getContext) return;
+    canvas.width = window.innerWidth - at_x;
+    canvas.height = window.innerHeight - at_y;
+    ctx = canvas.getContext('2d');
+    var xmin = Infinity;
+    var xmax = -Infinity;
+    var ymin = Infinity;
+    var ymax = -Infinity;
+    for (var curves in test) {
+        var curve = test[curves];
+        var last = curve.length;
+        for (var idx = 0; idx < last; idx += 2) {
+            xmin = Math.min(xmin, curve[idx]);
+            xmax = Math.max(xmax, curve[idx]);
+            ymin = Math.min(ymin, curve[idx + 1]);
+            ymax = Math.max(ymax, curve[idx + 1]);
+        }
+    }
+    subscale = 1;
+    decimal_places = init_decimal_places;
+    if (xmax != xmin && ymax != ymin) {
+        while ((xmax - xmin) * subscale < 0.1 && (ymax - ymin) * subscale < 0.1) {
+            subscale *= 10;
+            decimal_places += 1;
+     //       if (subscale > 100000) {
+     //           break;
+     //       }
+        }
+    }
+    columns = Math.ceil(xmax * subscale) - Math.floor(xmin * subscale) + 1;
+    rows = Math.ceil(ymax * subscale) - Math.floor(ymin * subscale) + 1;
+    
+    xStart = Math.floor(xmin * subscale) / subscale;
+    yStart = Math.floor(ymin * subscale) / subscale;
+    var hscale = ctx.canvas.width / columns / ticks;
+    var vscale = ctx.canvas.height / rows / ticks;
+    minScale = Math.floor(Math.min(hscale, vscale));
+    scale = minScale * subscale;
+ //   while (columns < 1000 && rows < 1000) {
+  //      columns *= 2;
+ //       rows *= 2;
+ //   }
+}
+
+function drawPoint(px, py, xoffset, yoffset, unit) {
+    var label = px.toFixed(decimal_places) + ", " + py.toFixed(decimal_places);
+    var _px = px * unit + xoffset;
+    var _py = py * unit + yoffset;
+    ctx.beginPath();
+    ctx.arc(_px, _py, 3, 0, Math.PI*2, true);
+    ctx.closePath();
+    ctx.fill();
+    ctx.fillText(label, _px + 5, _py);
+}
+
+function draw(test, title, _at_x, _at_y, scale) {
+    ctx.fillStyle = "rgba(0,0,0, 0.1)";
+    ctx.font = "normal 50px Arial";
+    ctx.fillText(title, 50, 50);
+    ctx.font = "normal 10px Arial";
+
+    var unit = scale * ticks;
+    ctx.lineWidth = 1;
+    var i;
+    for (i = 0; i <= rows * ticks; ++i) {
+        ctx.strokeStyle = (i % ticks) != 0 ? "rgb(160,160,160)" : "black";
+        ctx.beginPath();
+        ctx.moveTo(_at_x + 0, _at_y + i * minScale);
+        ctx.lineTo(_at_x + unit * columns, _at_y + i * minScale);
+        ctx.stroke();
+    }
+    for (i = 0; i <= columns * ticks; ++i) {
+        ctx.strokeStyle = (i % ticks) != 0 ? "rgb(160,160,160)" : "black";
+        ctx.beginPath();
+        ctx.moveTo(_at_x + i * minScale, _at_y + 0);
+        ctx.lineTo(_at_x + i * minScale, _at_y + unit * rows);
+        ctx.stroke();
+    }
+ 
+    var xoffset = xStart * -unit + _at_x;
+    var yoffset = yStart * -unit + _at_y;
+
+    ctx.fillStyle = "rgb(40,80,60)"
+    for (i = 0; i <= columns; i += (1 / ticks))
+    {
+        num = xStart + i / subscale; 
+        ctx.fillText(num.toFixed(decimal_places), xoffset + num * unit - 5, 10);
+    }
+    for (i = 0; i <= rows; i += (1 / ticks))
+    {
+        num = yStart + i / subscale; 
+        ctx.fillText(num.toFixed(decimal_places), 0, yoffset + num * unit + 0);
+    }
+
+    // draw curve 1 and 2
+    var curves, pts;
+    for (curves in test) {
+        var curve = test[curves];
+        ctx.beginPath();
+        ctx.moveTo(xoffset + curve[0] * unit, yoffset + curve[1] * unit);
+        switch (curve.length) {
+            case 6:
+                ctx.quadraticCurveTo(
+                    xoffset + curve[2] * unit, yoffset + curve[3] * unit,
+                    xoffset + curve[4] * unit, yoffset + curve[5] * unit);
+                break;
+            case 8:
+                ctx.bezierCurveTo(
+                    xoffset + curve[2] * unit, yoffset + curve[3] * unit,
+                    xoffset + curve[4] * unit, yoffset + curve[5] * unit,
+                    xoffset + curve[6] * unit, yoffset + curve[7] * unit);
+                break;
+        }
+        if (curves == 2) ctx.strokeStyle = curves ? "red" : "blue";
+        ctx.stroke();
+        ctx.strokeStyle = "rgba(0,0,0, 0.3)";
+        ctx.beginPath();
+        ctx.moveTo(xoffset + curve[0] * unit, yoffset + curve[1] * unit);
+        ctx.lineTo(xoffset + curve[2] * unit, yoffset + curve[3] * unit);
+        ctx.lineTo(xoffset + curve[4] * unit, yoffset + curve[5] * unit);
+        if (curve.length == 8)
+            ctx.lineTo(xoffset + curve[6] * unit, yoffset + curve[7] * unit);
+        ctx.stroke();
+    }
+    // optionally draw fat lines for curve 
+    if (fat1)
+        drawFat(test[0], xoffset, yoffset, unit);
+    if (fat2)
+        drawFat(test[1], xoffset, yoffset, unit);
+    if (ctl1)
+        drawCtl(test[0], xoffset, yoffset, unit);
+    if (ctl2)
+        drawCtl(test[1], xoffset, yoffset, unit);
+    if (ctlPts1)
+        drawCtlPts(test[0], xoffset, yoffset, unit);
+    if (ctlPts2)
+        drawCtlPts(test[1], xoffset, yoffset, unit);
+}
+
+function drawCtl(curve, xoffset, yoffset, unit) {
+    var last = curve.length - 2;
+    ctx.strokeStyle = "rgba(0,0,0, 0.5)";
+    ctx.beginPath();
+    ctx.moveTo(xoffset + curve[0] * unit, yoffset + curve[1] * unit);
+    ctx.lineTo(xoffset + curve[2] * unit, yoffset + curve[3] * unit);
+    ctx.lineTo(xoffset + curve[4] * unit, yoffset + curve[5] * unit);
+    ctx.stroke();
+}
+
+function drawCtlPts(curve, xoffset, yoffset, unit) {
+    drawPoint(curve[0], curve[1], xoffset, yoffset, unit);
+    drawPoint(curve[2], curve[3], xoffset, yoffset, unit);
+    drawPoint(curve[4], curve[5], xoffset, yoffset, unit);
+}
+
+function drawFat(curve, xoffset, yoffset, unit) {
+    var last = curve.length - 2;
+    ctx.strokeStyle = "rgba(0,0,0, 0.5)";
+    ctx.beginPath();
+    ctx.moveTo(xoffset + curve[0] * unit, yoffset + curve[1] * unit);
+    ctx.lineTo(xoffset + curve[last] * unit, yoffset + curve[last + 1] * unit);
+    ctx.stroke();
+    // draw line parallel to end points through control points
+    var dx = curve[last] - curve[0];
+    var dy = curve[last + 1] - curve[1];
+    drawParallelLine(curve[2], curve[3], dx, dy, xoffset, yoffset, unit);
+    if (curve.length == 8)
+        drawParallelLine(curve[4], curve[5], dx, dy, xoffset, yoffset, unit);
+}
+
+function drawParallelLine(x, y, dx, dy, xoffset, yoffset, unit) {
+    var x1 = x - dx;
+    var y1 = y - dy;
+    var x2 = x + dx;
+    var y2 = y + dy;
+    ctx.beginPath();
+    ctx.moveTo(xoffset + x1 * unit, yoffset + y1 * unit);
+    ctx.lineTo(xoffset + x2 * unit, yoffset + y2 * unit);
+    ctx.stroke();
+}
+
+function drawTop() {
+    init(tests[testIndex]);
+    redraw();
+}
+
+function redraw() {
+    ctx.beginPath();
+    ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
+    ctx.fillStyle="white";
+    ctx.fill();
+    draw(tests[testIndex], testTitles[testIndex], at_x, at_y, scale);
+}
+
+function doKeyPress(evt) {
+    var char = String.fromCharCode(evt.charCode);
+    switch (char) {
+    case 'c':
+        ctl2 ^= true;
+        if (ctl2 == false)
+            ctl1 ^= true;
+        drawTop();
+        break;
+    case 'd':
+        ctlPts2 ^= true;
+        if (ctlPts2 == false)
+            ctlPts1 ^= true;
+        drawTop();
+        break;
+    case 'f':
+        fat2 ^= true;
+        if (fat2 == false)
+            fat1 ^= true;
+        drawTop();
+        break;
+    case 'N':
+        testIndex += 9;
+    case 'n':
+        if (++testIndex >= tests.length)
+            testIndex = 0;
+        mouseX = Infinity;
+        drawTop();
+        break;
+    case 'P':
+        testIndex -= 9;
+    case 'p':
+        if (--testIndex < 0)
+            testIndex = tests.length - 1;
+        mouseX = Infinity;
+        drawTop();
+        break;
+    }
+}
+
+function handleMouseClick() {
+}
+
+function handleMouseOver() {
+}
+
+function start() {
+    for (i = 0; i < testDivs.length; ++i) {
+        var title = testDivs[i].id.toString();
+        var str = testDivs[i].firstChild.data;
+        parse(str, title);
+    }
+    drawTop();
+    window.addEventListener('keypress', doKeyPress, true);
+    window.onresize = function() {
+        drawTop();
+    }
+}
+
+</script>
+</head>
+
+<body onLoad="start();">
+<canvas id="canvas" width="750" height="500"
+    onmousemove="handleMouseOver()"
+    onclick="handleMouseClick()"
+    ></canvas >
+</body>
+</html>
diff --git a/experimental/Intersection/edge_Prefix.pch b/experimental/Intersection/edge_Prefix.pch
new file mode 100644
index 0000000..dd26d56
--- /dev/null
+++ b/experimental/Intersection/edge_Prefix.pch
@@ -0,0 +1,5 @@
+//
+// Prefix header for all source files of the 'edge' target in the 'edge' project
+//
+
+#include <Carbon/Carbon.h>
diff --git a/experimental/Intersection/op.htm b/experimental/Intersection/op.htm
new file mode 100644
index 0000000..eb737ab
--- /dev/null
+++ b/experimental/Intersection/op.htm
@@ -0,0 +1,2843 @@
+<!-- path visualizer -->
+<html>
+<head>
+<div style="height:0">
+
+<div id="testSimplifyQuadratic1">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.quadTo(0, 0, 0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+</div>
+
+<div id="testSimplifyQuadratic2">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(20, 0, 20, 20);
+    path.close();
+    path.moveTo(20, 0);
+    path.quadTo(0, 0, 0, 20);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+</div>
+
+<div id="testSimplifyQuadratic3">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(20, 0, 20, 20);
+    path.close();
+    path.moveTo(0, 20);
+    path.quadTo(0, 0, 20, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+}
+</div>
+
+<div id="testSimplifyQuadratic4">
+    SkPath path, out;
+    path.moveTo(0, 20);
+    path.quadTo(20, 0, 40, 20);
+    path.close();
+    path.moveTo(40, 10);
+    path.quadTo(20, 30, 0, 10);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic5">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 0, 0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic6">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(1, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic7">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic8">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic9">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 2, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic10">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 1);
+    path.quadTo(1, 1, 1, 2);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic11">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 1);
+    path.quadTo(2, 2, 3, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic12">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.lineTo(0, 2);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(3, 0);
+    path.quadTo(1, 1, 0, 2);
+    path.lineTo(3, 0);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic13">
+    SkPath path, out;
+path.moveTo(0, 0);
+path.quadTo(0, 0, 1, 0);
+path.lineTo(1, 1);
+path.lineTo(0, 0);
+path.close();
+path.moveTo(0, 0);
+path.quadTo(3, 0, 1, 1);
+path.lineTo(0, 0);
+path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic14">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 1, 2, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic15">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(1, 1);
+    path.quadTo(0, 3, 3, 3);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic16">
+    SkPath path, out;
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic17">
+    SkPath path, out;
+    path.moveTo(8, 8);
+    path.quadTo(10, 10, 8, -10);
+    path.close();
+    path.moveTo(8, 8);
+    path.quadTo(12, 12, 14, 4);
+    path.close();
+    path.moveTo(8, 8);
+    path.quadTo(9, 9, 10, 8);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+}
+</div>
+
+<div id="testSimplifyQuadratic18">
+    SkPath path, out;
+    path.moveTo(8.0000000000000071, 8.0000000000000071);
+    path.quadTo(8.7289570079366854, 8.7289570079366889, 9.3914917259458743, 9.0593802763083691);
+    path.close();
+    path.moveTo(8.0000000000000142, 8.0000000000000142);
+    path.quadTo(8.1250000000000107, 8.1250000000000071, 8.2500000000000071, 8.2187500000000053);
+    path.close();
+    testSimplify(path, true, out, bitmap);
+    drawAsciiPaths(path, out, true);
+</div>
+
+<div id="testSimplifyQuadratic19">
+    SkPath path, simple;
+    path.moveTo(0,4);
+    path.lineTo(6,4);
+    path.lineTo(3,1);
+    path.close();
+    path.moveTo(2,3);
+    path.lineTo(3,2);
+    path.lineTo(4,3);
+    path.close();
+    testSimplifyx(path);
+</div>
+
+<div id="testSimplifyQuadratic20">
+    SkPath path, simple;
+    path.moveTo(0,4);
+    path.lineTo(6,4);
+    path.lineTo(3,1);
+    path.close();
+    path.moveTo(2,3);
+    path.lineTo(4,3);
+    path.lineTo(3,2);
+    path.close();
+    testSimplifyx(path);
+</div>
+
+<div id="testSimplifyQuadratic21">
+    SkPath path, simple;
+    path.moveTo(0,4);
+    path.lineTo(8,4);
+    path.lineTo(4,0);
+    path.close();
+    path.moveTo(2,2);
+    path.lineTo(3,3);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+</div>
+
+<div id="testLine6">
+    SkPath path, simple;
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(2,0);
+    path.lineTo(6,0);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+</div>
+
+<div id="testLine7">
+    SkPath path, simple;
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(6,0);
+    path.lineTo(2,0);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+</div>
+
+<div id="testLine7b">
+    path.moveTo(0,0);
+    path.lineTo(4,0);
+    path.close();
+    path.moveTo(6,0);
+    path.lineTo(2,0);
+    path.lineTo(4,2);
+    path.close();
+</div>
+
+<div id="testLine9">
+    SkPath path, simple;
+    path.moveTo(0,4);
+    path.lineTo(4,4);
+    path.lineTo(2,2);
+    path.close();
+    path.moveTo(6,4);
+    path.lineTo(2,4);
+    path.lineTo(4,2);
+    path.close();
+    testSimplifyx(path);
+</div>
+
+<div id="testLine12">
+    path.moveTo(0,4);
+    path.lineTo(6,4);
+    path.lineTo(3,1);
+    path.close();
+    path.moveTo(2,3);
+    path.lineTo(3,2);
+    path.lineTo(4,3);
+    path.close();
+</div>
+
+<div id="testLine13">
+    path.moveTo(6,4);
+    path.lineTo(0,4);
+    path.lineTo(3,1);
+    path.close();
+    path.moveTo(3,2);
+    path.lineTo(2,3);
+    path.lineTo(4,3);
+    path.close();
+</div>
+
+<div id="testLine17">
+    SkPath path, simple;
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+    testSimplifyx(path);
+</div>
+
+<div id="testLine19">
+    SkPath path, simple;
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 16, 21, 21, (SkPath::Direction) 0);    
+    testSimplifyx(path);
+</div>
+
+<div id="testLine22">
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine24">
+    path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine28">
+    SkPath path, simple;
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+    testSimplifyx(path);
+</div>
+
+<div id="testLine29">
+    SkPath path, simple;
+    path.addRect(0, 18, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 12, 21, 21, (SkPath::Direction) 0);
+    testSimplifyx(path);
+</div>
+
+<div id="testLine30">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 4, 13, 13, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine31">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 4, 9, 9, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine32">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine33">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine34">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine35">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 0, 18, 18, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine36">
+    path.addRect(0, 10, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 12, 18, 18, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine37">
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(18, 24, 30, 30, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine38">
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(6, 12, 18, 18, (SkPath::Direction) 0);
+    path.addRect(12, 12, 21, 21, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine39">
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 6, 24, 24, (SkPath::Direction) 0);
+    path.addRect(12, 4, 21, 21, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine40">
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 18, 24, 24, (SkPath::Direction) 0);
+    path.addRect(4, 16, 13, 13, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine41">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(18, 24, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine42">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(8, 16, 17, 17, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine43">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 24, 18, 18, (SkPath::Direction) 0);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine44">
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 32, 27, 36, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine45">
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 32, 33, 36, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine46">
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 0, 36, 36, (SkPath::Direction) 0);
+    path.addRect(24, 32, 33, 36, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine47">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine48">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine49">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine50">
+    path.addRect(10, 30, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine51">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine52">
+    path.addRect(0, 30, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 20, 18, 30, (SkPath::Direction) 0);
+    path.addRect(32, 0, 36, 41, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine53">
+    path.addRect(10, 30, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 20, 24, 30, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine54">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 0, 18, 18, (SkPath::Direction) 0);
+    path.addRect(8, 4, 17, 17, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine55">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 6, 18, 18, (SkPath::Direction) 0);
+    path.addRect(4, 4, 13, 13, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine56">
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(18, 20, 30, 30, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine57">
+    path.addRect(20, 0, 40, 40, (SkPath::Direction) 0);
+    path.addRect(20, 0, 30, 40, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine58">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 1);
+    path.addRect(0, 12, 9, 9, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine59">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 6, 18, 18, (SkPath::Direction) 1);
+    path.addRect(4, 4, 13, 13, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine60">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(6, 12, 18, 18, (SkPath::Direction) 1);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine61">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(12, 0, 24, 24, (SkPath::Direction) 1);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine62">
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 12, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 12, 13, 13, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine63">
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 10, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 6, 12, 12, (SkPath::Direction) 1);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine64">
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 6, 30, 30, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine65">
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 0, 36, 36, (SkPath::Direction) 0);
+    path.addRect(32, 6, 36, 41, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine66">
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 30, 20, 20, (SkPath::Direction) 0);
+    path.addRect(12, 20, 24, 30, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine67">
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 0);
+    path.addRect(32, 0, 36, 41, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine68a">
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 0);
+    path.addRect(1, 2, 4, 2, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine68b">
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 4, 2, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine68c">
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 0);
+    path.addRect(1, 2, 4, 2, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine68d">
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 4, 2, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine68e">
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine68f">
+    path.addRect(0, 0, 8, 8, (SkPath::Direction) 0);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(2, 2, 6, 6, (SkPath::Direction) 1);
+    path.addRect(1, 2, 2, 2, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine69">
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine70">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 24, 12, 12, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine71">
+    path.addRect(0, 0, 20, 20, (SkPath::Direction) 0);
+    path.addRect(12, 0, 24, 24, (SkPath::Direction) 0);
+    path.addRect(12, 32, 21, 36, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine72">
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 40, 30, 30, (SkPath::Direction) 0);
+    path.addRect(6, 20, 18, 30, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine73">
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(0, 40, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 0);
+    path.addRect(0, 0, 9, 9, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine74">
+    path.addRect(20, 30, 40, 40, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 1);
+    path.addRect(32, 24, 36, 41, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine75">
+    path.addRect(0, 0, 60, 60, (SkPath::Direction) 0);
+    path.addRect(10, 0, 30, 30, (SkPath::Direction) 1);
+    path.addRect(18, 0, 30, 30, (SkPath::Direction) 1);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine76">
+    path.addRect(36, 0, 66, 60, (SkPath::Direction) 0);
+    path.addRect(10, 20, 40, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 1);
+    path.addRect(32, 6, 36, 41, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine77">
+    path.addRect(20, 0, 40, 40, (SkPath::Direction) 0);
+    path.addRect(24, 6, 36, 36, (SkPath::Direction) 1);
+    path.addRect(24, 32, 33, 36, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine78">
+    path.addRect(0, 0, 30, 60, (SkPath::Direction) 0);
+    path.addRect(10, 20, 30, 30, (SkPath::Direction) 1);
+    path.addRect(18, 20, 30, 30, (SkPath::Direction) 1);
+    path.addRect(32, 0, 36, 41, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine79">
+    path.addRect(0, 36, 60, 30, (SkPath::Direction) 0);
+    path.addRect(10, 30, 40, 30, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 1);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+</div>
+
+<div id="testDegenerate1">
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(2, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 0);
+    path.close();
+</div>
+
+<div id="testDegenerate2">
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(0, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(0, 1);
+    path.close();
+</div>
+
+<div id="testDegenerate3">
+    path.moveTo(0, 0);
+    path.lineTo(2, 0);
+    path.lineTo(1, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(3, 0);
+    path.close();
+</div>
+
+<div id="testDegenerate4">
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.lineTo(1, 2);
+    path.close();
+</div>
+
+<div id="testNondegenerate1">
+    path.moveTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(1, 1);
+    path.lineTo(2, 1);
+    path.lineTo(1, 2);
+    path.close();
+</div>
+
+<div id="testNondegenerate2">
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 2);
+    path.lineTo(0, 3);
+    path.lineTo(1, 2);
+    path.close();
+</div>
+
+<div id="testNondegenerate3">
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(0, 1);
+    path.lineTo(1, 1);
+    path.lineTo(0, 2);
+    path.close();
+</div>
+
+<div id="testNondegenerate4">
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 2);
+    path.lineTo(0, 3);
+    path.lineTo(1, 3);
+    path.close();
+</div>
+
+<div id="testQuadralateral5">
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(2, 2);
+    path.lineTo(3, 2);
+    path.lineTo(3, 3);
+    path.close();
+</div>
+
+<div id="testQuadralateral6">
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.lineTo(0, 2);
+    path.lineTo(2, 2);
+    path.close();
+</div>
+
+<div id="testFauxQuadralateral6">
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(2, 0);
+    path.lineTo(1.333, 0.667);
+    path.close();
+    path.moveTo(1.333, 0.667);
+    path.lineTo(0, 2);
+    path.lineTo(2, 2);
+    path.close();
+</div>
+
+<div id="testFauxQuadralateral6a">
+    path.moveTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(0, 6);
+    path.lineTo(6, 6);
+    path.close();
+</div>
+
+<div id="testFauxQuadralateral6b">
+    path.moveTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(6, 6);
+    path.lineTo(0, 6);
+    path.close();
+</div>
+
+<div id="testFauxQuadralateral6c">
+    path.moveTo(0, 0);
+    path.lineTo(3, 3);
+    path.lineTo(3, 0);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(0, 6);
+    path.lineTo(6, 6);
+    path.close();
+</div>
+
+<div id="testFauxQuadralateral6d">
+    path.moveTo(0, 0);
+    path.lineTo(3, 3);
+    path.lineTo(3, 0);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(4, 2);
+    path.close();
+    path.moveTo(4, 2);
+    path.lineTo(6, 6);
+    path.lineTo(0, 6);
+</div>
+
+<div id="testQuadralateral6a">
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(3, 0);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(3, 0);
+    path.lineTo(6, 0);
+    path.lineTo(0, 6);
+    path.lineTo(6, 6);
+</div>
+
+<div id="testQuadralateral7">
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.lineTo(2, 2);
+    path.lineTo(1, 3);
+    path.close();
+</div>
+
+<div id="testQuadralateral8">
+    path.moveTo(0, 0);
+    path.lineTo(3, 1);
+    path.lineTo(1, 3);
+    path.lineTo(3, 3);
+    path.close();
+    path.moveTo(2, 1);
+    path.lineTo(0, 2);
+    path.lineTo(3, 2);
+    path.lineTo(2, 3);
+    path.close();
+</div>
+
+<div id="testQuadralateral9">
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(1, 2);
+    path.lineTo(2, 2);
+    path.close();
+    path.moveTo(1, 1);
+    path.lineTo(2, 1);
+    path.lineTo(1, 3);
+    path.lineTo(2, 3);
+    path.close();
+</div>
+
+<div id="testLine1x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 12, 12, (SkPath::Direction) 0);
+    path.addRect(4, 0, 13, 13, (SkPath::Direction) 0);
+</div>
+
+<div id="testLine2x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 20, 20, 20, (SkPath::Direction) 0);
+    path.addRect(0, 20, 12, 30, (SkPath::Direction) 0);
+    path.addRect(12, 0, 21, 21, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine3x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 30, 30, 30, (SkPath::Direction) 0);
+    path.addRect(18, 20, 30, 30, (SkPath::Direction) 1);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+</div>
+
+<div id="testLine4x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(10, 30, 30, 30, (SkPath::Direction) 0);
+    path.addRect(24, 20, 36, 30, (SkPath::Direction) 1);
+    path.addRect(0, 32, 9, 36, (SkPath::Direction) 1);
+</div>
+
+<div id="testQuadratic1">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(1, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.close();
+</div>
+
+<div id="testQuadratic2">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(3, 0);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic3">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic4x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic5">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 0, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic6">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 0, 0, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic7">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(3, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(3, 0, 1, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic8">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic9">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(3, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(1, 2, 3, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic9a">
+    path.moveTo(1.08000004, 0.720000029);
+    path.lineTo(0, 0);
+    path.lineTo(1, 0);
+    path.lineTo(3, 1);
+    path.lineTo(1.01568651, 0.338562161);
+    path.quadTo(1.03542483, 0.541699469, 1.08000004, 0.720000029);
+    path.close();
+    path.moveTo(1.08000004, 0.720000029);
+    path.lineTo(3, 2);
+    path.quadTo(1.39999998, 2, 1.08000004, 0.720000029);
+    path.close();
+
+</div>
+
+<div id="testQuadratic10a">
+path.moveTo(15.5, 15.5);
+path.lineTo(46.5, 15.5);
+path.quadTo(0, 31, 0, 46.5);
+path.lineTo(15.5, 15.5);
+path.close();
+</div>
+
+<div id="testQuadratic10b">
+path.moveTo(5.16666698, 36.1666641);
+path.lineTo(15.5, 15.5);
+path.lineTo(46.5, 15.5);
+path.quadTo(15.5, 25.8333321, 5.16666698, 36.1666641);
+path.close();
+path.moveTo(5.16666698, 36.1666641);
+path.lineTo(0, 46.5);
+path.quadTo(0, 41.3333359, 5.16666698, 36.1666641);
+path.close();
+</div>
+
+<div id="testQuadratic11a">
+path.moveTo(0, 0);
+path.lineTo(15.5, 31);
+path.lineTo(0, 0);
+path.close();
+path.moveTo(0, 15.5);
+path.lineTo(15.5, 15.5);
+path.quadTo(15.5, 15.5, 46.5, 31);
+path.lineTo(0, 15.5);
+path.close();
+</div>
+
+<div id="testQuadratic11b">
+path.moveTo(9.30000019, 18.6000004);
+path.lineTo(0, 15.5);
+path.lineTo(7.75, 15.5);
+path.lineTo(15.5, 15.5);
+path.lineTo(46.5, 31);
+path.lineTo(9.30000019, 18.6000004);
+path.close();
+</div>
+
+<div id="testQuadratic12">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 0, 0);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.quadTo(1, 1, 0, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic13a">
+path.moveTo(0, 0);
+path.quadTo(0, 0, 15.5, 0);
+path.lineTo(15.5, 31);
+path.lineTo(0, 0);
+path.close();
+path.moveTo(0, 0);
+path.quadTo(15.5, 46.5, 46.5, 46.5);
+path.lineTo(0, 0);
+path.close();
+</div>
+
+<div id="testQuadratic13b">
+path.moveTo(14.8800001, 29.7600002);
+path.quadTo(6.20000029, 18.6000004, 0, 0);
+path.lineTo(14.8800001, 29.7600002);
+path.close();
+path.moveTo(15.5, 30.5437222);
+path.lineTo(15.5, 31);
+path.lineTo(14.8800001, 29.7600002);
+path.quadTo(15.1884346, 30.156559, 15.5, 30.5437222);
+path.close();
+path.moveTo(15.5, 15.5);
+path.lineTo(0, 0);
+path.lineTo(15.5, 0);
+path.lineTo(15.5, 15.5);
+path.close();
+path.moveTo(15.5, 30.5437222);
+path.lineTo(15.5, 15.5);
+path.lineTo(46.5, 46.5);
+path.quadTo(28.34062, 46.5, 15.5, 30.5437222);
+path.close();
+</div>
+
+<div id="testQuadratic14">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(3, 2, 3, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic15">
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 1, 0);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(0, 1);
+    path.quadTo(1, 1, 0, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic16a">
+path.moveTo(0, 0);
+path.quadTo(0, 0, 31, 0);
+path.lineTo(46.5, 31);
+path.lineTo(0, 0);
+path.close();
+path.moveTo(46.5, 15.5);
+path.lineTo(0, 31);
+path.quadTo(0, 31, 15.5, 31);
+path.lineTo(46.5, 15.5);
+path.close();
+</div>
+
+<div id="testQuadratic16b">
+path.moveTo(31, 20.6666679);
+path.lineTo(0, 0);
+path.lineTo(31, 0);
+path.lineTo(39.8571434, 17.7142868);
+path.lineTo(31, 20.6666679);
+path.close();
+path.moveTo(33.214283, 22.1428585);
+path.lineTo(15.5, 31);
+path.lineTo(0, 31);
+path.lineTo(31, 20.6666679);
+path.lineTo(33.214283, 22.1428585);
+path.close();
+path.moveTo(40.2999992, 18.6000004);
+path.lineTo(46.5, 31);
+path.lineTo(33.214283, 22.1428585);
+path.lineTo(40.2999992, 18.6000004);
+path.close();
+path.moveTo(39.8571434, 17.7142868);
+path.lineTo(46.5, 15.5);
+path.lineTo(40.2999992, 18.6000004);
+path.lineTo(39.8571434, 17.7142868);
+path.close();
+</div>
+
+<div id="testQuadratic17x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 0);
+    path.quadTo(0, 0, 3, 1);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(3, 1, 0, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic18">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic19">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 0, 0, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic20">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic21">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic22">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 1, 2, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic23">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(0, 2, 1, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic24">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(2, 0, 0, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic25">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic26">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic27">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 1);
+    path.lineTo(2, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(2, 1, 0, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic28">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 2);
+    path.quadTo(1, 2, 0, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic29">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 2, 1);
+    path.lineTo(0, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(1, 0, 0, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic30">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 2);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 1, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic31">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 1, 2);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 0);
+    path.quadTo(0, 1, 1, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic32">
+    path.moveTo(0, 0);
+    path.quadTo(1, 0, 2, 3);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.quadTo(3, 1, 0, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic33">
+    path.moveTo(0, 0);
+    path.quadTo(2, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(1, 1);
+    path.quadTo(2, 1, 2, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic34">
+    path.moveTo(0, 0);
+    path.quadTo(2, 0, 0, 1);
+    path.lineTo(0, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 1);
+    path.quadTo(2, 1, 1, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic35">
+    path.moveTo(0, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.lineTo(1, 3);
+    path.close();
+    path.moveTo(2, 0);
+    path.lineTo(3, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.close();
+</div>
+
+<div id="testQuadratic36">
+    path.moveTo(0, 0);
+    path.quadTo(2, 1, 2, 3);
+    path.lineTo(2, 3);
+    path.close();
+    path.moveTo(3, 1);
+    path.lineTo(1, 2);
+    path.quadTo(3, 2, 1, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic37">
+    path.moveTo(0, 0);
+    path.quadTo(0, 2, 1, 2);
+    path.lineTo(1, 2);
+    path.close();
+    path.moveTo(0, 0);
+    path.lineTo(3, 1);
+    path.quadTo(0, 2, 1, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic38">
+    path.moveTo(1, 0);
+    path.quadTo(0, 1, 1, 1);
+    path.lineTo(1, 1);
+    path.close();
+    path.moveTo(1, 0);
+    path.lineTo(1, 2);
+    path.quadTo(2, 2, 1, 3);
+    path.close();
+</div>
+
+<div id="testQuadratic39">
+path.moveTo(15.5, 0);
+path.quadTo(46.5, 15.5, 46.5, 31);
+path.lineTo(15.5, 0);
+path.close();
+path.moveTo(46.5, 15.5);
+path.lineTo(0, 31);
+path.quadTo(0, 31, 15.5, 31);
+path.lineTo(46.5, 15.5);
+    path.close();
+</div>
+
+<div id="testQuadratic39a">
+path.moveTo(34.875, 19.375);
+path.lineTo(15.5, 0);
+path.quadTo(32.9687576, 8.73437881, 40.5937271, 17.4687576);
+path.lineTo(34.875, 19.375);
+path.close();
+path.moveTo(36.1666641, 20.6666679);
+path.lineTo(15.5, 31);
+path.lineTo(0, 31);
+path.lineTo(34.875, 19.375);
+path.lineTo(36.1666641, 20.6666679);
+path.close();
+path.moveTo(41.1812401, 18.15938);
+path.quadTo(46.5, 24.5796909, 46.5, 31);
+path.lineTo(36.1666641, 20.6666679);
+path.lineTo(41.1812401, 18.15938);
+path.close();
+path.moveTo(40.5937271, 17.4687576);
+path.lineTo(46.5, 15.5);
+path.lineTo(41.1812401, 18.15938);
+path.quadTo(40.8951759, 17.8140678, 40.5937271, 17.4687576);
+    path.close();
+</div>
+
+<div id="testQuadratic40x">
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(2, 0);
+    path.quadTo(3, 0, 2, 2);
+    path.lineTo(3, 2);
+    path.close();
+    path.moveTo(3, 1);
+    path.lineTo(0, 2);
+    path.quadTo(0, 2, 1, 2);
+    path.close();
+</div>
+
+<div id="testQuadratic40xa">
+path.moveTo(31, 0);
+path.quadTo(41.3333359, 0, 37.8888893, 13.7777777);
+path.lineTo(31, 0);
+path.close();
+path.moveTo(37.8888893, 13.7777777);
+path.quadTo(37.2993202, 16.1360455, 36.3061028, 18.8979664);
+path.lineTo(0, 31);
+path.lineTo(15.5, 31);
+path.lineTo(35.5182915, 20.9908543);
+path.quadTo(33.7454262, 25.5091457, 31, 31);
+path.lineTo(46.5, 31);
+path.lineTo(40.2999992, 18.6000004);
+path.lineTo(46.5, 15.5);
+path.lineTo(39.8571434, 17.7142868);
+path.lineTo(37.8888893, 13.7777777);
+path.close();
+path.moveTo(36.3061028, 18.8979664);
+path.quadTo(35.9396667, 19.9169388, 35.5182915, 20.9908543);
+path.lineTo(40.2999992, 18.6000004);
+path.lineTo(39.8571434, 17.7142868);
+path.lineTo(36.3061028, 18.8979664);
+</div>
+
+<div id="testQuadratic40xb">
+path.moveTo(31, 0);
+path.quadTo(46.5, 0, 31, 31);
+path.lineTo(46.5, 31);
+path.lineTo(31, 0);
+path.close();
+path.moveTo(46.5, 15.5);
+path.lineTo(0, 31);
+path.quadTo(0, 31, 15.5, 31);
+path.lineTo(46.5, 15.5);
+path.close();
+</div>
+
+<div id="testQuadratic41o">
+path.moveTo(419.33905, 236.377808);
+path.quadTo(398.847778, 242.58728, 384.255524, 242.58728);
+path.quadTo(359.417633, 242.58728, 343.738708, 226.080429);
+path.quadTo(328.059784, 209.573578, 328.059784, 183.286819);
+path.quadTo(328.059784, 157.724487, 341.875854, 141.372879);
+path.quadTo(355.691956, 125.021263, 377.218109, 125.021263);
+path.quadTo(397.605896, 125.021263, 408.731201, 139.51004);
+path.quadTo(419.856506, 153.99881, 419.856506, 180.699539);
+path.lineTo(419.752991, 187.012497);
+path.lineTo(348.861511, 187.012497);
+path.quadTo(353.311646, 227.063599, 388.084686, 227.063599);
+path.quadTo(400.814117, 227.063599, 419.33905, 220.233185);
+path.lineTo(419.33905, 236.377808);
+path.close();
+path.moveTo(349.792938, 171.695801);
+path.lineTo(399.365234, 171.695801);
+path.quadTo(399.365234, 140.337967, 375.976227, 140.337967);
+path.quadTo(352.483704, 140.337967, 349.792938, 171.695801);
+path.close();
+path.moveTo(378.682587, 277.360321);
+path.lineTo(381.062897, 259.66333);
+path.quadTo(398.759888, 268.046112, 415.939423, 268.046112);
+path.quadTo(450.402008, 268.046112, 450.402008, 231.513718);
+path.lineTo(450.402008, 213.816727);
+path.quadTo(439.12146, 237.41272, 413.352142, 237.41272);
+path.quadTo(393.171356, 237.41272, 381.269867, 222.716965);
+path.quadTo(369.368378, 208.02121, 369.368378, 183.079834);
+path.quadTo(369.368378, 157.414017, 382.92572, 141.269379);
+path.quadTo(396.483093, 125.124756, 418.009247, 125.124756);
+path.quadTo(436.844666, 125.124756, 450.402008, 140.441467);
+path.lineTo(450.402008, 127.608543);
+path.lineTo(470.89325, 127.608543);
+path.lineTo(470.89325, 209.366608);
+path.quadTo(470.89325, 235.756866, 468.150757, 248.43454);
+path.quadTo(465.408234, 261.112213, 457.853363, 269.184509);
+path.quadTo(444.502991, 283.362823, 416.353394, 283.362823);
+path.quadTo(396.690063, 283.362823, 378.682587, 277.360321);
+path.close();
+path.moveTo(450.402008, 201.087311);
+path.lineTo(450.402008, 154.412781);
+path.quadTo(436.948151, 140.441467, 421.113983, 140.441467);
+path.quadTo(407.039185, 140.441467, 399.070374, 151.722);
+path.quadTo(391.101532, 163.002533, 391.101532, 182.665863);
+path.quadTo(391.101532, 219.612228, 417.07782, 219.612228);
+path.quadTo(434.774841, 219.612228, 450.402008, 201.087311);
+path.close();
+path.moveTo(482.9328, 236.377808);
+path.quadTo(462.441528, 242.58728, 447.849274, 242.58728);
+path.quadTo(423.011383, 242.58728, 407.332458, 226.080429);
+path.quadTo(391.653534, 209.573578, 391.653534, 183.286819);
+path.quadTo(391.653534, 157.724487, 405.469604, 141.372879);
+path.quadTo(419.285706, 125.021263, 440.811859, 125.021263);
+path.quadTo(461.199646, 125.021263, 472.324951, 139.51004);
+path.quadTo(483.450256, 153.99881, 483.450256, 180.699539);
+path.lineTo(483.346741, 187.012497);
+path.lineTo(412.455261, 187.012497);
+path.quadTo(416.905396, 227.063599, 451.678436, 227.063599);
+path.quadTo(464.407867, 227.063599, 482.9328, 220.233185);
+path.lineTo(482.9328, 236.377808);
+path.close();
+path.moveTo(413.386688, 171.695801);
+path.lineTo(462.958984, 171.695801);
+path.quadTo(462.958984, 140.337967, 439.569977, 140.337967);
+path.quadTo(416.077454, 140.337967, 413.386688, 171.695801);
+path.close();
+</div>
+
+<div id="testQuadratic41s">
+path.moveTo(341.875854, 141.372879);
+path.quadTo(355.691956,125.021263, 377.218109,125.021263);
+path.quadTo(388.787811,125.021263, 397.374664,129.687164);
+path.quadTo(406.565979,125.124756, 418.009247,125.124756);
+path.quadTo(423.583374,125.124756, 428.695251,126.466187);
+path.quadTo(434.412903,125.021263, 440.811859,125.021263);
+path.quadTo(449.427277,125.021263, 456.388672,127.608543);
+path.lineTo(470.89325,127.608543);
+path.lineTo(470.89325,137.749908);
+path.quadTo(471.627319,138.601486, 472.324951,139.51004);
+path.quadTo(483.450256,153.99881, 483.450256,180.699539);
+path.lineTo(483.346741,187.012497);
+path.lineTo(470.89325,187.012497);
+path.lineTo(470.89325,209.366608);
+path.quadTo(470.89325,217.414856, 470.638184,224.187729);
+path.quadTo(476.428223,222.631516, 482.9328,220.233185);
+path.lineTo(482.9328,236.377808);
+path.quadTo(475.87207,238.517426, 469.511749,239.919785);
+path.quadTo(468.946777,244.754791, 468.150757,248.43454);
+path.quadTo(465.408234,261.112213, 457.853363,269.184509);
+path.quadTo(444.502991,283.362823, 416.353394,283.362823);
+path.quadTo(396.690063,283.362823, 378.682587,277.360321);
+path.lineTo(381.062897,259.66333);
+path.quadTo(398.759888,268.046112, 415.939423,268.046112);
+path.quadTo(444.719147,268.046112, 449.464905,242.568665);
+path.quadTo(448.648254,242.58728, 447.849274,242.58728);
+path.quadTo(433.084625,242.58728, 421.556366,236.754425);
+path.quadTo(418.89566,237.203537, 416.046783,237.346252);
+path.quadTo(397.661652,242.58728, 384.255524,242.58728);
+path.quadTo(359.417633,242.58728, 343.738708,226.080429);
+path.quadTo(328.059784,209.573578, 328.059784,183.286819);
+path.quadTo(328.059784,157.724487, 341.875854,141.372879);
+path.close();
+path.moveTo(442.014923, 226.179474);
+path.quadTo(445.951935,226.953491, 450.402008,227.049881);
+path.lineTo(450.402008,213.816727);
+path.quadTo(446.904755,221.132065, 442.014923,226.179474);
+path.close();
+path.moveTo(395.347717, 206.501785);
+path.quadTo(392.200165,197.593536, 391.734406,187.012497);
+path.lineTo(391.197113,187.012497);
+path.quadTo(391.738647,198.938644, 395.347717,206.501785);
+path.close();
+path.moveTo(391.808533, 171.695801);
+path.lineTo(392.428436,171.695801);
+path.quadTo(393.693451,162.656265, 397.02359,154.9935);
+path.quadTo(397.023804,154.992996, 397.024048,154.992493);
+path.quadTo(393.175995,143.845093, 383.003235,141.177292);
+path.quadTo(382.964447,141.223267, 382.92572,141.269379);
+</div>
+
+<div id="testQuadratic42o">
+path.moveTo(421.962158, 236.285355);
+path.quadTo(400.947845, 242.65332, 385.983124, 242.65332);
+path.quadTo(360.511261, 242.65332, 344.432129, 225.725143);
+path.quadTo(328.352997, 208.796951, 328.352997, 181.839218);
+path.quadTo(328.352997, 155.62442, 342.521729, 138.855438);
+path.quadTo(356.69046, 122.086449, 378.766083, 122.086449);
+path.quadTo(399.674255, 122.086449, 411.083527, 136.945038);
+path.quadTo(422.492798, 151.803635, 422.492798, 179.185898);
+path.lineTo(422.386688, 185.660004);
+path.lineTo(349.685699, 185.660004);
+path.quadTo(354.24942, 226.733398, 389.910034, 226.733398);
+path.quadTo(402.964386, 226.733398, 421.962158, 219.728638);
+path.lineTo(421.962158, 236.285355);
+path.close();
+path.moveTo(350.6409, 169.952347);
+path.lineTo(401.478516, 169.952347);
+path.quadTo(401.478516, 137.794098, 377.492493, 137.794098);
+path.quadTo(353.40036, 137.794098, 350.6409, 169.952347);
+path.close();
+path.moveTo(379.213562, 278.313934);
+path.lineTo(381.654602, 260.165222);
+path.quadTo(399.803314, 268.761993, 417.421356, 268.761993);
+path.quadTo(452.763611, 268.761993, 452.763611, 231.297104);
+path.lineTo(452.763611, 213.148392);
+path.quadTo(441.195129, 237.34668, 414.768036, 237.34668);
+path.quadTo(394.072144, 237.34668, 381.866882, 222.275818);
+path.quadTo(369.661591, 207.204956, 369.661591, 181.626953);
+path.quadTo(369.661591, 155.306015, 383.565002, 138.749298);
+path.quadTo(397.468384, 122.192581, 419.544037, 122.192581);
+path.quadTo(438.860199, 122.192581, 452.763611, 137.900238);
+path.lineTo(452.763611, 124.739769);
+path.lineTo(473.777893, 124.739769);
+path.lineTo(473.777893, 208.584686);
+path.quadTo(473.777893, 235.64856, 470.965363, 248.649826);
+path.quadTo(468.152863, 261.651093, 460.405151, 269.929443);
+path.quadTo(446.71402, 284.469666, 417.845886, 284.469666);
+path.quadTo(397.680664, 284.469666, 379.213562, 278.313934);
+path.close();
+path.moveTo(452.763611, 200.094055);
+path.lineTo(452.763611, 152.228165);
+path.quadTo(438.966339, 137.900238, 422.727997, 137.900238);
+path.quadTo(408.293945, 137.900238, 400.121704, 149.468719);
+path.quadTo(391.949493, 161.037186, 391.949493, 181.202423);
+path.quadTo(391.949493, 219.091827, 418.588837, 219.091827);
+path.quadTo(436.737549, 219.091827, 452.763611, 200.094055);
+path.close();
+path.moveTo(485.555908, 236.285355);
+path.quadTo(464.541595, 242.65332, 449.576874, 242.65332);
+path.quadTo(424.105011, 242.65332, 408.025879, 225.725143);
+path.quadTo(391.946747, 208.796951, 391.946747, 181.839218);
+path.quadTo(391.946747, 155.62442, 406.115479, 138.855438);
+path.quadTo(420.28421, 122.086449, 442.359833, 122.086449);
+path.quadTo(463.268005, 122.086449, 474.677277, 136.945038);
+path.quadTo(486.086548, 151.803635, 486.086548, 179.185898);
+path.lineTo(485.980438, 185.660004);
+path.lineTo(413.279449, 185.660004);
+path.quadTo(417.84317, 226.733398, 453.503784, 226.733398);
+path.quadTo(466.558136, 226.733398, 485.555908, 219.728638);
+path.lineTo(485.555908, 236.285355);
+path.close();
+path.moveTo(414.23465, 169.952347);
+path.lineTo(465.072266, 169.952347);
+path.quadTo(465.072266, 137.794098, 441.086243, 137.794098);
+path.quadTo(416.99411, 137.794098, 414.23465, 169.952347);
+path.close();
+</div>
+
+<div id="testQuadratic42s">
+path.moveTo(342.521729, 138.855438);
+path.quadTo(356.69046,122.086449, 378.766083,122.086449);
+path.quadTo(390.293488,122.086449, 398.933502,126.603004);
+path.quadTo(408.150299,122.192581, 419.544037,122.192581);
+path.quadTo(425.108429,122.192581, 430.223633,123.496056);
+path.quadTo(435.959412,122.086449, 442.359833,122.086449);
+path.quadTo(451.19516,122.086449, 458.334229,124.739769);
+path.lineTo(473.777893,124.739769);
+path.lineTo(473.777893,135.814713);
+path.quadTo(474.234741,136.368698, 474.677277,136.945038);
+path.quadTo(486.086548,151.803635, 486.086548,179.185898);
+path.lineTo(485.980438,185.660004);
+path.lineTo(473.777893,185.660004);
+path.lineTo(473.777893,208.584686);
+path.quadTo(473.777893,216.745773, 473.522156,223.628113);
+path.quadTo(479.207153,222.069519, 485.555908,219.728638);
+path.lineTo(485.555908,236.285355);
+path.quadTo(478.638306,238.381592, 472.376221,239.787796);
+path.quadTo(471.792389,244.826782, 470.965363,248.649826);
+path.quadTo(468.152863,261.651093, 460.405151,269.929443);
+path.quadTo(446.71402,284.469666, 417.845886,284.469666);
+path.quadTo(397.680664,284.469666, 379.213562,278.313934);
+path.lineTo(381.654602,260.165222);
+path.quadTo(399.803314,268.761993, 417.421356,268.761993);
+path.quadTo(446.944275,268.761993, 451.80542,242.619034);
+path.quadTo(450.674866,242.65332, 449.576874,242.65332);
+path.quadTo(434.524628,242.65332, 422.75238,236.741913);
+path.quadTo(420.864227,237.041901, 418.884674,237.193085);
+path.quadTo(399.840271,242.65332, 385.983124,242.65332);
+path.quadTo(360.511261,242.65332, 344.432129,225.725143);
+path.quadTo(328.352997,208.796951, 328.352997,181.839218);
+path.quadTo(328.352997,155.62442, 342.521729,138.855438);
+path.close();
+path.moveTo(383.823944, 138.443222);
+path.quadTo(380.900299,137.794098, 377.492493,137.794098);
+path.quadTo(353.40036,137.794098, 350.6409,169.952347);
+path.lineTo(370.408203,169.952347);
+path.quadTo(372.883484,151.469254, 383.565002,138.749298);
+path.quadTo(383.694122,138.595551, 383.823944,138.443222);
+path.close();
+path.moveTo(369.740021, 185.660004);
+path.lineTo(349.685699,185.660004);
+path.quadTo(353.983734,224.342361, 385.863525,226.594208);
+path.quadTo(383.762756,224.616837, 381.866882,222.275818);
+path.quadTo(370.639954,208.41301, 369.740021,185.660004);
+path.close();
+path.moveTo(413.279449, 185.660004);
+path.quadTo(415.737305,207.780716, 427.214905,217.987976);
+path.quadTo(440.600586,214.512451, 452.763611,200.094055);
+path.lineTo(452.763611,185.660004);
+</div>
+
+<div id="testQuadratic43o">
+path.moveTo(288.755981, 240);
+path.lineTo(288.755981, 102.232819);
+path.lineTo(315.843994, 102.232819);
+path.lineTo(354.009216, 208.816208);
+path.lineTo(393.291473, 102.232819);
+path.lineTo(417.493835, 102.232819);
+path.lineTo(417.493835, 240);
+path.lineTo(399.248962, 240);
+path.lineTo(399.248962, 127.92453);
+path.lineTo(361.269928, 230.784485);
+path.lineTo(342.373474, 230.784485);
+path.lineTo(305.511444, 127.645271);
+path.lineTo(305.511444, 240);
+path.lineTo(288.755981, 240);
+path.close();
+path.moveTo(397.864014, 236.741989);
+path.quadTo(379.433014, 242.327148, 366.307892, 242.327148);
+path.quadTo(343.967255, 242.327148, 329.864746, 227.479935);
+path.quadTo(315.762238, 212.632736, 315.762238, 188.988907);
+path.quadTo(315.762238, 165.996674, 328.189209, 151.289093);
+path.quadTo(340.61618, 136.581512, 359.978058, 136.581512);
+path.quadTo(378.315979, 136.581512, 388.322723, 149.613556);
+path.quadTo(398.329468, 162.645584, 398.329468, 186.661758);
+path.lineTo(398.236359, 192.339996);
+path.lineTo(334.472504, 192.339996);
+path.quadTo(338.475189, 228.364258, 369.752075, 228.364258);
+path.quadTo(381.20163, 228.364258, 397.864014, 222.220581);
+path.lineTo(397.864014, 236.741989);
+path.close();
+path.moveTo(335.310272, 178.563278);
+path.lineTo(379.898438, 178.563278);
+path.quadTo(379.898438, 150.358246, 358.861023, 150.358246);
+path.quadTo(337.730499, 150.358246, 335.310272, 178.563278);
+path.close();
+path.moveTo(346.052765, 240);
+path.lineTo(346.052765, 138.908661);
+path.lineTo(364.390686, 138.908661);
+path.lineTo(364.390686, 157.898193);
+path.quadTo(375.281769, 136.674606, 396.039917, 136.674606);
+path.quadTo(398.832489, 136.674606, 401.904327, 137.140045);
+path.lineTo(401.904327, 154.267853);
+path.quadTo(397.156952, 152.685394, 393.526611, 152.685394);
+path.quadTo(376.119537, 152.685394, 364.390686, 173.350464);
+path.lineTo(364.390686, 240);
+path.lineTo(346.052765, 240);
+path.close();
+path.moveTo(362.792297, 273.604034);
+path.lineTo(364.933289, 257.68634);
+path.quadTo(380.850983, 265.226288, 396.303253, 265.226288);
+path.quadTo(427.300842, 265.226288, 427.300842, 232.366959);
+path.lineTo(427.300842, 216.449265);
+path.quadTo(417.15448, 237.672852, 393.976105, 237.672852);
+path.quadTo(375.824341, 237.672852, 365.119446, 224.454651);
+path.quadTo(354.414581, 211.23645, 354.414581, 188.802734);
+path.quadTo(354.414581, 165.717422, 366.608826, 151.196014);
+path.quadTo(378.803101, 136.674606, 398.164948, 136.674606);
+path.quadTo(415.106598, 136.674606, 427.300842, 150.451324);
+path.lineTo(427.300842, 138.908661);
+path.lineTo(445.731873, 138.908661);
+path.lineTo(445.731873, 212.446564);
+path.quadTo(445.731873, 236.183472, 443.265106, 247.586502);
+path.quadTo(440.798309, 258.989532, 434.003052, 266.250244);
+path.quadTo(421.994965, 279.002991, 396.675598, 279.002991);
+path.quadTo(378.989258, 279.002991, 362.792297, 273.604034);
+path.close();
+path.moveTo(427.300842, 204.999695);
+path.lineTo(427.300842, 163.017929);
+path.quadTo(415.199677, 150.451324, 400.95755, 150.451324);
+path.quadTo(388.297852, 150.451324, 381.130249, 160.597687);
+path.quadTo(373.962616, 170.744064, 373.962616, 188.430389);
+path.quadTo(373.962616, 221.662079, 397.327179, 221.662079);
+path.quadTo(413.244873, 221.662079, 427.300842, 204.999695);
+path.close();
+path.moveTo(461.457764, 236.741989);
+path.quadTo(443.026764, 242.327148, 429.901642, 242.327148);
+path.quadTo(407.561005, 242.327148, 393.458496, 227.479935);
+path.quadTo(379.355988, 212.632736, 379.355988, 188.988907);
+path.quadTo(379.355988, 165.996674, 391.782959, 151.289093);
+path.quadTo(404.20993, 136.581512, 423.571808, 136.581512);
+path.quadTo(441.909729, 136.581512, 451.916473, 149.613556);
+path.quadTo(461.923218, 162.645584, 461.923218, 186.661758);
+path.lineTo(461.830109, 192.339996);
+path.lineTo(398.066254, 192.339996);
+path.quadTo(402.068939, 228.364258, 433.345825, 228.364258);
+path.quadTo(444.79538, 228.364258, 461.457764, 222.220581);
+path.lineTo(461.457764, 236.741989);
+path.close();
+path.moveTo(398.904022, 178.563278);
+path.lineTo(443.492188, 178.563278);
+path.quadTo(443.492188, 150.358246, 422.454773, 150.358246);
+path.quadTo(401.324249, 150.358246, 398.904022, 178.563278);
+path.close();
+</div>
+
+<div id="testQuadratic43s">
+path.moveTo(288.755981, 240);
+path.lineTo(288.755981,102.232819);
+path.lineTo(315.843994,102.232819);
+path.lineTo(331.979736,147.294876);
+path.quadTo(343.453125,136.581512, 359.978058,136.581512);
+path.quadTo(370.869446,136.581512, 378.822021,141.178574);
+path.quadTo(378.893585,141.140915, 378.965302,141.103577);
+path.lineTo(393.291473,102.232819);
+path.lineTo(417.493835,102.232819);
+path.lineTo(417.493835,136.965759);
+path.quadTo(420.44223,136.581512, 423.571808,136.581512);
+path.quadTo(431.320984,136.581512, 437.582458,138.908661);
+path.lineTo(445.731873,138.908661);
+path.lineTo(445.731873,143.392502);
+path.quadTo(449.143951,146.002823, 451.916473,149.613556);
+path.quadTo(461.923218,162.645584, 461.923218,186.661758);
+path.lineTo(461.830109,192.339996);
+path.lineTo(445.731873,192.339996);
+path.lineTo(445.731873,212.446564);
+path.quadTo(445.731873,220.39856, 445.455017,226.966339);
+path.quadTo(452.7435,225.43367, 461.457764,222.220581);
+path.lineTo(461.457764,236.741989);
+path.quadTo(452.250824,239.531982, 444.367889,240.928268);
+path.quadTo(443.897583,244.662796, 443.265106,247.586502);
+path.quadTo(440.798309,258.989532, 434.003052,266.250244);
+path.quadTo(421.994965,279.002991, 396.675598,279.002991);
+path.quadTo(378.989258,279.002991, 362.792297,273.604034);
+path.lineTo(364.933289,257.68634);
+path.quadTo(380.850983,265.226288, 396.303253,265.226288);
+path.quadTo(422.230743,265.226288, 426.471558,242.237076);
+path.quadTo(419.570892,241.869324, 413.503387,240);
+path.lineTo(399.248962,240);
+path.lineTo(399.248962,237.37915);
+path.quadTo(397.047638,237.633072, 394.711517,237.667465);
+path.quadTo(378.296356,242.327148, 366.307892,242.327148);
+path.quadTo(357.463165,242.327148, 349.909637,240);
+path.lineTo(346.052765,240);
+path.lineTo(346.052765,238.625916);
+path.quadTo(336.926056,234.914124, 329.864746,227.479935);
+path.quadTo(315.762238,212.632736, 315.762238,188.988907);
+path.quadTo(315.762238,176.540054, 319.405273,166.519882);
+path.lineTo(305.511444,127.645271);
+path.lineTo(305.511444,240);
+path.lineTo(288.755981,240);
+path.close();
+path.moveTo(375.464813, 192.339996);
+path.lineTo(374.267029,195.583939);
+path.quadTo(375.987579,214.575378, 387.432068,219.736267);
+path.quadTo(380.122528,208.101486, 379.428741,192.339996);
+path.lineTo(375.464813,192.339996);
+path.close();
+path.moveTo(427.300842, 178.563278);
+path.lineTo(427.300842,163.017929);
+path.quadTo(422.561523,158.096329, 417.493835,155.102234);
+path.lineTo(417.493835,178.563278);
+path.lineTo(427.300842,178.563278);
+path.close();
+path.moveTo(427.300842, 192.339996);
+path.lineTo(417.493835,192.339996);
+path.lineTo(417.493835,214.429169);
+path.quadTo(422.505676,210.684036, 427.300842,204.999695);
+path.lineTo(427.300842,192.339996);
+path.close();
+path.moveTo(420.700134, 226.556015);
+path.quadTo(423.794342,227.54834, 427.300842,227.996094);
+path.lineTo(427.300842,216.449265);
+path.quadTo(424.497772,222.312531, 420.700134,226.556015);
+path.close();
+path.moveTo(368.744965, 228.354782);
+path.quadTo(366.836426,226.574738, 365.119446,224.454651);
+path.quadTo(364.748657,223.996796, 364.390686,223.527878);
+path.lineTo(364.390686,228.077774);
+path.quadTo(366.495239,228.312164, 368.744965,228.354782);
+path.close();
+path.moveTo(399.248962, 199.701065);
+path.lineTo(399.248962,192.339996);
+path.lineTo(398.236359,192.339996);
+path.lineTo(398.066254,192.339996);
+path.quadTo(398.498535,196.230621, 399.248962,199.701065);
+path.close();
+path.moveTo(399.248962, 178.563278);
+path.lineTo(399.248962,175.376892);
+path.quadTo(399.04483,176.922348, 398.904022,178.563278);
+path.lineTo(399.248962,178.563278);
+path.close();
+path.moveTo(399.248962, 136.688828);
+path.lineTo(399.248962,127.92453);
+path.lineTo(396.018158,136.674606);
+path.quadTo(396.029053,136.674606, 396.039917,136.674606);
+path.quadTo(396.513672,136.674606, 396.995453,136.688004);
+path.quadTo(397.576904,136.674606, 398.164948,136.674606);
+path.quadTo(398.709412,136.674606, 399.248962,136.688828);
+path.close();
+path.moveTo(346.052765, 178.563278);
+path.lineTo(346.052765,154.02713);
+path.quadTo(340.97113,157.621338, 338.22525,164.736588);
+path.lineTo(343.1763,178.563278);
+path.lineTo(346.052765,178.563278);
+path.close();
+path.moveTo(364.390686, 150.922379);
+path.lineTo(364.390686,154.048065);
+path.quadTo(365.340851,152.726639, 366.38147,151.468765);
+path.quadTo(365.420258,151.14975, 364.390686,150.922379);
+path.close();
+path.moveTo(367.863586, 152.032623);
+path.quadTo(367.144043,151.721848, 366.38147,151.468765);
+</div>
+
+<div id="testQuadratic44o">
+path.moveTo(354.009216, 208.816208);
+path.lineTo(393.291473, 102.232819);
+path.lineTo(399.248962, 127.92453);
+path.lineTo(361.269928, 230.784485);
+path.lineTo(354.009216, 208.816208);
+path.close();
+path.moveTo(328.189209, 151.289093);
+path.quadTo(340.61618, 136.581512, 359.978058, 136.581512);
+path.quadTo(378.315979, 136.581512, 388.322723, 149.613556);
+path.lineTo(328.189209, 151.289093);
+path.close();
+path.moveTo(346.052765, 138.908661);
+path.lineTo(364.390686, 138.908661);
+path.lineTo(364.390686, 157.898193);
+path.quadTo(375.281769, 136.674606, 396.039917, 136.674606);
+path.lineTo(346.052765, 138.908661);
+path.close();
+</div>
+
+<div id="testQuadratic44s">
+path.moveTo(380.33902, 137.376312);
+path.lineTo(393.291473,102.232819);
+path.lineTo(399.248962,127.92453);
+path.lineTo(396.018158,136.674606);
+path.quadTo(396.029053,136.674606, 396.039917,136.674606);
+path.lineTo(396.017792,136.675598);
+path.lineTo(361.269928,230.784485);
+path.lineTo(354.009216,208.816208);
+path.lineTo(375.699249,149.965286);
+path.lineTo(369.22699,150.14563);
+path.quadTo(373.524384,144.511566, 378.917297,141.233871);
+path.lineTo(380.33902,137.376312);
+path.close();
+path.moveTo(380.33902, 137.376312);
+path.lineTo(378.917297,141.233856);
+path.quadTo(375.048248,138.978912, 370.480499,137.816925);
+path.lineTo(380.33902,137.376312);
+path.close();
+path.moveTo(392.55661, 136.830276);
+path.lineTo(380.33902,137.376312);
+</div>
+
+<div id="testQuadratic45o">
+path.moveTo(315.843994, 102.232819);
+path.lineTo(354.009216, 208.816208);
+path.lineTo(393.291473, 102.232819);
+path.lineTo(399.248962, 127.92453);
+path.lineTo(361.269928, 230.784485);
+path.lineTo(342.373474, 230.784485);
+path.lineTo(305.511444, 127.645271);
+path.lineTo(315.843994, 102.232819);
+path.close();
+path.moveTo(366.307892, 242.327148);
+path.quadTo(343.967255, 242.327148, 329.864746, 227.479935);
+path.quadTo(315.762238, 212.632736, 315.762238, 188.988907);
+path.quadTo(315.762238, 165.996674, 328.189209, 151.289093);
+path.quadTo(340.61618, 136.581512, 359.978058, 136.581512);
+path.quadTo(378.315979, 136.581512, 388.322723, 149.613556);
+path.quadTo(398.329468, 162.645584, 398.329468, 186.661758);
+path.lineTo(398.236359, 192.339996);
+path.lineTo(334.472504, 192.339996);
+path.quadTo(338.475189, 228.364258, 369.752075, 228.364258);
+path.quadTo(381.20163, 228.364258, 397.864014, 222.220581);
+path.lineTo(366.307892, 242.327148);
+path.close();
+path.moveTo(335.310272, 178.563278);
+path.lineTo(379.898438, 178.563278);
+path.quadTo(379.898438, 150.358246, 358.861023, 150.358246);
+path.quadTo(337.730499, 150.358246, 335.310272, 178.563278);
+path.close();
+path.moveTo(346.052765, 240);
+path.lineTo(346.052765, 138.908661);
+path.lineTo(364.390686, 138.908661);
+path.lineTo(364.390686, 157.898193);
+path.quadTo(375.281769, 136.674606, 396.039917, 136.674606);
+path.lineTo(401.904327, 154.267853);
+path.quadTo(397.156952, 152.685394, 393.526611, 152.685394);
+path.quadTo(376.119537, 152.685394, 364.390686, 173.350464);
+path.lineTo(364.390686, 240);
+path.lineTo(346.052765, 240);
+path.close();
+path.moveTo(396.303253, 265.226288);
+path.quadTo(427.300842, 265.226288, 427.300842, 232.366959);
+path.lineTo(427.300842, 216.449265);
+path.quadTo(417.15448, 237.672852, 393.976105, 237.672852);
+path.quadTo(375.824341, 237.672852, 365.119446, 224.454651);
+path.quadTo(354.414581, 211.23645, 354.414581, 188.802734);
+path.quadTo(354.414581, 165.717422, 366.608826, 151.196014);
+path.quadTo(378.803101, 136.674606, 398.164948, 136.674606);
+path.lineTo(396.303253, 265.226288);
+path.close();
+path.moveTo(400.95755, 150.451324);
+path.quadTo(388.297852, 150.451324, 381.130249, 160.597687);
+path.quadTo(373.962616, 170.744064, 373.962616, 188.430389);
+path.quadTo(373.962616, 221.662079, 397.327179, 221.662079);
+path.lineTo(400.95755, 150.451324);
+path.close();
+path.moveTo(429.901642, 242.327148);
+path.quadTo(407.561005, 242.327148, 393.458496, 227.479935);
+path.quadTo(379.355988, 212.632736, 379.355988, 188.988907);
+path.quadTo(379.355988, 165.996674, 391.782959, 151.289093);
+path.quadTo(404.20993, 136.581512, 423.571808, 136.581512);
+path.lineTo(429.901642, 242.327148);
+path.close();
+</div>
+
+<div id="testQuadratic45s">
+path.moveTo(305.511444, 127.645271);
+path.lineTo(315.843994,102.232819);
+path.lineTo(331.979736,147.294876);
+path.quadTo(343.453125,136.581512, 359.978058,136.581512);
+path.quadTo(370.869446,136.581512, 378.822021,141.178574);
+path.quadTo(378.893585,141.140915, 378.965302,141.103577);
+path.lineTo(393.291473,102.232819);
+path.lineTo(399.248962,127.92453);
+path.lineTo(396.018158,136.674606);
+path.quadTo(396.029053,136.674606, 396.039917,136.674606);
+path.lineTo(396.054596,136.718628);
+path.quadTo(397.098907,136.674606, 398.164948,136.674606);
+path.lineTo(398.076477,142.784256);
+path.lineTo(398.697632,144.647751);
+path.quadTo(409.233032,136.581512, 423.571808,136.581512);
+path.lineTo(429.901642,242.327148);
+path.quadTo(428.161621,242.327148, 426.471558,242.237076);
+path.quadTo(427.300842,237.741562, 427.300842,232.366959);
+path.lineTo(427.300842,216.449265);
+path.quadTo(419.710114,232.327133, 404.8255,236.326401);
+path.quadTo(400.557983,233.971252, 396.803375,230.691772);
+path.lineTo(396.7034,237.596863);
+path.quadTo(395.363068,237.672852, 393.976105,237.672852);
+path.quadTo(385.309937,237.672852, 378.341187,234.659912);
+path.lineTo(366.307892,242.327148);
+path.quadTo(357.463165,242.327148, 349.909637,240);
+path.lineTo(346.052765,240);
+path.lineTo(346.052765,238.625916);
+path.quadTo(336.926056,234.914124, 329.864746,227.479935);
+path.quadTo(315.762238,212.632736, 315.762238,188.988907);
+path.quadTo(315.762238,176.540054, 319.405273,166.519882);
+path.lineTo(305.511444,127.645271);
+path.close();
+path.moveTo(375.464813, 192.339996);
+path.lineTo(374.267029,195.583939);
+path.quadTo(375.987579,214.575378, 387.432068,219.736267);
+path.quadTo(380.122528,208.101486, 379.428741,192.339996);
+path.lineTo(375.464813,192.339996);
+path.close();
+path.moveTo(397.925934, 153.178131);
+path.lineTo(397.615479,174.615356);
+path.quadTo(398.329468,180.246704, 398.329468,186.661758);
+path.lineTo(398.236359,192.339996);
+path.lineTo(397.358795,192.339996);
+path.lineTo(396.934174,221.659714);
+path.quadTo(397.129852,221.662079, 397.327179,221.662079);
+path.lineTo(400.781189,153.910889);
+path.quadTo(399.295654,153.462463, 397.925934,153.178131);
+path.close();
+path.moveTo(400.914398, 151.298019);
+path.lineTo(400.632721,150.453003);
+path.quadTo(400.794678,150.451324, 400.95755,150.451324);
+path.lineTo(400.914398,151.298019);
+path.close();
+path.moveTo(368.744965, 228.354782);
+path.quadTo(366.836426,226.574738, 365.119446,224.454651);
+path.quadTo(364.748657,223.996796, 364.390686,223.527878);
+path.lineTo(364.390686,228.077774);
+path.quadTo(366.495239,228.312164, 368.744965,228.354782);
+path.close();
+path.moveTo(346.052765, 178.563278);
+path.lineTo(346.052765,154.02713);
+path.quadTo(340.97113,157.621338, 338.22525,164.736588);
+path.lineTo(343.1763,178.563278);
+path.lineTo(346.052765,178.563278);
+path.close();
+path.moveTo(364.390686, 150.922379);
+path.lineTo(364.390686,154.048065);
+path.quadTo(365.340851,152.726639, 366.38147,151.468765);
+path.quadTo(365.420258,151.14975, 364.390686,150.922379);
+path.close();
+path.moveTo(367.863586, 152.032623);
+path.quadTo(367.144043,151.721848, 366.38147,151.468765);
+</div>
+
+<div id="testQuadratic46o">
+path.moveTo(366.608826, 151.196014);
+path.quadTo(378.803101, 136.674606, 398.164948, 136.674606);
+path.lineTo(354.009216, 208.816208);
+path.lineTo(393.291473, 102.232819);
+path.lineTo(359.978058, 136.581512);
+path.quadTo(378.315979, 136.581512, 388.322723, 149.613556);
+path.lineTo(364.390686, 157.898193);
+path.quadTo(375.281769, 136.674606, 396.039917, 136.674606);
+path.lineTo(350, 120);
+path.lineTo(366.608826, 151.196014);
+path.close();
+</div>
+
+<div id="testQuadratic46s">
+path.moveTo(369.285553, 126.984779);
+path.lineTo(393.291473,102.232819);
+path.lineTo(382.416199,131.740402);
+path.lineTo(396.039917,136.674606);
+path.quadTo(387.290802,136.674606, 380.294495,140.44487);
+path.quadTo(379.623352,140.760971, 378.965302,141.103577);
+path.lineTo(378.917297,141.233856);
+path.quadTo(378.86972,141.206131, 378.822021,141.178574);
+path.quadTo(372.011871,144.761871, 366.608826,151.196014);
+path.lineTo(350,120);
+path.lineTo(369.285553,126.984779);
+path.close();
+path.moveTo(374.00174, 154.571106);
+path.lineTo(378.917297,141.233871);
+path.quadTo(378.917297,141.233871, 378.917297,141.233856);
+path.quadTo(384.294891,144.368011, 388.322723,149.613556);
+path.lineTo(374.00174,154.571106);
+path.close();
+path.moveTo(378.917297, 141.233871);
+path.quadTo(370.233887,146.511475, 364.390686,157.898193);
+path.lineTo(374.00174,154.571106);
+path.lineTo(354.009216,208.816208);
+path.lineTo(398.164948,136.674606);
+path.quadTo(388.299255,136.674606, 380.294495,140.44487);
+</div>
+
+<div id="testQuadratic47o">
+path.moveTo(343.939362, 212.598053);
+path.lineTo(378.457642, 118.940636);
+path.lineTo(383.692657, 141.516571);
+path.lineTo(350.319519, 231.902115);
+path.lineTo(343.939362, 212.598053);
+path.close();
+path.moveTo(325.429016, 162.047577);
+path.quadTo(336.348907, 149.123688, 353.36264, 149.123688);
+path.quadTo(369.476624, 149.123688, 378.269806, 160.575241);
+path.lineTo(325.429016, 162.047577);
+path.close();
+path.moveTo(370.867188, 186.014069);
+path.quadTo(370.867188, 161.229614, 352.381104, 161.229614);
+path.quadTo(333.813202, 161.229614, 331.686493, 186.014069);
+path.lineTo(370.867188, 186.014069);
+path.close();
+path.moveTo(353.161499, 195.011719);
+path.quadTo(353.161499, 174.726105, 363.876862, 161.96579);
+path.lineTo(353.161499, 195.011719);
+path.close();
+</div>
+
+<div id="testQuadratic47s">
+path.moveTo(366.466309, 151.476364);
+path.lineTo(378.457642,118.940636);
+path.lineTo(383.692657,141.516571);
+path.lineTo(377.159943,159.209305);
+path.quadTo(377.728729,159.87059, 378.269806,160.575241);
+path.lineTo(376.638824,160.620682);
+path.lineTo(370.26593,177.8806);
+path.quadTo(368.708496,168.390671, 363.116943,164.309357);
+path.lineTo(356.079041,186.014069);
+path.lineTo(367.262817,186.014069);
+path.lineTo(350.319519,231.902115);
+path.lineTo(343.939362,212.598053);
+path.lineTo(353.736816,186.014923);
+path.lineTo(353.737122,186.014069);
+path.lineTo(353.736938,186.014069);
+path.quadTo(353.736877,186.014496, 353.736816,186.014923);
+path.quadTo(353.161499,190.31131, 353.161499,195.011719);
+</div>
+
+</div>
+
+<script type="text/javascript">
+
+var testDivs = [
+    testQuadratic47o,
+    testQuadratic47s,
+    testQuadratic46o,
+    testQuadratic46s,
+    testQuadratic45o,
+    testQuadratic45s,
+    testQuadratic44o,
+    testQuadratic44s,
+    testQuadratic43o,
+    testQuadratic43s,
+    testQuadratic42o,
+    testQuadratic42s,
+    testQuadratic41o,
+    testQuadratic41s,
+    testQuadratic40xb,
+    testQuadratic40xa,
+    testQuadratic40x,
+    testQuadratic39,
+    testQuadratic39a,
+    testQuadratic38,
+    testQuadratic37,
+    testQuadratic36,
+    testQuadratic35,
+    testQuadratic34,
+    testQuadratic33,
+    testQuadratic32,
+    testQuadratic31,
+    testQuadratic30,
+    testQuadratic29,
+    testQuadratic28,
+    testQuadratic27,
+    testQuadratic26,
+    testQuadratic25,
+    testQuadratic24,
+    testQuadratic23,
+    testQuadratic22,
+    testQuadratic21,
+    testQuadratic20,
+    testQuadratic19,
+    testQuadratic18,
+    testQuadratic17x,
+    testQuadratic16b,
+    testQuadratic16a,
+    testQuadratic15,
+    testQuadratic14,
+    testQuadratic13b,
+    testQuadratic13a,
+    testQuadratic12,
+    testQuadratic11b,
+    testQuadratic11a,
+    testQuadratic10b,
+    testQuadratic10a,
+    testQuadratic9a,
+    testQuadratic9,
+    testQuadratic8,
+    testQuadratic7,
+    testQuadratic6,
+    testQuadratic5,
+    testQuadratic4x,
+    testQuadratic3,
+    testQuadratic2,
+    testQuadratic1,
+    testLine4x,
+    testLine3x,
+    testLine2x,
+    testLine1x,
+    testQuadralateral9,
+    testQuadralateral8,
+    testQuadralateral7,
+    testFauxQuadralateral6d,
+    testFauxQuadralateral6c,
+    testFauxQuadralateral6b,
+    testFauxQuadralateral6a,
+    testFauxQuadralateral6,
+    testQuadralateral6a,
+    testQuadralateral6,
+    testQuadralateral5,
+    testNondegenerate4,
+    testNondegenerate3,
+    testNondegenerate2,
+    testNondegenerate1,
+    testDegenerate4,
+    testDegenerate3,
+    testDegenerate2,
+    testDegenerate1,
+    testLine79,
+    testLine78,
+    testLine77,
+    testLine76,
+    testLine75,
+    testLine74,
+    testLine73,
+    testLine72,
+    testLine71,
+    testLine70,
+    testLine69,
+    testLine68f,
+    testLine68e,
+    testLine68d,
+    testLine68c,
+    testLine68b,
+    testLine68a,
+    testLine67,
+    testLine66,
+    testLine65,
+    testLine64,
+    testLine63,
+    testLine62,
+    testLine61,
+    testLine60,
+    testLine59,
+    testLine58,
+    testLine57,
+    testLine56,
+    testLine55,
+    testLine54,
+    testLine53,
+    testLine52,
+    testLine51,
+    testLine50,
+    testLine49,
+    testLine48,
+    testLine47,
+    testLine46,
+    testLine45,
+    testLine44,
+    testLine43,
+    testLine42,
+    testLine41,
+    testLine40,
+    testLine39,
+    testLine38,
+    testLine37,
+    testLine36,
+    testLine35,
+    testLine34,
+    testLine33,
+    testLine32,
+    testLine31,
+    testLine30,
+    testLine29,
+    testLine28,
+    testLine24,
+    testLine22,
+    testLine19,
+    testLine17,
+    testLine13,
+    testLine12,
+    testLine9,
+    testLine7b,
+    testLine7,
+    testSimplifyQuadratic21,
+    testSimplifyQuadratic20,
+    testSimplifyQuadratic19,
+    testSimplifyQuadratic18,
+    testSimplifyQuadratic17,
+    testSimplifyQuadratic16,
+    testSimplifyQuadratic15,
+    testSimplifyQuadratic14,
+    testSimplifyQuadratic13,
+    testSimplifyQuadratic12,
+    testSimplifyQuadratic11,
+    testSimplifyQuadratic10,
+    testSimplifyQuadratic9,
+    testSimplifyQuadratic8,
+    testSimplifyQuadratic7,
+    testSimplifyQuadratic6,
+    testSimplifyQuadratic5,
+    testSimplifyQuadratic4,
+    testSimplifyQuadratic3,
+    testSimplifyQuadratic2,
+    testSimplifyQuadratic1,
+];
+
+var scale, columns, rows, xStart, yStart;
+
+var ticks = 0.1;
+var at_x = 13 + 0.5;
+var at_y = 13 + 0.5;
+var decimal_places = 0; // make this 3 to show more precision
+
+var tests = [];
+var testTitles = [];
+var testIndex = 0;
+var hasXor = false;
+var draw_labels = true;
+
+var ctx;
+
+function parse(test, title) {
+    var contours = [];
+    var contourStrs = test.split("path.close();");
+    var pattern = /-?\d+\.*\d*/g;
+    hasXor = test.split("kEvenOdd_FillType").length > 1;
+    for (var c in contourStrs) {
+        var contour = contourStrs[c];
+        var verbStrs = contour.split("path");
+        var verbs = [];
+        for (var v in verbStrs) {
+            var verbStr = verbStrs[v];
+            var points = verbStr.match(pattern);
+            var pts = [];
+            for (var wd in points) {
+                var num = parseFloat(points[wd]);
+                if (isNaN(num)) continue;
+                pts.push(num);
+            }
+            if (pts.length > 0)
+                verbs.push(pts);
+        }
+        if (verbs.length > 0) {
+            var lastIndex = verbs.length - 1;
+            var lastVerb = verbs[lastIndex];
+            var lastLen = lastVerb.length;
+            if (verbs[0][0] != lastVerb[lastLen - 2] && verbs[0][1] != lastVerb[lastLen - 1]) {
+                var lastPts = [];
+                lastPts.push(verbs[0][0]);
+                lastPts.push(verbs[0][1]);
+                verbs.push(lastPts);
+            }
+            contours.push(verbs);
+        }
+    }
+    if (contours.length > 0) {
+        tests.push(contours);
+        testTitles.push(title);
+    }
+}
+
+function parseRect(test, title) {
+    var contours = [];
+    var rectStrs = test.split("path.addRect");
+    var pattern = /-?\d+\.*\d*/g;
+    hasXor = test.split("kEvenOdd_FillType").length > 1;
+    for (var r in rectStrs) {
+        var rect = rectStrs[r];
+        var sideStrs = rect.match(pattern);
+        var sides = [];
+        for (var wd in sideStrs) {
+            var num = parseFloat(sideStrs[wd]);
+            if (isNaN(num)) continue;
+            sides.push(num);
+        }
+        if (sides.length == 0)
+            continue;
+        var verbs = [];
+        var topLeft = [];
+        topLeft.push(sides[0]); topLeft.push(sides[1]);
+        var topRight = [];
+        topRight.push(sides[2]); topRight.push(sides[1]);
+        var botLeft = [];
+        botLeft.push(sides[0]); botLeft.push(sides[3]);
+        var botRight = [];
+        botRight.push(sides[2]); botRight.push(sides[3]);
+        verbs.push(topLeft);
+        if (sides[4] == 0) {
+            verbs.push(topRight);
+            verbs.push(botRight);
+            verbs.push(botLeft);
+        } else {
+            verbs.push(botLeft);
+            verbs.push(botRight);
+            verbs.push(topRight);
+        }
+        verbs.push(topLeft);
+        contours.push(verbs);
+    }
+    if (contours.length > 0) {
+        tests.push(contours);
+        testTitles.push(title);
+    }
+}
+
+function init(test) {
+    var canvas = document.getElementById('canvas');
+    if (!canvas.getContext) return;
+    canvas.width = window.innerWidth - at_x;
+    canvas.height = window.innerHeight - at_y;
+    ctx = canvas.getContext('2d');
+    var xmin = Infinity;
+    var xmax = -Infinity;
+    var ymin = Infinity;
+    var ymax = -Infinity;
+    for (var contours in test) {
+        var contour = test[contours];
+        for (var verbs in contour) {
+            var verb = contour[verbs];
+            var last = verb.length;
+            for (var idx = 0; idx < last; idx += 2) {
+                xmin = Math.min(xmin, verb[idx]);
+                xmax = Math.max(xmax, verb[idx]);
+                ymin = Math.min(ymin, verb[idx + 1]);
+                ymax = Math.max(ymax, verb[idx + 1]);
+            }
+        }
+    }
+    var subscale = 1;
+    while ((xmax - xmin) * subscale < 0.1 && (ymax - ymin) * subscale < 0.1) {
+        subscale *= 10;
+    }
+    columns = Math.ceil(xmax) - Math.floor(xmin) + 1;
+    rows = Math.ceil(ymax) - Math.floor(ymin) + 1;
+    xStart = Math.floor(xmin);
+    yStart = Math.floor(ymin);
+    var hscale = ctx.canvas.width / columns / ticks;
+    var vscale = ctx.canvas.height / rows / ticks;
+    scale = Math.floor(Math.min(hscale, vscale)) * subscale;
+}
+
+function drawPoint(px, py, xoffset, yoffset, unit) {
+    var label = px.toFixed(decimal_places) + ", " + py.toFixed(decimal_places);
+    var _px = px * unit + xoffset;
+    var _py = py * unit + yoffset;
+    ctx.beginPath();
+    ctx.arc(_px, _py, 3, 0, Math.PI*2, true);
+    ctx.closePath();
+    ctx.fill();
+    ctx.fillText(label, _px + 5, _py);
+}
+
+function draw(test, title, _at_x, _at_y, scale) {
+    ctx.fillStyle = "rgba(0,0,0, 0.1)";
+    ctx.font = "normal 50px Arial";
+    ctx.fillText(title, 50, 50);
+    ctx.font = "normal 10px Arial";
+    
+    var unit = scale * ticks;
+    ctx.lineWidth = 1;
+    var i;
+    for (i = 0; i <= rows * ticks; ++i) {
+        ctx.strokeStyle = (i % ticks) != 0 ? "rgb(160,160,160)" : "black";
+        ctx.beginPath();
+        ctx.moveTo(_at_x + 0, _at_y + i * scale);
+        ctx.lineTo(_at_x + unit * columns, _at_y + i * scale);
+        ctx.stroke();
+    }
+    for (i = 0; i <= columns * ticks; ++i) {
+        ctx.strokeStyle = (i % ticks) != 0 ? "rgb(160,160,160)" : "black";
+        ctx.beginPath();
+        ctx.moveTo(_at_x + i * scale, _at_y + 0);
+        ctx.lineTo(_at_x + i * scale, _at_y + unit * rows);
+        ctx.stroke();
+    }
+ 
+    var xoffset = xStart * -unit + _at_x;
+    var yoffset = yStart * -unit + _at_y;
+
+    ctx.fillStyle = "rgb(40,80,60)"
+    for (i = 0; i <= columns; i += (1 / ticks))
+    {
+        num = (xoffset - _at_x) / -unit + i; 
+        ctx.fillText(num.toFixed(0), i * unit + _at_y - 5, 10);
+    }
+    for (i = 0; i <= rows; i += (1 / ticks))
+    {
+        num = (yoffset - _at_x) / -unit + i; 
+        ctx.fillText(num.toFixed(0), 0, i * unit + _at_y + 0);
+    }
+
+    ctx.strokeStyle = "red";
+    var contours, verbs, pts;
+    ctx.beginPath();
+    for (contours in test) {
+        var contour = test[contours];
+        if (contours == 2) ctx.strokeStyle = "blue";
+        var first = true;
+        for (verbs in contour) {
+            var verb = contour[verbs];
+            switch (verb.length) {
+                case 2:
+                    if (first) {
+                        ctx.moveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit);
+                        first = false;
+                    } else
+                        ctx.lineTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit);
+                    break;
+                case 4:
+                    ctx.quadraticCurveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit,
+                        xoffset + verb[2] * unit, yoffset + verb[3] * unit);
+                    break;
+                case 6:
+                    ctx.bezierCurveTo(xoffset + verb[0] * unit, yoffset + verb[1] * unit,
+                        xoffset + verb[2] * unit, yoffset + verb[3] * unit,
+                        xoffset + verb[4] * unit, yoffset + verb[5] * unit);
+                    break;
+            }
+        }
+        ctx.closePath();
+    }
+    if (hasXor) {
+        ctx.fillType=xor; // how is this done?
+    }
+    ctx.stroke();
+    ctx.fillStyle="rgba(192,192,255, 0.3)";
+    ctx.fill();
+    
+    if (!draw_labels) {
+        return;
+    }
+    ctx.fillStyle="blue";
+    for (contours in test) {
+        var contour = test[contours];
+        for (verbs in contour) {
+            var verb = contour[verbs];
+            for (i = 0; i < verb.length; i += 2) {
+                x = verb[i];
+                y = verb[i + 1];
+                drawPoint(x, y, xoffset, yoffset, unit);
+            }
+        }
+    }
+}
+
+var mouseX = Infinity, mouseY;
+
+function calcXY() {
+    var e = window.event;
+	var tgt = e.target || e.srcElement;
+    var left = tgt.offsetLeft;
+    var top = tgt.offsetTop;
+    var unit = scale * ticks;
+    mouseX = (e.clientX - left - Math.ceil(at_x) + 1) / unit + xStart;
+    mouseY = (e.clientY - top - Math.ceil(at_y)) / unit + yStart;
+}
+
+function handleMouseOver() {
+    calcXY();
+    var num = mouseX.toFixed(3) + ", " + mouseY.toFixed(3);
+    ctx.beginPath();
+    ctx.rect(300,100,200,10);
+    ctx.fillStyle="white";
+    ctx.fill();
+    ctx.fillStyle="black";
+    ctx.fillText(num, 300, 108);
+}
+
+function handleMouseClick() {
+    calcXY();
+//    drawInset();
+}
+
+function drawTop() {
+    init(tests[testIndex]);
+    redraw();
+}
+
+function redraw() {
+    ctx.beginPath();
+    ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
+    ctx.fillStyle="white";
+    ctx.fill();
+    draw(tests[testIndex], testTitles[testIndex], at_x, at_y, scale);
+//    if (insetScale != scale && mouseX != Infinity)
+//        drawInset();
+}
+
+function doKeyPress(evt) {
+    var char = String.fromCharCode(evt.charCode);
+    switch (char) {
+    case 'N':
+        testIndex += 9;
+    case 'n':
+        if (++testIndex >= tests.length)
+            testIndex = 0;
+        mouseX = Infinity;
+        drawTop();
+        break;
+    case 'P':
+        testIndex -= 9;
+    case 'p':
+        if (--testIndex < 0)
+            testIndex = tests.length - 1;
+        mouseX = Infinity;
+        drawTop();
+        break;
+    case 'T':
+    case 't':
+        break;
+    case '-':
+        drawTop();
+        break;
+    case '=':
+    case '+':
+        drawTop();
+        break;
+    case 'x':
+        draw_labels ^= true;
+        drawTop();
+        break;
+    }
+}
+
+function start() {
+    for (i = 0; i < testDivs.length; ++i) {
+        var title = testDivs[i].id.toString();
+        var str = testDivs[i].firstChild.data;
+        if (str.split("addRect").length > 1) {
+            parseRect(str, title);
+        } else {
+            parse(str, title);
+        }
+    }
+    drawTop();
+    window.addEventListener('keypress', doKeyPress, true);
+    window.onresize = function() {
+        drawTop();
+    }
+}
+
+</script>
+</head>
+
+<body onLoad="start();">
+<canvas id="canvas" width="750" height="500"
+    onmousemove="handleMouseOver()"
+    onclick="handleMouseClick()"
+    ></canvas >
+</body>
+</html>
diff --git a/experimental/Intersection/thingsToDo.txt b/experimental/Intersection/thingsToDo.txt
new file mode 100644
index 0000000..f74d3ee
--- /dev/null
+++ b/experimental/Intersection/thingsToDo.txt
@@ -0,0 +1,525 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+add unit test for quadratic horizontal intersection
+add unit test for cubic horizontal intersection with left/right
+add unit test for ActiveEdge::calcLeft (can currently loop forever)
+does ActiveEdge::isCoincidentWith need to support quad, cubic?
+figure out why variation in ActiveEdge::tooCloseToCall isn't better
+why does 'lastPtr - 2' in addIntersectingTs break testSimplifyTriangle22?
+add code to promote quad to cubic, or add quad/cubic intersection
+figure out why testSimplifySkinnyTriangle13 fails
+
+for quadratics and cubics, once various T values are added, see if consecutive
+Ts have ys that go up instead of down. If so, the edge needs to be broken.
+
+when splitting curves at inflection pts, should I retain the original curve
+data and note that the first/last T are no longer 0/1 ?
+I need to figure this out before I can proceed
+
+would it make sense to leave the InEdge alone, and add multiple copies of
+ActiveEdge, pointing to the same InEdge, where the copy has only the subset
+of Ts that need to be walked in reverse order?
+
+
+-- A Digression Which Shows Why Resolving Coincidence Does Not Make Sense --
+
+Consider the following fine ASCII art:
+
+  +------>-------+       +------>-------+
+  |              |       |              |
+  ^              V       ^              V
+  |              |       |              |
+  +------<-------+       +------<-------+
+  +------>-------+       +------<-------+
+  |              |       |              |
+  ^              V       V              ^
+  |              |       |              |
+  +------<-------+       +------>-------+
+
+(assume the bottom and top of the stacked rectangles are coincident)
+
+Simplifying said rectangles, regardless of rectangle direction, and regardless
+of winding or even/odd, eliminates the coincident edge, i.e., the result is
+always:
+
+  +------>-------+
+  |              |
+  |              |
+  |              |
+  ^              V
+  |              |
+  |              |
+  |              |
+  +------<-------+
+
+But when the rectangles are enclosed in a larger rectangle:
+
++-------->---------+    +-------->---------+
+| +------>-------+ |    | +------>-------+ |
+| |              | |    | |              | |
+| ^              V |    | ^              V |
+| |              | |    | |              | |
+| +------<-------+ |    | +------<-------+ |
+| +------>-------+ |    | +------<-------+ |
+| |              | |    | |              | |
+| ^              V |    | V              ^ |
+| |              | |    | |              | |
+| +------<-------+ |    | +------>-------+ |
++--------<---------+    +--------<---------+
+
+Simplifying them gives different results depending on the winding setting:
+
+winding:
++-------->---------+    +-------->---------+
+|                  |    |                  |
+|                  |    |                  |
+|                  |    |                  |
+|                  |    |                  |
+|                  |    | +------<-------+ |
+|                  |    | |              | |
+|                  |    | V              ^ |
+|                  |    | |              | |
+|                  |    | +------>-------+ |
++--------<---------+    +--------<---------+
+
+even odd:
++-------->---------+    +-------->---------+
+| +------<-------+ |    | +------<-------+ |
+| |              | |    | |              | |
+| |              | |    | |              | |
+| |              | |    | |              | |
+| |              | |    | |              | |
+| V              ^ |    | V              ^ |
+| |              | |    | |              | |
+| |              | |    | |              | |
+| |              | |    | |              | |
+| +------>-------+ |    | +------>-------+ |
++--------<---------+    +--------<---------+
+
+So, given the inner rectangles alone (e.g., given coincident pairs in some local
+context), we can't know whether to keep the coincident edges or not.
+
+
+-- Thoughts About Sortless Ops --
+
+I can't come up with anything truly sortless. It seems that the crossings need
+to be sorted to know which segment is next on the outside, although sometimes
+we can use that it is not coincident just to follow the direction.
+
+If it is coincident or if there's more than two crossing segments, sorting
+seems inevitable.
+
+Likewise, to resolve whether one contour is inside another, it seems that
+sorting is required. Given a pair of segments on different contours, to know
+if one is inside of the other, I need to know for each which side of the edge
+is the inside/filled side. When the outer contour is walked, it seems like I
+could record the inside info. I guess when the inner contour is found, its
+inside sense is reversed (inside is above the top). But how do I know if the
+next contour is inside another? Maybe shoot out a line and brute-force
+intersect it with all the segments in all the other contours? If every contour
+has an extra segment when the intersections are computed, this may not be as
+crazy as it seems.
+
+Suppose each contour has one extra segment shooting straight up from the top
+(or straight up from any point on the segment). This ray is not intersected
+with the home contour, but is intersected with all other contours as part of
+the normal intersection engine. If it is possible to get from the T values to
+the other segments to the other contours, it would be straightforward to
+count the contour crossings and determine if the home contour is in another
+contour or not (if the count is even, not, if odd, is inside). By itself that
+doesn't tell us about winding, but it's a start.
+
+
+Since intersecting these rays is unrelated to computing other intersections,
+it can be lazily done once the contour is found.
+
+So
+repeat the following
+find the top segment of all contours
+trace the outside, marking touching first and last segments as inside
+continue tracing the touched segments with reversed outside/inside sense
+once the edges are exhausted, remaining must be disjoint contours
+send a ray from a disjoint point through all other contours
+count the crossings, determine if disjoint is inside or outside, then continue
+
+===
+
+On Quadratic (and Cubic) Intersections
+
+Currently, if only the end points touch, QuadracticIntersections does a lot of
+work to figure that out. Can I test for that up front, then short circuit the
+recursive search for the end points?
+
+Or, is there something defective in the current approach that makes the end
+point recursion go so deep? I'm seeing 56 stack frames (about 28 divides, but
+thankfully, no splits) to find one matching endpoint.
+
+
+Bezier curve focus may allow more quickly determining that end points with
+identical tangents are practically coicident for some range of T, but I don't
+understand the math yet to know.
+
+Another approach is to determine how flat the curve is to make good guesses
+about how far to move away in T before doing the intersection for the remainder
+and/or to determine whether one curve is to the inside or outside of another.
+According to Mike/Rob, the flatness for quadratics increases by 4 for each
+subdivision, and a crude guess of the curvature can be had by comparing P1 to
+(P0+P2)/2. By looking at the ULPS of the numbers, I can guess what value of
+T may be far enough that the curves diverge but don't cross.
+
+====
+
+Code I May Not Need Any More
+
+    static bool CoincidentCandidate(const Angle* current) {
+        const Segment* segment = current->segment();
+        int min = SkMin32(current->start(), current->end());
+        do {
+            const Span& span = segment->fTs[min];
+            if (span.fCoincident == Span::kStart_Coincidence) {
+                return true;
+            }
+        } while (--min >= 0);
+        return false;
+    }
+
+    static bool CoincidentHalf(const Angle* current, const Angle* next) {
+        const Segment* other = next->segment();
+        const Segment* segment = current->segment();
+        int min = SkMin32(current->start(), current->end());
+        const Span& minSpan = segment->fTs[min];
+        if (minSpan.fOther == other) {
+            return minSpan.fCoincident == Span::kStart_Coincidence;
+        }
+        int index = min;
+        int spanCount = segment->fTs.count();
+        while (++index < spanCount) {
+            const Span& span = segment->fTs[index];
+            if (minSpan.fT != span.fT) {
+                break;
+            }
+            if (span.fOther != other) {
+                continue;
+            }
+            return span.fCoincident == Span::kStart_Coincidence;
+        }
+        index = min;
+        while (--index >= 0) {
+            const Span& span = segment->fTs[index];
+            if (span.fOther != other) {
+                continue;
+            }
+            return span.fCoincident == Span::kStart_Coincidence;
+        }
+        return false;
+    }
+    
+    static bool Coincident(const Angle* current, const Angle* next) {
+        return CoincidentHalf(current, next) &&
+                CoincidentHalf(next, current);
+    }
+
+    // If three lines cancel in a - b - c order, a - b may or may not
+    // eliminate the edge that describes the b - c cancellation. Check done to
+    // determine this.
+    static bool CoincidentCancels(const Angle* current, const Angle* next) {
+        int curMin = SkMin32(current->start(), current->end());
+        if (current->segment()->fTs[curMin].fDone) {
+            return false;
+        }
+        int nextMin = SkMin32(next->start(), next->end());
+        if (next->segment()->fTs[nextMin].fDone) {
+            return false;
+        }
+        return SkSign32(current->start() - current->end())
+                != SkSign32(next->start() - next->end());
+    }
+
+    // FIXME: at this point, just have two functions for the different steps
+    int coincidentEnd(int from, int step) const {
+        double fromT = fTs[from].fT;
+        int count = fTs.count();
+        int to = from;
+        while (step > 0 ? ++to < count : --to >= 0) {
+            const Span& span = fTs[to];
+            if ((step > 0 ? span.fT - fromT : fromT - span.fT) >= FLT_EPSILON ) {
+                // FIXME: we assume that if the T changes, we don't care about 
+                // coincident -- but in nextSpan, we require that both the T
+                // and actual loc change to represent a span. This asymettry may
+                // be OK or may be trouble -- if trouble, probably will need to
+                // detect coincidence earlier or sort differently 
+                break;
+            }
+#if 01
+            if (span.fCoincident == (step < 0 ? Span::kStart_Coincidence :
+                    Span::kEnd_Coincidence)) {
+                from = to;
+            }
+#else
+            from = to;
+#endif
+        }
+        return from;
+    }
+
+    // once past current span, if step>0, look for coicident==1
+    // if step<0, look for coincident==-1
+    int nextSpanEnd(int from, int step) const {
+        int result = nextSpan(from, step);
+        if (result < 0) {
+            return result;
+        }
+        return coincidentEnd(result, step);
+    }
+
+    
+    void adjustFirst(const SkTDArray<Angle*>& sorted, int& first, int& winding,
+            bool outside) {
+        int firstIndex = first;
+        int angleCount = sorted.count();
+        if (true || outside) {
+            const Angle* angle = sorted[firstIndex];
+            int prior = firstIndex;
+            do {
+                if (--prior < 0) {
+                    prior = angleCount - 1;
+                }
+                if (prior == firstIndex) { // all are coincident with each other
+                    return;
+                }
+                if (!Coincident(sorted[prior], sorted[first])) {
+                    return;
+                }
+                winding += angle->sign();
+                first = prior;
+                angle = sorted[prior];
+                winding -= angle->sign();
+            } while (true);
+        }
+        do {
+            int next = first + 1;
+            if (next == angleCount) {
+                next = 0;
+            }
+            if (next == firstIndex) { // all are coincident with each other
+                return;
+            }
+            if (!Coincident(sorted[first], sorted[next])) {
+                return;
+            }
+            first = next;
+        } while (true);
+    }
+
+            bool nextIsCoincident = CoincidentCandidate(nextAngle);
+            bool finalOrNoCoincident = true;
+            bool pairCoincides = false;
+            bool pairCancels = false;
+            if (nextIsCoincident) {
+                int followIndex = nextIndex + 1;
+                if (followIndex == angleCount) {
+                    followIndex = 0;
+                }
+                const Angle* followAngle = sorted[followIndex];
+                finalOrNoCoincident = !Coincident(nextAngle, followAngle);
+                if ((pairCoincides = Coincident(angle, nextAngle))) {
+                    pairCancels = CoincidentCancels(angle, nextAngle);
+                }
+            }
+            if (pairCancels && !foundAngle && !nextSegment->done()) {
+                Segment* aSeg = angle->segment();
+      //          alreadyMarked |= aSeg == sorted[firstIndex]->segment();
+                aSeg->markAndChaseCoincident(angle->start(), angle->end(),
+                        nextSegment);
+                if (firstEdge) {
+                    return NULL;
+                }
+            }
+            if (pairCoincides) {
+                if (pairCancels) {
+                    goto doNext;
+                }
+                int minT = SkMin32(nextAngle->start(), nextAngle->end());
+                bool markNext = abs(maxWinding) < abs(winding);
+                if (markNext) {
+                    nextSegment->markDone(minT, winding);
+                } 
+                int oldMinT = SkMin32(angle->start(), angle->end());
+                if (true || !foundAngle) {
+                 //   SkASSERT(0); // do we ever get here?
+                    Segment* aSeg = angle->segment();
+        //            alreadyMarked |= aSeg == sorted[firstIndex]->segment();
+                    aSeg->markDone(oldMinT, maxWinding);
+                }
+            }
+
+    // OPTIMIZATION: uses tail recursion. Unwise?
+    void innerCoincidentChase(int step, Segment* other) {
+        // find other at index
+   //     SkASSERT(!done());
+        const Span* start = NULL;
+        const Span* end = NULL;
+        int index, startIndex, endIndex;
+        int count = fTs.count();
+        for (index = 0; index < count; ++index) {
+            const Span& span = fTs[index];
+            if (!span.fCoincident || span.fOther != other) {
+                continue;
+            }
+            if (!start) {
+                startIndex = index;
+                start = &span;
+            } else {
+                SkASSERT(!end);
+                endIndex = index;
+                end = &span;
+            }
+        }
+        if (!end) {
+            return;
+        }
+        bool thisDone = fTs[SkMin32(startIndex, endIndex)].fDone;
+        bool otherDone = other->fTs[SkMin32(start->fOtherIndex,
+                end->fOtherIndex)].fDone;
+        if (thisDone && otherDone) {
+            return;
+        }
+        Segment* next;
+        Segment* nextOther;
+        if (step < 0) {
+            next = start->fT == 0 ? NULL : this;
+            nextOther = other->fTs[start->fOtherIndex].fT > 1 - FLT_EPSILON ? NULL : other;
+        } else {
+            next = end->fT == 1 ? NULL : this;
+            nextOther = other->fTs[end->fOtherIndex].fT < FLT_EPSILON ? NULL : other;
+        }
+        SkASSERT(!next || !nextOther);
+        for (index = 0; index < count; ++index) {
+            const Span& span = fTs[index];
+            if (span.fCoincident || span.fOther == other) {
+                continue;
+            }
+            bool checkNext = !next && (step < 0 ? span.fT < FLT_EPSILON
+                && span.fOtherT > 1 - FLT_EPSILON : span.fT > 1 - FLT_EPSILON
+                && span.fOtherT < FLT_EPSILON);
+            bool checkOther = !nextOther && (step < 0 ? fabs(span.fT - start->fT) < FLT_EPSILON
+                && span.fOtherT < FLT_EPSILON : fabs(span.fT - end->fT) < FLT_EPSILON
+                && span.fOtherT > 1 - FLT_EPSILON);
+            if (!checkNext && !checkOther) {
+                continue;
+            }
+            Segment* oSegment = span.fOther;
+            if (oSegment->done()) {
+                continue;
+            }
+            int oCount = oSegment->fTs.count();
+            for (int oIndex = 0; oIndex < oCount; ++oIndex) {
+                const Span& oSpan = oSegment->fTs[oIndex];
+                if (oSpan.fT >= FLT_EPSILON && oSpan.fT <= 1 - FLT_EPSILON) {
+                    continue;
+                }
+                if (!oSpan.fCoincident) {
+                    continue;
+                }
+                if (checkNext && (oSpan.fT < FLT_EPSILON ^ step < 0)) { 
+                    next = oSegment;
+                    checkNext = false;
+                }
+                if (checkOther && (oSpan.fT > 1 - FLT_EPSILON ^ step < 0)) {
+                    nextOther = oSegment;
+                    checkOther = false;
+                }
+            }
+        }
+        // this needs to walk both spans in lock step, skipping edges that
+        // are already marked done on one or the other
+        markCanceled(startIndex, endIndex);
+        if (next && nextOther) {
+            next->innerCoincidentChase(step, nextOther);
+        }
+    }
+
+    // cancel coincident edges in lock step
+    void markCanceled(int start, int end) {
+        if (done()) {
+            return;
+        }
+        Segment* other = fTs[start].fOther;
+        if (other->done()) {
+            return;
+        }
+        if (start > end) {
+            SkTSwap<int>(start, end);
+        }
+        double maxT = fTs[end].fT - FLT_EPSILON;
+        int spanCount = fTs.count();
+        // since these cancel, this walks up and other walks down
+        int oStart = fTs[start].fOtherIndex;
+        double oStartT = other->fTs[oStart].fT;
+        while (oStartT - other->fTs[--oStart].fT < FLT_EPSILON)
+            ;
+        double startT = fTs[start].fT;
+        while (start > 0 && startT - fTs[start - 1].fT < FLT_EPSILON) {
+            --start;
+        }
+        do {
+            Span* span = &fTs[start];
+            Span* oSpan = &other->fTs[oStart];
+            // find start of each, and see if both are not done
+            bool markDone = !span->fDone && !oSpan->fDone;
+            double spanT = span->fT;
+            do {
+                if (markDone) {
+                    span->fCanceled = true;
+                #if DEBUG_MARK_DONE
+                    const SkPoint& pt = xyAtT(span);
+                    SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g)\n",
+                            __FUNCTION__, fID, start, span->fT, pt.fX, pt.fY);
+                #endif
+                    SkASSERT(!span->fDone);
+                    span->fDone = true;
+                    span->fWinding = 0;
+                    fDoneSpans++;
+                }
+                if (++start == spanCount) {
+                    break;
+                }
+                span = &fTs[start];
+            } while (span->fT - spanT < FLT_EPSILON);
+            double oSpanT = oSpan->fT;
+            do {
+                if (markDone) {
+                    oSpan->fCanceled = true;
+                #if DEBUG_MARK_DONE
+                    const SkPoint& oPt = xyAtT(oSpan);
+                    SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g)\n",
+                            __FUNCTION__, other->fID, oStart, oSpan->fT,
+                            oPt.fX, oPt.fY);
+                #endif
+                    SkASSERT(!oSpan->fDone);
+                    oSpan->fDone = true;
+                    oSpan->fWinding = 0;
+                    other->fDoneSpans++;
+                }
+                if (--oStart < 0) {
+                    break;
+                }
+                oSpan = &other->fTs[oStart];
+            } while (oSpanT - oSpan->fT < FLT_EPSILON);
+        } while (fTs[start].fT <= maxT);
+    }
+
+    bool canceled(int start, int end) const {
+        int min = SkMin32(start, end);
+        return fTs[min].fCanceled;
+    }
+
+    void markAndChaseCoincident(int index, int endIndex, Segment* other) {
+        int step = SkSign32(endIndex - index);
+        innerCoincidentChase(step, other);
+    }
+
diff --git a/experimental/Networking/SampleNetPipeReader.cpp b/experimental/Networking/SampleNetPipeReader.cpp
new file mode 100644
index 0000000..4332cbd
--- /dev/null
+++ b/experimental/Networking/SampleNetPipeReader.cpp
@@ -0,0 +1,130 @@
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGPipe.h"
+#include "SkSockets.h"
+#include "SkOSMenu.h"
+
+/**
+ * A simple networked pipe reader
+ *
+ * This view will connect to a user specified server, in this case meaning any
+ * Skia app that's has a SkTCPServer set up to broadcast its piped drawing data,
+ * received all the data transmitted and attempt to reproduce the drawing calls.
+ * This reader will only keep the latest batch of data. In order to keep up with
+ * the server, which may be producing data at a much higher rate than the reader
+ * is consuming, the reader will attempt multiple reads and only render the
+ * latest frame. this behavior can be adjusted by changing MAX_READS_PER_FRAME
+ * or disabled by setting fSync to false
+ */
+
+#define MAX_READS_PER_FRAME 12
+
+class NetPipeReaderView : public SampleView {
+public:
+    NetPipeReaderView() {
+        fSocket = NULL;
+        fSync = true;
+    }
+
+    ~NetPipeReaderView() {
+        if (fSocket) {
+            delete fSocket;
+        }
+        fDataArray.reset();
+    }
+    virtual void requestMenu(SkOSMenu* menu) {
+        menu->setTitle("Net Pipe Reader");
+        menu->appendTextField("Server IP", "Server IP", this->getSinkID(),
+                              "IP address");
+        menu->appendSwitch("Sync", "Sync", this->getSinkID(), fSync);
+    }
+
+protected:
+    static void readData(int cid, const void* data, size_t size,
+                         SkSocket::DataType type, void* context) {
+        NetPipeReaderView* view = (NetPipeReaderView*)context;
+        view->onRead(data, size);
+    }
+
+    void onRead(const void* data, size_t size) {
+        if (size > 0)
+            fDataArray.append(size, (const char*)data);
+    }
+
+    bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Net Pipe Reader");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    bool onEvent(const SkEvent& evt) {
+        SkString s;
+        if (SkOSMenu::FindText(evt, "Server IP", &s)) {
+            if (NULL != fSocket) {
+                delete fSocket;
+            }
+            fSocket = new SkTCPClient(s.c_str());
+            fSocket->connectToServer();
+            SkDebugf("Connecting to %s\n", s.c_str());
+            return true;
+        }
+        if (SkOSMenu::FindSwitchState(evt, "Sync", &fSync))
+            return true;
+        return this->INHERITED::onEvent(evt);
+    }
+
+    void onDrawContent(SkCanvas* canvas) {
+        if (NULL == fSocket)
+            return;
+
+        if (fSocket->isConnected()) {
+            int dataToRemove = fDataArray.count();
+            if (fSync) {
+                int numreads = 0;
+                while (fSocket->readPacket(readData, this) > 0 &&
+                       numreads < MAX_READS_PER_FRAME) {
+                    // at this point, new data has been read and stored, discard
+                    // old data since it's not needed anymore
+                    SkASSERT(fDataArray.count() > dataToRemove);
+                    fDataArray.remove(0, dataToRemove);
+                    dataToRemove = fDataArray.count();
+                    ++numreads;
+                }
+                // clean up if max reads reached
+                if (numreads == MAX_READS_PER_FRAME &&
+                    fDataArray.count() > dataToRemove)
+                    fDataArray.remove(0, dataToRemove);
+            }
+            else {
+                if (fSocket->readPacket(readData, this) > 0)
+                    fDataArray.remove(0, dataToRemove);
+            }
+        }
+        else
+            fSocket->connectToServer();
+
+        SkGPipeReader reader(canvas);
+        size_t bytesRead;
+        SkGPipeReader::Status fStatus = reader.playback(fDataArray.begin(),
+                                                        fDataArray.count(),
+                                                        &bytesRead);
+        SkASSERT(SkGPipeReader::kError_Status != fStatus);
+        this->inval(NULL);
+    }
+
+private:
+    bool fSync;
+    SkTDArray<char> fDataArray;
+    SkTCPClient* fSocket;
+    typedef SampleView INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new NetPipeReaderView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/experimental/Networking/SkSockets.cpp b/experimental/Networking/SkSockets.cpp
new file mode 100644
index 0000000..db9da09
--- /dev/null
+++ b/experimental/Networking/SkSockets.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "SkSockets.h"
+#include "SkData.h"
+
+SkSocket::SkSocket() {
+    fMaxfd = 0;
+    FD_ZERO(&fMasterSet);
+    fConnected = false;
+    fReady = false;
+    fReadSuspended = false;
+    fWriteSuspended = false;
+    fSockfd = this->createSocket();
+}
+
+SkSocket::~SkSocket() {
+    this->closeSocket(fSockfd);
+    shutdown(fSockfd, 2); //stop sending/receiving
+}
+
+int SkSocket::createSocket() {
+    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+    if (sockfd < 0) {
+        SkDebugf("ERROR opening socket\n");
+        return -1;
+    }
+    int reuse = 1;
+
+    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0) {
+        SkDebugf("error: %s\n", strerror(errno));
+        return -1;
+    }
+#ifdef NONBLOCKING_SOCKETS
+    this->setNonBlocking(sockfd);
+#endif
+    //SkDebugf("Opened fd:%d\n", sockfd);
+    fReady = true;
+    return sockfd;
+}
+
+void SkSocket::closeSocket(int sockfd) {
+    if (!fReady)
+        return;
+
+    close(sockfd);
+    //SkDebugf("Closed fd:%d\n", sockfd);
+
+    if (FD_ISSET(sockfd, &fMasterSet)) {
+        FD_CLR(sockfd, &fMasterSet);
+        if (sockfd >= fMaxfd) {
+            while (FD_ISSET(fMaxfd, &fMasterSet) == false && fMaxfd > 0)
+                fMaxfd -= 1;
+        }
+    }
+    if (0 == fMaxfd)
+        fConnected = false;
+}
+
+void SkSocket::onFailedConnection(int sockfd) {
+    this->closeSocket(sockfd);
+}
+
+void SkSocket::setNonBlocking(int sockfd) {
+    int flags = fcntl(sockfd, F_GETFL);
+    fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+}
+
+void SkSocket::addToMasterSet(int sockfd) {
+    FD_SET(sockfd, &fMasterSet);
+    if (sockfd > fMaxfd)
+        fMaxfd = sockfd;
+}
+
+int SkSocket::readPacket(void (*onRead)(int, const void*, size_t, DataType,
+                                        void*), void* context) {
+    if (!fConnected || !fReady || NULL == onRead || NULL == context
+        || fReadSuspended)
+        return -1;
+
+    int totalBytesRead = 0;
+
+    char packet[PACKET_SIZE];
+    for (int i = 0; i <= fMaxfd; ++i) {
+        if (!FD_ISSET (i, &fMasterSet))
+            continue;
+
+        memset(packet, 0, PACKET_SIZE);
+        SkDynamicMemoryWStream stream;
+        int attempts = 0;
+        bool failure = false;
+        int bytesReadInTransfer = 0;
+        int bytesReadInPacket = 0;
+        header h;
+        h.done = false;
+        h.bytes = 0;
+        while (!h.done && fConnected && !failure) {
+            int retval = read(i, packet + bytesReadInPacket,
+                              PACKET_SIZE - bytesReadInPacket);
+
+            ++attempts;
+            if (retval < 0) {
+#ifdef NONBLOCKING_SOCKETS
+                if (errno == EWOULDBLOCK || errno == EAGAIN) {
+                    if (bytesReadInPacket > 0 || bytesReadInTransfer > 0)
+                        continue; //incomplete packet or frame, keep tring
+                    else
+                        break; //nothing to read
+                }
+#endif
+                //SkDebugf("Read() failed with error: %s\n", strerror(errno));
+                failure = true;
+                break;
+            }
+
+            if (retval == 0) {
+                //SkDebugf("Peer closed connection or connection failed\n");
+                failure = true;
+                break;
+            }
+
+            SkASSERT(retval > 0);
+            bytesReadInPacket += retval;
+            if (bytesReadInPacket < PACKET_SIZE) {
+                //SkDebugf("Read %d/%d\n", bytesReadInPacket, PACKET_SIZE);
+                continue; //incomplete packet, keep trying
+            }
+
+            SkASSERT((bytesReadInPacket == PACKET_SIZE) && !failure);
+            memcpy(&h.done, packet, sizeof(bool));
+            memcpy(&h.bytes, packet + sizeof(bool), sizeof(int));
+            memcpy(&h.type, packet + sizeof(bool) + sizeof(int), sizeof(DataType));
+            if (h.bytes > CONTENT_SIZE || h.bytes <= 0) {
+                //SkDebugf("bad packet\n");
+                failure = true;
+                break;
+            }
+            //SkDebugf("read packet(done:%d, bytes:%d) from fd:%d in %d tries\n",
+            //         h.done, h.bytes, fSockfd, attempts);
+            stream.write(packet + HEADER_SIZE, h.bytes);
+            bytesReadInPacket = 0;
+            attempts = 0;
+            bytesReadInTransfer += h.bytes;
+        }
+
+        if (failure) {
+            onRead(i, NULL, 0, h.type, context);
+            this->onFailedConnection(i);
+            continue;
+        }
+
+        if (bytesReadInTransfer > 0) {
+            SkData* data = stream.copyToData();
+            SkASSERT(data->size() == bytesReadInTransfer);
+            onRead(i, data->data(), data->size(), h.type, context);
+            data->unref();
+
+            totalBytesRead += bytesReadInTransfer;
+        }
+    }
+    return totalBytesRead;
+}
+
+int SkSocket::writePacket(void* data, size_t size, DataType type) {
+    if (size < 0|| NULL == data || !fConnected || !fReady || fWriteSuspended)
+        return -1;
+
+    int totalBytesWritten = 0;
+    header h;
+    char packet[PACKET_SIZE];
+    for (int i = 0; i <= fMaxfd; ++i) {
+        if (!FD_ISSET (i, &fMasterSet))
+            continue;
+
+        int bytesWrittenInTransfer = 0;
+        int bytesWrittenInPacket = 0;
+        int attempts = 0;
+        bool failure = false;
+        while (bytesWrittenInTransfer < size && fConnected && !failure) {
+            memset(packet, 0, PACKET_SIZE);
+            h.done = (size - bytesWrittenInTransfer <= CONTENT_SIZE);
+            h.bytes = (h.done) ? size - bytesWrittenInTransfer : CONTENT_SIZE;
+            h.type = type;
+            memcpy(packet, &h.done, sizeof(bool));
+            memcpy(packet + sizeof(bool), &h.bytes, sizeof(int));
+            memcpy(packet + sizeof(bool) + sizeof(int), &h.type, sizeof(DataType));
+            memcpy(packet + HEADER_SIZE, (char*)data + bytesWrittenInTransfer,
+                   h.bytes);
+
+            int retval = write(i, packet + bytesWrittenInPacket,
+                               PACKET_SIZE - bytesWrittenInPacket);
+            attempts++;
+
+            if (retval < 0) {
+                if (errno == EPIPE) {
+                    //SkDebugf("broken pipe, client closed connection");
+                    failure = true;
+                    break;
+                }
+#ifdef NONBLOCKING_SOCKETS
+                else if (errno == EWOULDBLOCK || errno == EAGAIN) {
+                    if (bytesWrittenInPacket > 0 || bytesWrittenInTransfer > 0)
+                        continue; //incomplete packet or frame, keep trying
+                    else
+                        break; //client not available, skip current transfer
+                }
+#endif
+                else {
+                    //SkDebugf("write(%d) failed with error:%s\n", i,
+                    //         strerror(errno));
+                    failure = true;
+                    break;
+                }
+            }
+
+            bytesWrittenInPacket += retval;
+            if (bytesWrittenInPacket < PACKET_SIZE)
+                continue; //incomplete packet, keep trying
+
+            SkASSERT(bytesWrittenInPacket == PACKET_SIZE);
+            //SkDebugf("wrote to packet(done:%d, bytes:%d) to fd:%d in %d tries\n",
+            //         h.done, h.bytes, i, attempts);
+            bytesWrittenInTransfer += h.bytes;
+            bytesWrittenInPacket = 0;
+            attempts = 0;
+        }
+
+        if (failure)
+            this->onFailedConnection(i);
+
+        totalBytesWritten += bytesWrittenInTransfer;
+    }
+    return totalBytesWritten;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+SkTCPServer::SkTCPServer(int port) {
+    sockaddr_in serverAddr;
+    serverAddr.sin_family = AF_INET;
+    serverAddr.sin_addr.s_addr = INADDR_ANY;
+    serverAddr.sin_port = htons(port);
+
+    if (bind(fSockfd, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
+        SkDebugf("ERROR on binding: %s\n", strerror(errno));
+        fReady = false;
+    }
+}
+
+SkTCPServer::~SkTCPServer() {
+    this->disconnectAll();
+}
+
+int SkTCPServer::acceptConnections() {
+    if (!fReady)
+        return -1;
+
+    listen(fSockfd, MAX_WAITING_CLIENTS);
+    int newfd;
+    for (int i = 0; i < MAX_WAITING_CLIENTS; ++i) {
+#ifdef NONBLOCKING_SOCKETS
+        fd_set workingSet;
+        FD_ZERO(&workingSet);
+        FD_SET(fSockfd, &workingSet);
+        timeval timeout;
+        timeout.tv_sec  = 0;
+        timeout.tv_usec = 0;
+        int sel = select(fSockfd + 1, &workingSet, NULL, NULL, &timeout);
+        if (sel < 0) {
+            SkDebugf("select() failed with error %s\n", strerror(errno));
+            continue;
+        }
+        if (sel == 0) //select() timed out
+            continue;
+#endif
+        sockaddr_in clientAddr;
+        socklen_t clientLen = sizeof(clientAddr);
+        newfd = accept(fSockfd, (struct sockaddr*)&clientAddr, &clientLen);
+        if (newfd< 0) {
+            SkDebugf("accept() failed with error %s\n", strerror(errno));
+            continue;
+        }
+        SkDebugf("New incoming connection - %d\n", newfd);
+        fConnected = true;
+#ifdef NONBLOCKING_SOCKETS
+        this->setNonBlocking(newfd);
+#endif
+        this->addToMasterSet(newfd);
+    }
+    return 0;
+}
+
+
+int SkTCPServer::disconnectAll() {
+    if (!fConnected || !fReady)
+        return -1;
+    for (int i = 0; i <= fMaxfd; ++i) {
+        if (FD_ISSET(i, &fMasterSet))
+            this->closeSocket(i);
+    }
+    fConnected = false;
+    return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+SkTCPClient::SkTCPClient(const char* hostname, int port) {
+    //Add fSockfd since the client will be using it to read/write
+    this->addToMasterSet(fSockfd);
+
+    hostent* server = gethostbyname(hostname);
+    if (server) {
+        fServerAddr.sin_family = AF_INET;
+        memcpy((char*)&fServerAddr.sin_addr.s_addr, (char*)server->h_addr,
+               server->h_length);
+        fServerAddr.sin_port = htons(port);
+    }
+    else {
+        //SkDebugf("ERROR, no such host\n");
+        fReady = false;
+    }
+}
+
+void SkTCPClient::onFailedConnection(int sockfd) { //cleanup and recreate socket
+    SkASSERT(sockfd == fSockfd);
+    this->closeSocket(fSockfd);
+    fSockfd = this->createSocket();
+    //Add fSockfd since the client will be using it to read/write
+    this->addToMasterSet(fSockfd);
+}
+
+int SkTCPClient::connectToServer() {
+    if (!fReady)
+        return -1;
+    if (fConnected)
+        return 0;
+
+    int conn = connect(fSockfd, (sockaddr*)&fServerAddr, sizeof(fServerAddr));
+    if (conn < 0) {
+#ifdef NONBLOCKING_SOCKETS
+        if (errno == EINPROGRESS || errno == EALREADY)
+            return conn;
+#endif
+        if (errno != EISCONN) {
+            //SkDebugf("error: %s\n", strerror(errno));
+            this->onFailedConnection(fSockfd);
+            return conn;
+        }
+    }
+    fConnected = true;
+    SkDebugf("Succesfully reached server\n");
+    return 0;
+}
diff --git a/experimental/Networking/SkSockets.h b/experimental/Networking/SkSockets.h
new file mode 100644
index 0000000..8d85446
--- /dev/null
+++ b/experimental/Networking/SkSockets.h
@@ -0,0 +1,198 @@
+/*
+ * 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 SkNetIO_DEFINED
+#define SkNetIO_DEFINED
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include "SkTypes.h"
+#include "SkStream.h"
+
+/* PACKET and HEADER Format */
+#define PACKET_SIZE 1024
+#define HEADER_SIZE 20
+#define CONTENT_SIZE 1004
+
+#define DEFAULT_PORT 15555
+#define MAX_WAITING_CLIENTS 3
+#define NONBLOCKING_SOCKETS
+
+class SkSocket {
+public:
+    SkSocket();
+    virtual ~SkSocket();
+
+    enum State {
+        kError_state,
+        kBegin_state,
+        kIncomplete_state,
+        kDone_state
+    };
+
+    enum DataType {
+        kPipeAppend_type,
+        kPipeReplace_type,
+        kString_type,
+        kInt_type
+    };
+
+    bool isConnected() { return fConnected; }
+    /**
+     * Write data to the socket. Data is a pointer to the beginning of the data
+     * to be sent and dataSize specifies the number of bytes to send. This
+     * method will spread the data across multiple packets if the data can't all
+     * fit in a single packet. The method will write all the data to each of the
+     * socket's open connections until all the bytes have been successfully sent
+     * and return total the number of bytes written to all clients, unless there
+     * was an error during the transfer, in which case the method returns -1.
+     * For blocking sockets, write will block indefinitely if the socket at the
+     * other end of the connection doesn't receive any data.
+     * NOTE: This method guarantees that all of the data will be sent unless
+     * there was an error, so it may block temporarily when the write buffer is
+     * full
+     */
+    int writePacket(void* data, size_t size, DataType type = kPipeAppend_type);
+
+    /**
+     * Read a logical packet from socket. The data read will be stored
+     * sequentially in the dataArray. This method will keep running until all
+     * the data in a logical chunk has been read (assembling multiple partial
+     * packets if necessary) and return the number of bytes successfully read,
+     * unless there was an error, in which case the method returns -1. \For
+     * nonblocking sockets, read will return 0 if there's nothing to read. For
+     * blocking sockets, read will block indefinitely if the socket doesn't
+     * receive any data.
+     * NOTE: This method guarantees that all the data in a logical packet will
+     * be read so it may block temporarily if it's waiting for parts of a
+     * packet
+     */
+    int readPacket(void (*onRead)(int cid, const void* data, size_t size,
+                                  DataType type, void*), void* context);
+
+    /**
+     * Suspend network transfers until resume() is called. Leaves all
+     * connections in tact.
+     */
+    void suspendAll() { fReadSuspended = fWriteSuspended = true; }
+    /**
+     * Resume all network transfers.
+     */
+    void resumeAll() { fReadSuspended = fWriteSuspended = false; }
+    /**
+     * Other helper functions
+     */
+    void suspendRead() { fReadSuspended = true; }
+    void resumeRead() { fReadSuspended = false; }
+    void suspendWrite()  { fWriteSuspended = true; }
+    void resumeWrite()  { fWriteSuspended = false; }
+
+protected:
+    struct header {
+        bool        done;
+        int         bytes;
+        DataType    type;
+    };
+
+    /**
+     * Create a socket and return its file descriptor. Returns -1 on failure
+     */
+    int createSocket();
+
+    /**
+     * Close the socket specified by the socket file descriptor argument. Will
+     * update fMaxfd and working set properly
+     */
+    void closeSocket(int sockfd);
+
+    /**
+     * Called when a broken or terminated connection has been detected. Closes
+     * the socket file descriptor and removes it from the master set by default.
+     * Override to handle broken connections differently
+     */
+    virtual void onFailedConnection(int sockfd);
+
+    /**
+     * Set the socket specified by the socket file descriptor as nonblocking
+     */
+    void setNonBlocking(int sockfd);
+
+    /**
+     * Add the socket specified by the socket file descriptor to the master
+     * file descriptor set, which is used to in the select() to detect new data
+     * or connections
+     */
+    void addToMasterSet(int sockfd);
+
+    bool    fConnected;
+    bool    fReady;
+    bool    fReadSuspended;
+    bool    fWriteSuspended;
+    int     fMaxfd;
+    int     fPort;
+    int     fSockfd;
+
+    /**
+     * fMasterSet contains all the file descriptors to be used for read/write.
+     * For clients, this only contains the client socket. For servers, this
+     * contains all the file descriptors associated with established connections
+     * to clients
+     */
+    fd_set  fMasterSet;
+};
+
+/*
+ * TCP server. Can accept simultaneous connections to multiple SkTCPClients and
+ * read/write data back and forth using read/writePacket calls. Port number can
+ * be specified, but make sure that client/server use the same port
+ */
+class SkTCPServer : public SkSocket {
+public:
+    SkTCPServer(int port = DEFAULT_PORT);
+    virtual ~SkTCPServer();
+
+    /**
+     * Accept any incoming connections to the server, will accept 1 connection
+     * at a time. Returns -1 on error. For blocking sockets, this method will
+     * block until a client calls connectToServer()
+     */
+    int acceptConnections();
+
+    /**
+     * Disconnect all connections to clients. Returns -1 on error
+     */
+    int disconnectAll();
+private:
+    typedef SkSocket INHERITED;
+};
+
+/*
+ * TCP client. Will connect to the server specified in the constructor. If a
+ * port number is specified, make sure that it's the same as the port number on
+ * the server
+ */
+class SkTCPClient : public SkSocket {
+public:
+    SkTCPClient(const char* hostname, int port = DEFAULT_PORT);
+
+    /**
+     * Connect to server. Returns -1 on error or failure. Call this to connect
+     * or reconnect to the server. For blocking sockets, this method will block
+     * until the connection is accepted by the server.
+     */
+    int connectToServer();
+protected:
+    /**
+     * Client needs to recreate the socket when a connection is broken because
+     * connect can only be called successfully once.
+     */
+    virtual void onFailedConnection(int sockfd);
+private:
+    sockaddr_in fServerAddr;
+    typedef SkSocket INHERITED;
+};
+
+#endif
diff --git a/experimental/SimpleCocoaApp/SimpleApp-Info.plist b/experimental/SimpleCocoaApp/SimpleApp-Info.plist
new file mode 100644
index 0000000..dcb5a45
--- /dev/null
+++ b/experimental/SimpleCocoaApp/SimpleApp-Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>com.yourcompany.${PRODUCT_NAME:rfc1034identifier}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>${MACOSX_DEPLOYMENT_TARGET}</string>
+	<key>NSMainNibFile</key>
+	<string>SimpleApp</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>
diff --git a/experimental/SimpleCocoaApp/SimpleApp.h b/experimental/SimpleCocoaApp/SimpleApp.h
new file mode 100644
index 0000000..c36007c
--- /dev/null
+++ b/experimental/SimpleCocoaApp/SimpleApp.h
@@ -0,0 +1,17 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * Simple hello world app for skia on Mac OS (Cocoa)
+ */
+
+#import "SkNSView.h"
+@interface SimpleNSView : SkNSView
+- (id)initWithDefaults;
+@end
+
diff --git a/experimental/SimpleCocoaApp/SimpleApp.mm b/experimental/SimpleCocoaApp/SimpleApp.mm
new file mode 100644
index 0000000..922177e
--- /dev/null
+++ b/experimental/SimpleCocoaApp/SimpleApp.mm
@@ -0,0 +1,62 @@
+#import "SkCanvas.h"
+#import "SkPaint.h"
+#import "SkWindow.h"
+#include "SkGraphics.h"
+#include "SkCGUtils.h"
+class SkSampleView : public SkView {
+public:
+    SkSampleView() {
+        this->setVisibleP(true);
+        this->setClipToBounds(false);
+    };
+protected:
+    virtual void onDraw(SkCanvas* canvas) {
+        canvas->drawColor(0xFFFFFFFF);
+        SkPaint p;
+        p.setTextSize(20);
+        p.setAntiAlias(true);
+        canvas->drawText("Hello World!", 13, 50, 30, p);
+        SkRect r = {50, 50, 80, 80};
+        p.setColor(0xAA11EEAA);
+        canvas->drawRect(r, p);
+    }
+private:
+    typedef SkView INHERITED; 
+};
+
+void application_init() {
+    SkGraphics::Init();
+    SkEvent::Init();
+}
+
+void application_term() {
+    SkGraphics::Term();
+    SkEvent::Term();
+}
+
+class FillLayout : public SkView::Layout {
+protected:
+    virtual void onLayoutChildren(SkView* parent) {
+        SkView* view = SkView::F2BIter(parent).next();
+        view->setSize(parent->width(), parent->height());
+    }
+};
+
+#import "SimpleApp.h"
+@implementation SimpleNSView
+
+- (id)initWithDefaults {
+    if (self = [super initWithDefaults]) {
+        fWind = new SkOSWindow(self);
+        fWind->setLayout(new FillLayout, false);
+        fWind->attachChildToFront(new SkSampleView)->unref();
+    }
+    return self;
+}
+
+- (void)drawRect:(NSRect)dirtyRect {
+    CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+    SkCGDrawBitmap(ctx, fWind->getBitmap(), 0, 0);
+}
+
+@end
\ No newline at end of file
diff --git a/experimental/SimpleCocoaApp/SimpleApp.xib b/experimental/SimpleCocoaApp/SimpleApp.xib
new file mode 100644
index 0000000..8ce9bc8
--- /dev/null
+++ b/experimental/SimpleCocoaApp/SimpleApp.xib
@@ -0,0 +1,3734 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
+	<data>
+		<int key="IBDocument.SystemTarget">1060</int>
+		<string key="IBDocument.SystemVersion">10K540</string>
+		<string key="IBDocument.InterfaceBuilderVersion">851</string>
+		<string key="IBDocument.AppKitVersion">1038.36</string>
+		<string key="IBDocument.HIToolboxVersion">461.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+			<string key="NS.object.0">851</string>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<integer value="372"/>
+			<integer value="24"/>
+		</object>
+		<object class="NSArray" key="IBDocument.PluginDependencies">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+			<integer value="1" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSCustomObject" id="1021">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSCustomObject" id="1014">
+				<string key="NSClassName">FirstResponder</string>
+			</object>
+			<object class="NSCustomObject" id="1050">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSMenu" id="649796088">
+				<string key="NSTitle">AMainMenu</string>
+				<object class="NSMutableArray" key="NSMenuItems">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="NSMenuItem" id="694149608">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">SimpleCocoaApp</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<object class="NSCustomResource" key="NSOnImage" id="35465992">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuCheckmark</string>
+						</object>
+						<object class="NSCustomResource" key="NSMixedImage" id="502551668">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuMixedState</string>
+						</object>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="110575045">
+							<string key="NSTitle">SimpleCocoaApp</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="238522557">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">About SimpleCocoaApp</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="304266470">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="609285721">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Preferences…</string>
+									<string key="NSKeyEquiv">,</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="481834944">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1046388886">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Services</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="752062318">
+										<string key="NSTitle">Services</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+										</object>
+										<string key="NSName">_NSServicesMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="646227648">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="755159360">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Hide SimpleCocoaApp</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="342932134">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Hide Others</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="908899353">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Show All</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1056857174">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="632727374">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Quit SimpleCocoaApp</string>
+									<string key="NSKeyEquiv">q</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+							<string key="NSName">_NSAppleMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="379814623">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">File</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="720053764">
+							<string key="NSTitle">File</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="705341025">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">New</string>
+									<string key="NSKeyEquiv">n</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="722745758">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Open…</string>
+									<string key="NSKeyEquiv">o</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1025936716">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Open Recent</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="1065607017">
+										<string key="NSTitle">Open Recent</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="759406840">
+												<reference key="NSMenu" ref="1065607017"/>
+												<string key="NSTitle">Clear Menu</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+										<string key="NSName">_NSRecentDocumentsMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="425164168">
+									<reference key="NSMenu" ref="720053764"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="776162233">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Close</string>
+									<string key="NSKeyEquiv">w</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1023925487">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Save</string>
+									<string key="NSKeyEquiv">s</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="117038363">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Save As…</string>
+									<string key="NSKeyEquiv">S</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="579971712">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Revert to Saved</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1010469920">
+									<reference key="NSMenu" ref="720053764"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="294629803">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Page Setup...</string>
+									<string key="NSKeyEquiv">P</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSToolTip"/>
+								</object>
+								<object class="NSMenuItem" id="49223823">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Print…</string>
+									<string key="NSKeyEquiv">p</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="952259628">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Edit</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="789758025">
+							<string key="NSTitle">Edit</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="1058277027">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Undo</string>
+									<string key="NSKeyEquiv">z</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="790794224">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Redo</string>
+									<string key="NSKeyEquiv">Z</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1040322652">
+									<reference key="NSMenu" ref="789758025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="296257095">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Cut</string>
+									<string key="NSKeyEquiv">x</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="860595796">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Copy</string>
+									<string key="NSKeyEquiv">c</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="29853731">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Paste</string>
+									<string key="NSKeyEquiv">v</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="82994268">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Paste and Match Style</string>
+									<string key="NSKeyEquiv">V</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="437104165">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Delete</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="583158037">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Select All</string>
+									<string key="NSKeyEquiv">a</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="212016141">
+									<reference key="NSMenu" ref="789758025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="892235320">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Find</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="963351320">
+										<string key="NSTitle">Find</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="447796847">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find…</string>
+												<string key="NSKeyEquiv">f</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="326711663">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find Next</string>
+												<string key="NSKeyEquiv">g</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="270902937">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find Previous</string>
+												<string key="NSKeyEquiv">G</string>
+												<int key="NSKeyEquivModMask">1179648</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="159080638">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Use Selection for Find</string>
+												<string key="NSKeyEquiv">e</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">7</int>
+											</object>
+											<object class="NSMenuItem" id="88285865">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Jump to Selection</string>
+												<string key="NSKeyEquiv">j</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="972420730">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Spelling and Grammar</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="769623530">
+										<string key="NSTitle">Spelling and Grammar</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="679648819">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Show Spelling and Grammar</string>
+												<string key="NSKeyEquiv">:</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="96193923">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Document Now</string>
+												<string key="NSKeyEquiv">;</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="859480356">
+												<reference key="NSMenu" ref="769623530"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="948374510">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Spelling While Typing</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="967646866">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Grammar With Spelling</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="795346622">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Correct Spelling Automatically</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="507821607">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Substitutions</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="698887838">
+										<string key="NSTitle">Substitutions</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="65139061">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Show Substitutions</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="19036812">
+												<reference key="NSMenu" ref="698887838"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="605118523">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Copy/Paste</string>
+												<string key="NSKeyEquiv">f</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="197661976">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Quotes</string>
+												<string key="NSKeyEquiv">g</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="672708820">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Dashes</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="708854459">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Links</string>
+												<string key="NSKeyEquiv">G</string>
+												<int key="NSKeyEquivModMask">1179648</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="537092702">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Text Replacement</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="288088188">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Transformations</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="579392910">
+										<string key="NSTitle">Transformations</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="1060694897">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Make Upper Case</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="879586729">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Make Lower Case</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="56570060">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Capitalize</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="676164635">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Speech</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="785027613">
+										<string key="NSTitle">Speech</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="731782645">
+												<reference key="NSMenu" ref="785027613"/>
+												<string key="NSTitle">Start Speaking</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="680220178">
+												<reference key="NSMenu" ref="785027613"/>
+												<string key="NSTitle">Stop Speaking</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="302598603">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Format</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="941447902">
+							<string key="NSTitle">Format</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="792887677">
+									<reference key="NSMenu" ref="941447902"/>
+									<string key="NSTitle">Font</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="786677654">
+										<string key="NSTitle">Font</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="159677712">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Show Fonts</string>
+												<string key="NSKeyEquiv">t</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="305399458">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Bold</string>
+												<string key="NSKeyEquiv">b</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="814362025">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Italic</string>
+												<string key="NSKeyEquiv">i</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="330926929">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Underline</string>
+												<string key="NSKeyEquiv">u</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="533507878">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="158063935">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Bigger</string>
+												<string key="NSKeyEquiv">+</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="885547335">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Smaller</string>
+												<string key="NSKeyEquiv">-</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">4</int>
+											</object>
+											<object class="NSMenuItem" id="901062459">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="767671776">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Kern</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="175441468">
+													<string key="NSTitle">Kern</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="252969304">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="766922938">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Use None</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="677519740">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Tighten</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="238351151">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Loosen</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="691570813">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Ligature</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="1058217995">
+													<string key="NSTitle">Ligature</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="706297211">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="568384683">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use None</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="663508465">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use All</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="769124883">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Baseline</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="18263474">
+													<string key="NSTitle">Baseline</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="257962622">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="644725453">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Superscript</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="1037576581">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Subscript</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="941806246">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Raise</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="1045724900">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Lower</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="739652853">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="1012600125">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Show Colors</string>
+												<string key="NSKeyEquiv">C</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="214559597">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="596732606">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Copy Style</string>
+												<string key="NSKeyEquiv">c</string>
+												<int key="NSKeyEquivModMask">1572864</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="393423671">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Paste Style</string>
+												<string key="NSKeyEquiv">v</string>
+												<int key="NSKeyEquivModMask">1572864</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+										<string key="NSName">_NSFontMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="215659978">
+									<reference key="NSMenu" ref="941447902"/>
+									<string key="NSTitle">Text</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="446991534">
+										<string key="NSTitle">Text</string>
+										<object class="NSMutableArray" key="NSMenuItems">
+											<bool key="EncodedWithXMLCoder">YES</bool>
+											<object class="NSMenuItem" id="875092757">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Align Left</string>
+												<string key="NSKeyEquiv">{</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="630155264">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Center</string>
+												<string key="NSKeyEquiv">|</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="945678886">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Justify</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="512868991">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Align Right</string>
+												<string key="NSKeyEquiv">}</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="163117631">
+												<reference key="NSMenu" ref="446991534"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="31516759">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Writing Direction</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="956096989">
+													<string key="NSTitle">Writing Direction</string>
+													<object class="NSMutableArray" key="NSMenuItems">
+														<bool key="EncodedWithXMLCoder">YES</bool>
+														<object class="NSMenuItem" id="257099033">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<string key="NSTitle">Paragraph</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="551969625">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="249532473">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="607364498">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="508151438">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<bool key="NSIsSeparator">YES</bool>
+															<string key="NSTitle"/>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="981751889">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<string key="NSTitle">Selection</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="380031999">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="825984362">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="560145579">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</object>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="908105787">
+												<reference key="NSMenu" ref="446991534"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="644046920">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Show Ruler</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="231811626">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Copy Ruler</string>
+												<string key="NSKeyEquiv">c</string>
+												<int key="NSKeyEquivModMask">1310720</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="883618387">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Paste Ruler</string>
+												<string key="NSKeyEquiv">v</string>
+												<int key="NSKeyEquivModMask">1310720</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</object>
+									</object>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="586577488">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">View</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="466310130">
+							<string key="NSTitle">View</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="102151532">
+									<reference key="NSMenu" ref="466310130"/>
+									<string key="NSTitle">Show Toolbar</string>
+									<string key="NSKeyEquiv">t</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="237841660">
+									<reference key="NSMenu" ref="466310130"/>
+									<string key="NSTitle">Customize Toolbar…</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="713487014">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Window</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="835318025">
+							<string key="NSTitle">Window</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="1011231497">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Minimize</string>
+									<string key="NSKeyEquiv">m</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="575023229">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Zoom</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="299356726">
+									<reference key="NSMenu" ref="835318025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="625202149">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Bring All to Front</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+							<string key="NSName">_NSWindowsMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="448692316">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Help</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="992780483">
+							<string key="NSTitle">Help</string>
+							<object class="NSMutableArray" key="NSMenuItems">
+								<bool key="EncodedWithXMLCoder">YES</bool>
+								<object class="NSMenuItem" id="105068016">
+									<reference key="NSMenu" ref="992780483"/>
+									<string key="NSTitle">SimpleCocoaApp Help</string>
+									<string key="NSKeyEquiv">?</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</object>
+							<string key="NSName">_NSHelpMenu</string>
+						</object>
+					</object>
+				</object>
+				<string key="NSName">_NSMainMenu</string>
+			</object>
+			<object class="NSWindowTemplate" id="972006081">
+				<int key="NSWindowStyleMask">15</int>
+				<int key="NSWindowBacking">2</int>
+				<string key="NSWindowRect">{{335, 390}, {480, 360}}</string>
+				<int key="NSWTFlags">1954021376</int>
+				<string key="NSWindowTitle">SimpleApp</string>
+				<string key="NSWindowClass">NSWindow</string>
+				<nil key="NSViewClass"/>
+				<string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+				<object class="NSView" key="NSWindowView" id="439893737">
+					<reference key="NSNextResponder"/>
+					<int key="NSvFlags">256</int>
+					<string key="NSFrameSize">{480, 360}</string>
+					<reference key="NSSuperview"/>
+				</object>
+				<string key="NSScreenRect">{{0, 0}, {1920, 1178}}</string>
+				<string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+			</object>
+		</object>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<object class="NSMutableArray" key="connectionRecords">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performMiniaturize:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1011231497"/>
+					</object>
+					<int key="connectionID">37</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">arrangeInFront:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="625202149"/>
+					</object>
+					<int key="connectionID">39</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">print:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="49223823"/>
+					</object>
+					<int key="connectionID">86</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">runPageLayout:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="294629803"/>
+					</object>
+					<int key="connectionID">87</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">clearRecentDocuments:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="759406840"/>
+					</object>
+					<int key="connectionID">127</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontStandardAboutPanel:</string>
+						<reference key="source" ref="1021"/>
+						<reference key="destination" ref="238522557"/>
+					</object>
+					<int key="connectionID">142</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performClose:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="776162233"/>
+					</object>
+					<int key="connectionID">193</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleContinuousSpellChecking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="948374510"/>
+					</object>
+					<int key="connectionID">222</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">undo:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1058277027"/>
+					</object>
+					<int key="connectionID">223</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copy:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="860595796"/>
+					</object>
+					<int key="connectionID">224</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">checkSpelling:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="96193923"/>
+					</object>
+					<int key="connectionID">225</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">paste:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="29853731"/>
+					</object>
+					<int key="connectionID">226</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">stopSpeaking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="680220178"/>
+					</object>
+					<int key="connectionID">227</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">cut:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="296257095"/>
+					</object>
+					<int key="connectionID">228</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">showGuessPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="679648819"/>
+					</object>
+					<int key="connectionID">230</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">redo:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="790794224"/>
+					</object>
+					<int key="connectionID">231</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">selectAll:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="583158037"/>
+					</object>
+					<int key="connectionID">232</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">startSpeaking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="731782645"/>
+					</object>
+					<int key="connectionID">233</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">delete:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="437104165"/>
+					</object>
+					<int key="connectionID">235</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performZoom:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="575023229"/>
+					</object>
+					<int key="connectionID">240</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="447796847"/>
+					</object>
+					<int key="connectionID">241</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">centerSelectionInVisibleArea:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="88285865"/>
+					</object>
+					<int key="connectionID">245</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleGrammarChecking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="967646866"/>
+					</object>
+					<int key="connectionID">347</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleSmartInsertDelete:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="605118523"/>
+					</object>
+					<int key="connectionID">355</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticQuoteSubstitution:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="197661976"/>
+					</object>
+					<int key="connectionID">356</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticLinkDetection:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="708854459"/>
+					</object>
+					<int key="connectionID">357</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">saveDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1023925487"/>
+					</object>
+					<int key="connectionID">362</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">saveDocumentAs:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="117038363"/>
+					</object>
+					<int key="connectionID">363</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">revertDocumentToSaved:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="579971712"/>
+					</object>
+					<int key="connectionID">364</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">runToolbarCustomizationPalette:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="237841660"/>
+					</object>
+					<int key="connectionID">365</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleToolbarShown:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="102151532"/>
+					</object>
+					<int key="connectionID">366</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hide:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="755159360"/>
+					</object>
+					<int key="connectionID">367</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hideOtherApplications:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="342932134"/>
+					</object>
+					<int key="connectionID">368</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">unhideAllApplications:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="908899353"/>
+					</object>
+					<int key="connectionID">370</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">newDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="705341025"/>
+					</object>
+					<int key="connectionID">373</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="722745758"/>
+					</object>
+					<int key="connectionID">374</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">raiseBaseline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="941806246"/>
+					</object>
+					<int key="connectionID">426</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">lowerBaseline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1045724900"/>
+					</object>
+					<int key="connectionID">427</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copyFont:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="596732606"/>
+					</object>
+					<int key="connectionID">428</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">subscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1037576581"/>
+					</object>
+					<int key="connectionID">429</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">superscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="644725453"/>
+					</object>
+					<int key="connectionID">430</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">tightenKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="677519740"/>
+					</object>
+					<int key="connectionID">431</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">underline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="330926929"/>
+					</object>
+					<int key="connectionID">432</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontColorPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1012600125"/>
+					</object>
+					<int key="connectionID">433</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useAllLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="663508465"/>
+					</object>
+					<int key="connectionID">434</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">loosenKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="238351151"/>
+					</object>
+					<int key="connectionID">435</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteFont:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="393423671"/>
+					</object>
+					<int key="connectionID">436</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">unscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="257962622"/>
+					</object>
+					<int key="connectionID">437</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useStandardKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="252969304"/>
+					</object>
+					<int key="connectionID">438</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useStandardLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="706297211"/>
+					</object>
+					<int key="connectionID">439</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">turnOffLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="568384683"/>
+					</object>
+					<int key="connectionID">440</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">turnOffKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="766922938"/>
+					</object>
+					<int key="connectionID">441</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">terminate:</string>
+						<reference key="source" ref="1050"/>
+						<reference key="destination" ref="632727374"/>
+					</object>
+					<int key="connectionID">449</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticSpellingCorrection:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="795346622"/>
+					</object>
+					<int key="connectionID">456</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontSubstitutionsPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="65139061"/>
+					</object>
+					<int key="connectionID">458</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticDashSubstitution:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="672708820"/>
+					</object>
+					<int key="connectionID">461</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticTextReplacement:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="537092702"/>
+					</object>
+					<int key="connectionID">463</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">uppercaseWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1060694897"/>
+					</object>
+					<int key="connectionID">464</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">capitalizeWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="56570060"/>
+					</object>
+					<int key="connectionID">467</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">lowercaseWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="879586729"/>
+					</object>
+					<int key="connectionID">468</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteAsPlainText:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="82994268"/>
+					</object>
+					<int key="connectionID">486</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="326711663"/>
+					</object>
+					<int key="connectionID">487</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="270902937"/>
+					</object>
+					<int key="connectionID">488</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="159080638"/>
+					</object>
+					<int key="connectionID">489</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">showHelp:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="105068016"/>
+					</object>
+					<int key="connectionID">493</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignCenter:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="630155264"/>
+					</object>
+					<int key="connectionID">518</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="883618387"/>
+					</object>
+					<int key="connectionID">519</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="644046920"/>
+					</object>
+					<int key="connectionID">520</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="512868991"/>
+					</object>
+					<int key="connectionID">521</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copyRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="231811626"/>
+					</object>
+					<int key="connectionID">522</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignJustified:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="945678886"/>
+					</object>
+					<int key="connectionID">523</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="875092757"/>
+					</object>
+					<int key="connectionID">524</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionNatural:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="551969625"/>
+					</object>
+					<int key="connectionID">525</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionLeftToRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="249532473"/>
+					</object>
+					<int key="connectionID">526</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionRightToLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="607364498"/>
+					</object>
+					<int key="connectionID">527</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionNatural:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="380031999"/>
+					</object>
+					<int key="connectionID">528</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionLeftToRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="825984362"/>
+					</object>
+					<int key="connectionID">529</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionRightToLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="560145579"/>
+					</object>
+					<int key="connectionID">530</int>
+				</object>
+			</object>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<object class="NSArray" key="orderedObjects">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<object class="NSArray" key="object" id="0">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="children" ref="1048"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="1021"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="1014"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">First Responder</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-3</int>
+						<reference key="object" ref="1050"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Application</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">29</int>
+						<reference key="object" ref="649796088"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="713487014"/>
+							<reference ref="694149608"/>
+							<reference ref="952259628"/>
+							<reference ref="379814623"/>
+							<reference ref="586577488"/>
+							<reference ref="302598603"/>
+							<reference ref="448692316"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">19</int>
+						<reference key="object" ref="713487014"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="835318025"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">56</int>
+						<reference key="object" ref="694149608"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="110575045"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">217</int>
+						<reference key="object" ref="952259628"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="789758025"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">83</int>
+						<reference key="object" ref="379814623"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="720053764"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">81</int>
+						<reference key="object" ref="720053764"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1023925487"/>
+							<reference ref="117038363"/>
+							<reference ref="49223823"/>
+							<reference ref="722745758"/>
+							<reference ref="705341025"/>
+							<reference ref="1025936716"/>
+							<reference ref="294629803"/>
+							<reference ref="776162233"/>
+							<reference ref="425164168"/>
+							<reference ref="579971712"/>
+							<reference ref="1010469920"/>
+						</object>
+						<reference key="parent" ref="379814623"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">75</int>
+						<reference key="object" ref="1023925487"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">80</int>
+						<reference key="object" ref="117038363"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">78</int>
+						<reference key="object" ref="49223823"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">72</int>
+						<reference key="object" ref="722745758"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">82</int>
+						<reference key="object" ref="705341025"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">124</int>
+						<reference key="object" ref="1025936716"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1065607017"/>
+						</object>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">77</int>
+						<reference key="object" ref="294629803"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">73</int>
+						<reference key="object" ref="776162233"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">79</int>
+						<reference key="object" ref="425164168"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">112</int>
+						<reference key="object" ref="579971712"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">74</int>
+						<reference key="object" ref="1010469920"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">125</int>
+						<reference key="object" ref="1065607017"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="759406840"/>
+						</object>
+						<reference key="parent" ref="1025936716"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">126</int>
+						<reference key="object" ref="759406840"/>
+						<reference key="parent" ref="1065607017"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">205</int>
+						<reference key="object" ref="789758025"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="437104165"/>
+							<reference ref="583158037"/>
+							<reference ref="1058277027"/>
+							<reference ref="212016141"/>
+							<reference ref="296257095"/>
+							<reference ref="29853731"/>
+							<reference ref="860595796"/>
+							<reference ref="1040322652"/>
+							<reference ref="790794224"/>
+							<reference ref="892235320"/>
+							<reference ref="972420730"/>
+							<reference ref="676164635"/>
+							<reference ref="507821607"/>
+							<reference ref="288088188"/>
+							<reference ref="82994268"/>
+						</object>
+						<reference key="parent" ref="952259628"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">202</int>
+						<reference key="object" ref="437104165"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">198</int>
+						<reference key="object" ref="583158037"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">207</int>
+						<reference key="object" ref="1058277027"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">214</int>
+						<reference key="object" ref="212016141"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">199</int>
+						<reference key="object" ref="296257095"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">203</int>
+						<reference key="object" ref="29853731"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">197</int>
+						<reference key="object" ref="860595796"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">206</int>
+						<reference key="object" ref="1040322652"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">215</int>
+						<reference key="object" ref="790794224"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">218</int>
+						<reference key="object" ref="892235320"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="963351320"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">216</int>
+						<reference key="object" ref="972420730"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="769623530"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">200</int>
+						<reference key="object" ref="769623530"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="948374510"/>
+							<reference ref="96193923"/>
+							<reference ref="679648819"/>
+							<reference ref="967646866"/>
+							<reference ref="859480356"/>
+							<reference ref="795346622"/>
+						</object>
+						<reference key="parent" ref="972420730"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">219</int>
+						<reference key="object" ref="948374510"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">201</int>
+						<reference key="object" ref="96193923"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">204</int>
+						<reference key="object" ref="679648819"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">220</int>
+						<reference key="object" ref="963351320"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="270902937"/>
+							<reference ref="88285865"/>
+							<reference ref="159080638"/>
+							<reference ref="326711663"/>
+							<reference ref="447796847"/>
+						</object>
+						<reference key="parent" ref="892235320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">213</int>
+						<reference key="object" ref="270902937"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">210</int>
+						<reference key="object" ref="88285865"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">221</int>
+						<reference key="object" ref="159080638"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">208</int>
+						<reference key="object" ref="326711663"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">209</int>
+						<reference key="object" ref="447796847"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">57</int>
+						<reference key="object" ref="110575045"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="238522557"/>
+							<reference ref="755159360"/>
+							<reference ref="908899353"/>
+							<reference ref="632727374"/>
+							<reference ref="646227648"/>
+							<reference ref="609285721"/>
+							<reference ref="481834944"/>
+							<reference ref="304266470"/>
+							<reference ref="1046388886"/>
+							<reference ref="1056857174"/>
+							<reference ref="342932134"/>
+						</object>
+						<reference key="parent" ref="694149608"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">58</int>
+						<reference key="object" ref="238522557"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">134</int>
+						<reference key="object" ref="755159360"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">150</int>
+						<reference key="object" ref="908899353"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">136</int>
+						<reference key="object" ref="632727374"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">144</int>
+						<reference key="object" ref="646227648"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">129</int>
+						<reference key="object" ref="609285721"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">143</int>
+						<reference key="object" ref="481834944"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">236</int>
+						<reference key="object" ref="304266470"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">131</int>
+						<reference key="object" ref="1046388886"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="752062318"/>
+						</object>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">149</int>
+						<reference key="object" ref="1056857174"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">145</int>
+						<reference key="object" ref="342932134"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">130</int>
+						<reference key="object" ref="752062318"/>
+						<reference key="parent" ref="1046388886"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">24</int>
+						<reference key="object" ref="835318025"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="299356726"/>
+							<reference ref="625202149"/>
+							<reference ref="575023229"/>
+							<reference ref="1011231497"/>
+						</object>
+						<reference key="parent" ref="713487014"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">92</int>
+						<reference key="object" ref="299356726"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">5</int>
+						<reference key="object" ref="625202149"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">239</int>
+						<reference key="object" ref="575023229"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">23</int>
+						<reference key="object" ref="1011231497"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">295</int>
+						<reference key="object" ref="586577488"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="466310130"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">296</int>
+						<reference key="object" ref="466310130"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="102151532"/>
+							<reference ref="237841660"/>
+						</object>
+						<reference key="parent" ref="586577488"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">297</int>
+						<reference key="object" ref="102151532"/>
+						<reference key="parent" ref="466310130"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">298</int>
+						<reference key="object" ref="237841660"/>
+						<reference key="parent" ref="466310130"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">211</int>
+						<reference key="object" ref="676164635"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="785027613"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">212</int>
+						<reference key="object" ref="785027613"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="680220178"/>
+							<reference ref="731782645"/>
+						</object>
+						<reference key="parent" ref="676164635"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">195</int>
+						<reference key="object" ref="680220178"/>
+						<reference key="parent" ref="785027613"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">196</int>
+						<reference key="object" ref="731782645"/>
+						<reference key="parent" ref="785027613"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">346</int>
+						<reference key="object" ref="967646866"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">348</int>
+						<reference key="object" ref="507821607"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="698887838"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">349</int>
+						<reference key="object" ref="698887838"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="605118523"/>
+							<reference ref="197661976"/>
+							<reference ref="708854459"/>
+							<reference ref="65139061"/>
+							<reference ref="19036812"/>
+							<reference ref="672708820"/>
+							<reference ref="537092702"/>
+						</object>
+						<reference key="parent" ref="507821607"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">350</int>
+						<reference key="object" ref="605118523"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">351</int>
+						<reference key="object" ref="197661976"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">354</int>
+						<reference key="object" ref="708854459"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">371</int>
+						<reference key="object" ref="972006081"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="439893737"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">372</int>
+						<reference key="object" ref="439893737"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="parent" ref="972006081"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">375</int>
+						<reference key="object" ref="302598603"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="941447902"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">376</int>
+						<reference key="object" ref="941447902"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="792887677"/>
+							<reference ref="215659978"/>
+						</object>
+						<reference key="parent" ref="302598603"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">377</int>
+						<reference key="object" ref="792887677"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="786677654"/>
+						</object>
+						<reference key="parent" ref="941447902"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">388</int>
+						<reference key="object" ref="786677654"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="159677712"/>
+							<reference ref="305399458"/>
+							<reference ref="814362025"/>
+							<reference ref="330926929"/>
+							<reference ref="533507878"/>
+							<reference ref="158063935"/>
+							<reference ref="885547335"/>
+							<reference ref="901062459"/>
+							<reference ref="767671776"/>
+							<reference ref="691570813"/>
+							<reference ref="769124883"/>
+							<reference ref="739652853"/>
+							<reference ref="1012600125"/>
+							<reference ref="214559597"/>
+							<reference ref="596732606"/>
+							<reference ref="393423671"/>
+						</object>
+						<reference key="parent" ref="792887677"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">389</int>
+						<reference key="object" ref="159677712"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">390</int>
+						<reference key="object" ref="305399458"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">391</int>
+						<reference key="object" ref="814362025"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">392</int>
+						<reference key="object" ref="330926929"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">393</int>
+						<reference key="object" ref="533507878"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">394</int>
+						<reference key="object" ref="158063935"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">395</int>
+						<reference key="object" ref="885547335"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">396</int>
+						<reference key="object" ref="901062459"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">397</int>
+						<reference key="object" ref="767671776"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="175441468"/>
+						</object>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">398</int>
+						<reference key="object" ref="691570813"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1058217995"/>
+						</object>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">399</int>
+						<reference key="object" ref="769124883"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="18263474"/>
+						</object>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">400</int>
+						<reference key="object" ref="739652853"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">401</int>
+						<reference key="object" ref="1012600125"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">402</int>
+						<reference key="object" ref="214559597"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">403</int>
+						<reference key="object" ref="596732606"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">404</int>
+						<reference key="object" ref="393423671"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">405</int>
+						<reference key="object" ref="18263474"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="257962622"/>
+							<reference ref="644725453"/>
+							<reference ref="1037576581"/>
+							<reference ref="941806246"/>
+							<reference ref="1045724900"/>
+						</object>
+						<reference key="parent" ref="769124883"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">406</int>
+						<reference key="object" ref="257962622"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">407</int>
+						<reference key="object" ref="644725453"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">408</int>
+						<reference key="object" ref="1037576581"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">409</int>
+						<reference key="object" ref="941806246"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">410</int>
+						<reference key="object" ref="1045724900"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">411</int>
+						<reference key="object" ref="1058217995"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="706297211"/>
+							<reference ref="568384683"/>
+							<reference ref="663508465"/>
+						</object>
+						<reference key="parent" ref="691570813"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">412</int>
+						<reference key="object" ref="706297211"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">413</int>
+						<reference key="object" ref="568384683"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">414</int>
+						<reference key="object" ref="663508465"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">415</int>
+						<reference key="object" ref="175441468"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="252969304"/>
+							<reference ref="766922938"/>
+							<reference ref="677519740"/>
+							<reference ref="238351151"/>
+						</object>
+						<reference key="parent" ref="767671776"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">416</int>
+						<reference key="object" ref="252969304"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">417</int>
+						<reference key="object" ref="766922938"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">418</int>
+						<reference key="object" ref="677519740"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">419</int>
+						<reference key="object" ref="238351151"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">450</int>
+						<reference key="object" ref="288088188"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="579392910"/>
+						</object>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">451</int>
+						<reference key="object" ref="579392910"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1060694897"/>
+							<reference ref="879586729"/>
+							<reference ref="56570060"/>
+						</object>
+						<reference key="parent" ref="288088188"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">452</int>
+						<reference key="object" ref="1060694897"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">453</int>
+						<reference key="object" ref="859480356"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">454</int>
+						<reference key="object" ref="795346622"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">457</int>
+						<reference key="object" ref="65139061"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">459</int>
+						<reference key="object" ref="19036812"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">460</int>
+						<reference key="object" ref="672708820"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">462</int>
+						<reference key="object" ref="537092702"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">465</int>
+						<reference key="object" ref="879586729"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">466</int>
+						<reference key="object" ref="56570060"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">485</int>
+						<reference key="object" ref="82994268"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">490</int>
+						<reference key="object" ref="448692316"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="992780483"/>
+						</object>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">491</int>
+						<reference key="object" ref="992780483"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="105068016"/>
+						</object>
+						<reference key="parent" ref="448692316"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">492</int>
+						<reference key="object" ref="105068016"/>
+						<reference key="parent" ref="992780483"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">496</int>
+						<reference key="object" ref="215659978"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="446991534"/>
+						</object>
+						<reference key="parent" ref="941447902"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">497</int>
+						<reference key="object" ref="446991534"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="875092757"/>
+							<reference ref="630155264"/>
+							<reference ref="945678886"/>
+							<reference ref="512868991"/>
+							<reference ref="163117631"/>
+							<reference ref="31516759"/>
+							<reference ref="908105787"/>
+							<reference ref="644046920"/>
+							<reference ref="231811626"/>
+							<reference ref="883618387"/>
+						</object>
+						<reference key="parent" ref="215659978"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">498</int>
+						<reference key="object" ref="875092757"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">499</int>
+						<reference key="object" ref="630155264"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">500</int>
+						<reference key="object" ref="945678886"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">501</int>
+						<reference key="object" ref="512868991"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">502</int>
+						<reference key="object" ref="163117631"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">503</int>
+						<reference key="object" ref="31516759"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="956096989"/>
+						</object>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">504</int>
+						<reference key="object" ref="908105787"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">505</int>
+						<reference key="object" ref="644046920"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">506</int>
+						<reference key="object" ref="231811626"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">507</int>
+						<reference key="object" ref="883618387"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">508</int>
+						<reference key="object" ref="956096989"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="257099033"/>
+							<reference ref="551969625"/>
+							<reference ref="249532473"/>
+							<reference ref="607364498"/>
+							<reference ref="508151438"/>
+							<reference ref="981751889"/>
+							<reference ref="380031999"/>
+							<reference ref="825984362"/>
+							<reference ref="560145579"/>
+						</object>
+						<reference key="parent" ref="31516759"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">509</int>
+						<reference key="object" ref="257099033"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">510</int>
+						<reference key="object" ref="551969625"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">511</int>
+						<reference key="object" ref="249532473"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">512</int>
+						<reference key="object" ref="607364498"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">513</int>
+						<reference key="object" ref="508151438"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">514</int>
+						<reference key="object" ref="981751889"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">515</int>
+						<reference key="object" ref="380031999"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">516</int>
+						<reference key="object" ref="825984362"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">517</int>
+						<reference key="object" ref="560145579"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="flattenedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="NSArray" key="dict.sortedKeys">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>-3.IBPluginDependency</string>
+					<string>112.IBPluginDependency</string>
+					<string>112.ImportedFromIB2</string>
+					<string>124.IBPluginDependency</string>
+					<string>124.ImportedFromIB2</string>
+					<string>125.IBPluginDependency</string>
+					<string>125.ImportedFromIB2</string>
+					<string>125.editorWindowContentRectSynchronizationRect</string>
+					<string>126.IBPluginDependency</string>
+					<string>126.ImportedFromIB2</string>
+					<string>129.IBPluginDependency</string>
+					<string>129.ImportedFromIB2</string>
+					<string>130.IBPluginDependency</string>
+					<string>130.ImportedFromIB2</string>
+					<string>130.editorWindowContentRectSynchronizationRect</string>
+					<string>131.IBPluginDependency</string>
+					<string>131.ImportedFromIB2</string>
+					<string>134.IBPluginDependency</string>
+					<string>134.ImportedFromIB2</string>
+					<string>136.IBPluginDependency</string>
+					<string>136.ImportedFromIB2</string>
+					<string>143.IBPluginDependency</string>
+					<string>143.ImportedFromIB2</string>
+					<string>144.IBPluginDependency</string>
+					<string>144.ImportedFromIB2</string>
+					<string>145.IBPluginDependency</string>
+					<string>145.ImportedFromIB2</string>
+					<string>149.IBPluginDependency</string>
+					<string>149.ImportedFromIB2</string>
+					<string>150.IBPluginDependency</string>
+					<string>150.ImportedFromIB2</string>
+					<string>19.IBPluginDependency</string>
+					<string>19.ImportedFromIB2</string>
+					<string>195.IBPluginDependency</string>
+					<string>195.ImportedFromIB2</string>
+					<string>196.IBPluginDependency</string>
+					<string>196.ImportedFromIB2</string>
+					<string>197.IBPluginDependency</string>
+					<string>197.ImportedFromIB2</string>
+					<string>198.IBPluginDependency</string>
+					<string>198.ImportedFromIB2</string>
+					<string>199.IBPluginDependency</string>
+					<string>199.ImportedFromIB2</string>
+					<string>200.IBEditorWindowLastContentRect</string>
+					<string>200.IBPluginDependency</string>
+					<string>200.ImportedFromIB2</string>
+					<string>200.editorWindowContentRectSynchronizationRect</string>
+					<string>201.IBPluginDependency</string>
+					<string>201.ImportedFromIB2</string>
+					<string>202.IBPluginDependency</string>
+					<string>202.ImportedFromIB2</string>
+					<string>203.IBPluginDependency</string>
+					<string>203.ImportedFromIB2</string>
+					<string>204.IBPluginDependency</string>
+					<string>204.ImportedFromIB2</string>
+					<string>205.IBEditorWindowLastContentRect</string>
+					<string>205.IBPluginDependency</string>
+					<string>205.ImportedFromIB2</string>
+					<string>205.editorWindowContentRectSynchronizationRect</string>
+					<string>206.IBPluginDependency</string>
+					<string>206.ImportedFromIB2</string>
+					<string>207.IBPluginDependency</string>
+					<string>207.ImportedFromIB2</string>
+					<string>208.IBPluginDependency</string>
+					<string>208.ImportedFromIB2</string>
+					<string>209.IBPluginDependency</string>
+					<string>209.ImportedFromIB2</string>
+					<string>210.IBPluginDependency</string>
+					<string>210.ImportedFromIB2</string>
+					<string>211.IBPluginDependency</string>
+					<string>211.ImportedFromIB2</string>
+					<string>212.IBPluginDependency</string>
+					<string>212.ImportedFromIB2</string>
+					<string>212.editorWindowContentRectSynchronizationRect</string>
+					<string>213.IBPluginDependency</string>
+					<string>213.ImportedFromIB2</string>
+					<string>214.IBPluginDependency</string>
+					<string>214.ImportedFromIB2</string>
+					<string>215.IBPluginDependency</string>
+					<string>215.ImportedFromIB2</string>
+					<string>216.IBPluginDependency</string>
+					<string>216.ImportedFromIB2</string>
+					<string>217.IBPluginDependency</string>
+					<string>217.ImportedFromIB2</string>
+					<string>218.IBPluginDependency</string>
+					<string>218.ImportedFromIB2</string>
+					<string>219.IBPluginDependency</string>
+					<string>219.ImportedFromIB2</string>
+					<string>220.IBEditorWindowLastContentRect</string>
+					<string>220.IBPluginDependency</string>
+					<string>220.ImportedFromIB2</string>
+					<string>220.editorWindowContentRectSynchronizationRect</string>
+					<string>221.IBPluginDependency</string>
+					<string>221.ImportedFromIB2</string>
+					<string>23.IBPluginDependency</string>
+					<string>23.ImportedFromIB2</string>
+					<string>236.IBPluginDependency</string>
+					<string>236.ImportedFromIB2</string>
+					<string>239.IBPluginDependency</string>
+					<string>239.ImportedFromIB2</string>
+					<string>24.IBEditorWindowLastContentRect</string>
+					<string>24.IBPluginDependency</string>
+					<string>24.ImportedFromIB2</string>
+					<string>24.editorWindowContentRectSynchronizationRect</string>
+					<string>29.IBEditorWindowLastContentRect</string>
+					<string>29.IBPluginDependency</string>
+					<string>29.ImportedFromIB2</string>
+					<string>29.WindowOrigin</string>
+					<string>29.editorWindowContentRectSynchronizationRect</string>
+					<string>295.IBPluginDependency</string>
+					<string>296.IBEditorWindowLastContentRect</string>
+					<string>296.IBPluginDependency</string>
+					<string>296.editorWindowContentRectSynchronizationRect</string>
+					<string>297.IBPluginDependency</string>
+					<string>298.IBPluginDependency</string>
+					<string>346.IBPluginDependency</string>
+					<string>346.ImportedFromIB2</string>
+					<string>348.IBPluginDependency</string>
+					<string>348.ImportedFromIB2</string>
+					<string>349.IBEditorWindowLastContentRect</string>
+					<string>349.IBPluginDependency</string>
+					<string>349.ImportedFromIB2</string>
+					<string>349.editorWindowContentRectSynchronizationRect</string>
+					<string>350.IBPluginDependency</string>
+					<string>350.ImportedFromIB2</string>
+					<string>351.IBPluginDependency</string>
+					<string>351.ImportedFromIB2</string>
+					<string>354.IBPluginDependency</string>
+					<string>354.ImportedFromIB2</string>
+					<string>371.IBEditorWindowLastContentRect</string>
+					<string>371.IBPluginDependency</string>
+					<string>371.IBWindowTemplateEditedContentRect</string>
+					<string>371.NSWindowTemplate.visibleAtLaunch</string>
+					<string>371.editorWindowContentRectSynchronizationRect</string>
+					<string>371.windowTemplate.maxSize</string>
+					<string>371.windowTemplate.minSize</string>
+					<string>372.CustomClassName</string>
+					<string>372.IBPluginDependency</string>
+					<string>375.IBPluginDependency</string>
+					<string>376.IBEditorWindowLastContentRect</string>
+					<string>376.IBPluginDependency</string>
+					<string>377.IBPluginDependency</string>
+					<string>388.IBEditorWindowLastContentRect</string>
+					<string>388.IBPluginDependency</string>
+					<string>389.IBPluginDependency</string>
+					<string>390.IBPluginDependency</string>
+					<string>391.IBPluginDependency</string>
+					<string>392.IBPluginDependency</string>
+					<string>393.IBPluginDependency</string>
+					<string>394.IBPluginDependency</string>
+					<string>395.IBPluginDependency</string>
+					<string>396.IBPluginDependency</string>
+					<string>397.IBPluginDependency</string>
+					<string>398.IBPluginDependency</string>
+					<string>399.IBPluginDependency</string>
+					<string>400.IBPluginDependency</string>
+					<string>401.IBPluginDependency</string>
+					<string>402.IBPluginDependency</string>
+					<string>403.IBPluginDependency</string>
+					<string>404.IBPluginDependency</string>
+					<string>405.IBPluginDependency</string>
+					<string>406.IBPluginDependency</string>
+					<string>407.IBPluginDependency</string>
+					<string>408.IBPluginDependency</string>
+					<string>409.IBPluginDependency</string>
+					<string>410.IBPluginDependency</string>
+					<string>411.IBPluginDependency</string>
+					<string>412.IBPluginDependency</string>
+					<string>413.IBPluginDependency</string>
+					<string>414.IBPluginDependency</string>
+					<string>415.IBPluginDependency</string>
+					<string>416.IBPluginDependency</string>
+					<string>417.IBPluginDependency</string>
+					<string>418.IBPluginDependency</string>
+					<string>419.IBPluginDependency</string>
+					<string>450.IBPluginDependency</string>
+					<string>451.IBEditorWindowLastContentRect</string>
+					<string>451.IBPluginDependency</string>
+					<string>452.IBPluginDependency</string>
+					<string>453.IBPluginDependency</string>
+					<string>454.IBPluginDependency</string>
+					<string>457.IBPluginDependency</string>
+					<string>459.IBPluginDependency</string>
+					<string>460.IBPluginDependency</string>
+					<string>462.IBPluginDependency</string>
+					<string>465.IBPluginDependency</string>
+					<string>466.IBPluginDependency</string>
+					<string>485.IBPluginDependency</string>
+					<string>490.IBPluginDependency</string>
+					<string>491.IBEditorWindowLastContentRect</string>
+					<string>491.IBPluginDependency</string>
+					<string>492.IBPluginDependency</string>
+					<string>496.IBPluginDependency</string>
+					<string>497.IBEditorWindowLastContentRect</string>
+					<string>497.IBPluginDependency</string>
+					<string>498.IBPluginDependency</string>
+					<string>499.IBPluginDependency</string>
+					<string>5.IBPluginDependency</string>
+					<string>5.ImportedFromIB2</string>
+					<string>500.IBPluginDependency</string>
+					<string>501.IBPluginDependency</string>
+					<string>502.IBPluginDependency</string>
+					<string>503.IBPluginDependency</string>
+					<string>504.IBPluginDependency</string>
+					<string>505.IBPluginDependency</string>
+					<string>506.IBPluginDependency</string>
+					<string>507.IBPluginDependency</string>
+					<string>508.IBEditorWindowLastContentRect</string>
+					<string>508.IBPluginDependency</string>
+					<string>509.IBPluginDependency</string>
+					<string>510.IBPluginDependency</string>
+					<string>511.IBPluginDependency</string>
+					<string>512.IBPluginDependency</string>
+					<string>513.IBPluginDependency</string>
+					<string>514.IBPluginDependency</string>
+					<string>515.IBPluginDependency</string>
+					<string>516.IBPluginDependency</string>
+					<string>517.IBPluginDependency</string>
+					<string>56.IBPluginDependency</string>
+					<string>56.ImportedFromIB2</string>
+					<string>57.IBEditorWindowLastContentRect</string>
+					<string>57.IBPluginDependency</string>
+					<string>57.ImportedFromIB2</string>
+					<string>57.editorWindowContentRectSynchronizationRect</string>
+					<string>58.IBPluginDependency</string>
+					<string>58.ImportedFromIB2</string>
+					<string>72.IBPluginDependency</string>
+					<string>72.ImportedFromIB2</string>
+					<string>73.IBPluginDependency</string>
+					<string>73.ImportedFromIB2</string>
+					<string>74.IBPluginDependency</string>
+					<string>74.ImportedFromIB2</string>
+					<string>75.IBPluginDependency</string>
+					<string>75.ImportedFromIB2</string>
+					<string>77.IBPluginDependency</string>
+					<string>77.ImportedFromIB2</string>
+					<string>78.IBPluginDependency</string>
+					<string>78.ImportedFromIB2</string>
+					<string>79.IBPluginDependency</string>
+					<string>79.ImportedFromIB2</string>
+					<string>80.IBPluginDependency</string>
+					<string>80.ImportedFromIB2</string>
+					<string>81.IBEditorWindowLastContentRect</string>
+					<string>81.IBPluginDependency</string>
+					<string>81.ImportedFromIB2</string>
+					<string>81.editorWindowContentRectSynchronizationRect</string>
+					<string>82.IBPluginDependency</string>
+					<string>82.ImportedFromIB2</string>
+					<string>83.IBPluginDependency</string>
+					<string>83.ImportedFromIB2</string>
+					<string>92.IBPluginDependency</string>
+					<string>92.ImportedFromIB2</string>
+				</object>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{522, 812}, {146, 23}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{436, 809}, {64, 6}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{753, 187}, {275, 113}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {275, 83}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{559, 374}, {254, 283}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{187, 434}, {243, 243}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {167, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{753, 217}, {238, 103}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {241, 103}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{719, 584}, {194, 73}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{525, 802}, {197, 73}}</string>
+					<string>{{366, 657}, {485, 20}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{74, 862}</string>
+					<string>{{6, 978}, {478, 20}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{669, 614}, {231, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{475, 832}, {234, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{746, 287}, {220, 133}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{608, 612}, {215, 63}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{228, 569}, {480, 360}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{228, 569}, {480, 360}}</string>
+					<integer value="1"/>
+					<string>{{33, 99}, {480, 360}}</string>
+					<string>{3.40282e+38, 3.40282e+38}</string>
+					<string>{0, 0}</string>
+					<string>SimpleNSView</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{603, 614}, {83, 43}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{523, 2}, {178, 283}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{753, 197}, {170, 63}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{790, 634}, {221, 23}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{674, 260}, {204, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>{{878, 180}, {164, 173}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{378, 474}, {250, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{23, 794}, {245, 183}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{517, 454}, {196, 203}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>{{145, 474}, {199, 203}}</string>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+					<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+					<integer value="1"/>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="unlocalizedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="activeLocalization"/>
+			<object class="NSMutableDictionary" key="localizations">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="sourceID"/>
+			<int key="maxID">650</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">SimpleNSView</string>
+					<string key="superclassName">SkNSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">../../experimental/SimpleCocoaApp/SimpleApp.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkNSView</string>
+					<string key="superclassName">NSView</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">fOptionsDelegate</string>
+						<string key="NS.object.0">id</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">fOptionsDelegate</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">fOptionsDelegate</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">../../src/utils/mac/SkNSView.h</string>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSFormatter</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFormatter.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSClassDescription.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObjectScripting.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSPortCoder.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptClassDescription.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptObjectSpecifiers.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptWhoseTests.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLDownload.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">PrintCore.framework/Headers/PDEPluginInterface.h</string>
+					</object>
+				</object>
+			</object>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
+			<integer value="3000" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<string key="IBDocument.LastKnownRelativeProjectPath">../../../out/gyp/SimpleCocoaApp.xcodeproj</string>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSArray" key="dict.sortedKeys">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<string>NSMenuCheckmark</string>
+				<string>NSMenuMixedState</string>
+			</object>
+			<object class="NSMutableArray" key="dict.values">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<string>{9, 8}</string>
+				<string>{7, 2}</string>
+			</object>
+		</object>
+	</data>
+</archive>
diff --git a/experimental/SimpleiOSApp/SimpleApp.h b/experimental/SimpleiOSApp/SimpleApp.h
new file mode 100644
index 0000000..392353b
--- /dev/null
+++ b/experimental/SimpleiOSApp/SimpleApp.h
@@ -0,0 +1,6 @@
+#import <UIKit/UIKit.h>
+#import "SkUIView.h"
+
+@interface SimpleApp : SkUIView
+- (id)initWithDefaults;
+@end
diff --git a/experimental/SimpleiOSApp/SimpleApp.mm b/experimental/SimpleiOSApp/SimpleApp.mm
new file mode 100644
index 0000000..dba8bcc
--- /dev/null
+++ b/experimental/SimpleiOSApp/SimpleApp.mm
@@ -0,0 +1,70 @@
+#import "SkCanvas.h"
+#import "SkPaint.h"
+#import "SkWindow.h"
+#include "SkGraphics.h"
+#include "SkCGUtils.h"
+
+extern void tool_main(int argc, char *argv[]);
+void save_args(int argc, char *argv[]);
+
+class SkSampleView : public SkView {
+public:
+    SkSampleView() {
+        this->setVisibleP(true);
+        this->setClipToBounds(false);
+    };
+protected:
+    virtual void onDraw(SkCanvas* canvas) {
+        canvas->drawColor(0xFFFFFFFF);
+        SkPaint p;
+        p.setTextSize(20);
+        p.setAntiAlias(true);
+        canvas->drawText("finished", 13, 50, 30, p);
+        SkRect r = {50, 50, 80, 80};
+        p.setColor(0xAA11EEAA);
+        canvas->drawRect(r, p);
+    }
+private:
+    typedef SkView INHERITED; 
+};
+
+void application_init() {
+    SkGraphics::Init();
+    SkEvent::Init();
+}
+
+void application_term() {
+    SkGraphics::Term();
+    SkEvent::Term();
+}
+
+int saved_argc;
+char** saved_argv;
+
+void save_args(int argc, char *argv[]) {
+    saved_argc = argc;
+    saved_argv = argv;
+}
+
+class FillLayout : public SkView::Layout {
+protected:
+    virtual void onLayoutChildren(SkView* parent) {
+        SkView* view = SkView::F2BIter(parent).next();
+        view->setSize(parent->width(), parent->height());
+    }
+};
+
+#import "SimpleApp.h"
+@implementation SimpleApp
+
+- (id)initWithDefaults {
+    (void) tool_main(saved_argc, saved_argv);
+    if (self = [super initWithDefaults]) {
+        fWind = new SkOSWindow(self);
+        fWind->setLayout(new FillLayout, false);
+        fWind->attachChildToFront(new SkSampleView)->unref();
+    }
+    return self;
+}
+
+@end
diff --git a/experimental/SimpleiOSApp/SimpleiOSApp-Info.plist b/experimental/SimpleiOSApp/SimpleiOSApp-Info.plist
new file mode 100644
index 0000000..da6b2f1
--- /dev/null
+++ b/experimental/SimpleiOSApp/SimpleiOSApp-Info.plist
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleDisplayName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>com.yourcompany.${PRODUCT_NAME:rfc1034identifier}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1.0</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>NSMainNibFile</key>
+	<string>MainWindow_iPhone</string>
+	<key>NSMainNibFile~ipad</key>
+	<string>MainWindow_iPad</string>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
diff --git a/experimental/SimpleiOSApp/SimpleiOSApp.xcodeproj/project.pbxproj b/experimental/SimpleiOSApp/SimpleiOSApp.xcodeproj/project.pbxproj
new file mode 100755
index 0000000..977f6da
--- /dev/null
+++ b/experimental/SimpleiOSApp/SimpleiOSApp.xcodeproj/project.pbxproj
@@ -0,0 +1,2937 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
+		1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
+		26371977140D6C19006CC295 /* SkUIView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26371976140D6C19006CC295 /* SkUIView.mm */; };
+		26D90F81140D512A0036A311 /* SkAnimateActive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C4B140D51290036A311 /* SkAnimateActive.cpp */; };
+		26D90F82140D512A0036A311 /* SkAnimateBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C4D140D51290036A311 /* SkAnimateBase.cpp */; };
+		26D90F83140D512A0036A311 /* SkAnimateField.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C4F140D51290036A311 /* SkAnimateField.cpp */; };
+		26D90F84140D512A0036A311 /* SkAnimateMaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C50140D51290036A311 /* SkAnimateMaker.cpp */; };
+		26D90F85140D512A0036A311 /* SkAnimateSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C53140D51290036A311 /* SkAnimateSet.cpp */; };
+		26D90F86140D512A0036A311 /* SkAnimator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C55140D51290036A311 /* SkAnimator.cpp */; };
+		26D90F87140D512A0036A311 /* SkAnimatorScript.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C56140D51290036A311 /* SkAnimatorScript.cpp */; };
+		26D90F88140D512A0036A311 /* SkBase64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C58140D51290036A311 /* SkBase64.cpp */; };
+		26D90F89140D512A0036A311 /* SkBoundable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C5A140D51290036A311 /* SkBoundable.cpp */; };
+		26D90F8A140D512A0036A311 /* SkBuildCondensedInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C5C140D51290036A311 /* SkBuildCondensedInfo.cpp */; };
+		26D90F8B140D512A0036A311 /* SkDisplayAdd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C5D140D51290036A311 /* SkDisplayAdd.cpp */; };
+		26D90F8C140D512A0036A311 /* SkDisplayApply.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C5F140D51290036A311 /* SkDisplayApply.cpp */; };
+		26D90F8D140D512A0036A311 /* SkDisplayBounds.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C61140D51290036A311 /* SkDisplayBounds.cpp */; };
+		26D90F8E140D512A0036A311 /* SkDisplayEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C63140D51290036A311 /* SkDisplayEvent.cpp */; };
+		26D90F8F140D512A0036A311 /* SkDisplayEvents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C65140D51290036A311 /* SkDisplayEvents.cpp */; };
+		26D90F90140D512A0036A311 /* SkDisplayInclude.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C67140D51290036A311 /* SkDisplayInclude.cpp */; };
+		26D90F91140D512A0036A311 /* SkDisplayInput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C69140D51290036A311 /* SkDisplayInput.cpp */; };
+		26D90F92140D512A0036A311 /* SkDisplayList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C6B140D51290036A311 /* SkDisplayList.cpp */; };
+		26D90F93140D512A0036A311 /* SkDisplayMath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C6D140D51290036A311 /* SkDisplayMath.cpp */; };
+		26D90F94140D512A0036A311 /* SkDisplayMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C6F140D51290036A311 /* SkDisplayMovie.cpp */; };
+		26D90F95140D512A0036A311 /* SkDisplayNumber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C71140D51290036A311 /* SkDisplayNumber.cpp */; };
+		26D90F96140D512A0036A311 /* SkDisplayPost.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C73140D51290036A311 /* SkDisplayPost.cpp */; };
+		26D90F97140D512A0036A311 /* SkDisplayRandom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C75140D51290036A311 /* SkDisplayRandom.cpp */; };
+		26D90F98140D512A0036A311 /* SkDisplayScreenplay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C77140D51290036A311 /* SkDisplayScreenplay.cpp */; };
+		26D90F99140D512A0036A311 /* SkDisplayType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C79140D51290036A311 /* SkDisplayType.cpp */; };
+		26D90F9A140D512A0036A311 /* SkDisplayTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C7B140D51290036A311 /* SkDisplayTypes.cpp */; };
+		26D90F9B140D512A0036A311 /* SkDisplayXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C7D140D51290036A311 /* SkDisplayXMLParser.cpp */; };
+		26D90F9C140D512A0036A311 /* SkDisplayable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C7F140D51290036A311 /* SkDisplayable.cpp */; };
+		26D90F9D140D512A0036A311 /* SkDraw3D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C81140D51290036A311 /* SkDraw3D.cpp */; };
+		26D90F9E140D512A0036A311 /* SkDrawBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C83140D51290036A311 /* SkDrawBitmap.cpp */; };
+		26D90F9F140D512A0036A311 /* SkDrawBlur.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C85140D51290036A311 /* SkDrawBlur.cpp */; };
+		26D90FA0140D512A0036A311 /* SkDrawClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C87140D51290036A311 /* SkDrawClip.cpp */; };
+		26D90FA1140D512A0036A311 /* SkDrawColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C89140D51290036A311 /* SkDrawColor.cpp */; };
+		26D90FA2140D512A0036A311 /* SkDrawDash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C8B140D51290036A311 /* SkDrawDash.cpp */; };
+		26D90FA3140D512A0036A311 /* SkDrawDiscrete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C8D140D51290036A311 /* SkDrawDiscrete.cpp */; };
+		26D90FA4140D512A0036A311 /* SkDrawEmboss.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C8F140D51290036A311 /* SkDrawEmboss.cpp */; };
+		26D90FA5140D512A0036A311 /* SkDrawExtraPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C91140D51290036A311 /* SkDrawExtraPathEffect.cpp */; };
+		26D90FA6140D512A0036A311 /* SkDrawFull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C92140D51290036A311 /* SkDrawFull.cpp */; };
+		26D90FA7140D512A0036A311 /* SkDrawGradient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C94140D51290036A311 /* SkDrawGradient.cpp */; };
+		26D90FA8140D512A0036A311 /* SkDrawGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C96140D51290036A311 /* SkDrawGroup.cpp */; };
+		26D90FA9140D512A0036A311 /* SkDrawLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C98140D51290036A311 /* SkDrawLine.cpp */; };
+		26D90FAA140D512A0036A311 /* SkDrawMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C9A140D51290036A311 /* SkDrawMatrix.cpp */; };
+		26D90FAB140D512A0036A311 /* SkDrawOval.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C9C140D51290036A311 /* SkDrawOval.cpp */; };
+		26D90FAC140D512A0036A311 /* SkDrawPaint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90C9E140D51290036A311 /* SkDrawPaint.cpp */; };
+		26D90FAD140D512A0036A311 /* SkDrawPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CA0140D51290036A311 /* SkDrawPath.cpp */; };
+		26D90FAE140D512A0036A311 /* SkDrawPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CA2140D51290036A311 /* SkDrawPoint.cpp */; };
+		26D90FAF140D512A0036A311 /* SkDrawRectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CA4140D51290036A311 /* SkDrawRectangle.cpp */; };
+		26D90FB0140D512A0036A311 /* SkDrawSaveLayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CA6140D51290036A311 /* SkDrawSaveLayer.cpp */; };
+		26D90FB1140D512A0036A311 /* SkDrawShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CA8140D51290036A311 /* SkDrawShader.cpp */; };
+		26D90FB2140D512A0036A311 /* SkDrawText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CAA140D51290036A311 /* SkDrawText.cpp */; };
+		26D90FB3140D512A0036A311 /* SkDrawTextBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CAC140D51290036A311 /* SkDrawTextBox.cpp */; };
+		26D90FB4140D512A0036A311 /* SkDrawTo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CAE140D51290036A311 /* SkDrawTo.cpp */; };
+		26D90FB5140D512A0036A311 /* SkDrawTransparentShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CB0140D51290036A311 /* SkDrawTransparentShader.cpp */; };
+		26D90FB6140D512A0036A311 /* SkDrawable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CB2140D51290036A311 /* SkDrawable.cpp */; };
+		26D90FB7140D512A0036A311 /* SkDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CB4140D51290036A311 /* SkDump.cpp */; };
+		26D90FB8140D512A0036A311 /* SkGetCondensedInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CB7140D51290036A311 /* SkGetCondensedInfo.cpp */; };
+		26D90FB9140D512A0036A311 /* SkHitClear.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CB8140D51290036A311 /* SkHitClear.cpp */; };
+		26D90FBA140D512A0036A311 /* SkHitTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CBA140D51290036A311 /* SkHitTest.cpp */; };
+		26D90FBB140D512A0036A311 /* SkMatrixParts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CBD140D51290036A311 /* SkMatrixParts.cpp */; };
+		26D90FBC140D512A0036A311 /* SkMemberInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CBF140D51290036A311 /* SkMemberInfo.cpp */; };
+		26D90FBD140D512A0036A311 /* SkOpArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CC1140D51290036A311 /* SkOpArray.cpp */; };
+		26D90FBE140D512A0036A311 /* SkOperandIterpolator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CC6140D51290036A311 /* SkOperandIterpolator.cpp */; };
+		26D90FBF140D512A0036A311 /* SkPaintParts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CC7140D51290036A311 /* SkPaintParts.cpp */; };
+		26D90FC0140D512A0036A311 /* SkParseSVGPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CC9140D51290036A311 /* SkParseSVGPath.cpp */; };
+		26D90FC1140D512A0036A311 /* SkPathParts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CCA140D51290036A311 /* SkPathParts.cpp */; };
+		26D90FC2140D512A0036A311 /* SkPostParts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CCC140D51290036A311 /* SkPostParts.cpp */; };
+		26D90FC3140D512A0036A311 /* SkScript.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CCE140D51290036A311 /* SkScript.cpp */; };
+		26D90FC4140D512A0036A311 /* SkScriptDecompile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CD2140D51290036A311 /* SkScriptDecompile.cpp */; };
+		26D90FC5140D512A0036A311 /* SkScriptRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CD3140D51290036A311 /* SkScriptRuntime.cpp */; };
+		26D90FC6140D512A0036A311 /* SkScriptTokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CD5140D51290036A311 /* SkScriptTokenizer.cpp */; };
+		26D90FC7140D512A0036A311 /* SkSnapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CD6140D51290036A311 /* SkSnapshot.cpp */; };
+		26D90FC8140D512A0036A311 /* SkTextOnPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CD9140D51290036A311 /* SkTextOnPath.cpp */; };
+		26D90FC9140D512A0036A311 /* SkTextToPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CDB140D51290036A311 /* SkTextToPath.cpp */; };
+		26D90FCA140D512A0036A311 /* SkTime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CDD140D51290036A311 /* SkTime.cpp */; };
+		26D90FCB140D512A0036A311 /* SkTypedArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CDE140D51290036A311 /* SkTypedArray.cpp */; };
+		26D90FCC140D512A0036A311 /* SkXMLAnimatorWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90CE0140D51290036A311 /* SkXMLAnimatorWriter.cpp */; };
+		26D90FCD140D512A0036A311 /* SkData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D46140D51290036A311 /* SkData.cpp */; };
+		26D90FCE140D512A0036A311 /* Sk64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D49140D51290036A311 /* Sk64.cpp */; };
+		26D90FCF140D512A0036A311 /* SkAdvancedTypefaceMetrics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D4A140D51290036A311 /* SkAdvancedTypefaceMetrics.cpp */; };
+		26D90FD0140D512A0036A311 /* SkAlphaRuns.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D4B140D51290036A311 /* SkAlphaRuns.cpp */; };
+		26D90FD1140D512A0036A311 /* SkBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D4D140D51290036A311 /* SkBitmap.cpp */; };
+		26D90FD2140D512A0036A311 /* SkBitmapProcShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D4E140D51290036A311 /* SkBitmapProcShader.cpp */; };
+		26D90FD3140D512A0036A311 /* SkBitmapProcState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D50140D51290036A311 /* SkBitmapProcState.cpp */; };
+		26D90FD4140D512A0036A311 /* SkBitmapProcState_matrixProcs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D53140D51290036A311 /* SkBitmapProcState_matrixProcs.cpp */; };
+		26D90FD5140D512A0036A311 /* SkBitmapSampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D55140D51290036A311 /* SkBitmapSampler.cpp */; };
+		26D90FD6140D512A0036A311 /* SkBitmap_scroll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D5A140D51290036A311 /* SkBitmap_scroll.cpp */; };
+		26D90FD7140D512A0036A311 /* SkBlitRow_D16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D5C140D51290036A311 /* SkBlitRow_D16.cpp */; };
+		26D90FD8140D512A0036A311 /* SkBlitRow_D32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D5D140D51290036A311 /* SkBlitRow_D32.cpp */; };
+		26D90FD9140D512A0036A311 /* SkBlitRow_D4444.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D5E140D51290036A311 /* SkBlitRow_D4444.cpp */; };
+		26D90FDA140D512A0036A311 /* SkBlitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D5F140D51290036A311 /* SkBlitter.cpp */; };
+		26D90FDB140D512A0036A311 /* SkBlitter_4444.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D60140D51290036A311 /* SkBlitter_4444.cpp */; };
+		26D90FDC140D512A0036A311 /* SkBlitter_A1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D61140D51290036A311 /* SkBlitter_A1.cpp */; };
+		26D90FDD140D512A0036A311 /* SkBlitter_A8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D62140D51290036A311 /* SkBlitter_A8.cpp */; };
+		26D90FDE140D512A0036A311 /* SkBlitter_ARGB32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D63140D51290036A311 /* SkBlitter_ARGB32.cpp */; };
+		26D90FDF140D512A0036A311 /* SkBlitter_RGB16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D64140D51290036A311 /* SkBlitter_RGB16.cpp */; };
+		26D90FE0140D512A0036A311 /* SkBlitter_Sprite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D65140D51290036A311 /* SkBlitter_Sprite.cpp */; };
+		26D90FE1140D512A0036A311 /* SkBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D66140D51290036A311 /* SkBuffer.cpp */; };
+		26D90FE2140D512A0036A311 /* SkCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D67140D51290036A311 /* SkCanvas.cpp */; };
+		26D90FE3140D512A0036A311 /* SkChunkAlloc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D68140D51290036A311 /* SkChunkAlloc.cpp */; };
+		26D90FE4140D512A0036A311 /* SkClampRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D69140D51290036A311 /* SkClampRange.cpp */; };
+		26D90FE5140D512A0036A311 /* SkClipStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D6A140D51290036A311 /* SkClipStack.cpp */; };
+		26D90FE6140D512A0036A311 /* SkColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D6B140D51290036A311 /* SkColor.cpp */; };
+		26D90FE7140D512A0036A311 /* SkColorFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D6C140D51290036A311 /* SkColorFilter.cpp */; };
+		26D90FE8140D512A0036A311 /* SkColorTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D6D140D51290036A311 /* SkColorTable.cpp */; };
+		26D90FE9140D512A0036A311 /* SkComposeShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D6E140D51290036A311 /* SkComposeShader.cpp */; };
+		26D90FEA140D512A0036A311 /* SkConcaveToTriangles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D6F140D51290036A311 /* SkConcaveToTriangles.cpp */; };
+		26D90FEB140D512A0036A311 /* SkCordic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D71140D51290036A311 /* SkCordic.cpp */; };
+		26D90FEC140D512A0036A311 /* SkCubicClipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D74140D51290036A311 /* SkCubicClipper.cpp */; };
+		26D90FED140D512A0036A311 /* SkDebug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D76140D51290036A311 /* SkDebug.cpp */; };
+		26D90FEE140D512A0036A311 /* SkDeque.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D77140D51290036A311 /* SkDeque.cpp */; };
+		26D90FEF140D512A0036A311 /* SkDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D78140D51290036A311 /* SkDevice.cpp */; };
+		26D90FF0140D512A0036A311 /* SkDither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D79140D51290036A311 /* SkDither.cpp */; };
+		26D90FF1140D512A0036A311 /* SkDraw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D7A140D51290036A311 /* SkDraw.cpp */; };
+		26D90FF2140D512A0036A311 /* SkEdge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D7C140D51290036A311 /* SkEdge.cpp */; };
+		26D90FF3140D512A0036A311 /* SkEdgeBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D7E140D51290036A311 /* SkEdgeBuilder.cpp */; };
+		26D90FF4140D512A0036A311 /* SkEdgeClipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D7F140D51290036A311 /* SkEdgeClipper.cpp */; };
+		26D90FF5140D512A0036A311 /* SkFilterProc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D81140D51290036A311 /* SkFilterProc.cpp */; };
+		26D90FF6140D512A0036A311 /* SkFlate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D83140D51290036A311 /* SkFlate.cpp */; };
+		26D90FF7140D512A0036A311 /* SkFlattenable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D84140D51290036A311 /* SkFlattenable.cpp */; };
+		26D90FF8140D512A0036A311 /* SkFloat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D85140D51290036A311 /* SkFloat.cpp */; };
+		26D90FF9140D512A0036A311 /* SkFloatBits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D87140D51290036A311 /* SkFloatBits.cpp */; };
+		26D90FFA140D512A0036A311 /* SkFontHost.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D88140D51290036A311 /* SkFontHost.cpp */; };
+		26D90FFB140D512A0036A311 /* SkGeometry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D89140D51290036A311 /* SkGeometry.cpp */; };
+		26D90FFC140D512A0036A311 /* SkGlobals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D8A140D51290036A311 /* SkGlobals.cpp */; };
+		26D90FFD140D512A0036A311 /* SkGlyphCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D8B140D51290036A311 /* SkGlyphCache.cpp */; };
+		26D90FFE140D512A0036A311 /* SkGraphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D8D140D51290036A311 /* SkGraphics.cpp */; };
+		26D90FFF140D512A0036A311 /* SkLineClipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D8E140D51290036A311 /* SkLineClipper.cpp */; };
+		26D91000140D512A0036A311 /* SkMMapStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D8F140D51290036A311 /* SkMMapStream.cpp */; };
+		26D91001140D512A0036A311 /* SkMallocPixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D90140D51290036A311 /* SkMallocPixelRef.cpp */; };
+		26D91002140D512A0036A311 /* SkMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D91140D51290036A311 /* SkMask.cpp */; };
+		26D91003140D512A0036A311 /* SkMaskFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D92140D51290036A311 /* SkMaskFilter.cpp */; };
+		26D91004140D512A0036A311 /* SkMath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D93140D51290036A311 /* SkMath.cpp */; };
+		26D91005140D512A0036A311 /* SkMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D94140D51290036A311 /* SkMatrix.cpp */; };
+		26D91006140D512A0036A311 /* SkMetaData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D95140D51290036A311 /* SkMetaData.cpp */; };
+		26D91007140D512A0036A311 /* SkPackBits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D96140D51290036A311 /* SkPackBits.cpp */; };
+		26D91008140D512A0036A311 /* SkPaint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D97140D51290036A311 /* SkPaint.cpp */; };
+		26D91009140D512A0036A311 /* SkPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D98140D51290036A311 /* SkPath.cpp */; };
+		26D9100A140D512A0036A311 /* SkPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D99140D51290036A311 /* SkPathEffect.cpp */; };
+		26D9100B140D512A0036A311 /* SkPathHeap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D9A140D51290036A311 /* SkPathHeap.cpp */; };
+		26D9100C140D512A0036A311 /* SkPathMeasure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D9C140D51290036A311 /* SkPathMeasure.cpp */; };
+		26D9100D140D512A0036A311 /* SkPicture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D9D140D51290036A311 /* SkPicture.cpp */; };
+		26D9100E140D512A0036A311 /* SkPictureFlat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90D9E140D51290036A311 /* SkPictureFlat.cpp */; };
+		26D9100F140D512A0036A311 /* SkPicturePlayback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DA0140D51290036A311 /* SkPicturePlayback.cpp */; };
+		26D91010140D512A0036A311 /* SkPictureRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DA2140D51290036A311 /* SkPictureRecord.cpp */; };
+		26D91011140D512A0036A311 /* SkPixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DA4140D51290036A311 /* SkPixelRef.cpp */; };
+		26D91012140D512A0036A311 /* SkPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DA5140D51290036A311 /* SkPoint.cpp */; };
+		26D91013140D512A0036A311 /* SkProcSpriteBlitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DA6140D51290036A311 /* SkProcSpriteBlitter.cpp */; };
+		26D91014140D512A0036A311 /* SkPtrRecorder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DA7140D51290036A311 /* SkPtrRecorder.cpp */; };
+		26D91015140D512A0036A311 /* SkQuadClipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DA8140D51290036A311 /* SkQuadClipper.cpp */; };
+		26D91016140D512A0036A311 /* SkRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DAA140D51290036A311 /* SkRasterizer.cpp */; };
+		26D91017140D512A0036A311 /* SkRect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DAB140D51290036A311 /* SkRect.cpp */; };
+		26D91018140D512A0036A311 /* SkRefDict.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DAC140D51290036A311 /* SkRefDict.cpp */; };
+		26D91019140D512A0036A311 /* SkRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DAD140D51290036A311 /* SkRegion.cpp */; };
+		26D9101A140D512A0036A311 /* SkRegion_path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DAF140D51290036A311 /* SkRegion_path.cpp */; };
+		26D9101B140D512A0036A311 /* SkScalar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DB0140D51290036A311 /* SkScalar.cpp */; };
+		26D9101C140D512A0036A311 /* SkScalerContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DB1140D51290036A311 /* SkScalerContext.cpp */; };
+		26D9101D140D512A0036A311 /* SkScan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DB2140D51290036A311 /* SkScan.cpp */; };
+		26D9101E140D512A0036A311 /* SkScan_AntiPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DB4140D51290036A311 /* SkScan_AntiPath.cpp */; };
+		26D9101F140D512A0036A311 /* SkScan_Antihair.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DB5140D51290036A311 /* SkScan_Antihair.cpp */; };
+		26D91020140D512A0036A311 /* SkScan_Hairline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DB6140D51290036A311 /* SkScan_Hairline.cpp */; };
+		26D91021140D512A0036A311 /* SkScan_Path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DB7140D51290036A311 /* SkScan_Path.cpp */; };
+		26D91022140D512A0036A311 /* SkShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DB8140D51290036A311 /* SkShader.cpp */; };
+		26D91023140D512A0036A311 /* SkShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DB9140D51290036A311 /* SkShape.cpp */; };
+		26D91024140D512A0036A311 /* SkSpriteBlitter_ARGB32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DBD140D51290036A311 /* SkSpriteBlitter_ARGB32.cpp */; };
+		26D91025140D512A0036A311 /* SkSpriteBlitter_RGB16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DBE140D51290036A311 /* SkSpriteBlitter_RGB16.cpp */; };
+		26D91026140D512A0036A311 /* SkStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DBF140D51290036A311 /* SkStream.cpp */; };
+		26D91027140D512A0036A311 /* SkString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DC0140D51290036A311 /* SkString.cpp */; };
+		26D91028140D512A0036A311 /* SkStroke.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DC1140D51290036A311 /* SkStroke.cpp */; };
+		26D91029140D512A0036A311 /* SkStrokerPriv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DC2140D51290036A311 /* SkStrokerPriv.cpp */; };
+		26D9102A140D512A0036A311 /* SkTSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DC4140D51290036A311 /* SkTSearch.cpp */; };
+		26D9102B140D512A0036A311 /* SkTypeface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DC8140D51290036A311 /* SkTypeface.cpp */; };
+		26D9102C140D512A0036A311 /* SkTypefaceCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DC9140D51290036A311 /* SkTypefaceCache.cpp */; };
+		26D9102D140D512A0036A311 /* SkUnPreMultiply.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DCB140D51290036A311 /* SkUnPreMultiply.cpp */; };
+		26D9102E140D512A0036A311 /* SkUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DCC140D51290036A311 /* SkUtils.cpp */; };
+		26D9102F140D512A0036A311 /* SkWriter32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DCD140D51290036A311 /* SkWriter32.cpp */; };
+		26D91030140D512A0036A311 /* SkXfermode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DCE140D51290036A311 /* SkXfermode.cpp */; };
+		26D91032140D512A0036A311 /* SkDebug_stdio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DD2140D51290036A311 /* SkDebug_stdio.cpp */; };
+		26D91033140D512A0036A311 /* SkFontHost_mac_coretext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DD3140D51290036A311 /* SkFontHost_mac_coretext.cpp */; };
+		26D91034140D512A0036A311 /* SkGlobals_global.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DD4140D51290036A311 /* SkGlobals_global.cpp */; };
+		26D91035140D512A0036A311 /* SkMemory_malloc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DD5140D51290036A311 /* SkMemory_malloc.cpp */; };
+		26D91036140D512A0036A311 /* SkThread_pthread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DD6140D51290036A311 /* SkThread_pthread.cpp */; };
+		26D91037140D512A0036A311 /* SkTime_Unix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DD7140D51290036A311 /* SkTime_Unix.cpp */; };
+		26D91038140D512A0036A311 /* SkXMLParser_empty.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DD8140D51290036A311 /* SkXMLParser_empty.cpp */; };
+		26D91039140D512A0036A311 /* Sk1DPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DF3140D51290036A311 /* Sk1DPathEffect.cpp */; };
+		26D9103A140D512A0036A311 /* Sk2DPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DF4140D51290036A311 /* Sk2DPathEffect.cpp */; };
+		26D9103B140D512A0036A311 /* SkAvoidXfermode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DF5140D51290036A311 /* SkAvoidXfermode.cpp */; };
+		26D9103C140D512A0036A311 /* SkBitmapCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DF6140D51290036A311 /* SkBitmapCache.cpp */; };
+		26D9103D140D512A0036A311 /* SkBlurDrawLooper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DF8140D51290036A311 /* SkBlurDrawLooper.cpp */; };
+		26D9103E140D512A0036A311 /* SkBlurMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DF9140D51290036A311 /* SkBlurMask.cpp */; };
+		26D9103F140D512A0036A311 /* SkBlurMaskFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DFB140D51290036A311 /* SkBlurMaskFilter.cpp */; };
+		26D91040140D512A0036A311 /* SkColorFilters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DFC140D51290036A311 /* SkColorFilters.cpp */; };
+		26D91041140D512A0036A311 /* SkColorMatrixFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DFD140D51290036A311 /* SkColorMatrixFilter.cpp */; };
+		26D91042140D512A0036A311 /* SkCornerPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DFE140D51290036A311 /* SkCornerPathEffect.cpp */; };
+		26D91043140D512A0036A311 /* SkDashPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90DFF140D51290036A311 /* SkDashPathEffect.cpp */; };
+		26D91044140D512A0036A311 /* SkDiscretePathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E00140D51290036A311 /* SkDiscretePathEffect.cpp */; };
+		26D91045140D512A0036A311 /* SkEmbossMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E01140D51290036A311 /* SkEmbossMask.cpp */; };
+		26D91046140D512A0036A311 /* SkEmbossMaskFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E03140D51290036A311 /* SkEmbossMaskFilter.cpp */; };
+		26D91047140D512A0036A311 /* SkGradientShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E05140D51290036A311 /* SkGradientShader.cpp */; };
+		26D91048140D512A0036A311 /* SkGroupShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E06140D51290036A311 /* SkGroupShape.cpp */; };
+		26D91049140D512A0036A311 /* SkKernel33MaskFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E07140D51290036A311 /* SkKernel33MaskFilter.cpp */; };
+		26D9104A140D512A0036A311 /* SkLayerDrawLooper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E08140D51290036A311 /* SkLayerDrawLooper.cpp */; };
+		26D9104B140D512A0036A311 /* SkLayerRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E09140D51290036A311 /* SkLayerRasterizer.cpp */; };
+		26D9104C140D512A0036A311 /* SkPaintFlagsDrawFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E0A140D51290036A311 /* SkPaintFlagsDrawFilter.cpp */; };
+		26D9104D140D512A0036A311 /* SkPixelXorXfermode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E0B140D51290036A311 /* SkPixelXorXfermode.cpp */; };
+		26D9104E140D512A0036A311 /* SkPorterDuff.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E0C140D51290036A311 /* SkPorterDuff.cpp */; };
+		26D9104F140D512A0036A311 /* SkRectShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E0E140D51290036A311 /* SkRectShape.cpp */; };
+		26D91050140D512A0036A311 /* SkTransparentShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E0F140D51290036A311 /* SkTransparentShader.cpp */; };
+		26D91051140D512A0036A311 /* GrAddPathRenderers_none.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E4D140D51290036A311 /* GrAddPathRenderers_none.cpp */; };
+		26D91052140D512A0036A311 /* GrDefaultPathRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E4E140D51290036A311 /* GrDefaultPathRenderer.cpp */; };
+		26D91053140D512A0036A311 /* GrGLStencilBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E50140D51290036A311 /* GrGLStencilBuffer.cpp */; };
+		26D91054140D512A0036A311 /* GrPathRendererChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E53140D51290036A311 /* GrPathRendererChain.cpp */; };
+		26D91055140D512A0036A311 /* GrStencilBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E55140D51290036A311 /* GrStencilBuffer.cpp */; };
+		26D91056140D512A0036A311 /* GrRenderTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E56140D51290036A311 /* GrRenderTarget.cpp */; };
+		26D91057140D512A0036A311 /* GrGLRenderTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E57140D51290036A311 /* GrGLRenderTarget.cpp */; };
+		26D91058140D512A0036A311 /* GrResourceCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E5A140D51290036A311 /* GrResourceCache.cpp */; };
+		26D91059140D512A0036A311 /* FlingState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E5C140D51290036A311 /* FlingState.cpp */; };
+		26D9105A140D512A0036A311 /* GrDrawMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E5D140D51290036A311 /* GrDrawMesh.cpp */; };
+		26D9105B140D512A0036A311 /* GrAllocPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E5E140D51290036A311 /* GrAllocPool.cpp */; };
+		26D9105C140D512A0036A311 /* GrAtlas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E5F140D51290036A311 /* GrAtlas.cpp */; };
+		26D9105D140D512A0036A311 /* GrBufferAllocPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E61140D51290036A311 /* GrBufferAllocPool.cpp */; };
+		26D9105E140D512A0036A311 /* GrClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E63140D51290036A311 /* GrClip.cpp */; };
+		26D9105F140D512A0036A311 /* GrContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E64140D51290036A311 /* GrContext.cpp */; };
+		26D91060140D512A0036A311 /* GrDrawTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E65140D51290036A311 /* GrDrawTarget.cpp */; };
+		26D91061140D512A0036A311 /* GrGLDefaultInterface_none.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E66140D51290036A311 /* GrGLDefaultInterface_none.cpp */; };
+		26D91062140D512A0036A311 /* GrGLIndexBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E67140D51290036A311 /* GrGLIndexBuffer.cpp */; };
+		26D91063140D512A0036A311 /* GrGLInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E68140D51290036A311 /* GrGLInterface.cpp */; };
+		26D91064140D512A0036A311 /* GrGLProgram.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E69140D51290036A311 /* GrGLProgram.cpp */; };
+		26D91065140D512A0036A311 /* GrGLTexture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E6B140D51290036A311 /* GrGLTexture.cpp */; };
+		26D91066140D512A0036A311 /* GrGLUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E6C140D51290036A311 /* GrGLUtil.cpp */; };
+		26D91067140D512A0036A311 /* GrGLVertexBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E6D140D51290036A311 /* GrGLVertexBuffer.cpp */; };
+		26D91068140D512A0036A311 /* GrGpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E6E140D51290036A311 /* GrGpu.cpp */; };
+		26D91069140D512A0036A311 /* GrGpuFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E6F140D51290036A311 /* GrGpuFactory.cpp */; };
+		26D9106A140D512A0036A311 /* GrGpuGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E70140D51290036A311 /* GrGpuGL.cpp */; };
+		26D9106B140D512A0036A311 /* GrGpuGLFixed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E72140D51290036A311 /* GrGpuGLFixed.cpp */; };
+		26D9106C140D512A0036A311 /* GrGpuGLShaders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E74140D51290036A311 /* GrGpuGLShaders.cpp */; };
+		26D9106D140D512A0036A311 /* GrInOrderDrawBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E76140D51290036A311 /* GrInOrderDrawBuffer.cpp */; };
+		26D9106E140D512A0036A311 /* GrMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E77140D51290036A311 /* GrMatrix.cpp */; };
+		26D9106F140D512A0036A311 /* GrMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E78140D51290036A311 /* GrMemory.cpp */; };
+		26D91070140D512A0036A311 /* GrPathRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E79140D51290036A311 /* GrPathRenderer.cpp */; };
+		26D91071140D512A0036A311 /* GrPathUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E7A140D51290036A311 /* GrPathUtils.cpp */; };
+		26D91072140D512A0036A311 /* GrRectanizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E7C140D51290036A311 /* GrRectanizer.cpp */; };
+		26D91073140D512A0036A311 /* GrResource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E7E140D51290036A311 /* GrResource.cpp */; };
+		26D91074140D512A0036A311 /* GrStencil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E7F140D51290036A311 /* GrStencil.cpp */; };
+		26D91076140D512A0036A311 /* GrTextContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E81140D51290036A311 /* GrTextContext.cpp */; };
+		26D91077140D512A0036A311 /* GrTextStrike.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E82140D51290036A311 /* GrTextStrike.cpp */; };
+		26D91078140D512A0036A311 /* GrTexture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E84140D51290036A311 /* GrTexture.cpp */; };
+		26D91079140D512A0036A311 /* gr_unittests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E85140D51290036A311 /* gr_unittests.cpp */; };
+		26D9107A140D512A0036A311 /* GrPrintf_skia.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E8C140D51290036A311 /* GrPrintf_skia.cpp */; };
+		26D9107B140D512A0036A311 /* SkGpuCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E8D140D51290036A311 /* SkGpuCanvas.cpp */; };
+		26D9107C140D512A0036A311 /* SkGpuDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E8E140D51290036A311 /* SkGpuDevice.cpp */; };
+		26D9107D140D512A0036A311 /* SkGr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E8F140D51290036A311 /* SkGr.cpp */; };
+		26D9107E140D512A0036A311 /* SkGrFontScaler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E90140D51290036A311 /* SkGrFontScaler.cpp */; };
+		26D9107F140D512A0036A311 /* SkGrTexturePixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E91140D51290036A311 /* SkGrTexturePixelRef.cpp */; };
+		26D91080140D512A0036A311 /* SkMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90E9E140D51290036A311 /* SkMovie.cpp */; };
+		26D91081140D512A0036A311 /* SkCreateRLEPixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EA0140D51290036A311 /* SkCreateRLEPixelRef.cpp */; };
+		26D91082140D512A0036A311 /* SkFDStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EA1140D51290036A311 /* SkFDStream.cpp */; };
+		26D91083140D512A0036A311 /* SkFlipPixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EA2140D51290036A311 /* SkFlipPixelRef.cpp */; };
+		26D91084140D512A0036A311 /* SkImageDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EA3140D51290036A311 /* SkImageDecoder.cpp */; };
+		26D91085140D512A0036A311 /* SkImageEncoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EA4140D51290036A311 /* SkImageEncoder.cpp */; };
+		26D91086140D512A0036A311 /* SkImageDecoder_Factory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EA5140D51290036A311 /* SkImageDecoder_Factory.cpp */; };
+		26D91087140D512A0036A311 /* SkImageEncoder_Factory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EA6140D51290036A311 /* SkImageEncoder_Factory.cpp */; };
+		26D91088140D512A0036A311 /* SkPageFlipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EA7140D51290036A311 /* SkPageFlipper.cpp */; };
+		26D91089140D512A0036A311 /* SkImageRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EA8140D51290036A311 /* SkImageRef.cpp */; };
+		26D9108A140D512A0036A311 /* SkImageRefPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EA9140D51290036A311 /* SkImageRefPool.cpp */; };
+		26D9108B140D512A0036A311 /* SkImageRef_GlobalPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EAB140D51290036A311 /* SkImageRef_GlobalPool.cpp */; };
+		26D9108F140D512A0036A311 /* SkBitmapProcState_opts_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EB1140D51290036A311 /* SkBitmapProcState_opts_arm.cpp */; };
+		26D91097140D512A0036A311 /* SkGPipeRead.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EBE140D51290036A311 /* SkGPipeRead.cpp */; };
+		26D91098140D512A0036A311 /* SkGPipeWrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EBF140D51290036A311 /* SkGPipeWrite.cpp */; };
+		26D91099140D512A0036A311 /* SkBitSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90ED0140D51290036A311 /* SkBitSet.cpp */; };
+		26D9109A140D512A0036A311 /* SkPDFCatalog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90ED1140D51290036A311 /* SkPDFCatalog.cpp */; };
+		26D9109B140D512A0036A311 /* SkPDFDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90ED2140D51290036A311 /* SkPDFDevice.cpp */; };
+		26D9109C140D512A0036A311 /* SkPDFDocument.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90ED3140D51290036A311 /* SkPDFDocument.cpp */; };
+		26D9109D140D512A0036A311 /* SkPDFFont.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90ED4140D51290036A311 /* SkPDFFont.cpp */; };
+		26D9109E140D512A0036A311 /* SkPDFFormXObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90ED5140D51290036A311 /* SkPDFFormXObject.cpp */; };
+		26D9109F140D512A0036A311 /* SkPDFGraphicState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90ED6140D51290036A311 /* SkPDFGraphicState.cpp */; };
+		26D910A0140D512A0036A311 /* SkPDFImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90ED7140D51290036A311 /* SkPDFImage.cpp */; };
+		26D910A1140D512A0036A311 /* SkPDFPage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90ED8140D51290036A311 /* SkPDFPage.cpp */; };
+		26D910A2140D512A0036A311 /* SkPDFShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90ED9140D51290036A311 /* SkPDFShader.cpp */; };
+		26D910A3140D512A0036A311 /* SkPDFStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EDA140D51290036A311 /* SkPDFStream.cpp */; };
+		26D910A4140D512A0036A311 /* SkPDFTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EDB140D51290036A311 /* SkPDFTypes.cpp */; };
+		26D910A5140D512A0036A311 /* SkPDFUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EDC140D51290036A311 /* SkPDFUtils.cpp */; };
+		26D910A6140D512A0036A311 /* SkSVGCircle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EE5140D51290036A311 /* SkSVGCircle.cpp */; };
+		26D910A7140D512A0036A311 /* SkSVGClipPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EE7140D51290036A311 /* SkSVGClipPath.cpp */; };
+		26D910A8140D512A0036A311 /* SkSVGDefs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EE9140D51290036A311 /* SkSVGDefs.cpp */; };
+		26D910A9140D512A0036A311 /* SkSVGElements.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EEB140D51290036A311 /* SkSVGElements.cpp */; };
+		26D910AA140D512A0036A311 /* SkSVGEllipse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EED140D51290036A311 /* SkSVGEllipse.cpp */; };
+		26D910AB140D512A0036A311 /* SkSVGFeColorMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EEF140D51290036A311 /* SkSVGFeColorMatrix.cpp */; };
+		26D910AC140D512A0036A311 /* SkSVGFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EF1140D51290036A311 /* SkSVGFilter.cpp */; };
+		26D910AD140D512A0036A311 /* SkSVGG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EF3140D51290036A311 /* SkSVGG.cpp */; };
+		26D910AE140D512A0036A311 /* SkSVGGradient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EF5140D51290036A311 /* SkSVGGradient.cpp */; };
+		26D910AF140D512A0036A311 /* SkSVGGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EF7140D51290036A311 /* SkSVGGroup.cpp */; };
+		26D910B0140D512A0036A311 /* SkSVGImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EF9140D51290036A311 /* SkSVGImage.cpp */; };
+		26D910B1140D512A0036A311 /* SkSVGLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EFB140D51290036A311 /* SkSVGLine.cpp */; };
+		26D910B2140D512A0036A311 /* SkSVGLinearGradient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EFD140D51290036A311 /* SkSVGLinearGradient.cpp */; };
+		26D910B3140D512A0036A311 /* SkSVGMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90EFF140D512A0036A311 /* SkSVGMask.cpp */; };
+		26D910B4140D512A0036A311 /* SkSVGMetadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F01140D512A0036A311 /* SkSVGMetadata.cpp */; };
+		26D910B5140D512A0036A311 /* SkSVGPaintState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F03140D512A0036A311 /* SkSVGPaintState.cpp */; };
+		26D910B6140D512A0036A311 /* SkSVGParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F04140D512A0036A311 /* SkSVGParser.cpp */; };
+		26D910B7140D512A0036A311 /* SkSVGPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F05140D512A0036A311 /* SkSVGPath.cpp */; };
+		26D910B8140D512A0036A311 /* SkSVGPolygon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F07140D512A0036A311 /* SkSVGPolygon.cpp */; };
+		26D910B9140D512A0036A311 /* SkSVGPolyline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F09140D512A0036A311 /* SkSVGPolyline.cpp */; };
+		26D910BA140D512A0036A311 /* SkSVGRadialGradient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F0B140D512A0036A311 /* SkSVGRadialGradient.cpp */; };
+		26D910BB140D512A0036A311 /* SkSVGRect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F0D140D512A0036A311 /* SkSVGRect.cpp */; };
+		26D910BC140D512A0036A311 /* SkSVGSVG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F0F140D512A0036A311 /* SkSVGSVG.cpp */; };
+		26D910BD140D512A0036A311 /* SkSVGStop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F11140D512A0036A311 /* SkSVGStop.cpp */; };
+		26D910BE140D512A0036A311 /* SkSVGSymbol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F13140D512A0036A311 /* SkSVGSymbol.cpp */; };
+		26D910BF140D512A0036A311 /* SkSVGText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F15140D512A0036A311 /* SkSVGText.cpp */; };
+		26D910C0140D512A0036A311 /* SkSVGUse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F17140D512A0036A311 /* SkSVGUse.cpp */; };
+		26D910C1140D512A0036A311 /* SkCreateCGImageRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F32140D512A0036A311 /* SkCreateCGImageRef.cpp */; };
+		26D910C2140D512A0036A311 /* SkBoundaryPatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F33140D512A0036A311 /* SkBoundaryPatch.cpp */; };
+		26D910C3140D512A0036A311 /* SkCamera.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F34140D512A0036A311 /* SkCamera.cpp */; };
+		26D910C4140D512A0036A311 /* SkColorMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F35140D512A0036A311 /* SkColorMatrix.cpp */; };
+		26D910C5140D512A0036A311 /* SkCubicInterval.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F36140D512A0036A311 /* SkCubicInterval.cpp */; };
+		26D910C6140D512A0036A311 /* SkCullPoints.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F37140D512A0036A311 /* SkCullPoints.cpp */; };
+		26D910C7140D512A0036A311 /* SkDumpCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F38140D512A0036A311 /* SkDumpCanvas.cpp */; };
+		26D910C8140D512A0036A311 /* SkInterpolator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F39140D512A0036A311 /* SkInterpolator.cpp */; };
+		26D910C9140D512A0036A311 /* SkLayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F3A140D512A0036A311 /* SkLayer.cpp */; };
+		26D910CA140D512A0036A311 /* SkMatrix44.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F3B140D512A0036A311 /* SkMatrix44.cpp */; };
+		26D910CB140D512A0036A311 /* SkMeshUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F3C140D512A0036A311 /* SkMeshUtils.cpp */; };
+		26D910CC140D512A0036A311 /* SkNWayCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F3D140D512A0036A311 /* SkNWayCanvas.cpp */; };
+		26D910CD140D512A0036A311 /* SkNinePatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F3E140D512A0036A311 /* SkNinePatch.cpp */; };
+		26D910CE140D512A0036A311 /* SkOSFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F3F140D512A0036A311 /* SkOSFile.cpp */; };
+		26D910CF140D512A0036A311 /* SkParse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F40140D512A0036A311 /* SkParse.cpp */; };
+		26D910D0140D512A0036A311 /* SkParseColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F41140D512A0036A311 /* SkParseColor.cpp */; };
+		26D910D1140D512A0036A311 /* SkParsePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F42140D512A0036A311 /* SkParsePath.cpp */; };
+		26D910D2140D512A0036A311 /* SkProxyCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F43140D512A0036A311 /* SkProxyCanvas.cpp */; };
+		26D910D3140D512A0036A311 /* SkSfntUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F44140D512A0036A311 /* SkSfntUtils.cpp */; };
+		26D910D4140D512A0036A311 /* SkUnitMappers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F45140D512A0036A311 /* SkUnitMappers.cpp */; };
+		26D910D5140D512A0036A311 /* SkBGViewArtist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F5B140D512A0036A311 /* SkBGViewArtist.cpp */; };
+		26D910D6140D512A0036A311 /* SkEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F5C140D512A0036A311 /* SkEvent.cpp */; };
+		26D910D7140D512A0036A311 /* SkEventSink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F5D140D512A0036A311 /* SkEventSink.cpp */; };
+		26D910D8140D512A0036A311 /* SkImageView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F5E140D512A0036A311 /* SkImageView.cpp */; };
+		26D910D9140D512A0036A311 /* SkListView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F5F140D512A0036A311 /* SkListView.cpp */; };
+		26D910DB140D512A0036A311 /* SkOSMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F61140D512A0036A311 /* SkOSMenu.cpp */; };
+		26D910DC140D512A0036A311 /* SkParsePaint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F62140D512A0036A311 /* SkParsePaint.cpp */; };
+		26D910E1140D512A0036A311 /* SkStackViewLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F67140D512A0036A311 /* SkStackViewLayout.cpp */; };
+		26D910E2140D512A0036A311 /* SkStaticTextView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F68140D512A0036A311 /* SkStaticTextView.cpp */; };
+		26D910E3140D512A0036A311 /* SkTagList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F69140D512A0036A311 /* SkTagList.cpp */; };
+		26D910E4140D512A0036A311 /* SkTextBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F6B140D512A0036A311 /* SkTextBox.cpp */; };
+		26D910E5140D512A0036A311 /* SkTouchGesture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F6C140D512A0036A311 /* SkTouchGesture.cpp */; };
+		26D910E6140D512A0036A311 /* SkView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F6D140D512A0036A311 /* SkView.cpp */; };
+		26D910E7140D512A0036A311 /* SkViewInflate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F6E140D512A0036A311 /* SkViewInflate.cpp */; };
+		26D910E8140D512A0036A311 /* SkViewPriv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F6F140D512A0036A311 /* SkViewPriv.cpp */; };
+		26D910E9140D512A0036A311 /* SkWidget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F71140D512A0036A311 /* SkWidget.cpp */; };
+		26D910EB140D512A0036A311 /* SkWidgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F73140D512A0036A311 /* SkWidgets.cpp */; };
+		26D910EC140D512A0036A311 /* SkWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F74140D512A0036A311 /* SkWindow.cpp */; };
+		26D910ED140D512A0036A311 /* SkDOM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F7E140D512A0036A311 /* SkDOM.cpp */; };
+		26D910EE140D512A0036A311 /* SkXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F7F140D512A0036A311 /* SkXMLParser.cpp */; };
+		26D910EF140D512A0036A311 /* SkXMLWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D90F80140D512A0036A311 /* SkXMLWriter.cpp */; };
+		26D91120140D52CD0036A311 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26D9111D140D52CD0036A311 /* CoreFoundation.framework */; };
+		26D91122140D52CD0036A311 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26D9111F140D52CD0036A311 /* CoreText.framework */; };
+		26D91131140D53C30036A311 /* SkBlitRow_opts_none.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D9112F140D53C30036A311 /* SkBlitRow_opts_none.cpp */; };
+		26D91132140D53C30036A311 /* SkUtils_opts_none.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D91130140D53C30036A311 /* SkUtils_opts_none.cpp */; };
+		26D91146140D54720036A311 /* skia_ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26D9113B140D54720036A311 /* skia_ios.mm */; };
+		26D91155140D54E40036A311 /* SkEventNotifier.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26D91154140D54E40036A311 /* SkEventNotifier.mm */; };
+		26D9118F140D558A0036A311 /* SkOSFile_iOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26D9118E140D558A0036A311 /* SkOSFile_iOS.mm */; };
+		26D91194140D55980036A311 /* SkOSWindow_iOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26D91193140D55980036A311 /* SkOSWindow_iOS.mm */; };
+		26D912ED140D5FCC0036A311 /* SkiOSSampleApp-Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 26D912EA140D5FCC0036A311 /* SkiOSSampleApp-Debug.xcconfig */; };
+		26D912EE140D5FCC0036A311 /* SkiOSSampleApp-Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 26D912EB140D5FCC0036A311 /* SkiOSSampleApp-Release.xcconfig */; };
+		26D912EF140D5FCC0036A311 /* SkiOSSampleApp-Base.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 26D912EC140D5FCC0036A311 /* SkiOSSampleApp-Base.xcconfig */; };
+		26D91304140D60330036A311 /* SimpleApp.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26D91303140D60330036A311 /* SimpleApp.mm */; };
+		2860E328111B887F00E27156 /* AppDelegate_iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 2860E326111B887F00E27156 /* AppDelegate_iPhone.m */; };
+		2860E329111B887F00E27156 /* MainWindow_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2860E327111B887F00E27156 /* MainWindow_iPhone.xib */; };
+		2860E32E111B888700E27156 /* AppDelegate_iPad.m in Sources */ = {isa = PBXBuildFile; fileRef = 2860E32C111B888700E27156 /* AppDelegate_iPad.m */; };
+		2860E32F111B888700E27156 /* MainWindow_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2860E32D111B888700E27156 /* MainWindow_iPad.xib */; };
+		288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+		1D6058910D05DD3D006BFB54 /* SimpleiOSApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleiOSApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+		26371975140D6C19006CC295 /* SkUIView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUIView.h; path = ../iOSSampleApp/Shared/SkUIView.h; sourceTree = SOURCE_ROOT; };
+		26371976140D6C19006CC295 /* SkUIView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkUIView.mm; path = ../iOSSampleApp/Shared/SkUIView.mm; sourceTree = SOURCE_ROOT; };
+		26D90C44140D51290036A311 /* SkUserConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUserConfig.h; path = ../../include/config/SkUserConfig.h; sourceTree = SOURCE_ROOT; };
+		26D90C47140D51290036A311 /* SkAnimator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimator.h; sourceTree = "<group>"; };
+		26D90C48140D51290036A311 /* SkAnimatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimatorView.h; sourceTree = "<group>"; };
+		26D90C4A140D51290036A311 /* SkAnimate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimate.h; sourceTree = "<group>"; };
+		26D90C4B140D51290036A311 /* SkAnimateActive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimateActive.cpp; sourceTree = "<group>"; };
+		26D90C4C140D51290036A311 /* SkAnimateActive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimateActive.h; sourceTree = "<group>"; };
+		26D90C4D140D51290036A311 /* SkAnimateBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimateBase.cpp; sourceTree = "<group>"; };
+		26D90C4E140D51290036A311 /* SkAnimateBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimateBase.h; sourceTree = "<group>"; };
+		26D90C4F140D51290036A311 /* SkAnimateField.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimateField.cpp; sourceTree = "<group>"; };
+		26D90C50140D51290036A311 /* SkAnimateMaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimateMaker.cpp; sourceTree = "<group>"; };
+		26D90C51140D51290036A311 /* SkAnimateMaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimateMaker.h; sourceTree = "<group>"; };
+		26D90C52140D51290036A311 /* SkAnimateProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimateProperties.h; sourceTree = "<group>"; };
+		26D90C53140D51290036A311 /* SkAnimateSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimateSet.cpp; sourceTree = "<group>"; };
+		26D90C54140D51290036A311 /* SkAnimateSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimateSet.h; sourceTree = "<group>"; };
+		26D90C55140D51290036A311 /* SkAnimator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimator.cpp; sourceTree = "<group>"; };
+		26D90C56140D51290036A311 /* SkAnimatorScript.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimatorScript.cpp; sourceTree = "<group>"; };
+		26D90C57140D51290036A311 /* SkAnimatorScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimatorScript.h; sourceTree = "<group>"; };
+		26D90C58140D51290036A311 /* SkBase64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBase64.cpp; sourceTree = "<group>"; };
+		26D90C59140D51290036A311 /* SkBase64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBase64.h; sourceTree = "<group>"; };
+		26D90C5A140D51290036A311 /* SkBoundable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBoundable.cpp; sourceTree = "<group>"; };
+		26D90C5B140D51290036A311 /* SkBoundable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBoundable.h; sourceTree = "<group>"; };
+		26D90C5C140D51290036A311 /* SkBuildCondensedInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBuildCondensedInfo.cpp; sourceTree = "<group>"; };
+		26D90C5D140D51290036A311 /* SkDisplayAdd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayAdd.cpp; sourceTree = "<group>"; };
+		26D90C5E140D51290036A311 /* SkDisplayAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayAdd.h; sourceTree = "<group>"; };
+		26D90C5F140D51290036A311 /* SkDisplayApply.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayApply.cpp; sourceTree = "<group>"; };
+		26D90C60140D51290036A311 /* SkDisplayApply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayApply.h; sourceTree = "<group>"; };
+		26D90C61140D51290036A311 /* SkDisplayBounds.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayBounds.cpp; sourceTree = "<group>"; };
+		26D90C62140D51290036A311 /* SkDisplayBounds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayBounds.h; sourceTree = "<group>"; };
+		26D90C63140D51290036A311 /* SkDisplayEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayEvent.cpp; sourceTree = "<group>"; };
+		26D90C64140D51290036A311 /* SkDisplayEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayEvent.h; sourceTree = "<group>"; };
+		26D90C65140D51290036A311 /* SkDisplayEvents.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayEvents.cpp; sourceTree = "<group>"; };
+		26D90C66140D51290036A311 /* SkDisplayEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayEvents.h; sourceTree = "<group>"; };
+		26D90C67140D51290036A311 /* SkDisplayInclude.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayInclude.cpp; sourceTree = "<group>"; };
+		26D90C68140D51290036A311 /* SkDisplayInclude.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayInclude.h; sourceTree = "<group>"; };
+		26D90C69140D51290036A311 /* SkDisplayInput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayInput.cpp; sourceTree = "<group>"; };
+		26D90C6A140D51290036A311 /* SkDisplayInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayInput.h; sourceTree = "<group>"; };
+		26D90C6B140D51290036A311 /* SkDisplayList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayList.cpp; sourceTree = "<group>"; };
+		26D90C6C140D51290036A311 /* SkDisplayList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayList.h; sourceTree = "<group>"; };
+		26D90C6D140D51290036A311 /* SkDisplayMath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayMath.cpp; sourceTree = "<group>"; };
+		26D90C6E140D51290036A311 /* SkDisplayMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayMath.h; sourceTree = "<group>"; };
+		26D90C6F140D51290036A311 /* SkDisplayMovie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayMovie.cpp; sourceTree = "<group>"; };
+		26D90C70140D51290036A311 /* SkDisplayMovie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayMovie.h; sourceTree = "<group>"; };
+		26D90C71140D51290036A311 /* SkDisplayNumber.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayNumber.cpp; sourceTree = "<group>"; };
+		26D90C72140D51290036A311 /* SkDisplayNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayNumber.h; sourceTree = "<group>"; };
+		26D90C73140D51290036A311 /* SkDisplayPost.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayPost.cpp; sourceTree = "<group>"; };
+		26D90C74140D51290036A311 /* SkDisplayPost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayPost.h; sourceTree = "<group>"; };
+		26D90C75140D51290036A311 /* SkDisplayRandom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayRandom.cpp; sourceTree = "<group>"; };
+		26D90C76140D51290036A311 /* SkDisplayRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayRandom.h; sourceTree = "<group>"; };
+		26D90C77140D51290036A311 /* SkDisplayScreenplay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayScreenplay.cpp; sourceTree = "<group>"; };
+		26D90C78140D51290036A311 /* SkDisplayScreenplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayScreenplay.h; sourceTree = "<group>"; };
+		26D90C79140D51290036A311 /* SkDisplayType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayType.cpp; sourceTree = "<group>"; };
+		26D90C7A140D51290036A311 /* SkDisplayType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayType.h; sourceTree = "<group>"; };
+		26D90C7B140D51290036A311 /* SkDisplayTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayTypes.cpp; sourceTree = "<group>"; };
+		26D90C7C140D51290036A311 /* SkDisplayTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayTypes.h; sourceTree = "<group>"; };
+		26D90C7D140D51290036A311 /* SkDisplayXMLParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayXMLParser.cpp; sourceTree = "<group>"; };
+		26D90C7E140D51290036A311 /* SkDisplayXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayXMLParser.h; sourceTree = "<group>"; };
+		26D90C7F140D51290036A311 /* SkDisplayable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayable.cpp; sourceTree = "<group>"; };
+		26D90C80140D51290036A311 /* SkDisplayable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayable.h; sourceTree = "<group>"; };
+		26D90C81140D51290036A311 /* SkDraw3D.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDraw3D.cpp; sourceTree = "<group>"; };
+		26D90C82140D51290036A311 /* SkDraw3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDraw3D.h; sourceTree = "<group>"; };
+		26D90C83140D51290036A311 /* SkDrawBitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawBitmap.cpp; sourceTree = "<group>"; };
+		26D90C84140D51290036A311 /* SkDrawBitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawBitmap.h; sourceTree = "<group>"; };
+		26D90C85140D51290036A311 /* SkDrawBlur.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawBlur.cpp; sourceTree = "<group>"; };
+		26D90C86140D51290036A311 /* SkDrawBlur.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawBlur.h; sourceTree = "<group>"; };
+		26D90C87140D51290036A311 /* SkDrawClip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawClip.cpp; sourceTree = "<group>"; };
+		26D90C88140D51290036A311 /* SkDrawClip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawClip.h; sourceTree = "<group>"; };
+		26D90C89140D51290036A311 /* SkDrawColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawColor.cpp; sourceTree = "<group>"; };
+		26D90C8A140D51290036A311 /* SkDrawColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawColor.h; sourceTree = "<group>"; };
+		26D90C8B140D51290036A311 /* SkDrawDash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawDash.cpp; sourceTree = "<group>"; };
+		26D90C8C140D51290036A311 /* SkDrawDash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawDash.h; sourceTree = "<group>"; };
+		26D90C8D140D51290036A311 /* SkDrawDiscrete.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawDiscrete.cpp; sourceTree = "<group>"; };
+		26D90C8E140D51290036A311 /* SkDrawDiscrete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawDiscrete.h; sourceTree = "<group>"; };
+		26D90C8F140D51290036A311 /* SkDrawEmboss.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawEmboss.cpp; sourceTree = "<group>"; };
+		26D90C90140D51290036A311 /* SkDrawEmboss.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawEmboss.h; sourceTree = "<group>"; };
+		26D90C91140D51290036A311 /* SkDrawExtraPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawExtraPathEffect.cpp; sourceTree = "<group>"; };
+		26D90C92140D51290036A311 /* SkDrawFull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawFull.cpp; sourceTree = "<group>"; };
+		26D90C93140D51290036A311 /* SkDrawFull.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawFull.h; sourceTree = "<group>"; };
+		26D90C94140D51290036A311 /* SkDrawGradient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawGradient.cpp; sourceTree = "<group>"; };
+		26D90C95140D51290036A311 /* SkDrawGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawGradient.h; sourceTree = "<group>"; };
+		26D90C96140D51290036A311 /* SkDrawGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawGroup.cpp; sourceTree = "<group>"; };
+		26D90C97140D51290036A311 /* SkDrawGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawGroup.h; sourceTree = "<group>"; };
+		26D90C98140D51290036A311 /* SkDrawLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawLine.cpp; sourceTree = "<group>"; };
+		26D90C99140D51290036A311 /* SkDrawLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawLine.h; sourceTree = "<group>"; };
+		26D90C9A140D51290036A311 /* SkDrawMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawMatrix.cpp; sourceTree = "<group>"; };
+		26D90C9B140D51290036A311 /* SkDrawMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawMatrix.h; sourceTree = "<group>"; };
+		26D90C9C140D51290036A311 /* SkDrawOval.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawOval.cpp; sourceTree = "<group>"; };
+		26D90C9D140D51290036A311 /* SkDrawOval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawOval.h; sourceTree = "<group>"; };
+		26D90C9E140D51290036A311 /* SkDrawPaint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawPaint.cpp; sourceTree = "<group>"; };
+		26D90C9F140D51290036A311 /* SkDrawPaint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawPaint.h; sourceTree = "<group>"; };
+		26D90CA0140D51290036A311 /* SkDrawPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawPath.cpp; sourceTree = "<group>"; };
+		26D90CA1140D51290036A311 /* SkDrawPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawPath.h; sourceTree = "<group>"; };
+		26D90CA2140D51290036A311 /* SkDrawPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawPoint.cpp; sourceTree = "<group>"; };
+		26D90CA3140D51290036A311 /* SkDrawPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawPoint.h; sourceTree = "<group>"; };
+		26D90CA4140D51290036A311 /* SkDrawRectangle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawRectangle.cpp; sourceTree = "<group>"; };
+		26D90CA5140D51290036A311 /* SkDrawRectangle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawRectangle.h; sourceTree = "<group>"; };
+		26D90CA6140D51290036A311 /* SkDrawSaveLayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawSaveLayer.cpp; sourceTree = "<group>"; };
+		26D90CA7140D51290036A311 /* SkDrawSaveLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawSaveLayer.h; sourceTree = "<group>"; };
+		26D90CA8140D51290036A311 /* SkDrawShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawShader.cpp; sourceTree = "<group>"; };
+		26D90CA9140D51290036A311 /* SkDrawShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawShader.h; sourceTree = "<group>"; };
+		26D90CAA140D51290036A311 /* SkDrawText.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawText.cpp; sourceTree = "<group>"; };
+		26D90CAB140D51290036A311 /* SkDrawText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawText.h; sourceTree = "<group>"; };
+		26D90CAC140D51290036A311 /* SkDrawTextBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawTextBox.cpp; sourceTree = "<group>"; };
+		26D90CAD140D51290036A311 /* SkDrawTextBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawTextBox.h; sourceTree = "<group>"; };
+		26D90CAE140D51290036A311 /* SkDrawTo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawTo.cpp; sourceTree = "<group>"; };
+		26D90CAF140D51290036A311 /* SkDrawTo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawTo.h; sourceTree = "<group>"; };
+		26D90CB0140D51290036A311 /* SkDrawTransparentShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawTransparentShader.cpp; sourceTree = "<group>"; };
+		26D90CB1140D51290036A311 /* SkDrawTransparentShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawTransparentShader.h; sourceTree = "<group>"; };
+		26D90CB2140D51290036A311 /* SkDrawable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawable.cpp; sourceTree = "<group>"; };
+		26D90CB3140D51290036A311 /* SkDrawable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawable.h; sourceTree = "<group>"; };
+		26D90CB4140D51290036A311 /* SkDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDump.cpp; sourceTree = "<group>"; };
+		26D90CB5140D51290036A311 /* SkDump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDump.h; sourceTree = "<group>"; };
+		26D90CB6140D51290036A311 /* SkExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkExtras.h; sourceTree = "<group>"; };
+		26D90CB7140D51290036A311 /* SkGetCondensedInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGetCondensedInfo.cpp; sourceTree = "<group>"; };
+		26D90CB8140D51290036A311 /* SkHitClear.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkHitClear.cpp; sourceTree = "<group>"; };
+		26D90CB9140D51290036A311 /* SkHitClear.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkHitClear.h; sourceTree = "<group>"; };
+		26D90CBA140D51290036A311 /* SkHitTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkHitTest.cpp; sourceTree = "<group>"; };
+		26D90CBB140D51290036A311 /* SkHitTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkHitTest.h; sourceTree = "<group>"; };
+		26D90CBC140D51290036A311 /* SkIntArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkIntArray.h; sourceTree = "<group>"; };
+		26D90CBD140D51290036A311 /* SkMatrixParts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMatrixParts.cpp; sourceTree = "<group>"; };
+		26D90CBE140D51290036A311 /* SkMatrixParts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkMatrixParts.h; sourceTree = "<group>"; };
+		26D90CBF140D51290036A311 /* SkMemberInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMemberInfo.cpp; sourceTree = "<group>"; };
+		26D90CC0140D51290036A311 /* SkMemberInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkMemberInfo.h; sourceTree = "<group>"; };
+		26D90CC1140D51290036A311 /* SkOpArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkOpArray.cpp; sourceTree = "<group>"; };
+		26D90CC2140D51290036A311 /* SkOpArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkOpArray.h; sourceTree = "<group>"; };
+		26D90CC3140D51290036A311 /* SkOperand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkOperand.h; sourceTree = "<group>"; };
+		26D90CC4140D51290036A311 /* SkOperand2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkOperand2.h; sourceTree = "<group>"; };
+		26D90CC5140D51290036A311 /* SkOperandInterpolator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkOperandInterpolator.h; sourceTree = "<group>"; };
+		26D90CC6140D51290036A311 /* SkOperandIterpolator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkOperandIterpolator.cpp; sourceTree = "<group>"; };
+		26D90CC7140D51290036A311 /* SkPaintParts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPaintParts.cpp; sourceTree = "<group>"; };
+		26D90CC8140D51290036A311 /* SkPaintParts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPaintParts.h; sourceTree = "<group>"; };
+		26D90CC9140D51290036A311 /* SkParseSVGPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkParseSVGPath.cpp; sourceTree = "<group>"; };
+		26D90CCA140D51290036A311 /* SkPathParts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPathParts.cpp; sourceTree = "<group>"; };
+		26D90CCB140D51290036A311 /* SkPathParts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPathParts.h; sourceTree = "<group>"; };
+		26D90CCC140D51290036A311 /* SkPostParts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPostParts.cpp; sourceTree = "<group>"; };
+		26D90CCD140D51290036A311 /* SkPostParts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPostParts.h; sourceTree = "<group>"; };
+		26D90CCE140D51290036A311 /* SkScript.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScript.cpp; sourceTree = "<group>"; };
+		26D90CCF140D51290036A311 /* SkScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScript.h; sourceTree = "<group>"; };
+		26D90CD0140D51290036A311 /* SkScript2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScript2.h; sourceTree = "<group>"; };
+		26D90CD1140D51290036A311 /* SkScriptCallBack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScriptCallBack.h; sourceTree = "<group>"; };
+		26D90CD2140D51290036A311 /* SkScriptDecompile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScriptDecompile.cpp; sourceTree = "<group>"; };
+		26D90CD3140D51290036A311 /* SkScriptRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScriptRuntime.cpp; sourceTree = "<group>"; };
+		26D90CD4140D51290036A311 /* SkScriptRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScriptRuntime.h; sourceTree = "<group>"; };
+		26D90CD5140D51290036A311 /* SkScriptTokenizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScriptTokenizer.cpp; sourceTree = "<group>"; };
+		26D90CD6140D51290036A311 /* SkSnapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSnapshot.cpp; sourceTree = "<group>"; };
+		26D90CD7140D51290036A311 /* SkSnapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSnapshot.h; sourceTree = "<group>"; };
+		26D90CD8140D51290036A311 /* SkTDArray_Experimental.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTDArray_Experimental.h; sourceTree = "<group>"; };
+		26D90CD9140D51290036A311 /* SkTextOnPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTextOnPath.cpp; sourceTree = "<group>"; };
+		26D90CDA140D51290036A311 /* SkTextOnPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTextOnPath.h; sourceTree = "<group>"; };
+		26D90CDB140D51290036A311 /* SkTextToPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTextToPath.cpp; sourceTree = "<group>"; };
+		26D90CDC140D51290036A311 /* SkTextToPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTextToPath.h; sourceTree = "<group>"; };
+		26D90CDD140D51290036A311 /* SkTime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTime.cpp; sourceTree = "<group>"; };
+		26D90CDE140D51290036A311 /* SkTypedArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTypedArray.cpp; sourceTree = "<group>"; };
+		26D90CDF140D51290036A311 /* SkTypedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTypedArray.h; sourceTree = "<group>"; };
+		26D90CE0140D51290036A311 /* SkXMLAnimatorWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkXMLAnimatorWriter.cpp; sourceTree = "<group>"; };
+		26D90CE1140D51290036A311 /* SkXMLAnimatorWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkXMLAnimatorWriter.h; sourceTree = "<group>"; };
+		26D90CE3140D51290036A311 /* SkTypeface_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTypeface_mac.h; path = include/ports/SkTypeface_mac.h; sourceTree = "<group>"; };
+		26D90CE5140D51290036A311 /* Sk64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sk64.h; path = core/Sk64.h; sourceTree = "<group>"; };
+		26D90CE6140D51290036A311 /* SkAdvancedTypefaceMetrics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkAdvancedTypefaceMetrics.h; path = core/SkAdvancedTypefaceMetrics.h; sourceTree = "<group>"; };
+		26D90CE7140D51290036A311 /* SkAutoKern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkAutoKern.h; path = core/SkAutoKern.h; sourceTree = "<group>"; };
+		26D90CE8140D51290036A311 /* SkBitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmap.h; path = core/SkBitmap.h; sourceTree = "<group>"; };
+		26D90CE9140D51290036A311 /* SkBlitRow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBlitRow.h; path = core/SkBlitRow.h; sourceTree = "<group>"; };
+		26D90CEA140D51290036A311 /* SkBlitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBlitter.h; path = core/SkBlitter.h; sourceTree = "<group>"; };
+		26D90CEB140D51290036A311 /* SkBounder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBounder.h; path = core/SkBounder.h; sourceTree = "<group>"; };
+		26D90CEC140D51290036A311 /* SkBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBuffer.h; path = core/SkBuffer.h; sourceTree = "<group>"; };
+		26D90CED140D51290036A311 /* SkCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkCanvas.h; path = core/SkCanvas.h; sourceTree = "<group>"; };
+		26D90CEE140D51290036A311 /* SkChunkAlloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkChunkAlloc.h; path = core/SkChunkAlloc.h; sourceTree = "<group>"; };
+		26D90CEF140D51290036A311 /* SkClampRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkClampRange.h; path = core/SkClampRange.h; sourceTree = "<group>"; };
+		26D90CF0140D51290036A311 /* SkClipStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkClipStack.h; path = core/SkClipStack.h; sourceTree = "<group>"; };
+		26D90CF1140D51290036A311 /* SkColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkColor.h; path = core/SkColor.h; sourceTree = "<group>"; };
+		26D90CF2140D51290036A311 /* SkColorFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkColorFilter.h; path = core/SkColorFilter.h; sourceTree = "<group>"; };
+		26D90CF3140D51290036A311 /* SkColorPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkColorPriv.h; path = core/SkColorPriv.h; sourceTree = "<group>"; };
+		26D90CF4140D51290036A311 /* SkColorShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkColorShader.h; path = core/SkColorShader.h; sourceTree = "<group>"; };
+		26D90CF5140D51290036A311 /* SkComposeShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkComposeShader.h; path = core/SkComposeShader.h; sourceTree = "<group>"; };
+		26D90CF6140D51290036A311 /* SkDeque.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDeque.h; path = core/SkDeque.h; sourceTree = "<group>"; };
+		26D90CF7140D51290036A311 /* SkDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDescriptor.h; path = core/SkDescriptor.h; sourceTree = "<group>"; };
+		26D90CF8140D51290036A311 /* SkDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDevice.h; path = core/SkDevice.h; sourceTree = "<group>"; };
+		26D90CF9140D51290036A311 /* SkDither.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDither.h; path = core/SkDither.h; sourceTree = "<group>"; };
+		26D90CFA140D51290036A311 /* SkDraw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDraw.h; path = core/SkDraw.h; sourceTree = "<group>"; };
+		26D90CFB140D51290036A311 /* SkDrawFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDrawFilter.h; path = core/SkDrawFilter.h; sourceTree = "<group>"; };
+		26D90CFC140D51290036A311 /* SkDrawLooper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDrawLooper.h; path = core/SkDrawLooper.h; sourceTree = "<group>"; };
+		26D90CFD140D51290036A311 /* SkEndian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkEndian.h; path = core/SkEndian.h; sourceTree = "<group>"; };
+		26D90CFE140D51290036A311 /* SkFDot6.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFDot6.h; path = core/SkFDot6.h; sourceTree = "<group>"; };
+		26D90CFF140D51290036A311 /* SkFixed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFixed.h; path = core/SkFixed.h; sourceTree = "<group>"; };
+		26D90D00140D51290036A311 /* SkFlate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFlate.h; path = core/SkFlate.h; sourceTree = "<group>"; };
+		26D90D01140D51290036A311 /* SkFlattenable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFlattenable.h; path = core/SkFlattenable.h; sourceTree = "<group>"; };
+		26D90D02140D51290036A311 /* SkFloatBits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFloatBits.h; path = core/SkFloatBits.h; sourceTree = "<group>"; };
+		26D90D03140D51290036A311 /* SkFloatingPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFloatingPoint.h; path = core/SkFloatingPoint.h; sourceTree = "<group>"; };
+		26D90D04140D51290036A311 /* SkFontHost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFontHost.h; path = core/SkFontHost.h; sourceTree = "<group>"; };
+		26D90D05140D51290036A311 /* SkGeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkGeometry.h; path = core/SkGeometry.h; sourceTree = "<group>"; };
+		26D90D06140D51290036A311 /* SkGlobals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkGlobals.h; path = core/SkGlobals.h; sourceTree = "<group>"; };
+		26D90D07140D51290036A311 /* SkGraphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkGraphics.h; path = core/SkGraphics.h; sourceTree = "<group>"; };
+		26D90D08140D51290036A311 /* SkMMapStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMMapStream.h; path = core/SkMMapStream.h; sourceTree = "<group>"; };
+		26D90D09140D51290036A311 /* SkMallocPixelRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMallocPixelRef.h; path = core/SkMallocPixelRef.h; sourceTree = "<group>"; };
+		26D90D0A140D51290036A311 /* SkMask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMask.h; path = core/SkMask.h; sourceTree = "<group>"; };
+		26D90D0B140D51290036A311 /* SkMaskFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMaskFilter.h; path = core/SkMaskFilter.h; sourceTree = "<group>"; };
+		26D90D0C140D51290036A311 /* SkMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMath.h; path = core/SkMath.h; sourceTree = "<group>"; };
+		26D90D0D140D51290036A311 /* SkMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMatrix.h; path = core/SkMatrix.h; sourceTree = "<group>"; };
+		26D90D0E140D51290036A311 /* SkMetaData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMetaData.h; path = core/SkMetaData.h; sourceTree = "<group>"; };
+		26D90D0F140D51290036A311 /* SkOSFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkOSFile.h; path = core/SkOSFile.h; sourceTree = "<group>"; };
+		26D90D10140D51290036A311 /* SkPackBits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPackBits.h; path = core/SkPackBits.h; sourceTree = "<group>"; };
+		26D90D11140D51290036A311 /* SkPaint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPaint.h; path = core/SkPaint.h; sourceTree = "<group>"; };
+		26D90D12140D51290036A311 /* SkPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPath.h; path = core/SkPath.h; sourceTree = "<group>"; };
+		26D90D13140D51290036A311 /* SkPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPathEffect.h; path = core/SkPathEffect.h; sourceTree = "<group>"; };
+		26D90D14140D51290036A311 /* SkPathMeasure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPathMeasure.h; path = core/SkPathMeasure.h; sourceTree = "<group>"; };
+		26D90D15140D51290036A311 /* SkPerspIter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPerspIter.h; path = core/SkPerspIter.h; sourceTree = "<group>"; };
+		26D90D16140D51290036A311 /* SkPicture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPicture.h; path = core/SkPicture.h; sourceTree = "<group>"; };
+		26D90D17140D51290036A311 /* SkPixelRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPixelRef.h; path = core/SkPixelRef.h; sourceTree = "<group>"; };
+		26D90D18140D51290036A311 /* SkPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPoint.h; path = core/SkPoint.h; sourceTree = "<group>"; };
+		26D90D19140D51290036A311 /* SkPtrRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPtrRecorder.h; path = core/SkPtrRecorder.h; sourceTree = "<group>"; };
+		26D90D1A140D51290036A311 /* SkRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRandom.h; path = core/SkRandom.h; sourceTree = "<group>"; };
+		26D90D1B140D51290036A311 /* SkRasterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRasterizer.h; path = core/SkRasterizer.h; sourceTree = "<group>"; };
+		26D90D1C140D51290036A311 /* SkReader32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkReader32.h; path = core/SkReader32.h; sourceTree = "<group>"; };
+		26D90D1D140D51290036A311 /* SkRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRect.h; path = core/SkRect.h; sourceTree = "<group>"; };
+		26D90D1E140D51290036A311 /* SkRefCnt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRefCnt.h; path = core/SkRefCnt.h; sourceTree = "<group>"; };
+		26D90D1F140D51290036A311 /* SkRefDict.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRefDict.h; path = core/SkRefDict.h; sourceTree = "<group>"; };
+		26D90D20140D51290036A311 /* SkRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRegion.h; path = core/SkRegion.h; sourceTree = "<group>"; };
+		26D90D21140D51290036A311 /* SkScalar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkScalar.h; path = core/SkScalar.h; sourceTree = "<group>"; };
+		26D90D22140D51290036A311 /* SkScalarCompare.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkScalarCompare.h; path = core/SkScalarCompare.h; sourceTree = "<group>"; };
+		26D90D23140D51290036A311 /* SkScalerContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkScalerContext.h; path = core/SkScalerContext.h; sourceTree = "<group>"; };
+		26D90D24140D51290036A311 /* SkScan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkScan.h; path = core/SkScan.h; sourceTree = "<group>"; };
+		26D90D25140D51290036A311 /* SkShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkShader.h; path = core/SkShader.h; sourceTree = "<group>"; };
+		26D90D26140D51290036A311 /* SkStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkStream.h; path = core/SkStream.h; sourceTree = "<group>"; };
+		26D90D27140D51290036A311 /* SkString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkString.h; path = core/SkString.h; sourceTree = "<group>"; };
+		26D90D28140D51290036A311 /* SkStroke.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkStroke.h; path = core/SkStroke.h; sourceTree = "<group>"; };
+		26D90D29140D51290036A311 /* SkTDArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTDArray.h; path = core/SkTDArray.h; sourceTree = "<group>"; };
+		26D90D2A140D51290036A311 /* SkTDStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTDStack.h; path = core/SkTDStack.h; sourceTree = "<group>"; };
+		26D90D2B140D51290036A311 /* SkTDict.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTDict.h; path = core/SkTDict.h; sourceTree = "<group>"; };
+		26D90D2C140D51290036A311 /* SkTRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTRegistry.h; path = core/SkTRegistry.h; sourceTree = "<group>"; };
+		26D90D2D140D51290036A311 /* SkTScopedPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTScopedPtr.h; path = core/SkTScopedPtr.h; sourceTree = "<group>"; };
+		26D90D2E140D51290036A311 /* SkTSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTSearch.h; path = core/SkTSearch.h; sourceTree = "<group>"; };
+		26D90D2F140D51290036A311 /* SkTemplates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTemplates.h; path = core/SkTemplates.h; sourceTree = "<group>"; };
+		26D90D30140D51290036A311 /* SkThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkThread.h; path = core/SkThread.h; sourceTree = "<group>"; };
+		26D90D31140D51290036A311 /* SkThread_platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkThread_platform.h; path = core/SkThread_platform.h; sourceTree = "<group>"; };
+		26D90D32140D51290036A311 /* SkTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTime.h; path = core/SkTime.h; sourceTree = "<group>"; };
+		26D90D33140D51290036A311 /* SkTypeface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTypeface.h; path = core/SkTypeface.h; sourceTree = "<group>"; };
+		26D90D34140D51290036A311 /* SkTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTypes.h; path = core/SkTypes.h; sourceTree = "<group>"; };
+		26D90D35140D51290036A311 /* SkUnPreMultiply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUnPreMultiply.h; path = core/SkUnPreMultiply.h; sourceTree = "<group>"; };
+		26D90D36140D51290036A311 /* SkUnitMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUnitMapper.h; path = core/SkUnitMapper.h; sourceTree = "<group>"; };
+		26D90D37140D51290036A311 /* SkUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUtils.h; path = core/SkUtils.h; sourceTree = "<group>"; };
+		26D90D38140D51290036A311 /* SkWriter32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkWriter32.h; path = core/SkWriter32.h; sourceTree = "<group>"; };
+		26D90D39140D51290036A311 /* SkXfermode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkXfermode.h; path = core/SkXfermode.h; sourceTree = "<group>"; };
+		26D90D3A140D51290036A311 /* SkEdgeClipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkEdgeClipper.h; path = core/SkEdgeClipper.h; sourceTree = "<group>"; };
+		26D90D3B140D51290036A311 /* SkLineClipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkLineClipper.h; path = core/SkLineClipper.h; sourceTree = "<group>"; };
+		26D90D3C140D51290036A311 /* SkPostConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPostConfig.h; path = core/SkPostConfig.h; sourceTree = "<group>"; };
+		26D90D3D140D51290036A311 /* SkPreConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPreConfig.h; path = core/SkPreConfig.h; sourceTree = "<group>"; };
+		26D90D3E140D51290036A311 /* SkRelay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRelay.h; path = core/SkRelay.h; sourceTree = "<group>"; };
+		26D90D3F140D51290036A311 /* SkShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkShape.h; path = core/SkShape.h; sourceTree = "<group>"; };
+		26D90D40140D51290036A311 /* SkSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkSize.h; path = core/SkSize.h; sourceTree = "<group>"; };
+		26D90D41140D51290036A311 /* SkTLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTLazy.h; path = core/SkTLazy.h; sourceTree = "<group>"; };
+		26D90D42140D51290036A311 /* SkData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkData.h; path = core/SkData.h; sourceTree = "<group>"; };
+		26D90D44140D51290036A311 /* SkCGUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCGUtils.h; sourceTree = "<group>"; };
+		26D90D46140D51290036A311 /* SkData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkData.cpp; path = core/SkData.cpp; sourceTree = "<group>"; };
+		26D90D48140D51290036A311 /* ARGB32_Clamp_Bilinear_BitmapShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARGB32_Clamp_Bilinear_BitmapShader.h; sourceTree = "<group>"; };
+		26D90D49140D51290036A311 /* Sk64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sk64.cpp; sourceTree = "<group>"; };
+		26D90D4A140D51290036A311 /* SkAdvancedTypefaceMetrics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAdvancedTypefaceMetrics.cpp; sourceTree = "<group>"; };
+		26D90D4B140D51290036A311 /* SkAlphaRuns.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAlphaRuns.cpp; sourceTree = "<group>"; };
+		26D90D4C140D51290036A311 /* SkAntiRun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAntiRun.h; sourceTree = "<group>"; };
+		26D90D4D140D51290036A311 /* SkBitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmap.cpp; sourceTree = "<group>"; };
+		26D90D4E140D51290036A311 /* SkBitmapProcShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapProcShader.cpp; sourceTree = "<group>"; };
+		26D90D4F140D51290036A311 /* SkBitmapProcShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapProcShader.h; sourceTree = "<group>"; };
+		26D90D50140D51290036A311 /* SkBitmapProcState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapProcState.cpp; sourceTree = "<group>"; };
+		26D90D51140D51290036A311 /* SkBitmapProcState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapProcState.h; sourceTree = "<group>"; };
+		26D90D52140D51290036A311 /* SkBitmapProcState_matrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapProcState_matrix.h; sourceTree = "<group>"; };
+		26D90D53140D51290036A311 /* SkBitmapProcState_matrixProcs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapProcState_matrixProcs.cpp; sourceTree = "<group>"; };
+		26D90D54140D51290036A311 /* SkBitmapProcState_sample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapProcState_sample.h; sourceTree = "<group>"; };
+		26D90D55140D51290036A311 /* SkBitmapSampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapSampler.cpp; sourceTree = "<group>"; };
+		26D90D56140D51290036A311 /* SkBitmapSampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapSampler.h; sourceTree = "<group>"; };
+		26D90D57140D51290036A311 /* SkBitmapSamplerTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapSamplerTemplate.h; sourceTree = "<group>"; };
+		26D90D58140D51290036A311 /* SkBitmapShader16BilerpTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapShader16BilerpTemplate.h; sourceTree = "<group>"; };
+		26D90D59140D51290036A311 /* SkBitmapShaderTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapShaderTemplate.h; sourceTree = "<group>"; };
+		26D90D5A140D51290036A311 /* SkBitmap_scroll.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmap_scroll.cpp; sourceTree = "<group>"; };
+		26D90D5B140D51290036A311 /* SkBlitBWMaskTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBlitBWMaskTemplate.h; sourceTree = "<group>"; };
+		26D90D5C140D51290036A311 /* SkBlitRow_D16.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitRow_D16.cpp; sourceTree = "<group>"; };
+		26D90D5D140D51290036A311 /* SkBlitRow_D32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitRow_D32.cpp; sourceTree = "<group>"; };
+		26D90D5E140D51290036A311 /* SkBlitRow_D4444.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitRow_D4444.cpp; sourceTree = "<group>"; };
+		26D90D5F140D51290036A311 /* SkBlitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter.cpp; sourceTree = "<group>"; };
+		26D90D60140D51290036A311 /* SkBlitter_4444.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_4444.cpp; sourceTree = "<group>"; };
+		26D90D61140D51290036A311 /* SkBlitter_A1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_A1.cpp; sourceTree = "<group>"; };
+		26D90D62140D51290036A311 /* SkBlitter_A8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_A8.cpp; sourceTree = "<group>"; };
+		26D90D63140D51290036A311 /* SkBlitter_ARGB32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_ARGB32.cpp; sourceTree = "<group>"; };
+		26D90D64140D51290036A311 /* SkBlitter_RGB16.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_RGB16.cpp; sourceTree = "<group>"; };
+		26D90D65140D51290036A311 /* SkBlitter_Sprite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_Sprite.cpp; sourceTree = "<group>"; };
+		26D90D66140D51290036A311 /* SkBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBuffer.cpp; sourceTree = "<group>"; };
+		26D90D67140D51290036A311 /* SkCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCanvas.cpp; sourceTree = "<group>"; };
+		26D90D68140D51290036A311 /* SkChunkAlloc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkChunkAlloc.cpp; sourceTree = "<group>"; };
+		26D90D69140D51290036A311 /* SkClampRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkClampRange.cpp; sourceTree = "<group>"; };
+		26D90D6A140D51290036A311 /* SkClipStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkClipStack.cpp; sourceTree = "<group>"; };
+		26D90D6B140D51290036A311 /* SkColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColor.cpp; sourceTree = "<group>"; };
+		26D90D6C140D51290036A311 /* SkColorFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColorFilter.cpp; sourceTree = "<group>"; };
+		26D90D6D140D51290036A311 /* SkColorTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColorTable.cpp; sourceTree = "<group>"; };
+		26D90D6E140D51290036A311 /* SkComposeShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkComposeShader.cpp; sourceTree = "<group>"; };
+		26D90D6F140D51290036A311 /* SkConcaveToTriangles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkConcaveToTriangles.cpp; sourceTree = "<group>"; };
+		26D90D70140D51290036A311 /* SkConcaveToTriangles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkConcaveToTriangles.h; sourceTree = "<group>"; };
+		26D90D71140D51290036A311 /* SkCordic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCordic.cpp; sourceTree = "<group>"; };
+		26D90D72140D51290036A311 /* SkCordic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCordic.h; sourceTree = "<group>"; };
+		26D90D73140D51290036A311 /* SkCoreBlitters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCoreBlitters.h; sourceTree = "<group>"; };
+		26D90D74140D51290036A311 /* SkCubicClipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCubicClipper.cpp; sourceTree = "<group>"; };
+		26D90D75140D51290036A311 /* SkCubicClipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCubicClipper.h; sourceTree = "<group>"; };
+		26D90D76140D51290036A311 /* SkDebug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDebug.cpp; sourceTree = "<group>"; };
+		26D90D77140D51290036A311 /* SkDeque.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDeque.cpp; sourceTree = "<group>"; };
+		26D90D78140D51290036A311 /* SkDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDevice.cpp; sourceTree = "<group>"; };
+		26D90D79140D51290036A311 /* SkDither.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDither.cpp; sourceTree = "<group>"; };
+		26D90D7A140D51290036A311 /* SkDraw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDraw.cpp; sourceTree = "<group>"; };
+		26D90D7B140D51290036A311 /* SkDrawProcs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawProcs.h; sourceTree = "<group>"; };
+		26D90D7C140D51290036A311 /* SkEdge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEdge.cpp; sourceTree = "<group>"; };
+		26D90D7D140D51290036A311 /* SkEdge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEdge.h; sourceTree = "<group>"; };
+		26D90D7E140D51290036A311 /* SkEdgeBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEdgeBuilder.cpp; sourceTree = "<group>"; };
+		26D90D7F140D51290036A311 /* SkEdgeClipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEdgeClipper.cpp; sourceTree = "<group>"; };
+		26D90D80140D51290036A311 /* SkFP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkFP.h; sourceTree = "<group>"; };
+		26D90D81140D51290036A311 /* SkFilterProc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFilterProc.cpp; sourceTree = "<group>"; };
+		26D90D82140D51290036A311 /* SkFilterProc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkFilterProc.h; sourceTree = "<group>"; };
+		26D90D83140D51290036A311 /* SkFlate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFlate.cpp; sourceTree = "<group>"; };
+		26D90D84140D51290036A311 /* SkFlattenable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFlattenable.cpp; sourceTree = "<group>"; };
+		26D90D85140D51290036A311 /* SkFloat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFloat.cpp; sourceTree = "<group>"; };
+		26D90D86140D51290036A311 /* SkFloat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkFloat.h; sourceTree = "<group>"; };
+		26D90D87140D51290036A311 /* SkFloatBits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFloatBits.cpp; sourceTree = "<group>"; };
+		26D90D88140D51290036A311 /* SkFontHost.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFontHost.cpp; sourceTree = "<group>"; };
+		26D90D89140D51290036A311 /* SkGeometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGeometry.cpp; sourceTree = "<group>"; };
+		26D90D8A140D51290036A311 /* SkGlobals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGlobals.cpp; sourceTree = "<group>"; };
+		26D90D8B140D51290036A311 /* SkGlyphCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGlyphCache.cpp; sourceTree = "<group>"; };
+		26D90D8C140D51290036A311 /* SkGlyphCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGlyphCache.h; sourceTree = "<group>"; };
+		26D90D8D140D51290036A311 /* SkGraphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGraphics.cpp; sourceTree = "<group>"; };
+		26D90D8E140D51290036A311 /* SkLineClipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkLineClipper.cpp; sourceTree = "<group>"; };
+		26D90D8F140D51290036A311 /* SkMMapStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMMapStream.cpp; sourceTree = "<group>"; };
+		26D90D90140D51290036A311 /* SkMallocPixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMallocPixelRef.cpp; sourceTree = "<group>"; };
+		26D90D91140D51290036A311 /* SkMask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMask.cpp; sourceTree = "<group>"; };
+		26D90D92140D51290036A311 /* SkMaskFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMaskFilter.cpp; sourceTree = "<group>"; };
+		26D90D93140D51290036A311 /* SkMath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMath.cpp; sourceTree = "<group>"; };
+		26D90D94140D51290036A311 /* SkMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMatrix.cpp; sourceTree = "<group>"; };
+		26D90D95140D51290036A311 /* SkMetaData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMetaData.cpp; sourceTree = "<group>"; };
+		26D90D96140D51290036A311 /* SkPackBits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPackBits.cpp; sourceTree = "<group>"; };
+		26D90D97140D51290036A311 /* SkPaint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPaint.cpp; sourceTree = "<group>"; };
+		26D90D98140D51290036A311 /* SkPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPath.cpp; sourceTree = "<group>"; };
+		26D90D99140D51290036A311 /* SkPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPathEffect.cpp; sourceTree = "<group>"; };
+		26D90D9A140D51290036A311 /* SkPathHeap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPathHeap.cpp; sourceTree = "<group>"; };
+		26D90D9B140D51290036A311 /* SkPathHeap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPathHeap.h; sourceTree = "<group>"; };
+		26D90D9C140D51290036A311 /* SkPathMeasure.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPathMeasure.cpp; sourceTree = "<group>"; };
+		26D90D9D140D51290036A311 /* SkPicture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPicture.cpp; sourceTree = "<group>"; };
+		26D90D9E140D51290036A311 /* SkPictureFlat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPictureFlat.cpp; sourceTree = "<group>"; };
+		26D90D9F140D51290036A311 /* SkPictureFlat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPictureFlat.h; sourceTree = "<group>"; };
+		26D90DA0140D51290036A311 /* SkPicturePlayback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPicturePlayback.cpp; sourceTree = "<group>"; };
+		26D90DA1140D51290036A311 /* SkPicturePlayback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPicturePlayback.h; sourceTree = "<group>"; };
+		26D90DA2140D51290036A311 /* SkPictureRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPictureRecord.cpp; sourceTree = "<group>"; };
+		26D90DA3140D51290036A311 /* SkPictureRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPictureRecord.h; sourceTree = "<group>"; };
+		26D90DA4140D51290036A311 /* SkPixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPixelRef.cpp; sourceTree = "<group>"; };
+		26D90DA5140D51290036A311 /* SkPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPoint.cpp; sourceTree = "<group>"; };
+		26D90DA6140D51290036A311 /* SkProcSpriteBlitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkProcSpriteBlitter.cpp; sourceTree = "<group>"; };
+		26D90DA7140D51290036A311 /* SkPtrRecorder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPtrRecorder.cpp; sourceTree = "<group>"; };
+		26D90DA8140D51290036A311 /* SkQuadClipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkQuadClipper.cpp; sourceTree = "<group>"; };
+		26D90DA9140D51290036A311 /* SkQuadClipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkQuadClipper.h; sourceTree = "<group>"; };
+		26D90DAA140D51290036A311 /* SkRasterizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRasterizer.cpp; sourceTree = "<group>"; };
+		26D90DAB140D51290036A311 /* SkRect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRect.cpp; sourceTree = "<group>"; };
+		26D90DAC140D51290036A311 /* SkRefDict.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRefDict.cpp; sourceTree = "<group>"; };
+		26D90DAD140D51290036A311 /* SkRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRegion.cpp; sourceTree = "<group>"; };
+		26D90DAE140D51290036A311 /* SkRegionPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkRegionPriv.h; sourceTree = "<group>"; };
+		26D90DAF140D51290036A311 /* SkRegion_path.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRegion_path.cpp; sourceTree = "<group>"; };
+		26D90DB0140D51290036A311 /* SkScalar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScalar.cpp; sourceTree = "<group>"; };
+		26D90DB1140D51290036A311 /* SkScalerContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScalerContext.cpp; sourceTree = "<group>"; };
+		26D90DB2140D51290036A311 /* SkScan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScan.cpp; sourceTree = "<group>"; };
+		26D90DB3140D51290036A311 /* SkScanPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScanPriv.h; sourceTree = "<group>"; };
+		26D90DB4140D51290036A311 /* SkScan_AntiPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScan_AntiPath.cpp; sourceTree = "<group>"; };
+		26D90DB5140D51290036A311 /* SkScan_Antihair.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScan_Antihair.cpp; sourceTree = "<group>"; };
+		26D90DB6140D51290036A311 /* SkScan_Hairline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScan_Hairline.cpp; sourceTree = "<group>"; };
+		26D90DB7140D51290036A311 /* SkScan_Path.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScan_Path.cpp; sourceTree = "<group>"; };
+		26D90DB8140D51290036A311 /* SkShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkShader.cpp; sourceTree = "<group>"; };
+		26D90DB9140D51290036A311 /* SkShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkShape.cpp; sourceTree = "<group>"; };
+		26D90DBA140D51290036A311 /* SkSinTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSinTable.h; sourceTree = "<group>"; };
+		26D90DBB140D51290036A311 /* SkSpriteBlitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSpriteBlitter.h; sourceTree = "<group>"; };
+		26D90DBC140D51290036A311 /* SkSpriteBlitterTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSpriteBlitterTemplate.h; sourceTree = "<group>"; };
+		26D90DBD140D51290036A311 /* SkSpriteBlitter_ARGB32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSpriteBlitter_ARGB32.cpp; sourceTree = "<group>"; };
+		26D90DBE140D51290036A311 /* SkSpriteBlitter_RGB16.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSpriteBlitter_RGB16.cpp; sourceTree = "<group>"; };
+		26D90DBF140D51290036A311 /* SkStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkStream.cpp; sourceTree = "<group>"; };
+		26D90DC0140D51290036A311 /* SkString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkString.cpp; sourceTree = "<group>"; };
+		26D90DC1140D51290036A311 /* SkStroke.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkStroke.cpp; sourceTree = "<group>"; };
+		26D90DC2140D51290036A311 /* SkStrokerPriv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkStrokerPriv.cpp; sourceTree = "<group>"; };
+		26D90DC3140D51290036A311 /* SkStrokerPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkStrokerPriv.h; sourceTree = "<group>"; };
+		26D90DC4140D51290036A311 /* SkTSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTSearch.cpp; sourceTree = "<group>"; };
+		26D90DC5140D51290036A311 /* SkTSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTSort.h; sourceTree = "<group>"; };
+		26D90DC6140D51290036A311 /* SkTemplatesPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTemplatesPriv.h; sourceTree = "<group>"; };
+		26D90DC7140D51290036A311 /* SkTextFormatParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTextFormatParams.h; sourceTree = "<group>"; };
+		26D90DC8140D51290036A311 /* SkTypeface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTypeface.cpp; sourceTree = "<group>"; };
+		26D90DC9140D51290036A311 /* SkTypefaceCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTypefaceCache.cpp; sourceTree = "<group>"; };
+		26D90DCA140D51290036A311 /* SkTypefaceCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTypefaceCache.h; sourceTree = "<group>"; };
+		26D90DCB140D51290036A311 /* SkUnPreMultiply.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkUnPreMultiply.cpp; sourceTree = "<group>"; };
+		26D90DCC140D51290036A311 /* SkUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkUtils.cpp; sourceTree = "<group>"; };
+		26D90DCD140D51290036A311 /* SkWriter32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkWriter32.cpp; sourceTree = "<group>"; };
+		26D90DCE140D51290036A311 /* SkXfermode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkXfermode.cpp; sourceTree = "<group>"; };
+		26D90DD2140D51290036A311 /* SkDebug_stdio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDebug_stdio.cpp; sourceTree = "<group>"; };
+		26D90DD3140D51290036A311 /* SkFontHost_mac_coretext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFontHost_mac_coretext.cpp; sourceTree = "<group>"; };
+		26D90DD4140D51290036A311 /* SkGlobals_global.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGlobals_global.cpp; sourceTree = "<group>"; };
+		26D90DD5140D51290036A311 /* SkMemory_malloc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMemory_malloc.cpp; sourceTree = "<group>"; };
+		26D90DD6140D51290036A311 /* SkThread_pthread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkThread_pthread.cpp; sourceTree = "<group>"; };
+		26D90DD7140D51290036A311 /* SkTime_Unix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTime_Unix.cpp; sourceTree = "<group>"; };
+		26D90DD8140D51290036A311 /* SkXMLParser_empty.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkXMLParser_empty.cpp; sourceTree = "<group>"; };
+		26D90DD9140D51290036A311 /* sk_predefined_gamma.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sk_predefined_gamma.h; sourceTree = "<group>"; };
+		26D90DDC140D51290036A311 /* Sk1DPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sk1DPathEffect.h; sourceTree = "<group>"; };
+		26D90DDD140D51290036A311 /* Sk2DPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sk2DPathEffect.h; sourceTree = "<group>"; };
+		26D90DDE140D51290036A311 /* SkAvoidXfermode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAvoidXfermode.h; sourceTree = "<group>"; };
+		26D90DDF140D51290036A311 /* SkBlurDrawLooper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBlurDrawLooper.h; sourceTree = "<group>"; };
+		26D90DE0140D51290036A311 /* SkBlurMaskFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBlurMaskFilter.h; sourceTree = "<group>"; };
+		26D90DE1140D51290036A311 /* SkColorMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkColorMatrix.h; sourceTree = "<group>"; };
+		26D90DE2140D51290036A311 /* SkColorMatrixFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkColorMatrixFilter.h; sourceTree = "<group>"; };
+		26D90DE3140D51290036A311 /* SkCornerPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCornerPathEffect.h; sourceTree = "<group>"; };
+		26D90DE4140D51290036A311 /* SkDashPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDashPathEffect.h; sourceTree = "<group>"; };
+		26D90DE5140D51290036A311 /* SkDiscretePathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDiscretePathEffect.h; sourceTree = "<group>"; };
+		26D90DE6140D51290036A311 /* SkDrawExtraPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawExtraPathEffect.h; sourceTree = "<group>"; };
+		26D90DE7140D51290036A311 /* SkEmbossMaskFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEmbossMaskFilter.h; sourceTree = "<group>"; };
+		26D90DE8140D51290036A311 /* SkGradientShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGradientShader.h; sourceTree = "<group>"; };
+		26D90DE9140D51290036A311 /* SkGroupShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGroupShape.h; sourceTree = "<group>"; };
+		26D90DEA140D51290036A311 /* SkKernel33MaskFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkKernel33MaskFilter.h; sourceTree = "<group>"; };
+		26D90DEB140D51290036A311 /* SkLayerDrawLooper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkLayerDrawLooper.h; sourceTree = "<group>"; };
+		26D90DEC140D51290036A311 /* SkLayerRasterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkLayerRasterizer.h; sourceTree = "<group>"; };
+		26D90DED140D51290036A311 /* SkPaintFlagsDrawFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPaintFlagsDrawFilter.h; sourceTree = "<group>"; };
+		26D90DEE140D51290036A311 /* SkPixelXorXfermode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPixelXorXfermode.h; sourceTree = "<group>"; };
+		26D90DEF140D51290036A311 /* SkPorterDuff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPorterDuff.h; sourceTree = "<group>"; };
+		26D90DF0140D51290036A311 /* SkRectShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkRectShape.h; sourceTree = "<group>"; };
+		26D90DF1140D51290036A311 /* SkTransparentShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTransparentShader.h; sourceTree = "<group>"; };
+		26D90DF3140D51290036A311 /* Sk1DPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sk1DPathEffect.cpp; sourceTree = "<group>"; };
+		26D90DF4140D51290036A311 /* Sk2DPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sk2DPathEffect.cpp; sourceTree = "<group>"; };
+		26D90DF5140D51290036A311 /* SkAvoidXfermode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAvoidXfermode.cpp; sourceTree = "<group>"; };
+		26D90DF6140D51290036A311 /* SkBitmapCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapCache.cpp; sourceTree = "<group>"; };
+		26D90DF7140D51290036A311 /* SkBitmapCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapCache.h; sourceTree = "<group>"; };
+		26D90DF8140D51290036A311 /* SkBlurDrawLooper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlurDrawLooper.cpp; sourceTree = "<group>"; };
+		26D90DF9140D51290036A311 /* SkBlurMask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlurMask.cpp; sourceTree = "<group>"; };
+		26D90DFA140D51290036A311 /* SkBlurMask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBlurMask.h; sourceTree = "<group>"; };
+		26D90DFB140D51290036A311 /* SkBlurMaskFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlurMaskFilter.cpp; sourceTree = "<group>"; };
+		26D90DFC140D51290036A311 /* SkColorFilters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColorFilters.cpp; sourceTree = "<group>"; };
+		26D90DFD140D51290036A311 /* SkColorMatrixFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColorMatrixFilter.cpp; sourceTree = "<group>"; };
+		26D90DFE140D51290036A311 /* SkCornerPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCornerPathEffect.cpp; sourceTree = "<group>"; };
+		26D90DFF140D51290036A311 /* SkDashPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDashPathEffect.cpp; sourceTree = "<group>"; };
+		26D90E00140D51290036A311 /* SkDiscretePathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDiscretePathEffect.cpp; sourceTree = "<group>"; };
+		26D90E01140D51290036A311 /* SkEmbossMask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEmbossMask.cpp; sourceTree = "<group>"; };
+		26D90E02140D51290036A311 /* SkEmbossMask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEmbossMask.h; sourceTree = "<group>"; };
+		26D90E03140D51290036A311 /* SkEmbossMaskFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEmbossMaskFilter.cpp; sourceTree = "<group>"; };
+		26D90E04140D51290036A311 /* SkEmbossMask_Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEmbossMask_Table.h; sourceTree = "<group>"; };
+		26D90E05140D51290036A311 /* SkGradientShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGradientShader.cpp; sourceTree = "<group>"; };
+		26D90E06140D51290036A311 /* SkGroupShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGroupShape.cpp; sourceTree = "<group>"; };
+		26D90E07140D51290036A311 /* SkKernel33MaskFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkKernel33MaskFilter.cpp; sourceTree = "<group>"; };
+		26D90E08140D51290036A311 /* SkLayerDrawLooper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkLayerDrawLooper.cpp; sourceTree = "<group>"; };
+		26D90E09140D51290036A311 /* SkLayerRasterizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkLayerRasterizer.cpp; sourceTree = "<group>"; };
+		26D90E0A140D51290036A311 /* SkPaintFlagsDrawFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPaintFlagsDrawFilter.cpp; sourceTree = "<group>"; };
+		26D90E0B140D51290036A311 /* SkPixelXorXfermode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPixelXorXfermode.cpp; sourceTree = "<group>"; };
+		26D90E0C140D51290036A311 /* SkPorterDuff.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPorterDuff.cpp; sourceTree = "<group>"; };
+		26D90E0D140D51290036A311 /* SkRadialGradient_Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkRadialGradient_Table.h; sourceTree = "<group>"; };
+		26D90E0E140D51290036A311 /* SkRectShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRectShape.cpp; sourceTree = "<group>"; };
+		26D90E0F140D51290036A311 /* SkTransparentShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTransparentShader.cpp; sourceTree = "<group>"; };
+		26D90E13140D51290036A311 /* FlingState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlingState.h; sourceTree = "<group>"; };
+		26D90E14140D51290036A311 /* GrGLDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLDefines.h; sourceTree = "<group>"; };
+		26D90E15140D51290036A311 /* GrTemplates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTemplates.h; sourceTree = "<group>"; };
+		26D90E16140D51290036A311 /* GrAllocPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrAllocPool.h; sourceTree = "<group>"; };
+		26D90E17140D51290036A311 /* GrAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrAllocator.h; sourceTree = "<group>"; };
+		26D90E18140D51290036A311 /* GrAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrAtlas.h; sourceTree = "<group>"; };
+		26D90E19140D51290036A311 /* GrClip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrClip.h; sourceTree = "<group>"; };
+		26D90E1A140D51290036A311 /* GrClipIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrClipIterator.h; sourceTree = "<group>"; };
+		26D90E1B140D51290036A311 /* GrColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrColor.h; sourceTree = "<group>"; };
+		26D90E1C140D51290036A311 /* GrConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrConfig.h; sourceTree = "<group>"; };
+		26D90E1D140D51290036A311 /* GrContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrContext.h; sourceTree = "<group>"; };
+		26D90E1E140D51290036A311 /* GrContext_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrContext_impl.h; sourceTree = "<group>"; };
+		26D90E1F140D51290036A311 /* GrDrawTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrDrawTarget.h; sourceTree = "<group>"; };
+		26D90E20140D51290036A311 /* GrFontScaler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrFontScaler.h; sourceTree = "<group>"; };
+		26D90E21140D51290036A311 /* GrGLConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLConfig.h; sourceTree = "<group>"; };
+		26D90E22140D51290036A311 /* GrGLConfig_chrome.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLConfig_chrome.h; sourceTree = "<group>"; };
+		26D90E23140D51290036A311 /* GrGLInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLInterface.h; sourceTree = "<group>"; };
+		26D90E24140D51290036A311 /* GrGeometryBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGeometryBuffer.h; sourceTree = "<group>"; };
+		26D90E25140D51290036A311 /* GrGlyph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGlyph.h; sourceTree = "<group>"; };
+		26D90E26140D51290036A311 /* GrGpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGpu.h; sourceTree = "<group>"; };
+		26D90E27140D51290036A311 /* GrGpuVertex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGpuVertex.h; sourceTree = "<group>"; };
+		26D90E28140D51290036A311 /* GrIPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrIPoint.h; sourceTree = "<group>"; };
+		26D90E29140D51290036A311 /* GrInOrderDrawBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrInOrderDrawBuffer.h; sourceTree = "<group>"; };
+		26D90E2A140D51290036A311 /* GrIndexBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrIndexBuffer.h; sourceTree = "<group>"; };
+		26D90E2B140D51290036A311 /* GrInstanceCounter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrInstanceCounter.h; sourceTree = "<group>"; };
+		26D90E2C140D51290036A311 /* GrKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrKey.h; sourceTree = "<group>"; };
+		26D90E2D140D51290036A311 /* GrMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrMatrix.h; sourceTree = "<group>"; };
+		26D90E2E140D51290036A311 /* GrMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrMesh.h; sourceTree = "<group>"; };
+		26D90E2F140D51290036A311 /* GrNoncopyable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrNoncopyable.h; sourceTree = "<group>"; };
+		26D90E30140D51290036A311 /* GrPaint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPaint.h; sourceTree = "<group>"; };
+		26D90E31140D51290036A311 /* GrPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPath.h; sourceTree = "<group>"; };
+		26D90E32140D51290036A311 /* GrPathRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPathRenderer.h; sourceTree = "<group>"; };
+		26D90E33140D51290036A311 /* GrPathSink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPathSink.h; sourceTree = "<group>"; };
+		26D90E34140D51290036A311 /* GrPlotMgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPlotMgr.h; sourceTree = "<group>"; };
+		26D90E35140D51290036A311 /* GrPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPoint.h; sourceTree = "<group>"; };
+		26D90E36140D51290036A311 /* GrRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrRandom.h; sourceTree = "<group>"; };
+		26D90E37140D51290036A311 /* GrRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrRect.h; sourceTree = "<group>"; };
+		26D90E38140D51290036A311 /* GrRectanizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrRectanizer.h; sourceTree = "<group>"; };
+		26D90E39140D51290036A311 /* GrRefCnt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrRefCnt.h; sourceTree = "<group>"; };
+		26D90E3A140D51290036A311 /* GrResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrResource.h; sourceTree = "<group>"; };
+		26D90E3B140D51290036A311 /* GrSamplerState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrSamplerState.h; sourceTree = "<group>"; };
+		26D90E3C140D51290036A311 /* GrScalar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrScalar.h; sourceTree = "<group>"; };
+		26D90E3D140D51290036A311 /* GrStencil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrStencil.h; sourceTree = "<group>"; };
+		26D90E3E140D51290036A311 /* GrStopwatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrStopwatch.h; sourceTree = "<group>"; };
+		26D90E3F140D51290036A311 /* GrStringBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrStringBuilder.h; sourceTree = "<group>"; };
+		26D90E40140D51290036A311 /* GrTArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTArray.h; sourceTree = "<group>"; };
+		26D90E41140D51290036A311 /* GrTBSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTBSearch.h; sourceTree = "<group>"; };
+		26D90E42140D51290036A311 /* GrTDArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTDArray.h; sourceTree = "<group>"; };
+		26D90E43140D51290036A311 /* GrTHashCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTHashCache.h; sourceTree = "<group>"; };
+		26D90E44140D51290036A311 /* GrTLList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTLList.h; sourceTree = "<group>"; };
+		26D90E45140D51290036A311 /* GrTesselatedPathRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTesselatedPathRenderer.h; sourceTree = "<group>"; };
+		26D90E46140D51290036A311 /* GrTextContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTextContext.h; sourceTree = "<group>"; };
+		26D90E47140D51290036A311 /* GrTextStrike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTextStrike.h; sourceTree = "<group>"; };
+		26D90E48140D51290036A311 /* GrTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTexture.h; sourceTree = "<group>"; };
+		26D90E49140D51290036A311 /* GrTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTypes.h; sourceTree = "<group>"; };
+		26D90E4A140D51290036A311 /* GrUserConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrUserConfig.h; sourceTree = "<group>"; };
+		26D90E4B140D51290036A311 /* GrVertexBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrVertexBuffer.h; sourceTree = "<group>"; };
+		26D90E4D140D51290036A311 /* GrAddPathRenderers_none.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrAddPathRenderers_none.cpp; sourceTree = "<group>"; };
+		26D90E4E140D51290036A311 /* GrDefaultPathRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrDefaultPathRenderer.cpp; sourceTree = "<group>"; };
+		26D90E4F140D51290036A311 /* GrDefaultPathRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrDefaultPathRenderer.h; sourceTree = "<group>"; };
+		26D90E50140D51290036A311 /* GrGLStencilBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLStencilBuffer.cpp; sourceTree = "<group>"; };
+		26D90E51140D51290036A311 /* GrGLStencilBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLStencilBuffer.h; sourceTree = "<group>"; };
+		26D90E52140D51290036A311 /* GrPathRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPathRenderer.h; sourceTree = "<group>"; };
+		26D90E53140D51290036A311 /* GrPathRendererChain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrPathRendererChain.cpp; sourceTree = "<group>"; };
+		26D90E54140D51290036A311 /* GrPathRendererChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPathRendererChain.h; sourceTree = "<group>"; };
+		26D90E55140D51290036A311 /* GrStencilBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrStencilBuffer.cpp; sourceTree = "<group>"; };
+		26D90E56140D51290036A311 /* GrRenderTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrRenderTarget.cpp; sourceTree = "<group>"; };
+		26D90E57140D51290036A311 /* GrGLRenderTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLRenderTarget.cpp; sourceTree = "<group>"; };
+		26D90E58140D51290036A311 /* GrGLRenderTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLRenderTarget.h; sourceTree = "<group>"; };
+		26D90E59140D51290036A311 /* GrGLTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLTexture.h; sourceTree = "<group>"; };
+		26D90E5A140D51290036A311 /* GrResourceCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrResourceCache.cpp; sourceTree = "<group>"; };
+		26D90E5B140D51290036A311 /* GrResourceCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrResourceCache.h; sourceTree = "<group>"; };
+		26D90E5C140D51290036A311 /* FlingState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FlingState.cpp; sourceTree = "<group>"; };
+		26D90E5D140D51290036A311 /* GrDrawMesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrDrawMesh.cpp; sourceTree = "<group>"; };
+		26D90E5E140D51290036A311 /* GrAllocPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrAllocPool.cpp; sourceTree = "<group>"; };
+		26D90E5F140D51290036A311 /* GrAtlas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrAtlas.cpp; sourceTree = "<group>"; };
+		26D90E60140D51290036A311 /* GrBinHashKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrBinHashKey.h; sourceTree = "<group>"; };
+		26D90E61140D51290036A311 /* GrBufferAllocPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrBufferAllocPool.cpp; sourceTree = "<group>"; };
+		26D90E62140D51290036A311 /* GrBufferAllocPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrBufferAllocPool.h; sourceTree = "<group>"; };
+		26D90E63140D51290036A311 /* GrClip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrClip.cpp; sourceTree = "<group>"; };
+		26D90E64140D51290036A311 /* GrContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrContext.cpp; sourceTree = "<group>"; };
+		26D90E65140D51290036A311 /* GrDrawTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrDrawTarget.cpp; sourceTree = "<group>"; };
+		26D90E66140D51290036A311 /* GrGLDefaultInterface_none.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLDefaultInterface_none.cpp; sourceTree = "<group>"; };
+		26D90E67140D51290036A311 /* GrGLIndexBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLIndexBuffer.cpp; sourceTree = "<group>"; };
+		26D90E68140D51290036A311 /* GrGLInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLInterface.cpp; sourceTree = "<group>"; };
+		26D90E69140D51290036A311 /* GrGLProgram.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLProgram.cpp; sourceTree = "<group>"; };
+		26D90E6A140D51290036A311 /* GrGLProgram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLProgram.h; sourceTree = "<group>"; };
+		26D90E6B140D51290036A311 /* GrGLTexture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLTexture.cpp; sourceTree = "<group>"; };
+		26D90E6C140D51290036A311 /* GrGLUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLUtil.cpp; sourceTree = "<group>"; };
+		26D90E6D140D51290036A311 /* GrGLVertexBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLVertexBuffer.cpp; sourceTree = "<group>"; };
+		26D90E6E140D51290036A311 /* GrGpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGpu.cpp; sourceTree = "<group>"; };
+		26D90E6F140D51290036A311 /* GrGpuFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGpuFactory.cpp; sourceTree = "<group>"; };
+		26D90E70140D51290036A311 /* GrGpuGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGpuGL.cpp; sourceTree = "<group>"; };
+		26D90E71140D51290036A311 /* GrGpuGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGpuGL.h; sourceTree = "<group>"; };
+		26D90E72140D51290036A311 /* GrGpuGLFixed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGpuGLFixed.cpp; sourceTree = "<group>"; };
+		26D90E73140D51290036A311 /* GrGpuGLFixed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGpuGLFixed.h; sourceTree = "<group>"; };
+		26D90E74140D51290036A311 /* GrGpuGLShaders.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGpuGLShaders.cpp; sourceTree = "<group>"; };
+		26D90E75140D51290036A311 /* GrGpuGLShaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGpuGLShaders.h; sourceTree = "<group>"; };
+		26D90E76140D51290036A311 /* GrInOrderDrawBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrInOrderDrawBuffer.cpp; sourceTree = "<group>"; };
+		26D90E77140D51290036A311 /* GrMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrMatrix.cpp; sourceTree = "<group>"; };
+		26D90E78140D51290036A311 /* GrMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrMemory.cpp; sourceTree = "<group>"; };
+		26D90E79140D51290036A311 /* GrPathRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrPathRenderer.cpp; sourceTree = "<group>"; };
+		26D90E7A140D51290036A311 /* GrPathUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrPathUtils.cpp; sourceTree = "<group>"; };
+		26D90E7B140D51290036A311 /* GrPathUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPathUtils.h; sourceTree = "<group>"; };
+		26D90E7C140D51290036A311 /* GrRectanizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrRectanizer.cpp; sourceTree = "<group>"; };
+		26D90E7D140D51290036A311 /* GrRedBlackTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrRedBlackTree.h; sourceTree = "<group>"; };
+		26D90E7E140D51290036A311 /* GrResource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrResource.cpp; sourceTree = "<group>"; };
+		26D90E7F140D51290036A311 /* GrStencil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrStencil.cpp; sourceTree = "<group>"; };
+		26D90E81140D51290036A311 /* GrTextContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrTextContext.cpp; sourceTree = "<group>"; };
+		26D90E82140D51290036A311 /* GrTextStrike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrTextStrike.cpp; sourceTree = "<group>"; };
+		26D90E83140D51290036A311 /* GrTextStrike_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTextStrike_impl.h; sourceTree = "<group>"; };
+		26D90E84140D51290036A311 /* GrTexture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrTexture.cpp; sourceTree = "<group>"; };
+		26D90E85140D51290036A311 /* gr_unittests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gr_unittests.cpp; sourceTree = "<group>"; };
+		26D90E87140D51290036A311 /* SkGpuCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGpuCanvas.h; sourceTree = "<group>"; };
+		26D90E88140D51290036A311 /* SkGpuDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGpuDevice.h; sourceTree = "<group>"; };
+		26D90E89140D51290036A311 /* SkGr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGr.h; sourceTree = "<group>"; };
+		26D90E8A140D51290036A311 /* SkGrTexturePixelRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGrTexturePixelRef.h; sourceTree = "<group>"; };
+		26D90E8C140D51290036A311 /* GrPrintf_skia.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrPrintf_skia.cpp; sourceTree = "<group>"; };
+		26D90E8D140D51290036A311 /* SkGpuCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGpuCanvas.cpp; sourceTree = "<group>"; };
+		26D90E8E140D51290036A311 /* SkGpuDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGpuDevice.cpp; sourceTree = "<group>"; };
+		26D90E8F140D51290036A311 /* SkGr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGr.cpp; sourceTree = "<group>"; };
+		26D90E90140D51290036A311 /* SkGrFontScaler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGrFontScaler.cpp; sourceTree = "<group>"; };
+		26D90E91140D51290036A311 /* SkGrTexturePixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGrTexturePixelRef.cpp; sourceTree = "<group>"; };
+		26D90E94140D51290036A311 /* SkFlipPixelRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkFlipPixelRef.h; sourceTree = "<group>"; };
+		26D90E95140D51290036A311 /* SkImageRef_GlobalPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageRef_GlobalPool.h; sourceTree = "<group>"; };
+		26D90E96140D51290036A311 /* SkImageRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageRef.h; sourceTree = "<group>"; };
+		26D90E97140D51290036A311 /* SkJpegUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkJpegUtility.h; sourceTree = "<group>"; };
+		26D90E98140D51290036A311 /* SkMovie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkMovie.h; sourceTree = "<group>"; };
+		26D90E99140D51290036A311 /* SkPageFlipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPageFlipper.h; sourceTree = "<group>"; };
+		26D90E9A140D51290036A311 /* SkImageDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageDecoder.h; sourceTree = "<group>"; };
+		26D90E9B140D51290036A311 /* SkImageEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageEncoder.h; sourceTree = "<group>"; };
+		26D90E9E140D51290036A311 /* SkMovie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMovie.cpp; sourceTree = "<group>"; };
+		26D90E9F140D51290036A311 /* SkBitmap_RLEPixels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmap_RLEPixels.h; sourceTree = "<group>"; };
+		26D90EA0140D51290036A311 /* SkCreateRLEPixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCreateRLEPixelRef.cpp; sourceTree = "<group>"; };
+		26D90EA1140D51290036A311 /* SkFDStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFDStream.cpp; sourceTree = "<group>"; };
+		26D90EA2140D51290036A311 /* SkFlipPixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFlipPixelRef.cpp; sourceTree = "<group>"; };
+		26D90EA3140D51290036A311 /* SkImageDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageDecoder.cpp; sourceTree = "<group>"; };
+		26D90EA4140D51290036A311 /* SkImageEncoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageEncoder.cpp; sourceTree = "<group>"; };
+		26D90EA5140D51290036A311 /* SkImageDecoder_Factory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageDecoder_Factory.cpp; sourceTree = "<group>"; };
+		26D90EA6140D51290036A311 /* SkImageEncoder_Factory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageEncoder_Factory.cpp; sourceTree = "<group>"; };
+		26D90EA7140D51290036A311 /* SkPageFlipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPageFlipper.cpp; sourceTree = "<group>"; };
+		26D90EA8140D51290036A311 /* SkImageRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageRef.cpp; sourceTree = "<group>"; };
+		26D90EA9140D51290036A311 /* SkImageRefPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageRefPool.cpp; sourceTree = "<group>"; };
+		26D90EAA140D51290036A311 /* SkImageRefPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageRefPool.h; sourceTree = "<group>"; };
+		26D90EAB140D51290036A311 /* SkImageRef_GlobalPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageRef_GlobalPool.cpp; sourceTree = "<group>"; };
+		26D90EB1140D51290036A311 /* SkBitmapProcState_opts_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapProcState_opts_arm.cpp; sourceTree = "<group>"; };
+		26D90EB6140D51290036A311 /* SkBlitRow_opts_SSE2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBlitRow_opts_SSE2.h; sourceTree = "<group>"; };
+		26D90EB8140D51290036A311 /* SkUtils_opts_SSE2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkUtils_opts_SSE2.h; sourceTree = "<group>"; };
+		26D90EBD140D51290036A311 /* SkGPipe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkGPipe.h; path = ../../include/pipe/SkGPipe.h; sourceTree = SOURCE_ROOT; };
+		26D90EBE140D51290036A311 /* SkGPipeRead.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGPipeRead.cpp; sourceTree = "<group>"; };
+		26D90EBF140D51290036A311 /* SkGPipeWrite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGPipeWrite.cpp; sourceTree = "<group>"; };
+		26D90EC2140D51290036A311 /* SkBitSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitSet.h; path = ../../include/pdf/SkBitSet.h; sourceTree = SOURCE_ROOT; };
+		26D90EC3140D51290036A311 /* SkPDFCatalog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFCatalog.h; path = ../../include/pdf/SkPDFCatalog.h; sourceTree = SOURCE_ROOT; };
+		26D90EC4140D51290036A311 /* SkPDFDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFDevice.h; path = ../../include/pdf/SkPDFDevice.h; sourceTree = SOURCE_ROOT; };
+		26D90EC5140D51290036A311 /* SkPDFDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFDocument.h; path = ../../include/pdf/SkPDFDocument.h; sourceTree = SOURCE_ROOT; };
+		26D90EC6140D51290036A311 /* SkPDFFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFFont.h; path = ../../include/pdf/SkPDFFont.h; sourceTree = SOURCE_ROOT; };
+		26D90EC7140D51290036A311 /* SkPDFFormXObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFFormXObject.h; path = ../../include/pdf/SkPDFFormXObject.h; sourceTree = SOURCE_ROOT; };
+		26D90EC8140D51290036A311 /* SkPDFGraphicState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFGraphicState.h; path = ../../include/pdf/SkPDFGraphicState.h; sourceTree = SOURCE_ROOT; };
+		26D90EC9140D51290036A311 /* SkPDFImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFImage.h; path = ../../include/pdf/SkPDFImage.h; sourceTree = SOURCE_ROOT; };
+		26D90ECA140D51290036A311 /* SkPDFPage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFPage.h; path = ../../include/pdf/SkPDFPage.h; sourceTree = SOURCE_ROOT; };
+		26D90ECB140D51290036A311 /* SkPDFShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFShader.h; path = ../../include/pdf/SkPDFShader.h; sourceTree = SOURCE_ROOT; };
+		26D90ECC140D51290036A311 /* SkPDFStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFStream.h; path = ../../include/pdf/SkPDFStream.h; sourceTree = SOURCE_ROOT; };
+		26D90ECD140D51290036A311 /* SkPDFTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFTypes.h; path = ../../include/pdf/SkPDFTypes.h; sourceTree = SOURCE_ROOT; };
+		26D90ECE140D51290036A311 /* SkPDFUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFUtils.h; path = ../../include/pdf/SkPDFUtils.h; sourceTree = SOURCE_ROOT; };
+		26D90ED0140D51290036A311 /* SkBitSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBitSet.cpp; path = ../../src/pdf/SkBitSet.cpp; sourceTree = SOURCE_ROOT; };
+		26D90ED1140D51290036A311 /* SkPDFCatalog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFCatalog.cpp; path = ../../src/pdf/SkPDFCatalog.cpp; sourceTree = SOURCE_ROOT; };
+		26D90ED2140D51290036A311 /* SkPDFDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFDevice.cpp; path = ../../src/pdf/SkPDFDevice.cpp; sourceTree = SOURCE_ROOT; };
+		26D90ED3140D51290036A311 /* SkPDFDocument.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFDocument.cpp; path = ../../src/pdf/SkPDFDocument.cpp; sourceTree = SOURCE_ROOT; };
+		26D90ED4140D51290036A311 /* SkPDFFont.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFFont.cpp; path = ../../src/pdf/SkPDFFont.cpp; sourceTree = SOURCE_ROOT; };
+		26D90ED5140D51290036A311 /* SkPDFFormXObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFFormXObject.cpp; path = ../../src/pdf/SkPDFFormXObject.cpp; sourceTree = SOURCE_ROOT; };
+		26D90ED6140D51290036A311 /* SkPDFGraphicState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFGraphicState.cpp; path = ../../src/pdf/SkPDFGraphicState.cpp; sourceTree = SOURCE_ROOT; };
+		26D90ED7140D51290036A311 /* SkPDFImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFImage.cpp; path = ../../src/pdf/SkPDFImage.cpp; sourceTree = SOURCE_ROOT; };
+		26D90ED8140D51290036A311 /* SkPDFPage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFPage.cpp; path = ../../src/pdf/SkPDFPage.cpp; sourceTree = SOURCE_ROOT; };
+		26D90ED9140D51290036A311 /* SkPDFShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFShader.cpp; path = ../../src/pdf/SkPDFShader.cpp; sourceTree = SOURCE_ROOT; };
+		26D90EDA140D51290036A311 /* SkPDFStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFStream.cpp; path = ../../src/pdf/SkPDFStream.cpp; sourceTree = SOURCE_ROOT; };
+		26D90EDB140D51290036A311 /* SkPDFTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFTypes.cpp; path = ../../src/pdf/SkPDFTypes.cpp; sourceTree = SOURCE_ROOT; };
+		26D90EDC140D51290036A311 /* SkPDFUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFUtils.cpp; path = ../../src/pdf/SkPDFUtils.cpp; sourceTree = SOURCE_ROOT; };
+		26D90EDF140D51290036A311 /* SkSVGAttribute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGAttribute.h; sourceTree = "<group>"; };
+		26D90EE0140D51290036A311 /* SkSVGBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGBase.h; sourceTree = "<group>"; };
+		26D90EE1140D51290036A311 /* SkSVGPaintState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGPaintState.h; sourceTree = "<group>"; };
+		26D90EE2140D51290036A311 /* SkSVGParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGParser.h; sourceTree = "<group>"; };
+		26D90EE3140D51290036A311 /* SkSVGTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGTypes.h; sourceTree = "<group>"; };
+		26D90EE5140D51290036A311 /* SkSVGCircle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGCircle.cpp; sourceTree = "<group>"; };
+		26D90EE6140D51290036A311 /* SkSVGCircle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGCircle.h; sourceTree = "<group>"; };
+		26D90EE7140D51290036A311 /* SkSVGClipPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGClipPath.cpp; sourceTree = "<group>"; };
+		26D90EE8140D51290036A311 /* SkSVGClipPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGClipPath.h; sourceTree = "<group>"; };
+		26D90EE9140D51290036A311 /* SkSVGDefs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGDefs.cpp; sourceTree = "<group>"; };
+		26D90EEA140D51290036A311 /* SkSVGDefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGDefs.h; sourceTree = "<group>"; };
+		26D90EEB140D51290036A311 /* SkSVGElements.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGElements.cpp; sourceTree = "<group>"; };
+		26D90EEC140D51290036A311 /* SkSVGElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGElements.h; sourceTree = "<group>"; };
+		26D90EED140D51290036A311 /* SkSVGEllipse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGEllipse.cpp; sourceTree = "<group>"; };
+		26D90EEE140D51290036A311 /* SkSVGEllipse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGEllipse.h; sourceTree = "<group>"; };
+		26D90EEF140D51290036A311 /* SkSVGFeColorMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGFeColorMatrix.cpp; sourceTree = "<group>"; };
+		26D90EF0140D51290036A311 /* SkSVGFeColorMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGFeColorMatrix.h; sourceTree = "<group>"; };
+		26D90EF1140D51290036A311 /* SkSVGFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGFilter.cpp; sourceTree = "<group>"; };
+		26D90EF2140D51290036A311 /* SkSVGFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGFilter.h; sourceTree = "<group>"; };
+		26D90EF3140D51290036A311 /* SkSVGG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGG.cpp; sourceTree = "<group>"; };
+		26D90EF4140D51290036A311 /* SkSVGG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGG.h; sourceTree = "<group>"; };
+		26D90EF5140D51290036A311 /* SkSVGGradient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGGradient.cpp; sourceTree = "<group>"; };
+		26D90EF6140D51290036A311 /* SkSVGGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGGradient.h; sourceTree = "<group>"; };
+		26D90EF7140D51290036A311 /* SkSVGGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGGroup.cpp; sourceTree = "<group>"; };
+		26D90EF8140D51290036A311 /* SkSVGGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGGroup.h; sourceTree = "<group>"; };
+		26D90EF9140D51290036A311 /* SkSVGImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGImage.cpp; sourceTree = "<group>"; };
+		26D90EFA140D51290036A311 /* SkSVGImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGImage.h; sourceTree = "<group>"; };
+		26D90EFB140D51290036A311 /* SkSVGLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGLine.cpp; sourceTree = "<group>"; };
+		26D90EFC140D51290036A311 /* SkSVGLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGLine.h; sourceTree = "<group>"; };
+		26D90EFD140D51290036A311 /* SkSVGLinearGradient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGLinearGradient.cpp; sourceTree = "<group>"; };
+		26D90EFE140D51290036A311 /* SkSVGLinearGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGLinearGradient.h; sourceTree = "<group>"; };
+		26D90EFF140D512A0036A311 /* SkSVGMask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGMask.cpp; sourceTree = "<group>"; };
+		26D90F00140D512A0036A311 /* SkSVGMask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGMask.h; sourceTree = "<group>"; };
+		26D90F01140D512A0036A311 /* SkSVGMetadata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGMetadata.cpp; sourceTree = "<group>"; };
+		26D90F02140D512A0036A311 /* SkSVGMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGMetadata.h; sourceTree = "<group>"; };
+		26D90F03140D512A0036A311 /* SkSVGPaintState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGPaintState.cpp; sourceTree = "<group>"; };
+		26D90F04140D512A0036A311 /* SkSVGParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGParser.cpp; sourceTree = "<group>"; };
+		26D90F05140D512A0036A311 /* SkSVGPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGPath.cpp; sourceTree = "<group>"; };
+		26D90F06140D512A0036A311 /* SkSVGPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGPath.h; sourceTree = "<group>"; };
+		26D90F07140D512A0036A311 /* SkSVGPolygon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGPolygon.cpp; sourceTree = "<group>"; };
+		26D90F08140D512A0036A311 /* SkSVGPolygon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGPolygon.h; sourceTree = "<group>"; };
+		26D90F09140D512A0036A311 /* SkSVGPolyline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGPolyline.cpp; sourceTree = "<group>"; };
+		26D90F0A140D512A0036A311 /* SkSVGPolyline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGPolyline.h; sourceTree = "<group>"; };
+		26D90F0B140D512A0036A311 /* SkSVGRadialGradient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGRadialGradient.cpp; sourceTree = "<group>"; };
+		26D90F0C140D512A0036A311 /* SkSVGRadialGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGRadialGradient.h; sourceTree = "<group>"; };
+		26D90F0D140D512A0036A311 /* SkSVGRect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGRect.cpp; sourceTree = "<group>"; };
+		26D90F0E140D512A0036A311 /* SkSVGRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGRect.h; sourceTree = "<group>"; };
+		26D90F0F140D512A0036A311 /* SkSVGSVG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGSVG.cpp; sourceTree = "<group>"; };
+		26D90F10140D512A0036A311 /* SkSVGSVG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGSVG.h; sourceTree = "<group>"; };
+		26D90F11140D512A0036A311 /* SkSVGStop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGStop.cpp; sourceTree = "<group>"; };
+		26D90F12140D512A0036A311 /* SkSVGStop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGStop.h; sourceTree = "<group>"; };
+		26D90F13140D512A0036A311 /* SkSVGSymbol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGSymbol.cpp; sourceTree = "<group>"; };
+		26D90F14140D512A0036A311 /* SkSVGSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGSymbol.h; sourceTree = "<group>"; };
+		26D90F15140D512A0036A311 /* SkSVGText.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGText.cpp; sourceTree = "<group>"; };
+		26D90F16140D512A0036A311 /* SkSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGText.h; sourceTree = "<group>"; };
+		26D90F17140D512A0036A311 /* SkSVGUse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGUse.cpp; sourceTree = "<group>"; };
+		26D90F1B140D512A0036A311 /* SkCGUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCGUtils.h; sourceTree = "<group>"; };
+		26D90F1C140D512A0036A311 /* SkBoundaryPatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBoundaryPatch.h; sourceTree = "<group>"; };
+		26D90F1D140D512A0036A311 /* SkCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCamera.h; sourceTree = "<group>"; };
+		26D90F1E140D512A0036A311 /* SkCubicInterval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCubicInterval.h; sourceTree = "<group>"; };
+		26D90F1F140D512A0036A311 /* SkCullPoints.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCullPoints.h; sourceTree = "<group>"; };
+		26D90F20140D512A0036A311 /* SkDumpCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDumpCanvas.h; sourceTree = "<group>"; };
+		26D90F21140D512A0036A311 /* SkEGLContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEGLContext.h; sourceTree = "<group>"; };
+		26D90F22140D512A0036A311 /* SkGLCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGLCanvas.h; sourceTree = "<group>"; };
+		26D90F23140D512A0036A311 /* SkInterpolator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkInterpolator.h; sourceTree = "<group>"; };
+		26D90F24140D512A0036A311 /* SkLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkLayer.h; sourceTree = "<group>"; };
+		26D90F25140D512A0036A311 /* SkMatrix44.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkMatrix44.h; sourceTree = "<group>"; };
+		26D90F26140D512A0036A311 /* SkMeshUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkMeshUtils.h; sourceTree = "<group>"; };
+		26D90F27140D512A0036A311 /* SkNWayCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkNWayCanvas.h; sourceTree = "<group>"; };
+		26D90F28140D512A0036A311 /* SkNinePatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkNinePatch.h; sourceTree = "<group>"; };
+		26D90F29140D512A0036A311 /* SkParse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkParse.h; sourceTree = "<group>"; };
+		26D90F2A140D512A0036A311 /* SkParsePaint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkParsePaint.h; sourceTree = "<group>"; };
+		26D90F2B140D512A0036A311 /* SkParsePath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkParsePath.h; sourceTree = "<group>"; };
+		26D90F2C140D512A0036A311 /* SkProxyCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkProxyCanvas.h; sourceTree = "<group>"; };
+		26D90F2D140D512A0036A311 /* SkSfntUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSfntUtils.h; sourceTree = "<group>"; };
+		26D90F2E140D512A0036A311 /* SkTextBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTextBox.h; sourceTree = "<group>"; };
+		26D90F2F140D512A0036A311 /* SkUnitMappers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkUnitMappers.h; sourceTree = "<group>"; };
+		26D90F32140D512A0036A311 /* SkCreateCGImageRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCreateCGImageRef.cpp; sourceTree = "<group>"; };
+		26D90F33140D512A0036A311 /* SkBoundaryPatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBoundaryPatch.cpp; sourceTree = "<group>"; };
+		26D90F34140D512A0036A311 /* SkCamera.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCamera.cpp; sourceTree = "<group>"; };
+		26D90F35140D512A0036A311 /* SkColorMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColorMatrix.cpp; sourceTree = "<group>"; };
+		26D90F36140D512A0036A311 /* SkCubicInterval.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCubicInterval.cpp; sourceTree = "<group>"; };
+		26D90F37140D512A0036A311 /* SkCullPoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCullPoints.cpp; sourceTree = "<group>"; };
+		26D90F38140D512A0036A311 /* SkDumpCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDumpCanvas.cpp; sourceTree = "<group>"; };
+		26D90F39140D512A0036A311 /* SkInterpolator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkInterpolator.cpp; sourceTree = "<group>"; };
+		26D90F3A140D512A0036A311 /* SkLayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkLayer.cpp; sourceTree = "<group>"; };
+		26D90F3B140D512A0036A311 /* SkMatrix44.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMatrix44.cpp; sourceTree = "<group>"; };
+		26D90F3C140D512A0036A311 /* SkMeshUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMeshUtils.cpp; sourceTree = "<group>"; };
+		26D90F3D140D512A0036A311 /* SkNWayCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkNWayCanvas.cpp; sourceTree = "<group>"; };
+		26D90F3E140D512A0036A311 /* SkNinePatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkNinePatch.cpp; sourceTree = "<group>"; };
+		26D90F3F140D512A0036A311 /* SkOSFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkOSFile.cpp; sourceTree = "<group>"; };
+		26D90F40140D512A0036A311 /* SkParse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkParse.cpp; sourceTree = "<group>"; };
+		26D90F41140D512A0036A311 /* SkParseColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkParseColor.cpp; sourceTree = "<group>"; };
+		26D90F42140D512A0036A311 /* SkParsePath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkParsePath.cpp; sourceTree = "<group>"; };
+		26D90F43140D512A0036A311 /* SkProxyCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkProxyCanvas.cpp; sourceTree = "<group>"; };
+		26D90F44140D512A0036A311 /* SkSfntUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSfntUtils.cpp; sourceTree = "<group>"; };
+		26D90F45140D512A0036A311 /* SkUnitMappers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkUnitMappers.cpp; sourceTree = "<group>"; };
+		26D90F48140D512A0036A311 /* SkApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkApplication.h; sourceTree = "<group>"; };
+		26D90F49140D512A0036A311 /* SkBGViewArtist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBGViewArtist.h; sourceTree = "<group>"; };
+		26D90F4A140D512A0036A311 /* SkBorderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBorderView.h; sourceTree = "<group>"; };
+		26D90F4B140D512A0036A311 /* SkEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEvent.h; sourceTree = "<group>"; };
+		26D90F4C140D512A0036A311 /* SkEventSink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEventSink.h; sourceTree = "<group>"; };
+		26D90F4D140D512A0036A311 /* SkImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageView.h; sourceTree = "<group>"; };
+		26D90F4E140D512A0036A311 /* SkKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkKey.h; sourceTree = "<group>"; };
+		26D90F4F140D512A0036A311 /* SkOSMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkOSMenu.h; sourceTree = "<group>"; };
+		26D90F50140D512A0036A311 /* SkProgressBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkProgressBarView.h; sourceTree = "<group>"; };
+		26D90F51140D512A0036A311 /* SkScrollBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScrollBarView.h; sourceTree = "<group>"; };
+		26D90F52140D512A0036A311 /* SkStackViewLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkStackViewLayout.h; sourceTree = "<group>"; };
+		26D90F53140D512A0036A311 /* SkSystemEventTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSystemEventTypes.h; sourceTree = "<group>"; };
+		26D90F54140D512A0036A311 /* SkTouchGesture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTouchGesture.h; sourceTree = "<group>"; };
+		26D90F55140D512A0036A311 /* SkView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkView.h; sourceTree = "<group>"; };
+		26D90F56140D512A0036A311 /* SkViewInflate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkViewInflate.h; sourceTree = "<group>"; };
+		26D90F57140D512A0036A311 /* SkWidget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkWidget.h; sourceTree = "<group>"; };
+		26D90F58140D512A0036A311 /* SkWidgetViews.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkWidgetViews.h; sourceTree = "<group>"; };
+		26D90F59140D512A0036A311 /* SkWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkWindow.h; sourceTree = "<group>"; };
+		26D90F5B140D512A0036A311 /* SkBGViewArtist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBGViewArtist.cpp; sourceTree = "<group>"; };
+		26D90F5C140D512A0036A311 /* SkEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEvent.cpp; sourceTree = "<group>"; };
+		26D90F5D140D512A0036A311 /* SkEventSink.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEventSink.cpp; sourceTree = "<group>"; };
+		26D90F5E140D512A0036A311 /* SkImageView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageView.cpp; sourceTree = "<group>"; };
+		26D90F5F140D512A0036A311 /* SkListView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkListView.cpp; sourceTree = "<group>"; };
+		26D90F61140D512A0036A311 /* SkOSMenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkOSMenu.cpp; sourceTree = "<group>"; };
+		26D90F62140D512A0036A311 /* SkParsePaint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkParsePaint.cpp; sourceTree = "<group>"; };
+		26D90F67140D512A0036A311 /* SkStackViewLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkStackViewLayout.cpp; sourceTree = "<group>"; };
+		26D90F68140D512A0036A311 /* SkStaticTextView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkStaticTextView.cpp; sourceTree = "<group>"; };
+		26D90F69140D512A0036A311 /* SkTagList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTagList.cpp; sourceTree = "<group>"; };
+		26D90F6A140D512A0036A311 /* SkTagList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTagList.h; sourceTree = "<group>"; };
+		26D90F6B140D512A0036A311 /* SkTextBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTextBox.cpp; sourceTree = "<group>"; };
+		26D90F6C140D512A0036A311 /* SkTouchGesture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTouchGesture.cpp; sourceTree = "<group>"; };
+		26D90F6D140D512A0036A311 /* SkView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkView.cpp; sourceTree = "<group>"; };
+		26D90F6E140D512A0036A311 /* SkViewInflate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkViewInflate.cpp; sourceTree = "<group>"; };
+		26D90F6F140D512A0036A311 /* SkViewPriv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkViewPriv.cpp; sourceTree = "<group>"; };
+		26D90F70140D512A0036A311 /* SkViewPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkViewPriv.h; sourceTree = "<group>"; };
+		26D90F71140D512A0036A311 /* SkWidget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkWidget.cpp; sourceTree = "<group>"; };
+		26D90F73140D512A0036A311 /* SkWidgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkWidgets.cpp; sourceTree = "<group>"; };
+		26D90F74140D512A0036A311 /* SkWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkWindow.cpp; sourceTree = "<group>"; };
+		26D90F77140D512A0036A311 /* SkBML_WXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBML_WXMLParser.h; sourceTree = "<group>"; };
+		26D90F78140D512A0036A311 /* SkBML_XMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBML_XMLParser.h; sourceTree = "<group>"; };
+		26D90F79140D512A0036A311 /* SkDOM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDOM.h; sourceTree = "<group>"; };
+		26D90F7A140D512A0036A311 /* SkJS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkJS.h; sourceTree = "<group>"; };
+		26D90F7B140D512A0036A311 /* SkXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkXMLParser.h; sourceTree = "<group>"; };
+		26D90F7C140D512A0036A311 /* SkXMLWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkXMLWriter.h; sourceTree = "<group>"; };
+		26D90F7E140D512A0036A311 /* SkDOM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDOM.cpp; sourceTree = "<group>"; };
+		26D90F7F140D512A0036A311 /* SkXMLParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkXMLParser.cpp; sourceTree = "<group>"; };
+		26D90F80140D512A0036A311 /* SkXMLWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkXMLWriter.cpp; sourceTree = "<group>"; };
+		26D9111D140D52CD0036A311 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+		26D9111F140D52CD0036A311 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
+		26D9112F140D53C30036A311 /* SkBlitRow_opts_none.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitRow_opts_none.cpp; sourceTree = "<group>"; };
+		26D91130140D53C30036A311 /* SkUtils_opts_none.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkUtils_opts_none.cpp; sourceTree = "<group>"; };
+		26D9113B140D54720036A311 /* skia_ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = skia_ios.mm; path = ../iOSSampleApp/Shared/skia_ios.mm; sourceTree = SOURCE_ROOT; };
+		26D91153140D54E40036A311 /* SkEventNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkEventNotifier.h; path = ../../src/utils/mac/SkEventNotifier.h; sourceTree = SOURCE_ROOT; };
+		26D91154140D54E40036A311 /* SkEventNotifier.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkEventNotifier.mm; path = ../../src/utils/mac/SkEventNotifier.mm; sourceTree = SOURCE_ROOT; };
+		26D9118E140D558A0036A311 /* SkOSFile_iOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkOSFile_iOS.mm; path = ../../src/utils/ios/SkOSFile_iOS.mm; sourceTree = SOURCE_ROOT; };
+		26D91192140D55980036A311 /* SkOSWindow_iOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkOSWindow_iOS.h; path = ../../include/views/SkOSWindow_iOS.h; sourceTree = SOURCE_ROOT; };
+		26D91193140D55980036A311 /* SkOSWindow_iOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkOSWindow_iOS.mm; path = ../../src/utils/ios/SkOSWindow_iOS.mm; sourceTree = SOURCE_ROOT; };
+		26D912EA140D5FCC0036A311 /* SkiOSSampleApp-Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "SkiOSSampleApp-Debug.xcconfig"; path = "../iOSSampleApp/SkiOSSampleApp-Debug.xcconfig"; sourceTree = SOURCE_ROOT; };
+		26D912EB140D5FCC0036A311 /* SkiOSSampleApp-Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "SkiOSSampleApp-Release.xcconfig"; path = "../iOSSampleApp/SkiOSSampleApp-Release.xcconfig"; sourceTree = SOURCE_ROOT; };
+		26D912EC140D5FCC0036A311 /* SkiOSSampleApp-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "SkiOSSampleApp-Base.xcconfig"; path = "../iOSSampleApp/SkiOSSampleApp-Base.xcconfig"; sourceTree = SOURCE_ROOT; };
+		26D91302140D60330036A311 /* SimpleApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleApp.h; sourceTree = "<group>"; };
+		26D91303140D60330036A311 /* SimpleApp.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SimpleApp.mm; sourceTree = "<group>"; };
+		2860E325111B887F00E27156 /* AppDelegate_iPhone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate_iPhone.h; sourceTree = "<group>"; };
+		2860E326111B887F00E27156 /* AppDelegate_iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate_iPhone.m; sourceTree = "<group>"; };
+		2860E327111B887F00E27156 /* MainWindow_iPhone.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow_iPhone.xib; sourceTree = "<group>"; };
+		2860E32B111B888700E27156 /* AppDelegate_iPad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate_iPad.h; sourceTree = "<group>"; };
+		2860E32C111B888700E27156 /* AppDelegate_iPad.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate_iPad.m; sourceTree = "<group>"; };
+		2860E32D111B888700E27156 /* MainWindow_iPad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow_iPad.xib; sourceTree = "<group>"; };
+		288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+		8D1107310486CEB800E47090 /* SimpleiOSApp-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SimpleiOSApp-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		1D60588F0D05DD3D006BFB54 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */,
+				1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */,
+				288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */,
+				26D91120140D52CD0036A311 /* CoreFoundation.framework in Frameworks */,
+				26D91122140D52CD0036A311 /* CoreText.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		19C28FACFE9D520D11CA2CBB /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				1D6058910D05DD3D006BFB54 /* SimpleiOSApp.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		26D90C43140D51290036A311 /* Skia */ = {
+			isa = PBXGroup;
+			children = (
+				26D90C44140D51290036A311 /* SkUserConfig.h */,
+				26D90C45140D51290036A311 /* animator */,
+				26D90CE2140D51290036A311 /* core */,
+				26D90DDA140D51290036A311 /* effects */,
+				26D90E10140D51290036A311 /* gpu */,
+				26D90E92140D51290036A311 /* images */,
+				26D90EAF140D51290036A311 /* opts */,
+				26D90EDD140D51290036A311 /* svg */,
+				26D90F18140D512A0036A311 /* utils */,
+				26D90F46140D512A0036A311 /* views */,
+				26D90F75140D512A0036A311 /* xml */,
+				26D90EC0140D51290036A311 /* pdf */,
+				26D90EBC140D51290036A311 /* pipe */,
+			);
+			name = Skia;
+			sourceTree = "<group>";
+		};
+		26D90C45140D51290036A311 /* animator */ = {
+			isa = PBXGroup;
+			children = (
+				26D90C46140D51290036A311 /* include */,
+				26D90C49140D51290036A311 /* src */,
+			);
+			name = animator;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90C46140D51290036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90C47140D51290036A311 /* SkAnimator.h */,
+				26D90C48140D51290036A311 /* SkAnimatorView.h */,
+			);
+			name = include;
+			path = include/animator;
+			sourceTree = "<group>";
+		};
+		26D90C49140D51290036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90C4A140D51290036A311 /* SkAnimate.h */,
+				26D90C4B140D51290036A311 /* SkAnimateActive.cpp */,
+				26D90C4C140D51290036A311 /* SkAnimateActive.h */,
+				26D90C4D140D51290036A311 /* SkAnimateBase.cpp */,
+				26D90C4E140D51290036A311 /* SkAnimateBase.h */,
+				26D90C4F140D51290036A311 /* SkAnimateField.cpp */,
+				26D90C50140D51290036A311 /* SkAnimateMaker.cpp */,
+				26D90C51140D51290036A311 /* SkAnimateMaker.h */,
+				26D90C52140D51290036A311 /* SkAnimateProperties.h */,
+				26D90C53140D51290036A311 /* SkAnimateSet.cpp */,
+				26D90C54140D51290036A311 /* SkAnimateSet.h */,
+				26D90C55140D51290036A311 /* SkAnimator.cpp */,
+				26D90C56140D51290036A311 /* SkAnimatorScript.cpp */,
+				26D90C57140D51290036A311 /* SkAnimatorScript.h */,
+				26D90C58140D51290036A311 /* SkBase64.cpp */,
+				26D90C59140D51290036A311 /* SkBase64.h */,
+				26D90C5A140D51290036A311 /* SkBoundable.cpp */,
+				26D90C5B140D51290036A311 /* SkBoundable.h */,
+				26D90C5C140D51290036A311 /* SkBuildCondensedInfo.cpp */,
+				26D90C5D140D51290036A311 /* SkDisplayAdd.cpp */,
+				26D90C5E140D51290036A311 /* SkDisplayAdd.h */,
+				26D90C5F140D51290036A311 /* SkDisplayApply.cpp */,
+				26D90C60140D51290036A311 /* SkDisplayApply.h */,
+				26D90C61140D51290036A311 /* SkDisplayBounds.cpp */,
+				26D90C62140D51290036A311 /* SkDisplayBounds.h */,
+				26D90C63140D51290036A311 /* SkDisplayEvent.cpp */,
+				26D90C64140D51290036A311 /* SkDisplayEvent.h */,
+				26D90C65140D51290036A311 /* SkDisplayEvents.cpp */,
+				26D90C66140D51290036A311 /* SkDisplayEvents.h */,
+				26D90C67140D51290036A311 /* SkDisplayInclude.cpp */,
+				26D90C68140D51290036A311 /* SkDisplayInclude.h */,
+				26D90C69140D51290036A311 /* SkDisplayInput.cpp */,
+				26D90C6A140D51290036A311 /* SkDisplayInput.h */,
+				26D90C6B140D51290036A311 /* SkDisplayList.cpp */,
+				26D90C6C140D51290036A311 /* SkDisplayList.h */,
+				26D90C6D140D51290036A311 /* SkDisplayMath.cpp */,
+				26D90C6E140D51290036A311 /* SkDisplayMath.h */,
+				26D90C6F140D51290036A311 /* SkDisplayMovie.cpp */,
+				26D90C70140D51290036A311 /* SkDisplayMovie.h */,
+				26D90C71140D51290036A311 /* SkDisplayNumber.cpp */,
+				26D90C72140D51290036A311 /* SkDisplayNumber.h */,
+				26D90C73140D51290036A311 /* SkDisplayPost.cpp */,
+				26D90C74140D51290036A311 /* SkDisplayPost.h */,
+				26D90C75140D51290036A311 /* SkDisplayRandom.cpp */,
+				26D90C76140D51290036A311 /* SkDisplayRandom.h */,
+				26D90C77140D51290036A311 /* SkDisplayScreenplay.cpp */,
+				26D90C78140D51290036A311 /* SkDisplayScreenplay.h */,
+				26D90C79140D51290036A311 /* SkDisplayType.cpp */,
+				26D90C7A140D51290036A311 /* SkDisplayType.h */,
+				26D90C7B140D51290036A311 /* SkDisplayTypes.cpp */,
+				26D90C7C140D51290036A311 /* SkDisplayTypes.h */,
+				26D90C7D140D51290036A311 /* SkDisplayXMLParser.cpp */,
+				26D90C7E140D51290036A311 /* SkDisplayXMLParser.h */,
+				26D90C7F140D51290036A311 /* SkDisplayable.cpp */,
+				26D90C80140D51290036A311 /* SkDisplayable.h */,
+				26D90C81140D51290036A311 /* SkDraw3D.cpp */,
+				26D90C82140D51290036A311 /* SkDraw3D.h */,
+				26D90C83140D51290036A311 /* SkDrawBitmap.cpp */,
+				26D90C84140D51290036A311 /* SkDrawBitmap.h */,
+				26D90C85140D51290036A311 /* SkDrawBlur.cpp */,
+				26D90C86140D51290036A311 /* SkDrawBlur.h */,
+				26D90C87140D51290036A311 /* SkDrawClip.cpp */,
+				26D90C88140D51290036A311 /* SkDrawClip.h */,
+				26D90C89140D51290036A311 /* SkDrawColor.cpp */,
+				26D90C8A140D51290036A311 /* SkDrawColor.h */,
+				26D90C8B140D51290036A311 /* SkDrawDash.cpp */,
+				26D90C8C140D51290036A311 /* SkDrawDash.h */,
+				26D90C8D140D51290036A311 /* SkDrawDiscrete.cpp */,
+				26D90C8E140D51290036A311 /* SkDrawDiscrete.h */,
+				26D90C8F140D51290036A311 /* SkDrawEmboss.cpp */,
+				26D90C90140D51290036A311 /* SkDrawEmboss.h */,
+				26D90C91140D51290036A311 /* SkDrawExtraPathEffect.cpp */,
+				26D90C92140D51290036A311 /* SkDrawFull.cpp */,
+				26D90C93140D51290036A311 /* SkDrawFull.h */,
+				26D90C94140D51290036A311 /* SkDrawGradient.cpp */,
+				26D90C95140D51290036A311 /* SkDrawGradient.h */,
+				26D90C96140D51290036A311 /* SkDrawGroup.cpp */,
+				26D90C97140D51290036A311 /* SkDrawGroup.h */,
+				26D90C98140D51290036A311 /* SkDrawLine.cpp */,
+				26D90C99140D51290036A311 /* SkDrawLine.h */,
+				26D90C9A140D51290036A311 /* SkDrawMatrix.cpp */,
+				26D90C9B140D51290036A311 /* SkDrawMatrix.h */,
+				26D90C9C140D51290036A311 /* SkDrawOval.cpp */,
+				26D90C9D140D51290036A311 /* SkDrawOval.h */,
+				26D90C9E140D51290036A311 /* SkDrawPaint.cpp */,
+				26D90C9F140D51290036A311 /* SkDrawPaint.h */,
+				26D90CA0140D51290036A311 /* SkDrawPath.cpp */,
+				26D90CA1140D51290036A311 /* SkDrawPath.h */,
+				26D90CA2140D51290036A311 /* SkDrawPoint.cpp */,
+				26D90CA3140D51290036A311 /* SkDrawPoint.h */,
+				26D90CA4140D51290036A311 /* SkDrawRectangle.cpp */,
+				26D90CA5140D51290036A311 /* SkDrawRectangle.h */,
+				26D90CA6140D51290036A311 /* SkDrawSaveLayer.cpp */,
+				26D90CA7140D51290036A311 /* SkDrawSaveLayer.h */,
+				26D90CA8140D51290036A311 /* SkDrawShader.cpp */,
+				26D90CA9140D51290036A311 /* SkDrawShader.h */,
+				26D90CAA140D51290036A311 /* SkDrawText.cpp */,
+				26D90CAB140D51290036A311 /* SkDrawText.h */,
+				26D90CAC140D51290036A311 /* SkDrawTextBox.cpp */,
+				26D90CAD140D51290036A311 /* SkDrawTextBox.h */,
+				26D90CAE140D51290036A311 /* SkDrawTo.cpp */,
+				26D90CAF140D51290036A311 /* SkDrawTo.h */,
+				26D90CB0140D51290036A311 /* SkDrawTransparentShader.cpp */,
+				26D90CB1140D51290036A311 /* SkDrawTransparentShader.h */,
+				26D90CB2140D51290036A311 /* SkDrawable.cpp */,
+				26D90CB3140D51290036A311 /* SkDrawable.h */,
+				26D90CB4140D51290036A311 /* SkDump.cpp */,
+				26D90CB5140D51290036A311 /* SkDump.h */,
+				26D90CB6140D51290036A311 /* SkExtras.h */,
+				26D90CB7140D51290036A311 /* SkGetCondensedInfo.cpp */,
+				26D90CB8140D51290036A311 /* SkHitClear.cpp */,
+				26D90CB9140D51290036A311 /* SkHitClear.h */,
+				26D90CBA140D51290036A311 /* SkHitTest.cpp */,
+				26D90CBB140D51290036A311 /* SkHitTest.h */,
+				26D90CBC140D51290036A311 /* SkIntArray.h */,
+				26D90CBD140D51290036A311 /* SkMatrixParts.cpp */,
+				26D90CBE140D51290036A311 /* SkMatrixParts.h */,
+				26D90CBF140D51290036A311 /* SkMemberInfo.cpp */,
+				26D90CC0140D51290036A311 /* SkMemberInfo.h */,
+				26D90CC1140D51290036A311 /* SkOpArray.cpp */,
+				26D90CC2140D51290036A311 /* SkOpArray.h */,
+				26D90CC3140D51290036A311 /* SkOperand.h */,
+				26D90CC4140D51290036A311 /* SkOperand2.h */,
+				26D90CC5140D51290036A311 /* SkOperandInterpolator.h */,
+				26D90CC6140D51290036A311 /* SkOperandIterpolator.cpp */,
+				26D90CC7140D51290036A311 /* SkPaintParts.cpp */,
+				26D90CC8140D51290036A311 /* SkPaintParts.h */,
+				26D90CC9140D51290036A311 /* SkParseSVGPath.cpp */,
+				26D90CCA140D51290036A311 /* SkPathParts.cpp */,
+				26D90CCB140D51290036A311 /* SkPathParts.h */,
+				26D90CCC140D51290036A311 /* SkPostParts.cpp */,
+				26D90CCD140D51290036A311 /* SkPostParts.h */,
+				26D90CCE140D51290036A311 /* SkScript.cpp */,
+				26D90CCF140D51290036A311 /* SkScript.h */,
+				26D90CD0140D51290036A311 /* SkScript2.h */,
+				26D90CD1140D51290036A311 /* SkScriptCallBack.h */,
+				26D90CD2140D51290036A311 /* SkScriptDecompile.cpp */,
+				26D90CD3140D51290036A311 /* SkScriptRuntime.cpp */,
+				26D90CD4140D51290036A311 /* SkScriptRuntime.h */,
+				26D90CD5140D51290036A311 /* SkScriptTokenizer.cpp */,
+				26D90CD6140D51290036A311 /* SkSnapshot.cpp */,
+				26D90CD7140D51290036A311 /* SkSnapshot.h */,
+				26D90CD8140D51290036A311 /* SkTDArray_Experimental.h */,
+				26D90CD9140D51290036A311 /* SkTextOnPath.cpp */,
+				26D90CDA140D51290036A311 /* SkTextOnPath.h */,
+				26D90CDB140D51290036A311 /* SkTextToPath.cpp */,
+				26D90CDC140D51290036A311 /* SkTextToPath.h */,
+				26D90CDD140D51290036A311 /* SkTime.cpp */,
+				26D90CDE140D51290036A311 /* SkTypedArray.cpp */,
+				26D90CDF140D51290036A311 /* SkTypedArray.h */,
+				26D90CE0140D51290036A311 /* SkXMLAnimatorWriter.cpp */,
+				26D90CE1140D51290036A311 /* SkXMLAnimatorWriter.h */,
+			);
+			name = src;
+			path = src/animator;
+			sourceTree = "<group>";
+		};
+		26D90CE2140D51290036A311 /* core */ = {
+			isa = PBXGroup;
+			children = (
+				26D90CE3140D51290036A311 /* SkTypeface_mac.h */,
+				26D90CE4140D51290036A311 /* include */,
+				26D90D45140D51290036A311 /* src */,
+			);
+			name = core;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90CE4140D51290036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90CE5140D51290036A311 /* Sk64.h */,
+				26D90CE6140D51290036A311 /* SkAdvancedTypefaceMetrics.h */,
+				26D90CE7140D51290036A311 /* SkAutoKern.h */,
+				26D90CE8140D51290036A311 /* SkBitmap.h */,
+				26D90CE9140D51290036A311 /* SkBlitRow.h */,
+				26D90CEA140D51290036A311 /* SkBlitter.h */,
+				26D90CEB140D51290036A311 /* SkBounder.h */,
+				26D90CEC140D51290036A311 /* SkBuffer.h */,
+				26D90CED140D51290036A311 /* SkCanvas.h */,
+				26D90CEE140D51290036A311 /* SkChunkAlloc.h */,
+				26D90CEF140D51290036A311 /* SkClampRange.h */,
+				26D90CF0140D51290036A311 /* SkClipStack.h */,
+				26D90CF1140D51290036A311 /* SkColor.h */,
+				26D90CF2140D51290036A311 /* SkColorFilter.h */,
+				26D90CF3140D51290036A311 /* SkColorPriv.h */,
+				26D90CF4140D51290036A311 /* SkColorShader.h */,
+				26D90CF5140D51290036A311 /* SkComposeShader.h */,
+				26D90CF6140D51290036A311 /* SkDeque.h */,
+				26D90CF7140D51290036A311 /* SkDescriptor.h */,
+				26D90CF8140D51290036A311 /* SkDevice.h */,
+				26D90CF9140D51290036A311 /* SkDither.h */,
+				26D90CFA140D51290036A311 /* SkDraw.h */,
+				26D90CFB140D51290036A311 /* SkDrawFilter.h */,
+				26D90CFC140D51290036A311 /* SkDrawLooper.h */,
+				26D90CFD140D51290036A311 /* SkEndian.h */,
+				26D90CFE140D51290036A311 /* SkFDot6.h */,
+				26D90CFF140D51290036A311 /* SkFixed.h */,
+				26D90D00140D51290036A311 /* SkFlate.h */,
+				26D90D01140D51290036A311 /* SkFlattenable.h */,
+				26D90D02140D51290036A311 /* SkFloatBits.h */,
+				26D90D03140D51290036A311 /* SkFloatingPoint.h */,
+				26D90D04140D51290036A311 /* SkFontHost.h */,
+				26D90D05140D51290036A311 /* SkGeometry.h */,
+				26D90D06140D51290036A311 /* SkGlobals.h */,
+				26D90D07140D51290036A311 /* SkGraphics.h */,
+				26D90D08140D51290036A311 /* SkMMapStream.h */,
+				26D90D09140D51290036A311 /* SkMallocPixelRef.h */,
+				26D90D0A140D51290036A311 /* SkMask.h */,
+				26D90D0B140D51290036A311 /* SkMaskFilter.h */,
+				26D90D0C140D51290036A311 /* SkMath.h */,
+				26D90D0D140D51290036A311 /* SkMatrix.h */,
+				26D90D0E140D51290036A311 /* SkMetaData.h */,
+				26D90D0F140D51290036A311 /* SkOSFile.h */,
+				26D90D10140D51290036A311 /* SkPackBits.h */,
+				26D90D11140D51290036A311 /* SkPaint.h */,
+				26D90D12140D51290036A311 /* SkPath.h */,
+				26D90D13140D51290036A311 /* SkPathEffect.h */,
+				26D90D14140D51290036A311 /* SkPathMeasure.h */,
+				26D90D15140D51290036A311 /* SkPerspIter.h */,
+				26D90D16140D51290036A311 /* SkPicture.h */,
+				26D90D17140D51290036A311 /* SkPixelRef.h */,
+				26D90D18140D51290036A311 /* SkPoint.h */,
+				26D90D19140D51290036A311 /* SkPtrRecorder.h */,
+				26D90D1A140D51290036A311 /* SkRandom.h */,
+				26D90D1B140D51290036A311 /* SkRasterizer.h */,
+				26D90D1C140D51290036A311 /* SkReader32.h */,
+				26D90D1D140D51290036A311 /* SkRect.h */,
+				26D90D1E140D51290036A311 /* SkRefCnt.h */,
+				26D90D1F140D51290036A311 /* SkRefDict.h */,
+				26D90D20140D51290036A311 /* SkRegion.h */,
+				26D90D21140D51290036A311 /* SkScalar.h */,
+				26D90D22140D51290036A311 /* SkScalarCompare.h */,
+				26D90D23140D51290036A311 /* SkScalerContext.h */,
+				26D90D24140D51290036A311 /* SkScan.h */,
+				26D90D25140D51290036A311 /* SkShader.h */,
+				26D90D26140D51290036A311 /* SkStream.h */,
+				26D90D27140D51290036A311 /* SkString.h */,
+				26D90D28140D51290036A311 /* SkStroke.h */,
+				26D90D29140D51290036A311 /* SkTDArray.h */,
+				26D90D2A140D51290036A311 /* SkTDStack.h */,
+				26D90D2B140D51290036A311 /* SkTDict.h */,
+				26D90D2C140D51290036A311 /* SkTRegistry.h */,
+				26D90D2D140D51290036A311 /* SkTScopedPtr.h */,
+				26D90D2E140D51290036A311 /* SkTSearch.h */,
+				26D90D2F140D51290036A311 /* SkTemplates.h */,
+				26D90D30140D51290036A311 /* SkThread.h */,
+				26D90D31140D51290036A311 /* SkThread_platform.h */,
+				26D90D32140D51290036A311 /* SkTime.h */,
+				26D90D33140D51290036A311 /* SkTypeface.h */,
+				26D90D34140D51290036A311 /* SkTypes.h */,
+				26D90D35140D51290036A311 /* SkUnPreMultiply.h */,
+				26D90D36140D51290036A311 /* SkUnitMapper.h */,
+				26D90D37140D51290036A311 /* SkUtils.h */,
+				26D90D38140D51290036A311 /* SkWriter32.h */,
+				26D90D39140D51290036A311 /* SkXfermode.h */,
+				26D90D3A140D51290036A311 /* SkEdgeClipper.h */,
+				26D90D3B140D51290036A311 /* SkLineClipper.h */,
+				26D90D3C140D51290036A311 /* SkPostConfig.h */,
+				26D90D3D140D51290036A311 /* SkPreConfig.h */,
+				26D90D3E140D51290036A311 /* SkRelay.h */,
+				26D90D3F140D51290036A311 /* SkShape.h */,
+				26D90D40140D51290036A311 /* SkSize.h */,
+				26D90D41140D51290036A311 /* SkTLazy.h */,
+				26D90D42140D51290036A311 /* SkData.h */,
+				26D90D43140D51290036A311 /* mac */,
+			);
+			path = include;
+			sourceTree = "<group>";
+		};
+		26D90D43140D51290036A311 /* mac */ = {
+			isa = PBXGroup;
+			children = (
+				26D90D44140D51290036A311 /* SkCGUtils.h */,
+			);
+			name = mac;
+			path = utils/mac;
+			sourceTree = "<group>";
+		};
+		26D90D45140D51290036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90D46140D51290036A311 /* SkData.cpp */,
+				26D90D47140D51290036A311 /* core */,
+				26D90DD1140D51290036A311 /* ports */,
+			);
+			path = src;
+			sourceTree = "<group>";
+		};
+		26D90D47140D51290036A311 /* core */ = {
+			isa = PBXGroup;
+			children = (
+				26D90D48140D51290036A311 /* ARGB32_Clamp_Bilinear_BitmapShader.h */,
+				26D90D49140D51290036A311 /* Sk64.cpp */,
+				26D90D4A140D51290036A311 /* SkAdvancedTypefaceMetrics.cpp */,
+				26D90D4B140D51290036A311 /* SkAlphaRuns.cpp */,
+				26D90D4C140D51290036A311 /* SkAntiRun.h */,
+				26D90D4D140D51290036A311 /* SkBitmap.cpp */,
+				26D90D4E140D51290036A311 /* SkBitmapProcShader.cpp */,
+				26D90D4F140D51290036A311 /* SkBitmapProcShader.h */,
+				26D90D50140D51290036A311 /* SkBitmapProcState.cpp */,
+				26D90D51140D51290036A311 /* SkBitmapProcState.h */,
+				26D90D52140D51290036A311 /* SkBitmapProcState_matrix.h */,
+				26D90D53140D51290036A311 /* SkBitmapProcState_matrixProcs.cpp */,
+				26D90D54140D51290036A311 /* SkBitmapProcState_sample.h */,
+				26D90D55140D51290036A311 /* SkBitmapSampler.cpp */,
+				26D90D56140D51290036A311 /* SkBitmapSampler.h */,
+				26D90D57140D51290036A311 /* SkBitmapSamplerTemplate.h */,
+				26D90D58140D51290036A311 /* SkBitmapShader16BilerpTemplate.h */,
+				26D90D59140D51290036A311 /* SkBitmapShaderTemplate.h */,
+				26D90D5A140D51290036A311 /* SkBitmap_scroll.cpp */,
+				26D90D5B140D51290036A311 /* SkBlitBWMaskTemplate.h */,
+				26D90D5C140D51290036A311 /* SkBlitRow_D16.cpp */,
+				26D90D5D140D51290036A311 /* SkBlitRow_D32.cpp */,
+				26D90D5E140D51290036A311 /* SkBlitRow_D4444.cpp */,
+				26D90D5F140D51290036A311 /* SkBlitter.cpp */,
+				26D90D60140D51290036A311 /* SkBlitter_4444.cpp */,
+				26D90D61140D51290036A311 /* SkBlitter_A1.cpp */,
+				26D90D62140D51290036A311 /* SkBlitter_A8.cpp */,
+				26D90D63140D51290036A311 /* SkBlitter_ARGB32.cpp */,
+				26D90D64140D51290036A311 /* SkBlitter_RGB16.cpp */,
+				26D90D65140D51290036A311 /* SkBlitter_Sprite.cpp */,
+				26D90D66140D51290036A311 /* SkBuffer.cpp */,
+				26D90D67140D51290036A311 /* SkCanvas.cpp */,
+				26D90D68140D51290036A311 /* SkChunkAlloc.cpp */,
+				26D90D69140D51290036A311 /* SkClampRange.cpp */,
+				26D90D6A140D51290036A311 /* SkClipStack.cpp */,
+				26D90D6B140D51290036A311 /* SkColor.cpp */,
+				26D90D6C140D51290036A311 /* SkColorFilter.cpp */,
+				26D90D6D140D51290036A311 /* SkColorTable.cpp */,
+				26D90D6E140D51290036A311 /* SkComposeShader.cpp */,
+				26D90D6F140D51290036A311 /* SkConcaveToTriangles.cpp */,
+				26D90D70140D51290036A311 /* SkConcaveToTriangles.h */,
+				26D90D71140D51290036A311 /* SkCordic.cpp */,
+				26D90D72140D51290036A311 /* SkCordic.h */,
+				26D90D73140D51290036A311 /* SkCoreBlitters.h */,
+				26D90D74140D51290036A311 /* SkCubicClipper.cpp */,
+				26D90D75140D51290036A311 /* SkCubicClipper.h */,
+				26D90D76140D51290036A311 /* SkDebug.cpp */,
+				26D90D77140D51290036A311 /* SkDeque.cpp */,
+				26D90D78140D51290036A311 /* SkDevice.cpp */,
+				26D90D79140D51290036A311 /* SkDither.cpp */,
+				26D90D7A140D51290036A311 /* SkDraw.cpp */,
+				26D90D7B140D51290036A311 /* SkDrawProcs.h */,
+				26D90D7C140D51290036A311 /* SkEdge.cpp */,
+				26D90D7D140D51290036A311 /* SkEdge.h */,
+				26D90D7E140D51290036A311 /* SkEdgeBuilder.cpp */,
+				26D90D7F140D51290036A311 /* SkEdgeClipper.cpp */,
+				26D90D80140D51290036A311 /* SkFP.h */,
+				26D90D81140D51290036A311 /* SkFilterProc.cpp */,
+				26D90D82140D51290036A311 /* SkFilterProc.h */,
+				26D90D83140D51290036A311 /* SkFlate.cpp */,
+				26D90D84140D51290036A311 /* SkFlattenable.cpp */,
+				26D90D85140D51290036A311 /* SkFloat.cpp */,
+				26D90D86140D51290036A311 /* SkFloat.h */,
+				26D90D87140D51290036A311 /* SkFloatBits.cpp */,
+				26D90D88140D51290036A311 /* SkFontHost.cpp */,
+				26D90D89140D51290036A311 /* SkGeometry.cpp */,
+				26D90D8A140D51290036A311 /* SkGlobals.cpp */,
+				26D90D8B140D51290036A311 /* SkGlyphCache.cpp */,
+				26D90D8C140D51290036A311 /* SkGlyphCache.h */,
+				26D90D8D140D51290036A311 /* SkGraphics.cpp */,
+				26D90D8E140D51290036A311 /* SkLineClipper.cpp */,
+				26D90D8F140D51290036A311 /* SkMMapStream.cpp */,
+				26D90D90140D51290036A311 /* SkMallocPixelRef.cpp */,
+				26D90D91140D51290036A311 /* SkMask.cpp */,
+				26D90D92140D51290036A311 /* SkMaskFilter.cpp */,
+				26D90D93140D51290036A311 /* SkMath.cpp */,
+				26D90D94140D51290036A311 /* SkMatrix.cpp */,
+				26D90D95140D51290036A311 /* SkMetaData.cpp */,
+				26D90D96140D51290036A311 /* SkPackBits.cpp */,
+				26D90D97140D51290036A311 /* SkPaint.cpp */,
+				26D90D98140D51290036A311 /* SkPath.cpp */,
+				26D90D99140D51290036A311 /* SkPathEffect.cpp */,
+				26D90D9A140D51290036A311 /* SkPathHeap.cpp */,
+				26D90D9B140D51290036A311 /* SkPathHeap.h */,
+				26D90D9C140D51290036A311 /* SkPathMeasure.cpp */,
+				26D90D9D140D51290036A311 /* SkPicture.cpp */,
+				26D90D9E140D51290036A311 /* SkPictureFlat.cpp */,
+				26D90D9F140D51290036A311 /* SkPictureFlat.h */,
+				26D90DA0140D51290036A311 /* SkPicturePlayback.cpp */,
+				26D90DA1140D51290036A311 /* SkPicturePlayback.h */,
+				26D90DA2140D51290036A311 /* SkPictureRecord.cpp */,
+				26D90DA3140D51290036A311 /* SkPictureRecord.h */,
+				26D90DA4140D51290036A311 /* SkPixelRef.cpp */,
+				26D90DA5140D51290036A311 /* SkPoint.cpp */,
+				26D90DA6140D51290036A311 /* SkProcSpriteBlitter.cpp */,
+				26D90DA7140D51290036A311 /* SkPtrRecorder.cpp */,
+				26D90DA8140D51290036A311 /* SkQuadClipper.cpp */,
+				26D90DA9140D51290036A311 /* SkQuadClipper.h */,
+				26D90DAA140D51290036A311 /* SkRasterizer.cpp */,
+				26D90DAB140D51290036A311 /* SkRect.cpp */,
+				26D90DAC140D51290036A311 /* SkRefDict.cpp */,
+				26D90DAD140D51290036A311 /* SkRegion.cpp */,
+				26D90DAE140D51290036A311 /* SkRegionPriv.h */,
+				26D90DAF140D51290036A311 /* SkRegion_path.cpp */,
+				26D90DB0140D51290036A311 /* SkScalar.cpp */,
+				26D90DB1140D51290036A311 /* SkScalerContext.cpp */,
+				26D90DB2140D51290036A311 /* SkScan.cpp */,
+				26D90DB3140D51290036A311 /* SkScanPriv.h */,
+				26D90DB4140D51290036A311 /* SkScan_AntiPath.cpp */,
+				26D90DB5140D51290036A311 /* SkScan_Antihair.cpp */,
+				26D90DB6140D51290036A311 /* SkScan_Hairline.cpp */,
+				26D90DB7140D51290036A311 /* SkScan_Path.cpp */,
+				26D90DB8140D51290036A311 /* SkShader.cpp */,
+				26D90DB9140D51290036A311 /* SkShape.cpp */,
+				26D90DBA140D51290036A311 /* SkSinTable.h */,
+				26D90DBB140D51290036A311 /* SkSpriteBlitter.h */,
+				26D90DBC140D51290036A311 /* SkSpriteBlitterTemplate.h */,
+				26D90DBD140D51290036A311 /* SkSpriteBlitter_ARGB32.cpp */,
+				26D90DBE140D51290036A311 /* SkSpriteBlitter_RGB16.cpp */,
+				26D90DBF140D51290036A311 /* SkStream.cpp */,
+				26D90DC0140D51290036A311 /* SkString.cpp */,
+				26D90DC1140D51290036A311 /* SkStroke.cpp */,
+				26D90DC2140D51290036A311 /* SkStrokerPriv.cpp */,
+				26D90DC3140D51290036A311 /* SkStrokerPriv.h */,
+				26D90DC4140D51290036A311 /* SkTSearch.cpp */,
+				26D90DC5140D51290036A311 /* SkTSort.h */,
+				26D90DC6140D51290036A311 /* SkTemplatesPriv.h */,
+				26D90DC7140D51290036A311 /* SkTextFormatParams.h */,
+				26D90DC8140D51290036A311 /* SkTypeface.cpp */,
+				26D90DC9140D51290036A311 /* SkTypefaceCache.cpp */,
+				26D90DCA140D51290036A311 /* SkTypefaceCache.h */,
+				26D90DCB140D51290036A311 /* SkUnPreMultiply.cpp */,
+				26D90DCC140D51290036A311 /* SkUtils.cpp */,
+				26D90DCD140D51290036A311 /* SkWriter32.cpp */,
+				26D90DCE140D51290036A311 /* SkXfermode.cpp */,
+			);
+			path = core;
+			sourceTree = "<group>";
+		};
+		26D90DD1140D51290036A311 /* ports */ = {
+			isa = PBXGroup;
+			children = (
+				26D90DD2140D51290036A311 /* SkDebug_stdio.cpp */,
+				26D90DD3140D51290036A311 /* SkFontHost_mac_coretext.cpp */,
+				26D90DD4140D51290036A311 /* SkGlobals_global.cpp */,
+				26D90DD5140D51290036A311 /* SkMemory_malloc.cpp */,
+				26D90DD6140D51290036A311 /* SkThread_pthread.cpp */,
+				26D90DD7140D51290036A311 /* SkTime_Unix.cpp */,
+				26D90DD8140D51290036A311 /* SkXMLParser_empty.cpp */,
+				26D90DD9140D51290036A311 /* sk_predefined_gamma.h */,
+			);
+			path = ports;
+			sourceTree = "<group>";
+		};
+		26D90DDA140D51290036A311 /* effects */ = {
+			isa = PBXGroup;
+			children = (
+				26D90DDB140D51290036A311 /* include */,
+				26D90DF2140D51290036A311 /* src */,
+			);
+			name = effects;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90DDB140D51290036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90DDC140D51290036A311 /* Sk1DPathEffect.h */,
+				26D90DDD140D51290036A311 /* Sk2DPathEffect.h */,
+				26D90DDE140D51290036A311 /* SkAvoidXfermode.h */,
+				26D90DDF140D51290036A311 /* SkBlurDrawLooper.h */,
+				26D90DE0140D51290036A311 /* SkBlurMaskFilter.h */,
+				26D90DE1140D51290036A311 /* SkColorMatrix.h */,
+				26D90DE2140D51290036A311 /* SkColorMatrixFilter.h */,
+				26D90DE3140D51290036A311 /* SkCornerPathEffect.h */,
+				26D90DE4140D51290036A311 /* SkDashPathEffect.h */,
+				26D90DE5140D51290036A311 /* SkDiscretePathEffect.h */,
+				26D90DE6140D51290036A311 /* SkDrawExtraPathEffect.h */,
+				26D90DE7140D51290036A311 /* SkEmbossMaskFilter.h */,
+				26D90DE8140D51290036A311 /* SkGradientShader.h */,
+				26D90DE9140D51290036A311 /* SkGroupShape.h */,
+				26D90DEA140D51290036A311 /* SkKernel33MaskFilter.h */,
+				26D90DEB140D51290036A311 /* SkLayerDrawLooper.h */,
+				26D90DEC140D51290036A311 /* SkLayerRasterizer.h */,
+				26D90DED140D51290036A311 /* SkPaintFlagsDrawFilter.h */,
+				26D90DEE140D51290036A311 /* SkPixelXorXfermode.h */,
+				26D90DEF140D51290036A311 /* SkPorterDuff.h */,
+				26D90DF0140D51290036A311 /* SkRectShape.h */,
+				26D90DF1140D51290036A311 /* SkTransparentShader.h */,
+			);
+			name = include;
+			path = include/effects;
+			sourceTree = "<group>";
+		};
+		26D90DF2140D51290036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90DF3140D51290036A311 /* Sk1DPathEffect.cpp */,
+				26D90DF4140D51290036A311 /* Sk2DPathEffect.cpp */,
+				26D90DF5140D51290036A311 /* SkAvoidXfermode.cpp */,
+				26D90DF6140D51290036A311 /* SkBitmapCache.cpp */,
+				26D90DF7140D51290036A311 /* SkBitmapCache.h */,
+				26D90DF8140D51290036A311 /* SkBlurDrawLooper.cpp */,
+				26D90DF9140D51290036A311 /* SkBlurMask.cpp */,
+				26D90DFA140D51290036A311 /* SkBlurMask.h */,
+				26D90DFB140D51290036A311 /* SkBlurMaskFilter.cpp */,
+				26D90DFC140D51290036A311 /* SkColorFilters.cpp */,
+				26D90DFD140D51290036A311 /* SkColorMatrixFilter.cpp */,
+				26D90DFE140D51290036A311 /* SkCornerPathEffect.cpp */,
+				26D90DFF140D51290036A311 /* SkDashPathEffect.cpp */,
+				26D90E00140D51290036A311 /* SkDiscretePathEffect.cpp */,
+				26D90E01140D51290036A311 /* SkEmbossMask.cpp */,
+				26D90E02140D51290036A311 /* SkEmbossMask.h */,
+				26D90E03140D51290036A311 /* SkEmbossMaskFilter.cpp */,
+				26D90E04140D51290036A311 /* SkEmbossMask_Table.h */,
+				26D90E05140D51290036A311 /* SkGradientShader.cpp */,
+				26D90E06140D51290036A311 /* SkGroupShape.cpp */,
+				26D90E07140D51290036A311 /* SkKernel33MaskFilter.cpp */,
+				26D90E08140D51290036A311 /* SkLayerDrawLooper.cpp */,
+				26D90E09140D51290036A311 /* SkLayerRasterizer.cpp */,
+				26D90E0A140D51290036A311 /* SkPaintFlagsDrawFilter.cpp */,
+				26D90E0B140D51290036A311 /* SkPixelXorXfermode.cpp */,
+				26D90E0C140D51290036A311 /* SkPorterDuff.cpp */,
+				26D90E0D140D51290036A311 /* SkRadialGradient_Table.h */,
+				26D90E0E140D51290036A311 /* SkRectShape.cpp */,
+				26D90E0F140D51290036A311 /* SkTransparentShader.cpp */,
+			);
+			name = src;
+			path = src/effects;
+			sourceTree = "<group>";
+		};
+		26D90E10140D51290036A311 /* gpu */ = {
+			isa = PBXGroup;
+			children = (
+				26D90E11140D51290036A311 /* gpu */,
+				26D90E86140D51290036A311 /* include */,
+				26D90E8B140D51290036A311 /* src */,
+			);
+			name = gpu;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90E11140D51290036A311 /* gpu */ = {
+			isa = PBXGroup;
+			children = (
+				26D90E12140D51290036A311 /* include */,
+				26D90E4C140D51290036A311 /* src */,
+			);
+			path = gpu;
+			sourceTree = "<group>";
+		};
+		26D90E12140D51290036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90E13140D51290036A311 /* FlingState.h */,
+				26D90E14140D51290036A311 /* GrGLDefines.h */,
+				26D90E15140D51290036A311 /* GrTemplates.h */,
+				26D90E16140D51290036A311 /* GrAllocPool.h */,
+				26D90E17140D51290036A311 /* GrAllocator.h */,
+				26D90E18140D51290036A311 /* GrAtlas.h */,
+				26D90E19140D51290036A311 /* GrClip.h */,
+				26D90E1A140D51290036A311 /* GrClipIterator.h */,
+				26D90E1B140D51290036A311 /* GrColor.h */,
+				26D90E1C140D51290036A311 /* GrConfig.h */,
+				26D90E1D140D51290036A311 /* GrContext.h */,
+				26D90E1E140D51290036A311 /* GrContext_impl.h */,
+				26D90E1F140D51290036A311 /* GrDrawTarget.h */,
+				26D90E20140D51290036A311 /* GrFontScaler.h */,
+				26D90E21140D51290036A311 /* GrGLConfig.h */,
+				26D90E22140D51290036A311 /* GrGLConfig_chrome.h */,
+				26D90E23140D51290036A311 /* GrGLInterface.h */,
+				26D90E24140D51290036A311 /* GrGeometryBuffer.h */,
+				26D90E25140D51290036A311 /* GrGlyph.h */,
+				26D90E26140D51290036A311 /* GrGpu.h */,
+				26D90E27140D51290036A311 /* GrGpuVertex.h */,
+				26D90E28140D51290036A311 /* GrIPoint.h */,
+				26D90E29140D51290036A311 /* GrInOrderDrawBuffer.h */,
+				26D90E2A140D51290036A311 /* GrIndexBuffer.h */,
+				26D90E2B140D51290036A311 /* GrInstanceCounter.h */,
+				26D90E2C140D51290036A311 /* GrKey.h */,
+				26D90E2D140D51290036A311 /* GrMatrix.h */,
+				26D90E2E140D51290036A311 /* GrMesh.h */,
+				26D90E2F140D51290036A311 /* GrNoncopyable.h */,
+				26D90E30140D51290036A311 /* GrPaint.h */,
+				26D90E31140D51290036A311 /* GrPath.h */,
+				26D90E32140D51290036A311 /* GrPathRenderer.h */,
+				26D90E33140D51290036A311 /* GrPathSink.h */,
+				26D90E34140D51290036A311 /* GrPlotMgr.h */,
+				26D90E35140D51290036A311 /* GrPoint.h */,
+				26D90E36140D51290036A311 /* GrRandom.h */,
+				26D90E37140D51290036A311 /* GrRect.h */,
+				26D90E38140D51290036A311 /* GrRectanizer.h */,
+				26D90E39140D51290036A311 /* GrRefCnt.h */,
+				26D90E3A140D51290036A311 /* GrResource.h */,
+				26D90E3B140D51290036A311 /* GrSamplerState.h */,
+				26D90E3C140D51290036A311 /* GrScalar.h */,
+				26D90E3D140D51290036A311 /* GrStencil.h */,
+				26D90E3E140D51290036A311 /* GrStopwatch.h */,
+				26D90E3F140D51290036A311 /* GrStringBuilder.h */,
+				26D90E40140D51290036A311 /* GrTArray.h */,
+				26D90E41140D51290036A311 /* GrTBSearch.h */,
+				26D90E42140D51290036A311 /* GrTDArray.h */,
+				26D90E43140D51290036A311 /* GrTHashCache.h */,
+				26D90E44140D51290036A311 /* GrTLList.h */,
+				26D90E45140D51290036A311 /* GrTesselatedPathRenderer.h */,
+				26D90E46140D51290036A311 /* GrTextContext.h */,
+				26D90E47140D51290036A311 /* GrTextStrike.h */,
+				26D90E48140D51290036A311 /* GrTexture.h */,
+				26D90E49140D51290036A311 /* GrTypes.h */,
+				26D90E4A140D51290036A311 /* GrUserConfig.h */,
+				26D90E4B140D51290036A311 /* GrVertexBuffer.h */,
+			);
+			path = include;
+			sourceTree = "<group>";
+		};
+		26D90E4C140D51290036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90E4D140D51290036A311 /* GrAddPathRenderers_none.cpp */,
+				26D90E4E140D51290036A311 /* GrDefaultPathRenderer.cpp */,
+				26D90E4F140D51290036A311 /* GrDefaultPathRenderer.h */,
+				26D90E50140D51290036A311 /* GrGLStencilBuffer.cpp */,
+				26D90E51140D51290036A311 /* GrGLStencilBuffer.h */,
+				26D90E52140D51290036A311 /* GrPathRenderer.h */,
+				26D90E53140D51290036A311 /* GrPathRendererChain.cpp */,
+				26D90E54140D51290036A311 /* GrPathRendererChain.h */,
+				26D90E55140D51290036A311 /* GrStencilBuffer.cpp */,
+				26D90E56140D51290036A311 /* GrRenderTarget.cpp */,
+				26D90E57140D51290036A311 /* GrGLRenderTarget.cpp */,
+				26D90E58140D51290036A311 /* GrGLRenderTarget.h */,
+				26D90E59140D51290036A311 /* GrGLTexture.h */,
+				26D90E5A140D51290036A311 /* GrResourceCache.cpp */,
+				26D90E5B140D51290036A311 /* GrResourceCache.h */,
+				26D90E5C140D51290036A311 /* FlingState.cpp */,
+				26D90E5D140D51290036A311 /* GrDrawMesh.cpp */,
+				26D90E5E140D51290036A311 /* GrAllocPool.cpp */,
+				26D90E5F140D51290036A311 /* GrAtlas.cpp */,
+				26D90E60140D51290036A311 /* GrBinHashKey.h */,
+				26D90E61140D51290036A311 /* GrBufferAllocPool.cpp */,
+				26D90E62140D51290036A311 /* GrBufferAllocPool.h */,
+				26D90E63140D51290036A311 /* GrClip.cpp */,
+				26D90E64140D51290036A311 /* GrContext.cpp */,
+				26D90E65140D51290036A311 /* GrDrawTarget.cpp */,
+				26D90E66140D51290036A311 /* GrGLDefaultInterface_none.cpp */,
+				26D90E67140D51290036A311 /* GrGLIndexBuffer.cpp */,
+				26D90E68140D51290036A311 /* GrGLInterface.cpp */,
+				26D90E69140D51290036A311 /* GrGLProgram.cpp */,
+				26D90E6A140D51290036A311 /* GrGLProgram.h */,
+				26D90E6B140D51290036A311 /* GrGLTexture.cpp */,
+				26D90E6C140D51290036A311 /* GrGLUtil.cpp */,
+				26D90E6D140D51290036A311 /* GrGLVertexBuffer.cpp */,
+				26D90E6E140D51290036A311 /* GrGpu.cpp */,
+				26D90E6F140D51290036A311 /* GrGpuFactory.cpp */,
+				26D90E70140D51290036A311 /* GrGpuGL.cpp */,
+				26D90E71140D51290036A311 /* GrGpuGL.h */,
+				26D90E72140D51290036A311 /* GrGpuGLFixed.cpp */,
+				26D90E73140D51290036A311 /* GrGpuGLFixed.h */,
+				26D90E74140D51290036A311 /* GrGpuGLShaders.cpp */,
+				26D90E75140D51290036A311 /* GrGpuGLShaders.h */,
+				26D90E76140D51290036A311 /* GrInOrderDrawBuffer.cpp */,
+				26D90E77140D51290036A311 /* GrMatrix.cpp */,
+				26D90E78140D51290036A311 /* GrMemory.cpp */,
+				26D90E79140D51290036A311 /* GrPathRenderer.cpp */,
+				26D90E7A140D51290036A311 /* GrPathUtils.cpp */,
+				26D90E7B140D51290036A311 /* GrPathUtils.h */,
+				26D90E7C140D51290036A311 /* GrRectanizer.cpp */,
+				26D90E7D140D51290036A311 /* GrRedBlackTree.h */,
+				26D90E7E140D51290036A311 /* GrResource.cpp */,
+				26D90E7F140D51290036A311 /* GrStencil.cpp */,
+				26D90E81140D51290036A311 /* GrTextContext.cpp */,
+				26D90E82140D51290036A311 /* GrTextStrike.cpp */,
+				26D90E83140D51290036A311 /* GrTextStrike_impl.h */,
+				26D90E84140D51290036A311 /* GrTexture.cpp */,
+				26D90E85140D51290036A311 /* gr_unittests.cpp */,
+			);
+			path = src;
+			sourceTree = "<group>";
+		};
+		26D90E86140D51290036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90E87140D51290036A311 /* SkGpuCanvas.h */,
+				26D90E88140D51290036A311 /* SkGpuDevice.h */,
+				26D90E89140D51290036A311 /* SkGr.h */,
+				26D90E8A140D51290036A311 /* SkGrTexturePixelRef.h */,
+			);
+			name = include;
+			path = include/gpu;
+			sourceTree = "<group>";
+		};
+		26D90E8B140D51290036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90E8C140D51290036A311 /* GrPrintf_skia.cpp */,
+				26D90E8D140D51290036A311 /* SkGpuCanvas.cpp */,
+				26D90E8E140D51290036A311 /* SkGpuDevice.cpp */,
+				26D90E8F140D51290036A311 /* SkGr.cpp */,
+				26D90E90140D51290036A311 /* SkGrFontScaler.cpp */,
+				26D90E91140D51290036A311 /* SkGrTexturePixelRef.cpp */,
+			);
+			name = src;
+			path = src/gpu;
+			sourceTree = "<group>";
+		};
+		26D90E92140D51290036A311 /* images */ = {
+			isa = PBXGroup;
+			children = (
+				26D90E93140D51290036A311 /* include */,
+				26D90E9C140D51290036A311 /* src */,
+			);
+			name = images;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90E93140D51290036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90E94140D51290036A311 /* SkFlipPixelRef.h */,
+				26D90E95140D51290036A311 /* SkImageRef_GlobalPool.h */,
+				26D90E96140D51290036A311 /* SkImageRef.h */,
+				26D90E97140D51290036A311 /* SkJpegUtility.h */,
+				26D90E98140D51290036A311 /* SkMovie.h */,
+				26D90E99140D51290036A311 /* SkPageFlipper.h */,
+				26D90E9A140D51290036A311 /* SkImageDecoder.h */,
+				26D90E9B140D51290036A311 /* SkImageEncoder.h */,
+			);
+			name = include;
+			path = include/images;
+			sourceTree = "<group>";
+		};
+		26D90E9C140D51290036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90E9D140D51290036A311 /* images */,
+			);
+			path = src;
+			sourceTree = "<group>";
+		};
+		26D90E9D140D51290036A311 /* images */ = {
+			isa = PBXGroup;
+			children = (
+				26D90E9E140D51290036A311 /* SkMovie.cpp */,
+				26D90E9F140D51290036A311 /* SkBitmap_RLEPixels.h */,
+				26D90EA0140D51290036A311 /* SkCreateRLEPixelRef.cpp */,
+				26D90EA1140D51290036A311 /* SkFDStream.cpp */,
+				26D90EA2140D51290036A311 /* SkFlipPixelRef.cpp */,
+				26D90EA3140D51290036A311 /* SkImageDecoder.cpp */,
+				26D90EA4140D51290036A311 /* SkImageEncoder.cpp */,
+				26D90EA5140D51290036A311 /* SkImageDecoder_Factory.cpp */,
+				26D90EA6140D51290036A311 /* SkImageEncoder_Factory.cpp */,
+				26D90EA7140D51290036A311 /* SkPageFlipper.cpp */,
+				26D90EA8140D51290036A311 /* SkImageRef.cpp */,
+				26D90EA9140D51290036A311 /* SkImageRefPool.cpp */,
+				26D90EAA140D51290036A311 /* SkImageRefPool.h */,
+				26D90EAB140D51290036A311 /* SkImageRef_GlobalPool.cpp */,
+			);
+			path = images;
+			sourceTree = "<group>";
+		};
+		26D90EAF140D51290036A311 /* opts */ = {
+			isa = PBXGroup;
+			children = (
+				26D90EB1140D51290036A311 /* SkBitmapProcState_opts_arm.cpp */,
+				26D9112F140D53C30036A311 /* SkBlitRow_opts_none.cpp */,
+				26D91130140D53C30036A311 /* SkUtils_opts_none.cpp */,
+				26D90EB6140D51290036A311 /* SkBlitRow_opts_SSE2.h */,
+				26D90EB8140D51290036A311 /* SkUtils_opts_SSE2.h */,
+			);
+			name = opts;
+			path = ../../src/opts;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90EBC140D51290036A311 /* pipe */ = {
+			isa = PBXGroup;
+			children = (
+				26D90EBD140D51290036A311 /* SkGPipe.h */,
+				26D90EBE140D51290036A311 /* SkGPipeRead.cpp */,
+				26D90EBF140D51290036A311 /* SkGPipeWrite.cpp */,
+			);
+			name = pipe;
+			path = ../../src/pipe;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90EC0140D51290036A311 /* pdf */ = {
+			isa = PBXGroup;
+			children = (
+				26D90ECF140D51290036A311 /* src */,
+				26D90EC1140D51290036A311 /* include */,
+			);
+			name = pdf;
+			sourceTree = "<group>";
+		};
+		26D90EC1140D51290036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90EC2140D51290036A311 /* SkBitSet.h */,
+				26D90EC3140D51290036A311 /* SkPDFCatalog.h */,
+				26D90EC4140D51290036A311 /* SkPDFDevice.h */,
+				26D90EC5140D51290036A311 /* SkPDFDocument.h */,
+				26D90EC6140D51290036A311 /* SkPDFFont.h */,
+				26D90EC7140D51290036A311 /* SkPDFFormXObject.h */,
+				26D90EC8140D51290036A311 /* SkPDFGraphicState.h */,
+				26D90EC9140D51290036A311 /* SkPDFImage.h */,
+				26D90ECA140D51290036A311 /* SkPDFPage.h */,
+				26D90ECB140D51290036A311 /* SkPDFShader.h */,
+				26D90ECC140D51290036A311 /* SkPDFStream.h */,
+				26D90ECD140D51290036A311 /* SkPDFTypes.h */,
+				26D90ECE140D51290036A311 /* SkPDFUtils.h */,
+			);
+			name = include;
+			sourceTree = "<group>";
+		};
+		26D90ECF140D51290036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90ED0140D51290036A311 /* SkBitSet.cpp */,
+				26D90ED1140D51290036A311 /* SkPDFCatalog.cpp */,
+				26D90ED2140D51290036A311 /* SkPDFDevice.cpp */,
+				26D90ED3140D51290036A311 /* SkPDFDocument.cpp */,
+				26D90ED4140D51290036A311 /* SkPDFFont.cpp */,
+				26D90ED5140D51290036A311 /* SkPDFFormXObject.cpp */,
+				26D90ED6140D51290036A311 /* SkPDFGraphicState.cpp */,
+				26D90ED7140D51290036A311 /* SkPDFImage.cpp */,
+				26D90ED8140D51290036A311 /* SkPDFPage.cpp */,
+				26D90ED9140D51290036A311 /* SkPDFShader.cpp */,
+				26D90EDA140D51290036A311 /* SkPDFStream.cpp */,
+				26D90EDB140D51290036A311 /* SkPDFTypes.cpp */,
+				26D90EDC140D51290036A311 /* SkPDFUtils.cpp */,
+			);
+			name = src;
+			sourceTree = "<group>";
+		};
+		26D90EDD140D51290036A311 /* svg */ = {
+			isa = PBXGroup;
+			children = (
+				26D90EDE140D51290036A311 /* include */,
+				26D90EE4140D51290036A311 /* src */,
+			);
+			name = svg;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90EDE140D51290036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90EDF140D51290036A311 /* SkSVGAttribute.h */,
+				26D90EE0140D51290036A311 /* SkSVGBase.h */,
+				26D90EE1140D51290036A311 /* SkSVGPaintState.h */,
+				26D90EE2140D51290036A311 /* SkSVGParser.h */,
+				26D90EE3140D51290036A311 /* SkSVGTypes.h */,
+			);
+			name = include;
+			path = include/svg;
+			sourceTree = "<group>";
+		};
+		26D90EE4140D51290036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90EE5140D51290036A311 /* SkSVGCircle.cpp */,
+				26D90EE6140D51290036A311 /* SkSVGCircle.h */,
+				26D90EE7140D51290036A311 /* SkSVGClipPath.cpp */,
+				26D90EE8140D51290036A311 /* SkSVGClipPath.h */,
+				26D90EE9140D51290036A311 /* SkSVGDefs.cpp */,
+				26D90EEA140D51290036A311 /* SkSVGDefs.h */,
+				26D90EEB140D51290036A311 /* SkSVGElements.cpp */,
+				26D90EEC140D51290036A311 /* SkSVGElements.h */,
+				26D90EED140D51290036A311 /* SkSVGEllipse.cpp */,
+				26D90EEE140D51290036A311 /* SkSVGEllipse.h */,
+				26D90EEF140D51290036A311 /* SkSVGFeColorMatrix.cpp */,
+				26D90EF0140D51290036A311 /* SkSVGFeColorMatrix.h */,
+				26D90EF1140D51290036A311 /* SkSVGFilter.cpp */,
+				26D90EF2140D51290036A311 /* SkSVGFilter.h */,
+				26D90EF3140D51290036A311 /* SkSVGG.cpp */,
+				26D90EF4140D51290036A311 /* SkSVGG.h */,
+				26D90EF5140D51290036A311 /* SkSVGGradient.cpp */,
+				26D90EF6140D51290036A311 /* SkSVGGradient.h */,
+				26D90EF7140D51290036A311 /* SkSVGGroup.cpp */,
+				26D90EF8140D51290036A311 /* SkSVGGroup.h */,
+				26D90EF9140D51290036A311 /* SkSVGImage.cpp */,
+				26D90EFA140D51290036A311 /* SkSVGImage.h */,
+				26D90EFB140D51290036A311 /* SkSVGLine.cpp */,
+				26D90EFC140D51290036A311 /* SkSVGLine.h */,
+				26D90EFD140D51290036A311 /* SkSVGLinearGradient.cpp */,
+				26D90EFE140D51290036A311 /* SkSVGLinearGradient.h */,
+				26D90EFF140D512A0036A311 /* SkSVGMask.cpp */,
+				26D90F00140D512A0036A311 /* SkSVGMask.h */,
+				26D90F01140D512A0036A311 /* SkSVGMetadata.cpp */,
+				26D90F02140D512A0036A311 /* SkSVGMetadata.h */,
+				26D90F03140D512A0036A311 /* SkSVGPaintState.cpp */,
+				26D90F04140D512A0036A311 /* SkSVGParser.cpp */,
+				26D90F05140D512A0036A311 /* SkSVGPath.cpp */,
+				26D90F06140D512A0036A311 /* SkSVGPath.h */,
+				26D90F07140D512A0036A311 /* SkSVGPolygon.cpp */,
+				26D90F08140D512A0036A311 /* SkSVGPolygon.h */,
+				26D90F09140D512A0036A311 /* SkSVGPolyline.cpp */,
+				26D90F0A140D512A0036A311 /* SkSVGPolyline.h */,
+				26D90F0B140D512A0036A311 /* SkSVGRadialGradient.cpp */,
+				26D90F0C140D512A0036A311 /* SkSVGRadialGradient.h */,
+				26D90F0D140D512A0036A311 /* SkSVGRect.cpp */,
+				26D90F0E140D512A0036A311 /* SkSVGRect.h */,
+				26D90F0F140D512A0036A311 /* SkSVGSVG.cpp */,
+				26D90F10140D512A0036A311 /* SkSVGSVG.h */,
+				26D90F11140D512A0036A311 /* SkSVGStop.cpp */,
+				26D90F12140D512A0036A311 /* SkSVGStop.h */,
+				26D90F13140D512A0036A311 /* SkSVGSymbol.cpp */,
+				26D90F14140D512A0036A311 /* SkSVGSymbol.h */,
+				26D90F15140D512A0036A311 /* SkSVGText.cpp */,
+				26D90F16140D512A0036A311 /* SkSVGText.h */,
+				26D90F17140D512A0036A311 /* SkSVGUse.cpp */,
+			);
+			name = src;
+			path = src/svg;
+			sourceTree = "<group>";
+		};
+		26D90F18140D512A0036A311 /* utils */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F19140D512A0036A311 /* include */,
+				26D90F30140D512A0036A311 /* src */,
+			);
+			name = utils;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90F19140D512A0036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F1A140D512A0036A311 /* mac */,
+				26D90F1C140D512A0036A311 /* SkBoundaryPatch.h */,
+				26D90F1D140D512A0036A311 /* SkCamera.h */,
+				26D90F1E140D512A0036A311 /* SkCubicInterval.h */,
+				26D90F1F140D512A0036A311 /* SkCullPoints.h */,
+				26D90F20140D512A0036A311 /* SkDumpCanvas.h */,
+				26D90F21140D512A0036A311 /* SkEGLContext.h */,
+				26D90F22140D512A0036A311 /* SkGLCanvas.h */,
+				26D90F23140D512A0036A311 /* SkInterpolator.h */,
+				26D90F24140D512A0036A311 /* SkLayer.h */,
+				26D90F25140D512A0036A311 /* SkMatrix44.h */,
+				26D90F26140D512A0036A311 /* SkMeshUtils.h */,
+				26D90F27140D512A0036A311 /* SkNWayCanvas.h */,
+				26D90F28140D512A0036A311 /* SkNinePatch.h */,
+				26D90F29140D512A0036A311 /* SkParse.h */,
+				26D90F2A140D512A0036A311 /* SkParsePaint.h */,
+				26D90F2B140D512A0036A311 /* SkParsePath.h */,
+				26D90F2C140D512A0036A311 /* SkProxyCanvas.h */,
+				26D90F2D140D512A0036A311 /* SkSfntUtils.h */,
+				26D90F2E140D512A0036A311 /* SkTextBox.h */,
+				26D90F2F140D512A0036A311 /* SkUnitMappers.h */,
+			);
+			name = include;
+			path = include/utils;
+			sourceTree = "<group>";
+		};
+		26D90F1A140D512A0036A311 /* mac */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F1B140D512A0036A311 /* SkCGUtils.h */,
+			);
+			path = mac;
+			sourceTree = "<group>";
+		};
+		26D90F30140D512A0036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F31140D512A0036A311 /* mac */,
+				26D90F33140D512A0036A311 /* SkBoundaryPatch.cpp */,
+				26D90F34140D512A0036A311 /* SkCamera.cpp */,
+				26D90F35140D512A0036A311 /* SkColorMatrix.cpp */,
+				26D90F36140D512A0036A311 /* SkCubicInterval.cpp */,
+				26D90F37140D512A0036A311 /* SkCullPoints.cpp */,
+				26D90F38140D512A0036A311 /* SkDumpCanvas.cpp */,
+				26D90F39140D512A0036A311 /* SkInterpolator.cpp */,
+				26D90F3A140D512A0036A311 /* SkLayer.cpp */,
+				26D90F3B140D512A0036A311 /* SkMatrix44.cpp */,
+				26D90F3C140D512A0036A311 /* SkMeshUtils.cpp */,
+				26D90F3D140D512A0036A311 /* SkNWayCanvas.cpp */,
+				26D90F3E140D512A0036A311 /* SkNinePatch.cpp */,
+				26D90F3F140D512A0036A311 /* SkOSFile.cpp */,
+				26D90F40140D512A0036A311 /* SkParse.cpp */,
+				26D90F41140D512A0036A311 /* SkParseColor.cpp */,
+				26D90F42140D512A0036A311 /* SkParsePath.cpp */,
+				26D90F43140D512A0036A311 /* SkProxyCanvas.cpp */,
+				26D90F44140D512A0036A311 /* SkSfntUtils.cpp */,
+				26D90F45140D512A0036A311 /* SkUnitMappers.cpp */,
+			);
+			name = src;
+			path = src/utils;
+			sourceTree = "<group>";
+		};
+		26D90F31140D512A0036A311 /* mac */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F32140D512A0036A311 /* SkCreateCGImageRef.cpp */,
+			);
+			path = mac;
+			sourceTree = "<group>";
+		};
+		26D90F46140D512A0036A311 /* views */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F47140D512A0036A311 /* include */,
+				26D90F5A140D512A0036A311 /* src */,
+			);
+			name = views;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90F47140D512A0036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F48140D512A0036A311 /* SkApplication.h */,
+				26D90F49140D512A0036A311 /* SkBGViewArtist.h */,
+				26D90F4A140D512A0036A311 /* SkBorderView.h */,
+				26D90F4B140D512A0036A311 /* SkEvent.h */,
+				26D90F4C140D512A0036A311 /* SkEventSink.h */,
+				26D90F4D140D512A0036A311 /* SkImageView.h */,
+				26D90F4E140D512A0036A311 /* SkKey.h */,
+				26D90F4F140D512A0036A311 /* SkOSMenu.h */,
+				26D90F50140D512A0036A311 /* SkProgressBarView.h */,
+				26D90F51140D512A0036A311 /* SkScrollBarView.h */,
+				26D90F52140D512A0036A311 /* SkStackViewLayout.h */,
+				26D90F53140D512A0036A311 /* SkSystemEventTypes.h */,
+				26D90F54140D512A0036A311 /* SkTouchGesture.h */,
+				26D90F55140D512A0036A311 /* SkView.h */,
+				26D90F56140D512A0036A311 /* SkViewInflate.h */,
+				26D90F57140D512A0036A311 /* SkWidget.h */,
+				26D90F58140D512A0036A311 /* SkWidgetViews.h */,
+				26D90F59140D512A0036A311 /* SkWindow.h */,
+			);
+			name = include;
+			path = include/views;
+			sourceTree = "<group>";
+		};
+		26D90F5A140D512A0036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F5B140D512A0036A311 /* SkBGViewArtist.cpp */,
+				26D90F5C140D512A0036A311 /* SkEvent.cpp */,
+				26D90F5D140D512A0036A311 /* SkEventSink.cpp */,
+				26D90F5E140D512A0036A311 /* SkImageView.cpp */,
+				26D90F5F140D512A0036A311 /* SkListView.cpp */,
+				26D90F61140D512A0036A311 /* SkOSMenu.cpp */,
+				26D90F62140D512A0036A311 /* SkParsePaint.cpp */,
+				26D90F67140D512A0036A311 /* SkStackViewLayout.cpp */,
+				26D90F68140D512A0036A311 /* SkStaticTextView.cpp */,
+				26D90F69140D512A0036A311 /* SkTagList.cpp */,
+				26D90F6A140D512A0036A311 /* SkTagList.h */,
+				26D90F6B140D512A0036A311 /* SkTextBox.cpp */,
+				26D90F6C140D512A0036A311 /* SkTouchGesture.cpp */,
+				26D90F6D140D512A0036A311 /* SkView.cpp */,
+				26D90F6E140D512A0036A311 /* SkViewInflate.cpp */,
+				26D90F6F140D512A0036A311 /* SkViewPriv.cpp */,
+				26D90F70140D512A0036A311 /* SkViewPriv.h */,
+				26D90F71140D512A0036A311 /* SkWidget.cpp */,
+				26D90F73140D512A0036A311 /* SkWidgets.cpp */,
+				26D90F74140D512A0036A311 /* SkWindow.cpp */,
+			);
+			name = src;
+			path = src/views;
+			sourceTree = "<group>";
+		};
+		26D90F75140D512A0036A311 /* xml */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F76140D512A0036A311 /* include */,
+				26D90F7D140D512A0036A311 /* src */,
+			);
+			name = xml;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		26D90F76140D512A0036A311 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F77140D512A0036A311 /* SkBML_WXMLParser.h */,
+				26D90F78140D512A0036A311 /* SkBML_XMLParser.h */,
+				26D90F79140D512A0036A311 /* SkDOM.h */,
+				26D90F7A140D512A0036A311 /* SkJS.h */,
+				26D90F7B140D512A0036A311 /* SkXMLParser.h */,
+				26D90F7C140D512A0036A311 /* SkXMLWriter.h */,
+			);
+			name = include;
+			path = include/xml;
+			sourceTree = "<group>";
+		};
+		26D90F7D140D512A0036A311 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26D90F7E140D512A0036A311 /* SkDOM.cpp */,
+				26D90F7F140D512A0036A311 /* SkXMLParser.cpp */,
+				26D90F80140D512A0036A311 /* SkXMLWriter.cpp */,
+			);
+			name = src;
+			path = src/xml;
+			sourceTree = "<group>";
+		};
+		26D9113C140D54720036A311 /* iOS */ = {
+			isa = PBXGroup;
+			children = (
+				26371975140D6C19006CC295 /* SkUIView.h */,
+				26371976140D6C19006CC295 /* SkUIView.mm */,
+				26D91153140D54E40036A311 /* SkEventNotifier.h */,
+				26D91154140D54E40036A311 /* SkEventNotifier.mm */,
+				26D9118E140D558A0036A311 /* SkOSFile_iOS.mm */,
+				26D91192140D55980036A311 /* SkOSWindow_iOS.h */,
+				26D91193140D55980036A311 /* SkOSWindow_iOS.mm */,
+			);
+			name = iOS;
+			sourceTree = "<group>";
+		};
+		26D912E9140D5FCC0036A311 /* xcconfig */ = {
+			isa = PBXGroup;
+			children = (
+				26D912EA140D5FCC0036A311 /* SkiOSSampleApp-Debug.xcconfig */,
+				26D912EB140D5FCC0036A311 /* SkiOSSampleApp-Release.xcconfig */,
+				26D912EC140D5FCC0036A311 /* SkiOSSampleApp-Base.xcconfig */,
+			);
+			name = xcconfig;
+			sourceTree = "<group>";
+		};
+		2860E324111B887F00E27156 /* iPhone */ = {
+			isa = PBXGroup;
+			children = (
+				2860E325111B887F00E27156 /* AppDelegate_iPhone.h */,
+				2860E326111B887F00E27156 /* AppDelegate_iPhone.m */,
+				2860E327111B887F00E27156 /* MainWindow_iPhone.xib */,
+			);
+			path = iPhone;
+			sourceTree = "<group>";
+		};
+		2860E32A111B888700E27156 /* iPad */ = {
+			isa = PBXGroup;
+			children = (
+				2860E32B111B888700E27156 /* AppDelegate_iPad.h */,
+				2860E32C111B888700E27156 /* AppDelegate_iPad.m */,
+				2860E32D111B888700E27156 /* MainWindow_iPad.xib */,
+			);
+			path = iPad;
+			sourceTree = "<group>";
+		};
+		28EEBF621118D79A00187D67 /* Shared */ = {
+			isa = PBXGroup;
+			children = (
+				26D9113B140D54720036A311 /* skia_ios.mm */,
+				26D912E9140D5FCC0036A311 /* xcconfig */,
+				26D9113C140D54720036A311 /* iOS */,
+				26D90C43140D51290036A311 /* Skia */,
+			);
+			name = Shared;
+			sourceTree = "<group>";
+		};
+		29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
+			isa = PBXGroup;
+			children = (
+				26D91302140D60330036A311 /* SimpleApp.h */,
+				26D91303140D60330036A311 /* SimpleApp.mm */,
+				2860E32A111B888700E27156 /* iPad */,
+				2860E324111B887F00E27156 /* iPhone */,
+				28EEBF621118D79A00187D67 /* Shared */,
+				29B97315FDCFA39411CA2CEA /* Other Sources */,
+				29B97323FDCFA39411CA2CEA /* Frameworks */,
+				19C28FACFE9D520D11CA2CBB /* Products */,
+			);
+			name = CustomTemplate;
+			sourceTree = "<group>";
+		};
+		29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+			isa = PBXGroup;
+			children = (
+				8D1107310486CEB800E47090 /* SimpleiOSApp-Info.plist */,
+			);
+			name = "Other Sources";
+			sourceTree = "<group>";
+		};
+		29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				26D9111D140D52CD0036A311 /* CoreFoundation.framework */,
+				26D9111F140D52CD0036A311 /* CoreText.framework */,
+				1DF5F4DF0D08C38300B7A737 /* UIKit.framework */,
+				1D30AB110D05D00D00671497 /* Foundation.framework */,
+				288765FC0DF74451002DB57D /* CoreGraphics.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		1D6058900D05DD3D006BFB54 /* SimpleiOSApp */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "SimpleiOSApp" */;
+			buildPhases = (
+				1D60588D0D05DD3D006BFB54 /* Resources */,
+				1D60588E0D05DD3D006BFB54 /* Sources */,
+				1D60588F0D05DD3D006BFB54 /* Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = SimpleiOSApp;
+			productName = iosshell;
+			productReference = 1D6058910D05DD3D006BFB54 /* SimpleiOSApp.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		29B97313FDCFA39411CA2CEA /* Project object */ = {
+			isa = PBXProject;
+			buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SimpleiOSApp" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 1;
+			knownRegions = (
+				English,
+				Japanese,
+				French,
+				German,
+			);
+			mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				1D6058900D05DD3D006BFB54 /* SimpleiOSApp */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		1D60588D0D05DD3D006BFB54 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				2860E329111B887F00E27156 /* MainWindow_iPhone.xib in Resources */,
+				2860E32F111B888700E27156 /* MainWindow_iPad.xib in Resources */,
+				26D912ED140D5FCC0036A311 /* SkiOSSampleApp-Debug.xcconfig in Resources */,
+				26D912EE140D5FCC0036A311 /* SkiOSSampleApp-Release.xcconfig in Resources */,
+				26D912EF140D5FCC0036A311 /* SkiOSSampleApp-Base.xcconfig in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		1D60588E0D05DD3D006BFB54 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				2860E328111B887F00E27156 /* AppDelegate_iPhone.m in Sources */,
+				2860E32E111B888700E27156 /* AppDelegate_iPad.m in Sources */,
+				26D90F81140D512A0036A311 /* SkAnimateActive.cpp in Sources */,
+				26D90F82140D512A0036A311 /* SkAnimateBase.cpp in Sources */,
+				26D90F83140D512A0036A311 /* SkAnimateField.cpp in Sources */,
+				26D90F84140D512A0036A311 /* SkAnimateMaker.cpp in Sources */,
+				26D90F85140D512A0036A311 /* SkAnimateSet.cpp in Sources */,
+				26D90F86140D512A0036A311 /* SkAnimator.cpp in Sources */,
+				26D90F87140D512A0036A311 /* SkAnimatorScript.cpp in Sources */,
+				26D90F88140D512A0036A311 /* SkBase64.cpp in Sources */,
+				26D90F89140D512A0036A311 /* SkBoundable.cpp in Sources */,
+				26D90F8A140D512A0036A311 /* SkBuildCondensedInfo.cpp in Sources */,
+				26D90F8B140D512A0036A311 /* SkDisplayAdd.cpp in Sources */,
+				26D90F8C140D512A0036A311 /* SkDisplayApply.cpp in Sources */,
+				26D90F8D140D512A0036A311 /* SkDisplayBounds.cpp in Sources */,
+				26D90F8E140D512A0036A311 /* SkDisplayEvent.cpp in Sources */,
+				26D90F8F140D512A0036A311 /* SkDisplayEvents.cpp in Sources */,
+				26D90F90140D512A0036A311 /* SkDisplayInclude.cpp in Sources */,
+				26D90F91140D512A0036A311 /* SkDisplayInput.cpp in Sources */,
+				26D90F92140D512A0036A311 /* SkDisplayList.cpp in Sources */,
+				26D90F93140D512A0036A311 /* SkDisplayMath.cpp in Sources */,
+				26D90F94140D512A0036A311 /* SkDisplayMovie.cpp in Sources */,
+				26D90F95140D512A0036A311 /* SkDisplayNumber.cpp in Sources */,
+				26D90F96140D512A0036A311 /* SkDisplayPost.cpp in Sources */,
+				26D90F97140D512A0036A311 /* SkDisplayRandom.cpp in Sources */,
+				26D90F98140D512A0036A311 /* SkDisplayScreenplay.cpp in Sources */,
+				26D90F99140D512A0036A311 /* SkDisplayType.cpp in Sources */,
+				26D90F9A140D512A0036A311 /* SkDisplayTypes.cpp in Sources */,
+				26D90F9B140D512A0036A311 /* SkDisplayXMLParser.cpp in Sources */,
+				26D90F9C140D512A0036A311 /* SkDisplayable.cpp in Sources */,
+				26D90F9D140D512A0036A311 /* SkDraw3D.cpp in Sources */,
+				26D90F9E140D512A0036A311 /* SkDrawBitmap.cpp in Sources */,
+				26D90F9F140D512A0036A311 /* SkDrawBlur.cpp in Sources */,
+				26D90FA0140D512A0036A311 /* SkDrawClip.cpp in Sources */,
+				26D90FA1140D512A0036A311 /* SkDrawColor.cpp in Sources */,
+				26D90FA2140D512A0036A311 /* SkDrawDash.cpp in Sources */,
+				26D90FA3140D512A0036A311 /* SkDrawDiscrete.cpp in Sources */,
+				26D90FA4140D512A0036A311 /* SkDrawEmboss.cpp in Sources */,
+				26D90FA5140D512A0036A311 /* SkDrawExtraPathEffect.cpp in Sources */,
+				26D90FA6140D512A0036A311 /* SkDrawFull.cpp in Sources */,
+				26D90FA7140D512A0036A311 /* SkDrawGradient.cpp in Sources */,
+				26D90FA8140D512A0036A311 /* SkDrawGroup.cpp in Sources */,
+				26D90FA9140D512A0036A311 /* SkDrawLine.cpp in Sources */,
+				26D90FAA140D512A0036A311 /* SkDrawMatrix.cpp in Sources */,
+				26D90FAB140D512A0036A311 /* SkDrawOval.cpp in Sources */,
+				26D90FAC140D512A0036A311 /* SkDrawPaint.cpp in Sources */,
+				26D90FAD140D512A0036A311 /* SkDrawPath.cpp in Sources */,
+				26D90FAE140D512A0036A311 /* SkDrawPoint.cpp in Sources */,
+				26D90FAF140D512A0036A311 /* SkDrawRectangle.cpp in Sources */,
+				26D90FB0140D512A0036A311 /* SkDrawSaveLayer.cpp in Sources */,
+				26D90FB1140D512A0036A311 /* SkDrawShader.cpp in Sources */,
+				26D90FB2140D512A0036A311 /* SkDrawText.cpp in Sources */,
+				26D90FB3140D512A0036A311 /* SkDrawTextBox.cpp in Sources */,
+				26D90FB4140D512A0036A311 /* SkDrawTo.cpp in Sources */,
+				26D90FB5140D512A0036A311 /* SkDrawTransparentShader.cpp in Sources */,
+				26D90FB6140D512A0036A311 /* SkDrawable.cpp in Sources */,
+				26D90FB7140D512A0036A311 /* SkDump.cpp in Sources */,
+				26D90FB8140D512A0036A311 /* SkGetCondensedInfo.cpp in Sources */,
+				26D90FB9140D512A0036A311 /* SkHitClear.cpp in Sources */,
+				26D90FBA140D512A0036A311 /* SkHitTest.cpp in Sources */,
+				26D90FBB140D512A0036A311 /* SkMatrixParts.cpp in Sources */,
+				26D90FBC140D512A0036A311 /* SkMemberInfo.cpp in Sources */,
+				26D90FBD140D512A0036A311 /* SkOpArray.cpp in Sources */,
+				26D90FBE140D512A0036A311 /* SkOperandIterpolator.cpp in Sources */,
+				26D90FBF140D512A0036A311 /* SkPaintParts.cpp in Sources */,
+				26D90FC0140D512A0036A311 /* SkParseSVGPath.cpp in Sources */,
+				26D90FC1140D512A0036A311 /* SkPathParts.cpp in Sources */,
+				26D90FC2140D512A0036A311 /* SkPostParts.cpp in Sources */,
+				26D90FC3140D512A0036A311 /* SkScript.cpp in Sources */,
+				26D90FC4140D512A0036A311 /* SkScriptDecompile.cpp in Sources */,
+				26D90FC5140D512A0036A311 /* SkScriptRuntime.cpp in Sources */,
+				26D90FC6140D512A0036A311 /* SkScriptTokenizer.cpp in Sources */,
+				26D90FC7140D512A0036A311 /* SkSnapshot.cpp in Sources */,
+				26D90FC8140D512A0036A311 /* SkTextOnPath.cpp in Sources */,
+				26D90FC9140D512A0036A311 /* SkTextToPath.cpp in Sources */,
+				26D90FCA140D512A0036A311 /* SkTime.cpp in Sources */,
+				26D90FCB140D512A0036A311 /* SkTypedArray.cpp in Sources */,
+				26D90FCC140D512A0036A311 /* SkXMLAnimatorWriter.cpp in Sources */,
+				26D90FCD140D512A0036A311 /* SkData.cpp in Sources */,
+				26D90FCE140D512A0036A311 /* Sk64.cpp in Sources */,
+				26D90FCF140D512A0036A311 /* SkAdvancedTypefaceMetrics.cpp in Sources */,
+				26D90FD0140D512A0036A311 /* SkAlphaRuns.cpp in Sources */,
+				26D90FD1140D512A0036A311 /* SkBitmap.cpp in Sources */,
+				26D90FD2140D512A0036A311 /* SkBitmapProcShader.cpp in Sources */,
+				26D90FD3140D512A0036A311 /* SkBitmapProcState.cpp in Sources */,
+				26D90FD4140D512A0036A311 /* SkBitmapProcState_matrixProcs.cpp in Sources */,
+				26D90FD5140D512A0036A311 /* SkBitmapSampler.cpp in Sources */,
+				26D90FD6140D512A0036A311 /* SkBitmap_scroll.cpp in Sources */,
+				26D90FD7140D512A0036A311 /* SkBlitRow_D16.cpp in Sources */,
+				26D90FD8140D512A0036A311 /* SkBlitRow_D32.cpp in Sources */,
+				26D90FD9140D512A0036A311 /* SkBlitRow_D4444.cpp in Sources */,
+				26D90FDA140D512A0036A311 /* SkBlitter.cpp in Sources */,
+				26D90FDB140D512A0036A311 /* SkBlitter_4444.cpp in Sources */,
+				26D90FDC140D512A0036A311 /* SkBlitter_A1.cpp in Sources */,
+				26D90FDD140D512A0036A311 /* SkBlitter_A8.cpp in Sources */,
+				26D90FDE140D512A0036A311 /* SkBlitter_ARGB32.cpp in Sources */,
+				26D90FDF140D512A0036A311 /* SkBlitter_RGB16.cpp in Sources */,
+				26D90FE0140D512A0036A311 /* SkBlitter_Sprite.cpp in Sources */,
+				26D90FE1140D512A0036A311 /* SkBuffer.cpp in Sources */,
+				26D90FE2140D512A0036A311 /* SkCanvas.cpp in Sources */,
+				26D90FE3140D512A0036A311 /* SkChunkAlloc.cpp in Sources */,
+				26D90FE4140D512A0036A311 /* SkClampRange.cpp in Sources */,
+				26D90FE5140D512A0036A311 /* SkClipStack.cpp in Sources */,
+				26D90FE6140D512A0036A311 /* SkColor.cpp in Sources */,
+				26D90FE7140D512A0036A311 /* SkColorFilter.cpp in Sources */,
+				26D90FE8140D512A0036A311 /* SkColorTable.cpp in Sources */,
+				26D90FE9140D512A0036A311 /* SkComposeShader.cpp in Sources */,
+				26D90FEA140D512A0036A311 /* SkConcaveToTriangles.cpp in Sources */,
+				26D90FEB140D512A0036A311 /* SkCordic.cpp in Sources */,
+				26D90FEC140D512A0036A311 /* SkCubicClipper.cpp in Sources */,
+				26D90FED140D512A0036A311 /* SkDebug.cpp in Sources */,
+				26D90FEE140D512A0036A311 /* SkDeque.cpp in Sources */,
+				26D90FEF140D512A0036A311 /* SkDevice.cpp in Sources */,
+				26D90FF0140D512A0036A311 /* SkDither.cpp in Sources */,
+				26D90FF1140D512A0036A311 /* SkDraw.cpp in Sources */,
+				26D90FF2140D512A0036A311 /* SkEdge.cpp in Sources */,
+				26D90FF3140D512A0036A311 /* SkEdgeBuilder.cpp in Sources */,
+				26D90FF4140D512A0036A311 /* SkEdgeClipper.cpp in Sources */,
+				26D90FF5140D512A0036A311 /* SkFilterProc.cpp in Sources */,
+				26D90FF6140D512A0036A311 /* SkFlate.cpp in Sources */,
+				26D90FF7140D512A0036A311 /* SkFlattenable.cpp in Sources */,
+				26D90FF8140D512A0036A311 /* SkFloat.cpp in Sources */,
+				26D90FF9140D512A0036A311 /* SkFloatBits.cpp in Sources */,
+				26D90FFA140D512A0036A311 /* SkFontHost.cpp in Sources */,
+				26D90FFB140D512A0036A311 /* SkGeometry.cpp in Sources */,
+				26D90FFC140D512A0036A311 /* SkGlobals.cpp in Sources */,
+				26D90FFD140D512A0036A311 /* SkGlyphCache.cpp in Sources */,
+				26D90FFE140D512A0036A311 /* SkGraphics.cpp in Sources */,
+				26D90FFF140D512A0036A311 /* SkLineClipper.cpp in Sources */,
+				26D91000140D512A0036A311 /* SkMMapStream.cpp in Sources */,
+				26D91001140D512A0036A311 /* SkMallocPixelRef.cpp in Sources */,
+				26D91002140D512A0036A311 /* SkMask.cpp in Sources */,
+				26D91003140D512A0036A311 /* SkMaskFilter.cpp in Sources */,
+				26D91004140D512A0036A311 /* SkMath.cpp in Sources */,
+				26D91005140D512A0036A311 /* SkMatrix.cpp in Sources */,
+				26D91006140D512A0036A311 /* SkMetaData.cpp in Sources */,
+				26D91007140D512A0036A311 /* SkPackBits.cpp in Sources */,
+				26D91008140D512A0036A311 /* SkPaint.cpp in Sources */,
+				26D91009140D512A0036A311 /* SkPath.cpp in Sources */,
+				26D9100A140D512A0036A311 /* SkPathEffect.cpp in Sources */,
+				26D9100B140D512A0036A311 /* SkPathHeap.cpp in Sources */,
+				26D9100C140D512A0036A311 /* SkPathMeasure.cpp in Sources */,
+				26D9100D140D512A0036A311 /* SkPicture.cpp in Sources */,
+				26D9100E140D512A0036A311 /* SkPictureFlat.cpp in Sources */,
+				26D9100F140D512A0036A311 /* SkPicturePlayback.cpp in Sources */,
+				26D91010140D512A0036A311 /* SkPictureRecord.cpp in Sources */,
+				26D91011140D512A0036A311 /* SkPixelRef.cpp in Sources */,
+				26D91012140D512A0036A311 /* SkPoint.cpp in Sources */,
+				26D91013140D512A0036A311 /* SkProcSpriteBlitter.cpp in Sources */,
+				26D91014140D512A0036A311 /* SkPtrRecorder.cpp in Sources */,
+				26D91015140D512A0036A311 /* SkQuadClipper.cpp in Sources */,
+				26D91016140D512A0036A311 /* SkRasterizer.cpp in Sources */,
+				26D91017140D512A0036A311 /* SkRect.cpp in Sources */,
+				26D91018140D512A0036A311 /* SkRefDict.cpp in Sources */,
+				26D91019140D512A0036A311 /* SkRegion.cpp in Sources */,
+				26D9101A140D512A0036A311 /* SkRegion_path.cpp in Sources */,
+				26D9101B140D512A0036A311 /* SkScalar.cpp in Sources */,
+				26D9101C140D512A0036A311 /* SkScalerContext.cpp in Sources */,
+				26D9101D140D512A0036A311 /* SkScan.cpp in Sources */,
+				26D9101E140D512A0036A311 /* SkScan_AntiPath.cpp in Sources */,
+				26D9101F140D512A0036A311 /* SkScan_Antihair.cpp in Sources */,
+				26D91020140D512A0036A311 /* SkScan_Hairline.cpp in Sources */,
+				26D91021140D512A0036A311 /* SkScan_Path.cpp in Sources */,
+				26D91022140D512A0036A311 /* SkShader.cpp in Sources */,
+				26D91023140D512A0036A311 /* SkShape.cpp in Sources */,
+				26D91024140D512A0036A311 /* SkSpriteBlitter_ARGB32.cpp in Sources */,
+				26D91025140D512A0036A311 /* SkSpriteBlitter_RGB16.cpp in Sources */,
+				26D91026140D512A0036A311 /* SkStream.cpp in Sources */,
+				26D91027140D512A0036A311 /* SkString.cpp in Sources */,
+				26D91028140D512A0036A311 /* SkStroke.cpp in Sources */,
+				26D91029140D512A0036A311 /* SkStrokerPriv.cpp in Sources */,
+				26D9102A140D512A0036A311 /* SkTSearch.cpp in Sources */,
+				26D9102B140D512A0036A311 /* SkTypeface.cpp in Sources */,
+				26D9102C140D512A0036A311 /* SkTypefaceCache.cpp in Sources */,
+				26D9102D140D512A0036A311 /* SkUnPreMultiply.cpp in Sources */,
+				26D9102E140D512A0036A311 /* SkUtils.cpp in Sources */,
+				26D9102F140D512A0036A311 /* SkWriter32.cpp in Sources */,
+				26D91030140D512A0036A311 /* SkXfermode.cpp in Sources */,
+				26D91032140D512A0036A311 /* SkDebug_stdio.cpp in Sources */,
+				26D91033140D512A0036A311 /* SkFontHost_mac_coretext.cpp in Sources */,
+				26D91034140D512A0036A311 /* SkGlobals_global.cpp in Sources */,
+				26D91035140D512A0036A311 /* SkMemory_malloc.cpp in Sources */,
+				26D91036140D512A0036A311 /* SkThread_pthread.cpp in Sources */,
+				26D91037140D512A0036A311 /* SkTime_Unix.cpp in Sources */,
+				26D91038140D512A0036A311 /* SkXMLParser_empty.cpp in Sources */,
+				26D91039140D512A0036A311 /* Sk1DPathEffect.cpp in Sources */,
+				26D9103A140D512A0036A311 /* Sk2DPathEffect.cpp in Sources */,
+				26D9103B140D512A0036A311 /* SkAvoidXfermode.cpp in Sources */,
+				26D9103C140D512A0036A311 /* SkBitmapCache.cpp in Sources */,
+				26D9103D140D512A0036A311 /* SkBlurDrawLooper.cpp in Sources */,
+				26D9103E140D512A0036A311 /* SkBlurMask.cpp in Sources */,
+				26D9103F140D512A0036A311 /* SkBlurMaskFilter.cpp in Sources */,
+				26D91040140D512A0036A311 /* SkColorFilters.cpp in Sources */,
+				26D91041140D512A0036A311 /* SkColorMatrixFilter.cpp in Sources */,
+				26D91042140D512A0036A311 /* SkCornerPathEffect.cpp in Sources */,
+				26D91043140D512A0036A311 /* SkDashPathEffect.cpp in Sources */,
+				26D91044140D512A0036A311 /* SkDiscretePathEffect.cpp in Sources */,
+				26D91045140D512A0036A311 /* SkEmbossMask.cpp in Sources */,
+				26D91046140D512A0036A311 /* SkEmbossMaskFilter.cpp in Sources */,
+				26D91047140D512A0036A311 /* SkGradientShader.cpp in Sources */,
+				26D91048140D512A0036A311 /* SkGroupShape.cpp in Sources */,
+				26D91049140D512A0036A311 /* SkKernel33MaskFilter.cpp in Sources */,
+				26D9104A140D512A0036A311 /* SkLayerDrawLooper.cpp in Sources */,
+				26D9104B140D512A0036A311 /* SkLayerRasterizer.cpp in Sources */,
+				26D9104C140D512A0036A311 /* SkPaintFlagsDrawFilter.cpp in Sources */,
+				26D9104D140D512A0036A311 /* SkPixelXorXfermode.cpp in Sources */,
+				26D9104E140D512A0036A311 /* SkPorterDuff.cpp in Sources */,
+				26D9104F140D512A0036A311 /* SkRectShape.cpp in Sources */,
+				26D91050140D512A0036A311 /* SkTransparentShader.cpp in Sources */,
+				26D91051140D512A0036A311 /* GrAddPathRenderers_none.cpp in Sources */,
+				26D91052140D512A0036A311 /* GrDefaultPathRenderer.cpp in Sources */,
+				26D91053140D512A0036A311 /* GrGLStencilBuffer.cpp in Sources */,
+				26D91054140D512A0036A311 /* GrPathRendererChain.cpp in Sources */,
+				26D91055140D512A0036A311 /* GrStencilBuffer.cpp in Sources */,
+				26D91056140D512A0036A311 /* GrRenderTarget.cpp in Sources */,
+				26D91057140D512A0036A311 /* GrGLRenderTarget.cpp in Sources */,
+				26D91058140D512A0036A311 /* GrResourceCache.cpp in Sources */,
+				26D91059140D512A0036A311 /* FlingState.cpp in Sources */,
+				26D9105A140D512A0036A311 /* GrDrawMesh.cpp in Sources */,
+				26D9105B140D512A0036A311 /* GrAllocPool.cpp in Sources */,
+				26D9105C140D512A0036A311 /* GrAtlas.cpp in Sources */,
+				26D9105D140D512A0036A311 /* GrBufferAllocPool.cpp in Sources */,
+				26D9105E140D512A0036A311 /* GrClip.cpp in Sources */,
+				26D9105F140D512A0036A311 /* GrContext.cpp in Sources */,
+				26D91060140D512A0036A311 /* GrDrawTarget.cpp in Sources */,
+				26D91061140D512A0036A311 /* GrGLDefaultInterface_none.cpp in Sources */,
+				26D91062140D512A0036A311 /* GrGLIndexBuffer.cpp in Sources */,
+				26D91063140D512A0036A311 /* GrGLInterface.cpp in Sources */,
+				26D91064140D512A0036A311 /* GrGLProgram.cpp in Sources */,
+				26D91065140D512A0036A311 /* GrGLTexture.cpp in Sources */,
+				26D91066140D512A0036A311 /* GrGLUtil.cpp in Sources */,
+				26D91067140D512A0036A311 /* GrGLVertexBuffer.cpp in Sources */,
+				26D91068140D512A0036A311 /* GrGpu.cpp in Sources */,
+				26D91069140D512A0036A311 /* GrGpuFactory.cpp in Sources */,
+				26D9106A140D512A0036A311 /* GrGpuGL.cpp in Sources */,
+				26D9106B140D512A0036A311 /* GrGpuGLFixed.cpp in Sources */,
+				26D9106C140D512A0036A311 /* GrGpuGLShaders.cpp in Sources */,
+				26D9106D140D512A0036A311 /* GrInOrderDrawBuffer.cpp in Sources */,
+				26D9106E140D512A0036A311 /* GrMatrix.cpp in Sources */,
+				26D9106F140D512A0036A311 /* GrMemory.cpp in Sources */,
+				26D91070140D512A0036A311 /* GrPathRenderer.cpp in Sources */,
+				26D91071140D512A0036A311 /* GrPathUtils.cpp in Sources */,
+				26D91072140D512A0036A311 /* GrRectanizer.cpp in Sources */,
+				26D91073140D512A0036A311 /* GrResource.cpp in Sources */,
+				26D91074140D512A0036A311 /* GrStencil.cpp in Sources */,
+				26D91076140D512A0036A311 /* GrTextContext.cpp in Sources */,
+				26D91077140D512A0036A311 /* GrTextStrike.cpp in Sources */,
+				26D91078140D512A0036A311 /* GrTexture.cpp in Sources */,
+				26D91079140D512A0036A311 /* gr_unittests.cpp in Sources */,
+				26D9107A140D512A0036A311 /* GrPrintf_skia.cpp in Sources */,
+				26D9107B140D512A0036A311 /* SkGpuCanvas.cpp in Sources */,
+				26D9107C140D512A0036A311 /* SkGpuDevice.cpp in Sources */,
+				26D9107D140D512A0036A311 /* SkGr.cpp in Sources */,
+				26D9107E140D512A0036A311 /* SkGrFontScaler.cpp in Sources */,
+				26D9107F140D512A0036A311 /* SkGrTexturePixelRef.cpp in Sources */,
+				26D91080140D512A0036A311 /* SkMovie.cpp in Sources */,
+				26D91081140D512A0036A311 /* SkCreateRLEPixelRef.cpp in Sources */,
+				26D91082140D512A0036A311 /* SkFDStream.cpp in Sources */,
+				26D91083140D512A0036A311 /* SkFlipPixelRef.cpp in Sources */,
+				26D91084140D512A0036A311 /* SkImageDecoder.cpp in Sources */,
+				26D91085140D512A0036A311 /* SkImageEncoder.cpp in Sources */,
+				26D91086140D512A0036A311 /* SkImageDecoder_Factory.cpp in Sources */,
+				26D91087140D512A0036A311 /* SkImageEncoder_Factory.cpp in Sources */,
+				26D91088140D512A0036A311 /* SkPageFlipper.cpp in Sources */,
+				26D91089140D512A0036A311 /* SkImageRef.cpp in Sources */,
+				26D9108A140D512A0036A311 /* SkImageRefPool.cpp in Sources */,
+				26D9108B140D512A0036A311 /* SkImageRef_GlobalPool.cpp in Sources */,
+				26D9108F140D512A0036A311 /* SkBitmapProcState_opts_arm.cpp in Sources */,
+				26D91097140D512A0036A311 /* SkGPipeRead.cpp in Sources */,
+				26D91098140D512A0036A311 /* SkGPipeWrite.cpp in Sources */,
+				26D91099140D512A0036A311 /* SkBitSet.cpp in Sources */,
+				26D9109A140D512A0036A311 /* SkPDFCatalog.cpp in Sources */,
+				26D9109B140D512A0036A311 /* SkPDFDevice.cpp in Sources */,
+				26D9109C140D512A0036A311 /* SkPDFDocument.cpp in Sources */,
+				26D9109D140D512A0036A311 /* SkPDFFont.cpp in Sources */,
+				26D9109E140D512A0036A311 /* SkPDFFormXObject.cpp in Sources */,
+				26D9109F140D512A0036A311 /* SkPDFGraphicState.cpp in Sources */,
+				26D910A0140D512A0036A311 /* SkPDFImage.cpp in Sources */,
+				26D910A1140D512A0036A311 /* SkPDFPage.cpp in Sources */,
+				26D910A2140D512A0036A311 /* SkPDFShader.cpp in Sources */,
+				26D910A3140D512A0036A311 /* SkPDFStream.cpp in Sources */,
+				26D910A4140D512A0036A311 /* SkPDFTypes.cpp in Sources */,
+				26D910A5140D512A0036A311 /* SkPDFUtils.cpp in Sources */,
+				26D910A6140D512A0036A311 /* SkSVGCircle.cpp in Sources */,
+				26D910A7140D512A0036A311 /* SkSVGClipPath.cpp in Sources */,
+				26D910A8140D512A0036A311 /* SkSVGDefs.cpp in Sources */,
+				26D910A9140D512A0036A311 /* SkSVGElements.cpp in Sources */,
+				26D910AA140D512A0036A311 /* SkSVGEllipse.cpp in Sources */,
+				26D910AB140D512A0036A311 /* SkSVGFeColorMatrix.cpp in Sources */,
+				26D910AC140D512A0036A311 /* SkSVGFilter.cpp in Sources */,
+				26D910AD140D512A0036A311 /* SkSVGG.cpp in Sources */,
+				26D910AE140D512A0036A311 /* SkSVGGradient.cpp in Sources */,
+				26D910AF140D512A0036A311 /* SkSVGGroup.cpp in Sources */,
+				26D910B0140D512A0036A311 /* SkSVGImage.cpp in Sources */,
+				26D910B1140D512A0036A311 /* SkSVGLine.cpp in Sources */,
+				26D910B2140D512A0036A311 /* SkSVGLinearGradient.cpp in Sources */,
+				26D910B3140D512A0036A311 /* SkSVGMask.cpp in Sources */,
+				26D910B4140D512A0036A311 /* SkSVGMetadata.cpp in Sources */,
+				26D910B5140D512A0036A311 /* SkSVGPaintState.cpp in Sources */,
+				26D910B6140D512A0036A311 /* SkSVGParser.cpp in Sources */,
+				26D910B7140D512A0036A311 /* SkSVGPath.cpp in Sources */,
+				26D910B8140D512A0036A311 /* SkSVGPolygon.cpp in Sources */,
+				26D910B9140D512A0036A311 /* SkSVGPolyline.cpp in Sources */,
+				26D910BA140D512A0036A311 /* SkSVGRadialGradient.cpp in Sources */,
+				26D910BB140D512A0036A311 /* SkSVGRect.cpp in Sources */,
+				26D910BC140D512A0036A311 /* SkSVGSVG.cpp in Sources */,
+				26D910BD140D512A0036A311 /* SkSVGStop.cpp in Sources */,
+				26D910BE140D512A0036A311 /* SkSVGSymbol.cpp in Sources */,
+				26D910BF140D512A0036A311 /* SkSVGText.cpp in Sources */,
+				26D910C0140D512A0036A311 /* SkSVGUse.cpp in Sources */,
+				26D910C1140D512A0036A311 /* SkCreateCGImageRef.cpp in Sources */,
+				26D910C2140D512A0036A311 /* SkBoundaryPatch.cpp in Sources */,
+				26D910C3140D512A0036A311 /* SkCamera.cpp in Sources */,
+				26D910C4140D512A0036A311 /* SkColorMatrix.cpp in Sources */,
+				26D910C5140D512A0036A311 /* SkCubicInterval.cpp in Sources */,
+				26D910C6140D512A0036A311 /* SkCullPoints.cpp in Sources */,
+				26D910C7140D512A0036A311 /* SkDumpCanvas.cpp in Sources */,
+				26D910C8140D512A0036A311 /* SkInterpolator.cpp in Sources */,
+				26D910C9140D512A0036A311 /* SkLayer.cpp in Sources */,
+				26D910CA140D512A0036A311 /* SkMatrix44.cpp in Sources */,
+				26D910CB140D512A0036A311 /* SkMeshUtils.cpp in Sources */,
+				26D910CC140D512A0036A311 /* SkNWayCanvas.cpp in Sources */,
+				26D910CD140D512A0036A311 /* SkNinePatch.cpp in Sources */,
+				26D910CE140D512A0036A311 /* SkOSFile.cpp in Sources */,
+				26D910CF140D512A0036A311 /* SkParse.cpp in Sources */,
+				26D910D0140D512A0036A311 /* SkParseColor.cpp in Sources */,
+				26D910D1140D512A0036A311 /* SkParsePath.cpp in Sources */,
+				26D910D2140D512A0036A311 /* SkProxyCanvas.cpp in Sources */,
+				26D910D3140D512A0036A311 /* SkSfntUtils.cpp in Sources */,
+				26D910D4140D512A0036A311 /* SkUnitMappers.cpp in Sources */,
+				26D910D5140D512A0036A311 /* SkBGViewArtist.cpp in Sources */,
+				26D910D6140D512A0036A311 /* SkEvent.cpp in Sources */,
+				26D910D7140D512A0036A311 /* SkEventSink.cpp in Sources */,
+				26D910D8140D512A0036A311 /* SkImageView.cpp in Sources */,
+				26D910D9140D512A0036A311 /* SkListView.cpp in Sources */,
+				26D910DB140D512A0036A311 /* SkOSMenu.cpp in Sources */,
+				26D910DC140D512A0036A311 /* SkParsePaint.cpp in Sources */,
+				26D910E1140D512A0036A311 /* SkStackViewLayout.cpp in Sources */,
+				26D910E2140D512A0036A311 /* SkStaticTextView.cpp in Sources */,
+				26D910E3140D512A0036A311 /* SkTagList.cpp in Sources */,
+				26D910E4140D512A0036A311 /* SkTextBox.cpp in Sources */,
+				26D910E5140D512A0036A311 /* SkTouchGesture.cpp in Sources */,
+				26D910E6140D512A0036A311 /* SkView.cpp in Sources */,
+				26D910E7140D512A0036A311 /* SkViewInflate.cpp in Sources */,
+				26D910E8140D512A0036A311 /* SkViewPriv.cpp in Sources */,
+				26D910E9140D512A0036A311 /* SkWidget.cpp in Sources */,
+				26D910EB140D512A0036A311 /* SkWidgets.cpp in Sources */,
+				26D910EC140D512A0036A311 /* SkWindow.cpp in Sources */,
+				26D910ED140D512A0036A311 /* SkDOM.cpp in Sources */,
+				26D910EE140D512A0036A311 /* SkXMLParser.cpp in Sources */,
+				26D910EF140D512A0036A311 /* SkXMLWriter.cpp in Sources */,
+				26D91131140D53C30036A311 /* SkBlitRow_opts_none.cpp in Sources */,
+				26D91132140D53C30036A311 /* SkUtils_opts_none.cpp in Sources */,
+				26D91146140D54720036A311 /* skia_ios.mm in Sources */,
+				26D91155140D54E40036A311 /* SkEventNotifier.mm in Sources */,
+				26D9118F140D558A0036A311 /* SkOSFile_iOS.mm in Sources */,
+				26D91194140D55980036A311 /* SkOSWindow_iOS.mm in Sources */,
+				26D91304140D60330036A311 /* SimpleApp.mm in Sources */,
+				26371977140D6C19006CC295 /* SkUIView.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		1D6058940D05DD3E006BFB54 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = SimpleiOSApp_Prefix.pch;
+				INFOPLIST_FILE = "SimpleiOSApp-Info.plist";
+				PRODUCT_NAME = SimpleiOSApp;
+			};
+			name = Debug;
+		};
+		1D6058950D05DD3E006BFB54 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				COPY_PHASE_STRIP = YES;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = SimpleiOSApp_Prefix.pch;
+				INFOPLIST_FILE = "SimpleiOSApp-Info.plist";
+				PRODUCT_NAME = SimpleiOSApp;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		C01FCF4F08A954540054247B /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 26D912EA140D5FCC0036A311 /* SkiOSSampleApp-Debug.xcconfig */;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_PREFIX_HEADER = "";
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = NO;
+				PREBINDING = NO;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		C01FCF5008A954540054247B /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 26D912EB140D5FCC0036A311 /* SkiOSSampleApp-Release.xcconfig */;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_PREFIX_HEADER = "";
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = NO;
+				OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+				PREBINDING = NO;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "SimpleiOSApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1D6058940D05DD3E006BFB54 /* Debug */,
+				1D6058950D05DD3E006BFB54 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SimpleiOSApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				C01FCF4F08A954540054247B /* Debug */,
+				C01FCF5008A954540054247B /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
diff --git a/experimental/SimpleiOSApp/SimpleiOSApp_Prefix.pch b/experimental/SimpleiOSApp/SimpleiOSApp_Prefix.pch
new file mode 100644
index 0000000..0791950
--- /dev/null
+++ b/experimental/SimpleiOSApp/SimpleiOSApp_Prefix.pch
@@ -0,0 +1,8 @@
+//
+// Prefix header for all source files of the 'iosshell' target in the 'iosshell' project
+//
+
+#ifdef __OBJC__
+    #import <Foundation/Foundation.h>
+    #import <UIKit/UIKit.h>
+#endif
diff --git a/experimental/SimpleiOSApp/iPad/AppDelegate_iPad.h b/experimental/SimpleiOSApp/iPad/AppDelegate_iPad.h
new file mode 100644
index 0000000..7783a67
--- /dev/null
+++ b/experimental/SimpleiOSApp/iPad/AppDelegate_iPad.h
@@ -0,0 +1,18 @@
+//
+//  AppDelegate_iPad.h
+//  iosshell
+//
+//  Created by Yang Su on 6/30/11.
+//  Copyright 2011 Google Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate_iPad : NSObject <UIApplicationDelegate> {
+    UIWindow *window;
+}
+
+@property (nonatomic, retain) IBOutlet UIWindow *window;
+
+@end
+
diff --git a/experimental/SimpleiOSApp/iPad/AppDelegate_iPad.m b/experimental/SimpleiOSApp/iPad/AppDelegate_iPad.m
new file mode 100644
index 0000000..1792658
--- /dev/null
+++ b/experimental/SimpleiOSApp/iPad/AppDelegate_iPad.m
@@ -0,0 +1,67 @@
+//
+//  AppDelegate_iPad.m
+//  iosshell
+//
+//  Created by Yang Su on 6/30/11.
+//  Copyright 2011 Google Inc. All rights reserved.
+//
+
+#import "AppDelegate_iPad.h"
+
+@implementation AppDelegate_iPad
+
+@synthesize window;
+
+
+#pragma mark -
+#pragma mark Application lifecycle
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
+    
+    // Override point for customization after application launch.
+    
+    [self.window makeKeyAndVisible];
+    
+    return YES;
+}
+
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+    /*
+     Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+     Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+     */
+}
+
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+    /*
+     Restart any tasks that were paused (or not yet started) while the application was inactive.
+     */
+}
+
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+    /*
+     Called when the application is about to terminate.
+     */
+}
+
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
+    /*
+     Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
+     */
+}
+
+
+- (void)dealloc {
+    [window release];
+    [super dealloc];
+}
+
+
+@end
diff --git a/experimental/SimpleiOSApp/iPad/MainWindow_iPad.xib b/experimental/SimpleiOSApp/iPad/MainWindow_iPad.xib
new file mode 100644
index 0000000..a5348b8
--- /dev/null
+++ b/experimental/SimpleiOSApp/iPad/MainWindow_iPad.xib
@@ -0,0 +1,439 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="7.10">
+	<data>
+		<int key="IBDocument.SystemTarget">1056</int>
+		<string key="IBDocument.SystemVersion">10K540</string>
+		<string key="IBDocument.InterfaceBuilderVersion">851</string>
+		<string key="IBDocument.AppKitVersion">1038.36</string>
+		<string key="IBDocument.HIToolboxVersion">461.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+			<string key="NS.object.0">141</string>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+		</object>
+		<object class="NSArray" key="IBDocument.PluginDependencies">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSArray" key="dict.sortedKeys" id="0">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+			</object>
+			<object class="NSMutableArray" key="dict.values">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+			</object>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="IBProxyObject" id="841351856">
+				<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+			</object>
+			<object class="IBProxyObject" id="606714003">
+				<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+			</object>
+			<object class="IBUIWindow" id="62075450">
+				<nil key="NSNextResponder"/>
+				<int key="NSvFlags">292</int>
+				<object class="NSMutableArray" key="NSSubviews">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBUIView" id="470150200">
+						<reference key="NSNextResponder" ref="62075450"/>
+						<int key="NSvFlags">274</int>
+						<string key="NSFrame">{{0, 10}, {768, 1004}}</string>
+						<reference key="NSSuperview" ref="62075450"/>
+						<object class="NSColor" key="IBUIBackgroundColor">
+							<int key="NSColorSpace">3</int>
+							<bytes key="NSWhite">MQA</bytes>
+							<object class="NSColorSpace" key="NSCustomColorSpace">
+								<int key="NSID">2</int>
+							</object>
+						</object>
+						<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+					</object>
+				</object>
+				<string key="NSFrameSize">{768, 1024}</string>
+				<object class="NSColor" key="IBUIBackgroundColor">
+					<int key="NSColorSpace">1</int>
+					<bytes key="NSRGB">MSAxIDEAA</bytes>
+				</object>
+				<bool key="IBUIOpaque">NO</bool>
+				<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
+				<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
+					<int key="IBUIStatusBarStyle">2</int>
+				</object>
+				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+				<bool key="IBUIResizesToFullScreen">YES</bool>
+			</object>
+			<object class="IBUICustomObject" id="250404236">
+				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+			</object>
+		</object>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<object class="NSMutableArray" key="connectionRecords">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">window</string>
+						<reference key="source" ref="250404236"/>
+						<reference key="destination" ref="62075450"/>
+					</object>
+					<int key="connectionID">7</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="841351856"/>
+						<reference key="destination" ref="250404236"/>
+					</object>
+					<int key="connectionID">8</int>
+				</object>
+			</object>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<object class="NSArray" key="orderedObjects">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<reference key="object" ref="0"/>
+						<reference key="children" ref="1000"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="841351856"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="606714003"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">2</int>
+						<reference key="object" ref="62075450"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="470150200"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">6</int>
+						<reference key="object" ref="250404236"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">10</int>
+						<reference key="object" ref="470150200"/>
+						<reference key="parent" ref="62075450"/>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="flattenedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="NSArray" key="dict.sortedKeys">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>-1.CustomClassName</string>
+					<string>-2.CustomClassName</string>
+					<string>10.CustomClassName</string>
+					<string>10.IBPluginDependency</string>
+					<string>10.IBViewBoundsToFrameTransform</string>
+					<string>2.IBEditorWindowLastContentRect</string>
+					<string>2.IBPluginDependency</string>
+					<string>6.CustomClassName</string>
+					<string>6.IBPluginDependency</string>
+				</object>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>UIApplication</string>
+					<string>UIResponder</string>
+					<string>SimpleApp</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAAAAAAAAxHqAAA</bytes>
+					</object>
+					<string>{{903, 55}, {768, 1024}}</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>AppDelegate_iPad</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="unlocalizedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="activeLocalization"/>
+			<object class="NSMutableDictionary" key="localizations">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="sourceID"/>
+			<int key="maxID">12</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">AppDelegate_iPad</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">window</string>
+						<string key="NS.object.0">UIWindow</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">window</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">window</string>
+							<string key="candidateClassName">UIWindow</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">iPad/AppDelegate_iPad.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SimpleApp</string>
+					<string key="superclassName">SkUIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">SimpleApp.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkUIView</string>
+					<string key="superclassName">UIView</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">fOptionsDelegate</string>
+						<string key="NS.object.0">id</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">fOptionsDelegate</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">fOptionsDelegate</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">../iOSSampleApp/Shared/SkUIView.h</string>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="786211723">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIApplication</string>
+					<string key="superclassName">UIResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIApplication.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIResponder</string>
+					<string key="superclassName">NSObject</string>
+					<reference key="sourceIdentifier" ref="786211723"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UISearchBar</string>
+					<string key="superclassName">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UISearchDisplayController</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIPrintFormatter.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIView</string>
+					<string key="superclassName">UIResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<string key="superclassName">UIResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIWindow</string>
+					<string key="superclassName">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIWindow.h</string>
+					</object>
+				</object>
+			</object>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBIPadFramework</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
+			<integer value="1056" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
+			<integer value="3100" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<string key="IBDocument.LastKnownRelativeProjectPath">../SimpleiOSApp.xcodeproj</string>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<string key="IBCocoaTouchPluginVersion">141</string>
+	</data>
+</archive>
diff --git a/experimental/SimpleiOSApp/iPhone/AppDelegate_iPhone.h b/experimental/SimpleiOSApp/iPhone/AppDelegate_iPhone.h
new file mode 100644
index 0000000..d4f8e4e
--- /dev/null
+++ b/experimental/SimpleiOSApp/iPhone/AppDelegate_iPhone.h
@@ -0,0 +1,18 @@
+//
+//  AppDelegate_iPhone.h
+//  iosshell
+//
+//  Created by Yang Su on 6/30/11.
+//  Copyright 2011 Google Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate_iPhone : NSObject <UIApplicationDelegate> {
+    UIWindow *window;
+}
+
+@property (nonatomic, retain) IBOutlet UIWindow *window;
+
+@end
+
diff --git a/experimental/SimpleiOSApp/iPhone/AppDelegate_iPhone.m b/experimental/SimpleiOSApp/iPhone/AppDelegate_iPhone.m
new file mode 100644
index 0000000..dcf14f2
--- /dev/null
+++ b/experimental/SimpleiOSApp/iPhone/AppDelegate_iPhone.m
@@ -0,0 +1,83 @@
+//
+//  AppDelegate_iPhone.m
+//  iosshell
+//
+//  Created by Yang Su on 6/30/11.
+//  Copyright 2011 Google Inc. All rights reserved.
+//
+
+#import "AppDelegate_iPhone.h"
+
+@implementation AppDelegate_iPhone
+
+@synthesize window;
+
+
+#pragma mark -
+#pragma mark Application lifecycle
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
+    
+    // Override point for customization after application launch.
+    
+    [self.window makeKeyAndVisible];
+    
+    return YES;
+}
+
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+    /*
+     Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+     Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+     */
+}
+
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+    /*
+     Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
+     If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
+     */
+}
+
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+    /*
+     Called as part of  transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
+     */
+}
+
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+    /*
+     Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+     */
+}
+
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+    /*
+     Called when the application is about to terminate.
+     See also applicationDidEnterBackground:.
+     */
+}
+
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
+    /*
+     Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
+     */
+}
+
+
+- (void)dealloc {
+    [window release];
+    [super dealloc];
+}
+
+
+@end
diff --git a/experimental/SimpleiOSApp/iPhone/MainWindow_iPhone.xib b/experimental/SimpleiOSApp/iPhone/MainWindow_iPhone.xib
new file mode 100644
index 0000000..1bd5b56
--- /dev/null
+++ b/experimental/SimpleiOSApp/iPhone/MainWindow_iPhone.xib
@@ -0,0 +1,449 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
+	<data>
+		<int key="IBDocument.SystemTarget">1056</int>
+		<string key="IBDocument.SystemVersion">10K540</string>
+		<string key="IBDocument.InterfaceBuilderVersion">851</string>
+		<string key="IBDocument.AppKitVersion">1038.36</string>
+		<string key="IBDocument.HIToolboxVersion">461.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+			<string key="NS.object.0">141</string>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+		</object>
+		<object class="NSArray" key="IBDocument.PluginDependencies">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSArray" key="dict.sortedKeys" id="0">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+			</object>
+			<object class="NSMutableArray" key="dict.values">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+			</object>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="IBProxyObject" id="841351856">
+				<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+			</object>
+			<object class="IBProxyObject" id="450319686">
+				<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+			</object>
+			<object class="IBUICustomObject" id="987256611">
+				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+			</object>
+			<object class="IBUIWindow" id="380026005">
+				<nil key="NSNextResponder"/>
+				<int key="NSvFlags">1316</int>
+				<object class="NSMutableArray" key="NSSubviews">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBUIView" id="268100401">
+						<reference key="NSNextResponder" ref="380026005"/>
+						<int key="NSvFlags">292</int>
+						<string key="NSFrame">{{0, 10}, {320, 460}}</string>
+						<reference key="NSSuperview" ref="380026005"/>
+						<object class="NSColor" key="IBUIBackgroundColor">
+							<int key="NSColorSpace">3</int>
+							<bytes key="NSWhite">MQA</bytes>
+							<object class="NSColorSpace" key="NSCustomColorSpace">
+								<int key="NSID">2</int>
+							</object>
+						</object>
+						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+					</object>
+				</object>
+				<object class="NSPSMatrix" key="NSFrameMatrix"/>
+				<string key="NSFrameSize">{320, 480}</string>
+				<object class="NSColor" key="IBUIBackgroundColor">
+					<int key="NSColorSpace">1</int>
+					<bytes key="NSRGB">MSAxIDEAA</bytes>
+				</object>
+				<bool key="IBUIOpaque">NO</bool>
+				<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
+				<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+				<bool key="IBUIResizesToFullScreen">YES</bool>
+			</object>
+		</object>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<object class="NSMutableArray" key="connectionRecords">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="841351856"/>
+						<reference key="destination" ref="987256611"/>
+					</object>
+					<int key="connectionID">5</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">window</string>
+						<reference key="source" ref="987256611"/>
+						<reference key="destination" ref="380026005"/>
+					</object>
+					<int key="connectionID">6</int>
+				</object>
+			</object>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<object class="NSArray" key="orderedObjects">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<reference key="object" ref="0"/>
+						<reference key="children" ref="1000"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">2</int>
+						<reference key="object" ref="380026005"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="268100401"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="841351856"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">4</int>
+						<reference key="object" ref="987256611"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">App Delegate</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="450319686"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">12</int>
+						<reference key="object" ref="268100401"/>
+						<reference key="parent" ref="380026005"/>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="flattenedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="NSArray" key="dict.sortedKeys">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>-1.CustomClassName</string>
+					<string>-2.CustomClassName</string>
+					<string>12.CustomClassName</string>
+					<string>12.IBPluginDependency</string>
+					<string>12.IBViewBoundsToFrameTransform</string>
+					<string>2.IBAttributePlaceholdersKey</string>
+					<string>2.IBEditorWindowLastContentRect</string>
+					<string>2.IBPluginDependency</string>
+					<string>2.UIWindow.visibleAtLaunch</string>
+					<string>4.CustomClassName</string>
+					<string>4.IBPluginDependency</string>
+				</object>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>UIApplication</string>
+					<string>UIResponder</string>
+					<string>SimpleApp</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAABC6AAAxCbAAA</bytes>
+					</object>
+					<object class="NSMutableDictionary">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<reference key="dict.sortedKeys" ref="0"/>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+					</object>
+					<string>{{520, 376}, {320, 480}}</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<integer value="1"/>
+					<string>AppDelegate_iPhone</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="unlocalizedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="activeLocalization"/>
+			<object class="NSMutableDictionary" key="localizations">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="sourceID"/>
+			<int key="maxID">12</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">AppDelegate_iPhone</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">window</string>
+						<string key="NS.object.0">UIWindow</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">window</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">window</string>
+							<string key="candidateClassName">UIWindow</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">iPhone/AppDelegate_iPhone.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SimpleApp</string>
+					<string key="superclassName">SkUIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">SimpleApp.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkUIView</string>
+					<string key="superclassName">UIView</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">fOptionsDelegate</string>
+						<string key="NS.object.0">id</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">fOptionsDelegate</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">fOptionsDelegate</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">../iOSSampleApp/Shared/SkUIView.h</string>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="565734826">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIApplication</string>
+					<string key="superclassName">UIResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIApplication.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIResponder</string>
+					<string key="superclassName">NSObject</string>
+					<reference key="sourceIdentifier" ref="565734826"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UISearchBar</string>
+					<string key="superclassName">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UISearchDisplayController</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIPrintFormatter.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIView</string>
+					<string key="superclassName">UIResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<string key="superclassName">UIResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIWindow</string>
+					<string key="superclassName">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIWindow.h</string>
+					</object>
+				</object>
+			</object>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
+			<integer value="1056" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
+			<integer value="3100" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<string key="IBDocument.LastKnownRelativeProjectPath">../SimpleiOSApp.xcodeproj</string>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<string key="IBCocoaTouchPluginVersion">141</string>
+	</data>
+</archive>
diff --git a/experimental/SimpleiOSApp/tool-Info.plist b/experimental/SimpleiOSApp/tool-Info.plist
new file mode 100644
index 0000000..6f8c6d8
--- /dev/null
+++ b/experimental/SimpleiOSApp/tool-Info.plist
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleDisplayName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>com.google.${EXECUTABLE_NAME}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1.0</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>NSMainNibFile</key>
+	<string>MainWindow_iPhone</string>
+	<key>NSMainNibFile~ipad</key>
+	<string>MainWindow_iPad</string>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
diff --git a/experimental/SkSetPoly3To3.cpp b/experimental/SkSetPoly3To3.cpp
new file mode 100644
index 0000000..cf94eb5
--- /dev/null
+++ b/experimental/SkSetPoly3To3.cpp
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkMatrix.h"
+
+// FIXME: needs to be in a header
+bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]);
+
+static void computeOuterProduct(SkScalar op[4],
+                                const SkPoint pts0[3], const SkPoint& ave0,
+                                const SkPoint pts1[3], const SkPoint& ave1) {
+    sk_bzero(op, 4 * sizeof(op[0]));
+    for (int i = 0; i < 3; i++) {
+        SkScalar x0 = pts0[i].fX - ave0.fX;
+        SkScalar y0 = pts0[i].fY - ave0.fY;
+        SkScalar x1 = pts1[i].fX - ave1.fX;
+        SkScalar y1 = pts1[i].fY - ave1.fY;
+        op[0] += SkScalarMul(x0, x1);
+        op[1] += SkScalarMul(x0, y1);
+        op[2] += SkScalarMul(y0, x1);
+        op[3] += SkScalarMul(y0, y1);
+    }
+}
+
+static SkScalar dot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) {
+    return SkScalarMul(ax, bx) + SkScalarMul(ay, by);
+}
+
+bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]) {
+    const SkPoint& srcAve = src[0];
+    const SkPoint& dstAve = dst[0];
+
+    SkScalar srcOP[4], dstOP[4];
+
+    computeOuterProduct(srcOP, src, srcAve, src, srcAve);
+    computeOuterProduct(dstOP, src, srcAve, dst, dstAve);
+
+    SkScalar det = SkScalarMul(srcOP[0], srcOP[3]) - SkScalarMul(srcOP[1], srcOP[2]);
+
+    // need SkScalarNearlyZeroSquared for this (to match Chrome's fix)
+    if (SkScalarNearlyZero(det)) {
+        return false;
+    }
+
+    SkScalar invDet = SkScalarInvert(det);
+
+    // now compute invDet * [srcOP]T * [dstOP]
+
+    // scale and transpose
+    const SkScalar srcOP0 = SkScalarMul( srcOP[3], invDet);
+    const SkScalar srcOP1 = SkScalarMul(-srcOP[1], invDet);
+    const SkScalar srcOP2 = SkScalarMul(-srcOP[2], invDet);
+    const SkScalar srcOP3 = SkScalarMul( srcOP[0], invDet);
+
+    matrix->reset();
+    matrix->setScaleX(dot(srcOP0, srcOP1, dstOP[0], dstOP[2]));
+    matrix->setSkewX( dot(srcOP2, srcOP3, dstOP[0], dstOP[2]));
+    matrix->setSkewY (dot(srcOP0, srcOP1, dstOP[1], dstOP[3]));
+    matrix->setScaleY(dot(srcOP2, srcOP3, dstOP[1], dstOP[3]));
+    matrix->setTranslateX(dstAve.fX - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getScaleX(), matrix->getSkewX()));
+    matrix->setTranslateY(dstAve.fY - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getSkewY(), matrix->getScaleY()));
+    return true;
+}
+
diff --git a/experimental/SkSetPoly3To3_A.cpp b/experimental/SkSetPoly3To3_A.cpp
new file mode 100644
index 0000000..c858145
--- /dev/null
+++ b/experimental/SkSetPoly3To3_A.cpp
@@ -0,0 +1,100 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkMatrix.h"
+
+// FIXME: needs to be in a header
+bool SkSetPoly3To3_A(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]);
+
+#ifdef SK_SCALAR_IS_FIXED
+    typedef int64_t SkDScalar;
+
+    static SkScalar SkDScalar_toScalar(SkDScalar value) {
+        SkDScalar result = (value + (1 << 15)) >> 16;
+        int top = result >> 31;
+        SkASSERT(top == 0 || top == -1);
+        return (SkScalar)result;
+    }
+    static SkScalar divide(SkDScalar numer, SkDScalar denom) {
+        denom >>= 16;
+        return numer / denom;
+    }
+#else
+    typedef double SkDScalar;
+
+    static SkScalar SkDScalar_toScalar(SkDScalar value) {
+        return static_cast<float>(value);
+    }
+    static SkScalar divide(SkDScalar numer, SkDScalar denom) {
+        return static_cast<float>(numer / denom);
+    }
+#endif
+
+static SkDScalar SkDScalar_setMul(SkScalar a, SkScalar b) {
+    return (SkDScalar) ((SkDScalar) a * b);
+}
+
+static void computeOuterProduct(SkScalar op[4],
+                                const SkPoint pts0[3], const SkPoint& ave0,
+                                const SkPoint pts1[3], const SkPoint& ave1) {
+    sk_bzero(op, 4 * sizeof(op[0]));
+    for (int i = 0; i < 3; i++) {
+        SkScalar x0 = pts0[i].fX - ave0.fX;
+        SkScalar y0 = pts0[i].fY - ave0.fY;
+        SkScalar x1 = pts1[i].fX - ave1.fX;
+        SkScalar y1 = pts1[i].fY - ave1.fY;
+        op[0] += SkScalarMul(x0, x1);
+        op[1] += SkScalarMul(x0, y1);
+        op[2] += SkScalarMul(y0, x1);
+        op[3] += SkScalarMul(y0, y1);
+    }
+}
+
+static SkDScalar ddot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) {
+    return SkDScalar_setMul(ax, bx) + SkDScalar_setMul(ay, by);
+}
+
+static SkScalar dot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) {
+    return SkDScalar_toScalar(ddot(ax, ay, bx, by));
+}
+
+bool SkSetPoly3To3_A(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]) {
+    const SkPoint& srcAve = src[0];
+    const SkPoint& dstAve = dst[0];
+
+    SkScalar srcOP[4], dstOP[4];
+
+    computeOuterProduct(srcOP, src, srcAve, src, srcAve);
+    computeOuterProduct(dstOP, src, srcAve, dst, dstAve);
+
+    SkDScalar det = SkDScalar_setMul(srcOP[0], srcOP[3]) -
+                    SkDScalar_setMul(srcOP[1], srcOP[2]);
+
+    SkDScalar M[4];
+
+    const SkScalar srcOP0 = srcOP[3];
+    const SkScalar srcOP1 = -srcOP[1];
+    const SkScalar srcOP2 = -srcOP[2];
+    const SkScalar srcOP3 = srcOP[0];
+
+    M[0] = ddot(srcOP0, srcOP1, dstOP[0], dstOP[2]);
+    M[1] = ddot(srcOP2, srcOP3, dstOP[0], dstOP[2]);
+    M[2] = ddot(srcOP0, srcOP1, dstOP[1], dstOP[3]);
+    M[3] = ddot(srcOP2, srcOP3, dstOP[1], dstOP[3]);
+
+    matrix->reset();
+    matrix->setScaleX(divide(M[0], det));
+    matrix->setSkewX( divide(M[1], det));
+    matrix->setSkewY (divide(M[2], det));
+    matrix->setScaleY(divide(M[3], det));
+    matrix->setTranslateX(dstAve.fX - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getScaleX(), matrix->getSkewX()));
+    matrix->setTranslateY(dstAve.fY - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getSkewY(), matrix->getScaleY()));
+    return true;
+}
+
diff --git a/experimental/SkSetPoly3To3_D.cpp b/experimental/SkSetPoly3To3_D.cpp
new file mode 100644
index 0000000..283b4e5
--- /dev/null
+++ b/experimental/SkSetPoly3To3_D.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 "SkMatrix.h"
+
+// FIXME: needs to be in a header
+bool SkSetPoly3To3_D(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]);
+
+typedef int64_t SkDScalar;
+
+static SkScalar SkDScalar_toScalar(SkDScalar value) {
+    SkDScalar result = (value + (1 << 15)) >> 16;
+    SkDEBUGCODE(int top = static_cast<int>(result >> 31);)
+    SkASSERT(top == 0 || top == -1);
+    return (SkScalar)result;
+}
+
+static SkDScalar SkDScalar_setMul(SkScalar a, SkScalar b) {
+    return (SkDScalar) ((SkDScalar) a * (SkDScalar) b);
+}
+
+static void computeOuterProduct(SkMatrix* matrix,
+                                const SkPoint pts0[3], const SkPoint& ave0,
+                                const SkPoint pts1[3], const SkPoint& ave1) {
+    SkDScalar tmp[4];
+    sk_bzero(tmp, sizeof(tmp));
+
+    for (int i = 0; i < 3; i++) {
+        SkScalar x0 = pts0[i].fX - ave0.fX;
+        SkScalar y0 = pts0[i].fY - ave0.fY;
+        SkScalar x1 = pts1[i].fX - ave1.fX;
+        SkScalar y1 = pts1[i].fY - ave1.fY;
+        tmp[0] += SkDScalar_setMul(x0, x1);
+        tmp[1] += SkDScalar_setMul(x0, y1);
+        tmp[2] += SkDScalar_setMul(y0, x1);
+        tmp[3] += SkDScalar_setMul(y0, y1);
+    }
+    matrix->reset();
+    matrix->setScaleX(SkDScalar_toScalar(tmp[0]));
+    matrix->setSkewY( SkDScalar_toScalar(tmp[1]));
+    matrix->setSkewX( SkDScalar_toScalar(tmp[2]));
+    matrix->setScaleY(SkDScalar_toScalar(tmp[3]));
+}
+
+static SkScalar dot(SkScalar ax, SkScalar ay, SkScalar bx, SkScalar by) {
+    return SkDScalar_toScalar(SkDScalar_setMul(ax, bx) +
+                              SkDScalar_setMul(ay, by));
+}
+
+bool SkSetPoly3To3_D(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]) {
+    const SkPoint& srcAve = src[0];
+    const SkPoint& dstAve = dst[0];
+
+    SkMatrix srcOP, dstOP;
+
+    computeOuterProduct(&srcOP, src, srcAve, src, srcAve);
+
+    if (!srcOP.invert(&srcOP)) {
+        return false;
+    }
+
+    computeOuterProduct(&dstOP, src, srcAve, dst, dstAve);
+
+    matrix->setConcat(dstOP, srcOP);
+    matrix->setTranslateX(dstAve.fX - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getScaleX(), matrix->getSkewX()));
+    matrix->setTranslateY(dstAve.fY - dot(srcAve.fX, srcAve.fY,
+                                    matrix->getSkewY(), matrix->getScaleY()));
+    return true;
+}
+
diff --git a/experimental/iOSSampleApp/Shared/SkOptionListController.h b/experimental/iOSSampleApp/Shared/SkOptionListController.h
new file mode 100644
index 0000000..87c044b
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/SkOptionListController.h
@@ -0,0 +1,16 @@
+#import <UIKit/UIKit.h>
+
+@interface SkOptionListController : UITableViewController {
+    NSMutableArray* fOptions;
+    NSInteger fSelectedIndex;
+    UITableViewCell* fSelectedCell;
+    UITableViewCell* fParentCell;
+}
+@property (nonatomic, retain) NSMutableArray* fOptions;
+@property (nonatomic, assign) NSInteger fSelectedIndex;
+@property (nonatomic, retain) UITableViewCell* fSelectedCell;
+@property (nonatomic, retain) UITableViewCell* fParentCell;
+
+- (void)addOption:(NSString*)option;
+- (NSString*)getSelectedOption;
+@end
diff --git a/experimental/iOSSampleApp/Shared/SkOptionListController.mm b/experimental/iOSSampleApp/Shared/SkOptionListController.mm
new file mode 100644
index 0000000..d524134
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/SkOptionListController.mm
@@ -0,0 +1,78 @@
+#import "SkOptionListController.h"
+
+@implementation SkOptionListController
+
+@synthesize fOptions, fSelectedIndex, fSelectedCell, fParentCell;
+
+#pragma mark -
+#pragma mark Initialization
+
+- (id)initWithStyle:(UITableViewStyle)style {
+    self = [super initWithStyle:style];
+    if (self) {
+        self.fOptions = [[NSMutableArray alloc] init];
+        self.fSelectedIndex = 0;
+        self.fSelectedCell = nil;
+    }
+    return self;
+}
+
+- (void)addOption:(NSString*)option {
+    [fOptions addObject:option];
+}
+
+- (NSString*)getSelectedOption {
+    return (NSString*)[fOptions objectAtIndex:self.fSelectedIndex];
+}
+
+#pragma mark -
+#pragma mark Table view data source
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+    return 1;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+    return [fOptions count];
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    
+    static NSString *CellIdentifier = @"Cell";
+    
+    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+    if (cell == nil) {
+        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+    }
+    
+    cell.textLabel.text = [fOptions objectAtIndex:indexPath.row];
+    if (indexPath.row == fSelectedIndex) {
+        cell.accessoryType = UITableViewCellAccessoryCheckmark;
+        self.fSelectedCell = cell;
+    }
+    else
+        cell.accessoryType = UITableViewCellAccessoryNone;
+    
+    return cell;
+}
+
+#pragma mark -
+#pragma mark Table view delegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+    UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
+    self.fSelectedCell.accessoryType = UITableViewCellAccessoryNone;
+    self.fSelectedCell = cell;
+    cell.accessoryType = UITableViewCellAccessoryCheckmark;
+    self.fParentCell.detailTextLabel.text = cell.textLabel.text;;
+    self.fSelectedIndex = indexPath.row;
+    [self.navigationController popViewControllerAnimated:YES];
+}
+
+- (void)dealloc {
+    self.fOptions = nil;
+    self.fSelectedCell = nil;
+    [super dealloc];
+}
+
+@end
diff --git a/experimental/iOSSampleApp/Shared/SkOptionsTableViewController.h b/experimental/iOSSampleApp/Shared/SkOptionsTableViewController.h
new file mode 100644
index 0000000..13d6c1b
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/SkOptionsTableViewController.h
@@ -0,0 +1,42 @@
+#import <UIKit/UIKit.h>
+#import "SkOptionListController.h"
+#import "SkOSMenu.h"
+#import "SkEvent.h"
+#import "SkUIView.h"
+@interface SkOptionItem : NSObject {
+    UITableViewCell* fCell;
+    const SkOSMenu::Item* fItem;
+}
+@property (nonatomic, assign) const SkOSMenu::Item* fItem;
+@property (nonatomic, retain) UITableViewCell* fCell;
+
+@end
+
+@interface SkOptionListItem : SkOptionItem{
+    SkOptionListController* fOptions;
+}
+@property (nonatomic, retain) SkOptionListController* fOptions;
+
+@end
+
+@interface SkOptionsTableViewController : UITableViewController <UINavigationControllerDelegate, SkUIViewOptionsDelegate> {
+    NSMutableArray* fItems;
+    const SkTDArray<SkOSMenu*>* fMenus;
+    SkOptionListItem* fCurrentList;
+}
+
+@property (nonatomic, retain) NSMutableArray* fItems;
+@property (nonatomic, retain) SkOptionListItem* fCurrentList;
+
+- (void)registerMenus:(const SkTDArray<SkOSMenu*>*)menus;
+- (void)updateMenu:(SkOSMenu*)menu;
+- (void)loadMenu:(SkOSMenu*)menu;
+
+- (UITableViewCell*)createAction:(NSString*)title;
+- (UITableViewCell*)createSlider:(NSString*)title min:(float)min max:(float)max default:(float)value;
+- (UITableViewCell*)createSwitch:(NSString*)title default:(BOOL)state;
+- (UITableViewCell*)createTriState:(NSString*)title default:(int)index;
+- (UITableViewCell*)createTextField:(NSString*)title default:(NSString*)value;
+- (UITableViewCell*)createList:(NSString*)title default:(NSString*)value;
+
+@end
diff --git a/experimental/iOSSampleApp/Shared/SkOptionsTableViewController.mm b/experimental/iOSSampleApp/Shared/SkOptionsTableViewController.mm
new file mode 100644
index 0000000..4383976
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/SkOptionsTableViewController.mm
@@ -0,0 +1,339 @@
+#import "SkOptionsTableViewController.h"
+#include "SkEvent.h"
+#include "SkTArray.h"
+
+@implementation SkOptionItem
+@synthesize fCell, fItem;
+- (void)dealloc {
+    [fCell release];
+    [super dealloc];
+}
+@end
+
+@implementation SkOptionListItem
+@synthesize fOptions;
+- (void)dealloc {
+    [fOptions release];
+    [super dealloc];
+}
+@end
+
+@implementation SkOptionsTableViewController
+
+@synthesize fItems, fCurrentList;
+
+- (id)initWithStyle:(UITableViewStyle)style {
+    self = [super initWithStyle:style];
+    if (self) {
+        self.fItems = [NSMutableArray array];
+    }
+    return self;
+}
+
+//SkUIViewOptionsDelegate
+- (void) view:(SkUIView*)view didAddMenu:(const SkOSMenu*)menu {}
+- (void) view:(SkUIView*)view didUpdateMenu:(SkOSMenu*)menu {
+    [self updateMenu:menu];
+}
+
+- (NSUInteger)convertPathToIndex:(NSIndexPath*)path {
+    NSUInteger index = 0;
+    for (NSInteger i = 0; i < path.section; ++i) {
+        index += (*fMenus)[i]->getCount();
+    }
+    return index + path.row;
+}
+
+- (void)registerMenus:(const SkTDArray<SkOSMenu*>*)menus {
+    fMenus = menus;
+    for (NSUInteger i = 0; i < fMenus->count(); ++i) {
+        [self loadMenu:(*fMenus)[i]];
+    }
+}
+
+- (void)updateMenu:(SkOSMenu*)menu {
+    // the first menu is always assumed to be the static, the second is 
+    // repopulated every time over and over again 
+    int menuIndex = fMenus->find(menu);
+    if (menuIndex >= 0 && menuIndex < fMenus->count()) {
+        NSUInteger first = 0;
+        for (NSInteger i = 0; i < menuIndex; ++i) {
+            first += (*fMenus)[i]->getCount();
+        }
+        [fItems removeObjectsInRange:NSMakeRange(first, [fItems count] - first)];
+        [self loadMenu:menu];
+    }
+    [self.tableView reloadData];
+}
+
+- (void)loadMenu:(SkOSMenu*)menu {
+    const SkOSMenu::Item* menuitems[menu->getCount()];
+    menu->getItems(menuitems);
+    for (int i = 0; i < menu->getCount(); ++i) {
+        const SkOSMenu::Item* item = menuitems[i];
+        NSString* title = [NSString stringWithUTF8String:item->getLabel()];
+        
+        if (SkOSMenu::kList_Type == item->getType()) {
+            int value = 0;
+            SkOptionListItem* List = [[SkOptionListItem alloc] init];
+
+            List.fItem = item;
+            List.fOptions = [[SkOptionListController alloc] initWithStyle:UITableViewStyleGrouped];
+            
+            int count = 0;
+            SkOSMenu::FindListItemCount(*item->getEvent(), &count);
+            SkTArray<SkString> options;
+            options.resize_back(count);
+            SkOSMenu::FindListItems(*item->getEvent(), &options.front());
+            for (int i = 0; i < count; ++i)
+                [List.fOptions addOption:[NSString stringWithUTF8String:options[i].c_str()]];
+            SkOSMenu::FindListIndex(*item->getEvent(), item->getSlotName(), &value);
+            
+            List.fOptions.fSelectedIndex = value;
+            List.fCell = [self createList:title
+                                      default:[List.fOptions getSelectedOption]];
+            List.fOptions.fParentCell = List.fCell;
+            [fItems addObject:List];
+            [List release];
+        }
+        else {
+            SkOptionItem* option = [[SkOptionItem alloc] init];
+            option.fItem = item;
+ 
+            bool state = false;
+            SkString str;
+            SkOSMenu::TriState tristate;
+            switch (item->getType()) {
+                case SkOSMenu::kAction_Type:
+                    option.fCell = [self createAction:title];
+                    break;
+                case SkOSMenu::kSwitch_Type:
+                    SkOSMenu::FindSwitchState(*item->getEvent(), item->getSlotName(), &state);
+                    option.fCell = [self createSwitch:title default:(BOOL)state];
+                    break;
+                case SkOSMenu::kSlider_Type:
+                    SkScalar min, max, value;
+                    SkOSMenu::FindSliderValue(*item->getEvent(), item->getSlotName(), &value);
+                    SkOSMenu::FindSliderMin(*item->getEvent(), &min);
+                    SkOSMenu::FindSliderMax(*item->getEvent(), &max);
+                    option.fCell = [self createSlider:title 
+                                                  min:min 
+                                                  max:max
+                                              default:value];
+                    break;                    
+                case SkOSMenu::kTriState_Type:
+                    SkOSMenu::FindTriState(*item->getEvent(), item->getSlotName(), &tristate);
+                    option.fCell = [self createTriState:title default:(int)tristate];
+                    break;
+                case SkOSMenu::kTextField_Type:
+                    SkOSMenu::FindText(*item->getEvent(), item->getSlotName(), &str);
+                    option.fCell = [self createTextField:title 
+                                                 default:[NSString stringWithUTF8String:str.c_str()]];
+                    break;
+                default:
+                    break;
+            }
+            [fItems addObject:option];
+            [option release];
+        }
+    }
+}
+
+- (void)valueChanged:(id)sender {
+    UITableViewCell* cell = (UITableViewCell*)(((UIView*)sender).superview);
+    NSUInteger index = [self convertPathToIndex:[self.tableView indexPathForCell:cell]];
+    SkOptionItem* item = (SkOptionItem*)[fItems objectAtIndex:index];
+    if ([sender isKindOfClass:[UISlider class]]) {//Slider
+        UISlider* slider = (UISlider *)sender;
+        cell.detailTextLabel.text = [NSString stringWithFormat:@"%1.1f", slider.value];
+        item.fItem->setScalar(slider.value);
+    }
+    else if ([sender isKindOfClass:[UISwitch class]]) {//Switch
+        UISwitch* switch_ = (UISwitch *)sender;
+        item.fItem->setBool(switch_.on);
+    }
+    else if ([sender isKindOfClass:[UITextField class]]) { //TextField
+        UITextField* textField = (UITextField *)sender;
+        [textField resignFirstResponder];
+        item.fItem->setString([textField.text UTF8String]);
+    }
+    else if ([sender isKindOfClass:[UISegmentedControl class]]) { //Action
+        UISegmentedControl* segmented = (UISegmentedControl *)sender;
+        SkOSMenu::TriState state;
+        if (2 == segmented.selectedSegmentIndex) {
+            state = SkOSMenu::kMixedState;
+        } else {
+            state = (SkOSMenu::TriState)segmented.selectedSegmentIndex;
+        }
+        item.fItem->setTriState(state);
+    }
+    else{
+        NSLog(@"unknown");
+    }
+    item.fItem->postEvent();
+}
+
+- (UITableViewCell*)createAction:(NSString*)title {
+    UITableViewCell* cell = [[[UITableViewCell alloc]
+                              initWithStyle:UITableViewCellStyleValue1 
+                              reuseIdentifier:nil] autorelease];
+    cell.textLabel.text = title;
+    return cell;
+}
+
+- (UITableViewCell*)createSwitch:(NSString*)title default:(BOOL)state {
+    UITableViewCell* cell = [[[UITableViewCell alloc] 
+                              initWithStyle:UITableViewCellStyleValue1 
+                              reuseIdentifier:nil] autorelease];
+    cell.textLabel.text = title;
+    cell.selectionStyle = UITableViewCellSelectionStyleNone;
+    UISwitch* switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
+    [switchView setOn:state animated:NO];
+    [switchView addTarget:self 
+                   action:@selector(valueChanged:) 
+         forControlEvents:UIControlEventValueChanged];
+    cell.accessoryView = switchView;
+    [switchView release];
+    return cell;
+}
+
+- (UITableViewCell*)createSlider:(NSString*)title 
+                             min:(float)min 
+                             max:(float)max 
+                         default:(float)value {
+    UITableViewCell* cell = [[[UITableViewCell alloc] 
+                             initWithStyle:UITableViewCellStyleValue1 
+                             reuseIdentifier:nil] autorelease];
+    cell.textLabel.text = title;
+    cell.selectionStyle = UITableViewCellSelectionStyleNone;
+    UISlider* sliderView = [[UISlider alloc] init];
+    sliderView.value = value;
+    sliderView.minimumValue = min;
+    sliderView.maximumValue = max;
+    [sliderView addTarget:self 
+                   action:@selector(valueChanged:) 
+         forControlEvents:UIControlEventValueChanged];
+    cell.detailTextLabel.text = [NSString stringWithFormat:@"%1.1f", value];
+    cell.accessoryView = sliderView; 
+    [sliderView release];
+    return cell;
+}
+
+- (UITableViewCell*)createTriState:(NSString*)title default:(int)index {
+    UITableViewCell* cell = [[[UITableViewCell alloc] 
+                              initWithStyle:UITableViewCellStyleValue1 
+                              reuseIdentifier:nil] autorelease];
+    cell.textLabel.text = title;
+    cell.selectionStyle = UITableViewCellSelectionStyleNone;
+    NSArray* items = [NSArray arrayWithObjects:@"Off", @"On", @"Mixed", nil];
+    UISegmentedControl* segmented = [[UISegmentedControl alloc] initWithItems:items];
+    segmented.selectedSegmentIndex = (index == -1) ? 2 : index;
+    segmented.segmentedControlStyle = UISegmentedControlStyleBar;
+    [segmented addTarget:self 
+                  action:@selector(valueChanged:) 
+        forControlEvents:UIControlEventValueChanged];
+    cell.accessoryView = segmented;
+    [segmented release];
+    return cell; 
+}
+
+- (UITableViewCell*)createTextField:(NSString*)title 
+                            default:(NSString*)value {
+    UITableViewCell* cell = [[[UITableViewCell alloc] 
+                              initWithStyle:UITableViewCellStyleValue1 
+                              reuseIdentifier:nil] autorelease];
+    cell.textLabel.text = title;
+    cell.selectionStyle = UITableViewCellSelectionStyleNone;
+    UITextField* textField = [[UITextField alloc] 
+                              initWithFrame:CGRectMake(0, 10, 150, 25)];
+    textField.adjustsFontSizeToFitWidth = YES;
+    textField.textAlignment = UITextAlignmentRight;
+    textField.textColor = cell.detailTextLabel.textColor;
+    textField.placeholder = value;
+    textField.returnKeyType = UIReturnKeyDone;
+    [textField addTarget:self 
+                  action:@selector(valueChanged:) 
+        forControlEvents:UIControlEventEditingDidEndOnExit];
+    cell.accessoryView = textField; 
+    [textField release];
+    return cell;
+}
+
+- (UITableViewCell*)createList:(NSString*)title default:(NSString*)value{
+    UITableViewCell* cell = [[[UITableViewCell alloc] 
+                              initWithStyle:UITableViewCellStyleValue1 
+                              reuseIdentifier:nil] autorelease];
+    cell.textLabel.text = title;
+    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
+    cell.detailTextLabel.text = value;
+    return cell; 
+}
+
+#pragma mark -
+#pragma mark Table view data source
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+    return fMenus->count();
+}
+
+- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
+    return [NSString stringWithUTF8String:(*fMenus)[section]->getTitle()];
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+    return (*fMenus)[section]->getCount();
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    return ((SkOptionItem*)[fItems objectAtIndex:[self convertPathToIndex:indexPath]]).fCell;
+}
+
+#pragma mark -
+#pragma mark Table view delegate
+
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+    UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
+    id item = [fItems objectAtIndex:[self convertPathToIndex:indexPath]];
+    
+    if ([item isKindOfClass:[SkOptionListItem class]]) {
+        SkOptionListItem* list = (SkOptionListItem*)item;
+        self.fCurrentList = list;
+        self.navigationController.delegate = self;
+        [self.navigationController pushViewController:list.fOptions animated:YES];
+    }
+    else if ([item isKindOfClass:[SkOptionItem class]]) {
+        if (UITableViewCellSelectionStyleNone != cell.selectionStyle) { //Actions
+            SkOptionItem* action = (SkOptionItem*)item;
+            action.fItem->postEvent();
+        }
+    } 
+    else{
+        NSLog(@"unknown");
+    }
+
+    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
+}
+
+#pragma mark -
+#pragma mark Navigation controller delegate
+
+- (void)navigationController:(UINavigationController *)navigationController 
+      willShowViewController:(UIViewController *)viewController 
+                    animated:(BOOL)animated {
+    if (self == viewController) { //when a List option is popped, trigger event
+        NSString* selectedOption = [fCurrentList.fOptions getSelectedOption];
+        fCurrentList.fCell.detailTextLabel.text = selectedOption;
+        fCurrentList.fItem->setInt(fCurrentList.fOptions.fSelectedIndex);
+        fCurrentList.fItem->postEvent();
+    }
+}
+
+#pragma mark -
+#pragma mark Memory management
+
+- (void)dealloc {
+    self.fItems = nil;
+    [super dealloc];
+}
+
+@end
\ No newline at end of file
diff --git a/experimental/iOSSampleApp/Shared/SkUIDetailViewController.h b/experimental/iOSSampleApp/Shared/SkUIDetailViewController.h
new file mode 100644
index 0000000..7435a9d
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/SkUIDetailViewController.h
@@ -0,0 +1,42 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#import <UIKit/UIKit.h>
+#import "SkOptionsTableViewController.h"
+#import "SkUIRootViewController.h"
+#import "SkUIView.h"
+
+class SampleWindow;
+class SkData;
+@interface SkUIDetailViewController : UIViewController {
+    UIPopoverController* fPopOverController;
+    SkOptionsTableViewController* fOptionsController;
+    UIBarButtonItem* fPrintButton;
+    UIBarButtonItem* fOptionsButton;
+    SkData* fData;
+    SkUIView* fSkUIView;
+    SampleWindow* fWind;
+}
+
+@property (nonatomic, retain) UIBarButtonItem* fPrintButton;
+@property (nonatomic, retain) UIBarButtonItem* fOptionsButton;
+@property (nonatomic, retain) SkOptionsTableViewController* fOptionsController;
+@property (nonatomic, assign) UIPopoverController* fPopOverController;
+
+//Instance methods
+- (void)populateRoot:(SkUIRootViewController*)root;
+- (void)goToItem:(NSUInteger)index;
+- (void)createButtons;
+//UI actions
+- (void)printContent;
+- (void)presentOptions;
+
+//SplitView popover management
+- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
+- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem;
+
+@end
diff --git a/experimental/iOSSampleApp/Shared/SkUIDetailViewController.mm b/experimental/iOSSampleApp/Shared/SkUIDetailViewController.mm
new file mode 100644
index 0000000..7c2aef4
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/SkUIDetailViewController.mm
@@ -0,0 +1,175 @@
+#import "SkUIDetailViewController.h"
+#include "SampleApp.h"
+#include "SkCGUtils.h"
+#include "SkData.h"
+#include "SkOSMenu.h"
+@implementation SkUIDetailViewController
+@synthesize fPrintButton, fOptionsButton, fPopOverController, fOptionsController;
+
+//Overwritten from UIViewController
+- (void)viewDidLoad {
+    [super viewDidLoad];
+
+    fSkUIView = (SkUIView*)self.view;
+    
+    fWind = (SampleWindow*)fSkUIView.fWind;
+    fSkUIView.fTitleItem = self.navigationItem;
+    
+    [self createButtons];
+    
+    UISwipeGestureRecognizer* swipe = [[UISwipeGestureRecognizer alloc]
+                                       initWithTarget:self 
+                                       action:@selector(handleSwipe:)];
+    [self.navigationController.navigationBar addGestureRecognizer:swipe];
+    [swipe release];
+    swipe = [[UISwipeGestureRecognizer alloc]
+             initWithTarget:self 
+             action:@selector(handleSwipe:)];
+    swipe.direction = UISwipeGestureRecognizerDirectionLeft;
+    [self.navigationController.navigationBar addGestureRecognizer:swipe];
+    [swipe release];
+    
+    fOptionsController = [[SkOptionsTableViewController alloc] 
+                          initWithStyle:UITableViewStyleGrouped];
+    fSkUIView.fOptionsDelegate = fOptionsController;
+    [fOptionsController registerMenus:fWind->getMenus()];
+    
+}
+
+- (void)createButtons {
+    UIToolbar* toolbar = [[UIToolbar alloc]
+                          initWithFrame:CGRectMake(0, 0, 125, 45)];
+    [toolbar setBarStyle: UIBarStyleBlackOpaque];
+    
+    UIBarButtonItem* flexibleSpace = [[UIBarButtonItem alloc]
+                                       initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
+                                       target:nil
+                                       action:nil];
+    
+    fOptionsButton = [[UIBarButtonItem alloc]
+                    initWithTitle:@"Options" 
+                    style:UIBarButtonItemStylePlain
+                    target:self
+                    action:@selector(presentOptions)];
+    UIBarButtonItem* fixedSpace = [[UIBarButtonItem alloc]
+                                    initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
+                                    target:nil
+                                    action:nil];
+    fixedSpace.width = 10;
+    
+    fPrintButton = [[UIBarButtonItem alloc]
+                    initWithBarButtonSystemItem:UIBarButtonSystemItemAction
+                    target:self
+                    action:@selector(printContent)];
+    fPrintButton.style = UIBarButtonItemStylePlain;
+
+    [toolbar setItems:[NSArray arrayWithObjects:flexibleSpace, fOptionsButton, fixedSpace, fPrintButton, nil]
+             animated:NO];
+    
+    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
+                                              initWithCustomView:toolbar];
+    [flexibleSpace release];
+    [fixedSpace release];
+    [toolbar release];
+}
+
+- (void)handleSwipe:(UISwipeGestureRecognizer *)sender {
+    if (UISwipeGestureRecognizerDirectionRight == sender.direction)
+        fWind->previousSample();
+    else
+        fWind->nextSample();
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+    return YES; // Overriden to allow auto rotation for any direction
+}
+
+- (void)dealloc {
+    [fPrintButton release];
+    [fOptionsButton release];
+    [fPopOverController release];
+    [fOptionsController release];
+    [super dealloc];
+}
+
+//Instance Methods
+- (void)populateRoot:(SkUIRootViewController*)rootVC {
+    for (int i = 0; i < fWind->sampleCount(); ++i) {
+        [rootVC addItem:[NSString stringWithUTF8String:fWind->getSampleTitle(i).c_str()]];
+    }
+}
+
+- (void)goToItem:(NSUInteger)index {
+    fWind->goToSample(index);
+}
+
+- (void)printContent {
+    UIPrintInteractionController *controller = [UIPrintInteractionController sharedPrintController];
+    UIPrintInfo *printInfo = [UIPrintInfo printInfo];
+    printInfo.jobName = @"Skia iOS SampleApp";
+    printInfo.duplex = UIPrintInfoDuplexLongEdge;
+    printInfo.outputType = UIPrintInfoOutputGeneral;
+    fWind->saveToPdf();
+    [fSkUIView forceRedraw];
+    fData = fWind->getPDFData();
+    NSData* data = [NSData dataWithBytesNoCopy:(void*)fData->data() length:fData->size()];
+    controller.printInfo = printInfo;
+    controller.printingItem = data;
+    //Add ref because data pointer retains a pointer to data
+    fData->ref();
+
+    void (^SkCompletionHandler)(UIPrintInteractionController *, BOOL, NSError *) =
+    ^(UIPrintInteractionController *pic, BOOL completed, NSError *error) {
+        fData->unref();
+        if (!completed && error)
+            NSLog(@"FAILED! due to error in domain %@ with error code %u",
+                  error.domain, error.code);
+    };
+
+    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+        [controller presentFromBarButtonItem:fPrintButton animated:YES
+                        completionHandler:SkCompletionHandler];
+    } else {
+        [controller presentAnimated:YES completionHandler:SkCompletionHandler];
+    }
+}
+
+- (void)presentOptions {
+    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
+        if (nil == fPopOverController) {
+            UINavigationController* navigation = [[UINavigationController alloc] 
+                                                  initWithRootViewController:fOptionsController];
+            navigation.navigationBar.topItem.title = @"Options";
+            fPopOverController = [[UIPopoverController alloc] initWithContentViewController:navigation];
+            [navigation release];
+        }
+        
+        if (fPopOverController.isPopoverVisible)
+            [fPopOverController dismissPopoverAnimated:YES];
+        else
+            [fPopOverController presentPopoverFromBarButtonItem:fOptionsButton 
+                                       permittedArrowDirections:UIPopoverArrowDirectionAny 
+                                                       animated:YES];
+        
+    } else {
+        UIBarButtonItem* backButton = [[UIBarButtonItem alloc] initWithTitle:@"Back"
+                                                                       style:UIBarButtonItemStyleBordered
+                                                                      target:nil
+                                                                      action:nil];
+        self.navigationItem.backBarButtonItem = backButton;
+        [backButton release];
+        [self.navigationController pushViewController:fOptionsController animated:YES];
+        self.navigationController.navigationBar.topItem.title = @"Options";
+    }
+}
+ 
+//Popover Management
+- (void)showRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
+    [self.navigationItem setLeftBarButtonItem:barButtonItem animated:NO];
+}
+
+- (void)invalidateRootPopoverButtonItem:(UIBarButtonItem *)barButtonItem {
+    [self.navigationItem setLeftBarButtonItem:nil animated:NO];
+}
+
+@end
\ No newline at end of file
diff --git a/experimental/iOSSampleApp/Shared/SkUIRootViewController.h b/experimental/iOSSampleApp/Shared/SkUIRootViewController.h
new file mode 100644
index 0000000..e4deec1
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/SkUIRootViewController.h
@@ -0,0 +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.
+ */
+#import <UIKit/UIKit.h>
+
+@interface SkUIRootViewController : UITableViewController <UITableViewDataSource> {
+@private
+    UIPopoverController *popoverController;
+    UIBarButtonItem *popoverButtonItem;
+    NSMutableArray* fSamples;
+}
+@property (nonatomic, retain) UIPopoverController *popoverController;
+@property (nonatomic, retain) UIBarButtonItem *popoverButtonItem;
+
+- (void)addItem:(NSString*)anItem;
+
+@end
diff --git a/experimental/iOSSampleApp/Shared/SkUIRootViewController.mm b/experimental/iOSSampleApp/Shared/SkUIRootViewController.mm
new file mode 100644
index 0000000..255a271
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/SkUIRootViewController.mm
@@ -0,0 +1,61 @@
+#import "SkUIRootViewController.h"
+#import "SkUISplitViewController.h"
+@implementation SkUIRootViewController
+@synthesize popoverController, popoverButtonItem;
+
+//Overwritten from UIViewController
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    self.contentSizeForViewInPopover = CGSizeMake(200, self.view.bounds.size.height);
+    fSamples = [[NSMutableArray alloc] init];
+}
+
+- (void)viewDidUnload {
+    [super viewDidUnload];
+    self.popoverButtonItem = nil;
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+    return YES;
+}
+
+- (void)dealloc {
+    [popoverController release];
+    [popoverButtonItem release];
+    [fSamples release];
+    [super dealloc];
+}
+
+
+//Table View Delegate Methods
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+    // Return the number of sections.
+    return 1;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+    // Return the number of rows in the section.
+    return [fSamples count];
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+
+    static NSString *CellIdentifier = @"Cell";
+
+    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
+    if (cell == nil) {
+        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
+                                       reuseIdentifier:CellIdentifier] autorelease];
+    }
+
+    cell.textLabel.text = [fSamples objectAtIndex:indexPath.row];
+    return cell;
+}
+
+//Instance methods
+- (void)addItem:(NSString*)anItem {
+    [fSamples addObject:anItem];
+}
+
+@end
+
diff --git a/experimental/iOSSampleApp/Shared/SkUIView.h b/experimental/iOSSampleApp/Shared/SkUIView.h
new file mode 100644
index 0000000..a0640d4
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/SkUIView.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.
+ */
+#import <OpenGLES/EAGL.h>
+#import <OpenGLES/ES1/gl.h>
+#import <OpenGLES/ES1/glext.h>
+#import <OpenGLES/ES2/gl.h>
+#import <OpenGLES/ES2/glext.h>
+#import <QuartzCore/QuartzCore.h>
+#import <UIKit/UIKit.h>
+#include "SkWindow.h"
+class SkOSWindow;
+class SkEvent;
+@class SkUIView;
+
+@protocol SkUIViewOptionsDelegate <NSObject>
+@optional
+// Called when the view needs to handle adding an SkOSMenu
+- (void) view:(SkUIView*)view didAddMenu:(const SkOSMenu*)menu;
+- (void) view:(SkUIView*)view didUpdateMenu:(SkOSMenu*)menu;
+@end
+
+@interface SkUIView : UIView  {
+    UINavigationItem* fTitleItem;
+    SkOSWindow* fWind;
+    id<SkUIViewOptionsDelegate> fOptionsDelegate;
+}
+
+@property (nonatomic, readonly) SkOSWindow *fWind;
+@property (nonatomic, retain) UINavigationItem* fTitleItem;
+@property (nonatomic, assign) id<SkUIViewOptionsDelegate> fOptionsDelegate;
+
+- (id)initWithDefaults;
+- (void)setUpWindow;
+- (void)forceRedraw;
+- (void)drawInRaster;
+
+- (void)setSkTitle:(const char*)title;
+- (void)onAddMenu:(const SkOSMenu*)menu;
+- (void)onUpdateMenu:(SkOSMenu*)menu;
+- (void)postInvalWithRect:(const SkIRect*)rectOrNil;
+- (BOOL)onHandleEvent:(const SkEvent&)event;
+@end
+
diff --git a/experimental/iOSSampleApp/Shared/SkUIView.mm b/experimental/iOSSampleApp/Shared/SkUIView.mm
new file mode 100644
index 0000000..835e970
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/SkUIView.mm
@@ -0,0 +1,109 @@
+#import "SkUIView.h"
+#include "SkCanvas.h"
+#include "SkCGUtils.h"
+@implementation SkUIView
+
+@synthesize fWind, fTitleItem, fOptionsDelegate;
+
+- (id)initWithDefaults {
+    fWind = NULL;
+    return self;
+}
+
+- (id)initWithCoder:(NSCoder*)coder {
+    if ((self = [super initWithCoder:coder])) {
+        self = [self initWithDefaults];
+        [self setUpWindow];
+    }
+    return self;
+}
+
+- (id)initWithFrame:(CGRect)frame {
+    if (self = [super initWithFrame:frame]) {
+        self = [self initWithDefaults];
+        [self setUpWindow];
+    }
+    return self;
+}
+
+- (void)setUpWindow {
+    if (NULL != fWind) {
+        fWind->setVisibleP(true);
+        fWind->resize(self.frame.size.width, self.frame.size.height, 
+                      SkBitmap::kARGB_8888_Config);
+    }
+}
+
+- (void)dealloc {
+    delete fWind;
+    [fTitleItem release];
+    [super dealloc];
+}
+
+- (void)forceRedraw {
+    [self drawInRaster];
+}
+
+- (void)drawInRaster {
+    SkCanvas canvas(fWind->getBitmap());
+    fWind->draw(&canvas);
+    CGImageRef cgimage = SkCreateCGImageRef(fWind->getBitmap());
+    self.layer.contents = (id)cgimage;
+    CGImageRelease(cgimage);
+}
+
+//Gesture Handlers
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+    for (UITouch *touch in touches) {
+        CGPoint loc = [touch locationInView:self];
+        fWind->handleClick(loc.x, loc.y, SkView::Click::kDown_State, touch);
+    }
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+    for (UITouch *touch in touches) {
+        CGPoint loc = [touch locationInView:self];
+        fWind->handleClick(loc.x, loc.y, SkView::Click::kMoved_State, touch);
+    }
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+    for (UITouch *touch in touches) {
+        CGPoint loc = [touch locationInView:self];
+        fWind->handleClick(loc.x, loc.y, SkView::Click::kUp_State, touch);
+    }
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
+    for (UITouch *touch in touches) {
+        CGPoint loc = [touch locationInView:self];
+        fWind->handleClick(loc.x, loc.y, SkView::Click::kUp_State, touch);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+- (void)setSkTitle:(const char *)title {
+    if (fTitleItem) {
+        fTitleItem.title = [NSString stringWithUTF8String:title];
+    }
+}
+
+- (BOOL)onHandleEvent:(const SkEvent&)evt {
+    return false;
+}
+
+#include "SkOSMenu.h"
+- (void)onAddMenu:(const SkOSMenu*)menu {
+    [self.fOptionsDelegate view:self didAddMenu:menu];
+}
+- (void)onUpdateMenu:(SkOSMenu*)menu {
+    [self.fOptionsDelegate view:self didUpdateMenu:menu];
+}
+
+- (void)postInvalWithRect:(const SkIRect*)r {
+    [self performSelector:@selector(drawInRaster) withObject:nil afterDelay:0];
+    [self setNeedsDisplay];
+}
+
+@end
diff --git a/experimental/iOSSampleApp/Shared/skia_ios.mm b/experimental/iOSSampleApp/Shared/skia_ios.mm
new file mode 100644
index 0000000..65b3e8a
--- /dev/null
+++ b/experimental/iOSSampleApp/Shared/skia_ios.mm
@@ -0,0 +1,15 @@
+#import <UIKit/UIKit.h>
+#include "SkApplication.h"
+
+extern void save_args(int argc, char *argv[]);
+
+int main(int argc, char *argv[]) {
+    signal(SIGPIPE, SIG_IGN);
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+    application_init();
+    save_args(argc, argv);
+    int retVal = UIApplicationMain(argc, argv, nil, nil);
+    application_term();
+    [pool release];
+    return retVal;
+}
diff --git a/experimental/iOSSampleApp/SkSampleUIView.h b/experimental/iOSSampleApp/SkSampleUIView.h
new file mode 100644
index 0000000..6797706
--- /dev/null
+++ b/experimental/iOSSampleApp/SkSampleUIView.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.
+ */
+
+#include "SampleApp.h"
+#import "SkUIView.h"
+
+class SkiOSDeviceManager;
+class SkOSWindow;
+class SkEvent;
+struct FPSState;
+
+@interface SkSampleUIView : SkUIView  {
+    BOOL fRedrawRequestPending;
+
+    struct {
+        EAGLContext*    fContext;
+        GLuint          fRenderbuffer;
+        GLuint          fStencilbuffer;
+        GLuint          fFramebuffer;
+        GLint           fWidth;
+        GLint           fHeight;
+    } fGL;
+
+    NSString* fTitle;
+    CALayer* fRasterLayer;
+    CAEAGLLayer* fGLLayer;
+
+    FPSState* fFPSState;
+    SkiOSDeviceManager* fDevManager;
+}
+
+@property (nonatomic, copy) NSString* fTitle;
+@property (nonatomic, retain) CALayer* fRasterLayer;
+@property (nonatomic, retain) CAEAGLLayer* fGLLayer;
+
+- (id)initWithDefaults;
+- (void)drawInRaster;
+- (void)forceRedraw;
+
+- (void)setSkTitle:(const char*)title;
+- (void)postInvalWithRect:(const SkIRect*)rectOrNil;
+@end
+
diff --git a/experimental/iOSSampleApp/SkSampleUIView.mm b/experimental/iOSSampleApp/SkSampleUIView.mm
new file mode 100644
index 0000000..b08414e
--- /dev/null
+++ b/experimental/iOSSampleApp/SkSampleUIView.mm
@@ -0,0 +1,483 @@
+#import "SkSampleUIView.h"
+
+//#define SKWIND_CONFIG       SkBitmap::kRGB_565_Config
+#define SKWIND_CONFIG       SkBitmap::kARGB_8888_Config
+#define SKGL_CONFIG         kEAGLColorFormatRGB565
+//#define SKGL_CONFIG         kEAGLColorFormatRGBA8
+
+#define FORCE_REDRAW
+
+#include "SkCanvas.h"
+#include "SkCGUtils.h"
+#include "SampleApp.h"
+
+#if SK_SUPPORT_GPU
+//#define USE_GL_1
+#define USE_GL_2
+
+#include "gl/GrGLInterface.h"
+#include "GrContext.h"
+#include "SkGpuDevice.h"
+#endif
+
+class SkiOSDeviceManager : public SampleWindow::DeviceManager {
+public:
+    SkiOSDeviceManager(GLint layerFBO) {
+#if SK_SUPPORT_GPU
+        fCurContext = NULL;
+        fCurIntf = NULL;
+        fCurRenderTarget = NULL;
+        fMSAASampleCount = 0;
+        fLayerFBO = layerFBO;
+#endif
+        fBackend = SkOSWindow::kNone_BackEndType;
+    }
+    
+    virtual ~SkiOSDeviceManager() {
+#if SK_SUPPORT_GPU
+        SkSafeUnref(fCurContext);
+        SkSafeUnref(fCurIntf);
+        SkSafeUnref(fCurRenderTarget);
+#endif
+    }
+    
+    virtual void setUpBackend(SampleWindow* win, int msaaSampleCount) SK_OVERRIDE {
+        SkASSERT(SkOSWindow::kNone_BackEndType == fBackend);
+        
+        fBackend = SkOSWindow::kNone_BackEndType;
+        
+#if SK_SUPPORT_GPU
+        switch (win->getDeviceType()) {
+            // these two don't use GL
+            case SampleWindow::kRaster_DeviceType:
+            case SampleWindow::kPicture_DeviceType:
+                break;
+            // these guys use the native backend
+            case SampleWindow::kGPU_DeviceType:
+            case SampleWindow::kNullGPU_DeviceType:
+                fBackend = SkOSWindow::kNativeGL_BackEndType;
+                break;
+            default:
+                SkASSERT(false);
+                break;
+        }
+        
+        bool result = win->attach(fBackend, msaaSampleCount);
+        if (!result) {
+            SkDebugf("Failed to initialize GL");
+            return;
+        }
+        fMSAASampleCount = msaaSampleCount;
+        
+        SkASSERT(NULL == fCurIntf);
+        switch (win->getDeviceType()) {
+            // these two don't use GL
+            case SampleWindow::kRaster_DeviceType:
+            case SampleWindow::kPicture_DeviceType:
+                fCurIntf = NULL;
+                break;
+            case SampleWindow::kGPU_DeviceType:
+                fCurIntf = GrGLCreateNativeInterface();
+                break;
+            case SampleWindow::kNullGPU_DeviceType:
+                fCurIntf = GrGLCreateNullInterface();
+                break;
+            default:
+                SkASSERT(false);
+                break;
+        }
+        
+        SkASSERT(NULL == fCurContext);
+        if (SkOSWindow::kNone_BackEndType != fBackend) {
+            fCurContext = GrContext::Create(kOpenGL_Shaders_GrEngine,
+                                            (GrPlatform3DContext) fCurIntf);
+        }
+        
+        if ((NULL == fCurContext || NULL == fCurIntf) &&
+            SkOSWindow::kNone_BackEndType != fBackend) {
+            // We need some context and interface to see results if we're using a GL backend
+            SkSafeUnref(fCurContext);
+            SkSafeUnref(fCurIntf);
+            SkDebugf("Failed to setup 3D");
+            win->detach();
+        }
+#endif // SK_SUPPORT_GPU
+        // call windowSizeChanged to create the render target
+        this->windowSizeChanged(win);
+    }
+    
+    virtual void tearDownBackend(SampleWindow *win) SK_OVERRIDE {
+#if SK_SUPPORT_GPU
+        SkSafeUnref(fCurContext);
+        fCurContext = NULL;
+        
+        SkSafeUnref(fCurIntf);
+        fCurIntf = NULL;
+        
+        SkSafeUnref(fCurRenderTarget);
+        fCurRenderTarget = NULL;
+#endif
+        win->detach();
+        fBackend = SampleWindow::kNone_BackEndType;
+    }
+
+    virtual SkCanvas* createCanvas(SampleWindow::DeviceType dType,
+                                   SampleWindow* win) {
+        switch (dType) {
+            case SampleWindow::kRaster_DeviceType:
+                // fallthrough
+            case SampleWindow::kPicture_DeviceType:
+                // fallthrough
+#if SK_ANGLE
+            case SampleWindow::kANGLE_DeviceType:
+#endif
+                break;
+#if SK_SUPPORT_GPU
+            case SampleWindow::kGPU_DeviceType:
+            case SampleWindow::kNullGPU_DeviceType:
+                if (fCurContext) {
+                    SkAutoTUnref<SkDevice> device(new SkGpuDevice(fCurContext,
+                                                                  fCurRenderTarget));
+                    return new SkCanvas(device);
+                } else {
+                    return NULL;
+                }
+                break;
+#endif
+            default:
+                SkASSERT(false);
+                return NULL;
+        }
+        return NULL;
+    }
+    
+    virtual void publishCanvas(SampleWindow::DeviceType dType,
+                               SkCanvas* canvas,
+                               SampleWindow* win) SK_OVERRIDE {
+#if SK_SUPPORT_GPU
+        if (NULL != fCurContext) {
+            fCurContext->flush();
+        }
+#endif
+        win->present();
+    }
+    
+    virtual void windowSizeChanged(SampleWindow* win) SK_OVERRIDE {
+#if SK_SUPPORT_GPU
+        if (NULL != fCurContext) {
+            win->attach(fBackend, fMSAASampleCount);
+            
+            glBindFramebuffer(GL_FRAMEBUFFER, fLayerFBO);
+            GrPlatformRenderTargetDesc desc;
+            desc.fWidth = SkScalarRound(win->width());
+            desc.fHeight = SkScalarRound(win->height());
+            desc.fConfig = kSkia8888_PM_GrPixelConfig;
+            desc.fRenderTargetHandle = fLayerFBO;
+            glGetIntegerv(GL_SAMPLES, &desc.fSampleCnt);
+            glGetIntegerv(GL_STENCIL_BITS, &desc.fStencilBits);
+            
+            SkSafeUnref(fCurRenderTarget);
+            fCurRenderTarget = fCurContext->createPlatformRenderTarget(desc);
+        }
+#endif
+    }
+    
+    virtual GrContext* getGrContext() SK_OVERRIDE {
+#if SK_SUPPORT_GPU
+        return fCurContext;
+#else
+        return NULL;
+#endif
+    }
+    
+    virtual GrRenderTarget* getGrRenderTarget() SK_OVERRIDE {
+#if SK_SUPPORT_GPU
+        return fCurRenderTarget;
+#else
+        return NULL;
+#endif
+    }
+    
+    bool isUsingGL() const { return SkOSWindow::kNone_BackEndType != fBackend; }
+    
+private:
+   
+#if SK_SUPPORT_GPU
+    GrContext*              fCurContext;
+    const GrGLInterface*    fCurIntf;
+    GrRenderTarget*         fCurRenderTarget;
+    int                     fMSAASampleCount;
+    GLint                   fLayerFBO;
+#endif
+    
+    SkOSWindow::SkBackEndTypes fBackend;
+    
+    typedef SampleWindow::DeviceManager INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+@implementation SkSampleUIView
+
+@synthesize fTitle, fRasterLayer, fGLLayer;
+
+#include "SkApplication.h"
+#include "SkEvent.h"
+#include "SkWindow.h"
+
+struct FPSState {
+    static const int FRAME_COUNT = 60;
+    
+    CFTimeInterval fNow0, fNow1;
+    CFTimeInterval fTime0, fTime1, fTotalTime;
+    int fFrameCounter;
+    SkString str;
+    FPSState() {
+        fTime0 = fTime1 = fTotalTime = 0;
+        fFrameCounter = 0;
+    }
+    
+    void startDraw() {
+        fNow0 = CACurrentMediaTime();
+    }
+    
+    void endDraw() {
+        fNow1 = CACurrentMediaTime();
+    }
+    
+    void flush(SkOSWindow* hwnd) {
+        CFTimeInterval now2 = CACurrentMediaTime();
+        
+        fTime0 += fNow1 - fNow0;
+        fTime1 += now2 - fNow1;
+        
+        if (++fFrameCounter == FRAME_COUNT) {
+            CFTimeInterval totalNow = CACurrentMediaTime();
+            fTotalTime = totalNow - fTotalTime;
+            
+            //SkMSec ms0 = (int)(1000 * fTime0 / FRAME_COUNT);
+            //SkMSec msTotal = (int)(1000 * fTotalTime / FRAME_COUNT);
+            //str.printf(" ms: %d [%d], fps: %3.1f", msTotal, ms0,
+            //           FRAME_COUNT / fTotalTime);
+            str.printf(" fps:%3.1f", FRAME_COUNT / fTotalTime);
+            hwnd->setTitle(NULL);
+            fTotalTime = totalNow;
+            fTime0 = fTime1 = 0;
+            fFrameCounter = 0;
+        }
+    }
+};
+
+static FPSState gFPS;
+
+#define FPS_StartDraw() gFPS.startDraw()
+#define FPS_EndDraw()   gFPS.endDraw()
+#define FPS_Flush(wind) gFPS.flush(wind)
+
+///////////////////////////////////////////////////////////////////////////////
+
+- (id)initWithDefaults {
+    if (self = [super initWithDefaults]) {
+        fRedrawRequestPending = false;
+        fFPSState = new FPSState;
+        
+#ifdef USE_GL_1
+        fGL.fContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
+#else
+        fGL.fContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+#endif
+        
+        if (!fGL.fContext || ![EAGLContext setCurrentContext:fGL.fContext])
+        {
+            [self release];
+            return nil;
+        }
+        
+        // Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
+        glGenFramebuffers(1, &fGL.fFramebuffer);
+        glBindFramebuffer(GL_FRAMEBUFFER, fGL.fFramebuffer);
+        
+        glGenRenderbuffers(1, &fGL.fRenderbuffer);
+        glGenRenderbuffers(1, &fGL.fStencilbuffer);
+        
+        glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fGL.fRenderbuffer);
+        
+        glBindRenderbuffer(GL_RENDERBUFFER, fGL.fStencilbuffer);
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fGL.fStencilbuffer);
+        
+        self.fGLLayer = [CAEAGLLayer layer];
+        fGLLayer.bounds = self.bounds;
+        fGLLayer.anchorPoint = CGPointMake(0, 0);
+        fGLLayer.opaque = TRUE;
+        [self.layer addSublayer:fGLLayer];
+        fGLLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
+                                       [NSNumber numberWithBool:NO],
+                                       kEAGLDrawablePropertyRetainedBacking,
+                                       SKGL_CONFIG,
+                                       kEAGLDrawablePropertyColorFormat,
+                                       nil];
+        
+        self.fRasterLayer = [CALayer layer];
+        fRasterLayer.anchorPoint = CGPointMake(0, 0);
+        fRasterLayer.opaque = TRUE;
+        [self.layer addSublayer:fRasterLayer];
+        
+        NSMutableDictionary *newActions = [[NSMutableDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"onOrderIn",
+                                           [NSNull null], @"onOrderOut",
+                                           [NSNull null], @"sublayers",
+                                           [NSNull null], @"contents",
+                                           [NSNull null], @"bounds",
+                                           nil];
+        fGLLayer.actions = newActions;
+        fRasterLayer.actions = newActions;
+        [newActions release];
+        
+        fDevManager = new SkiOSDeviceManager(fGL.fFramebuffer);
+        static char* kDummyArgv = const_cast<char*>("dummyExecutableName");
+        fWind = new SampleWindow(self, 1, &kDummyArgv, fDevManager);
+
+        fWind->resize(self.frame.size.width, self.frame.size.height, SKWIND_CONFIG);
+    }
+    return self;
+}
+
+- (void)dealloc {
+    delete fDevManager;
+    delete fFPSState;
+    self.fRasterLayer = nil;
+    self.fGLLayer = nil;
+    [fGL.fContext release];
+    [super dealloc];
+}
+
+- (void)layoutSubviews {
+    int W, H;
+    
+    // Allocate color buffer backing based on the current layer size
+    glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
+    [fGL.fContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:fGLLayer];
+    
+    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &fGL.fWidth);
+    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &fGL.fHeight);
+    
+    glBindRenderbuffer(GL_RENDERBUFFER, fGL.fStencilbuffer);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fGL.fWidth, fGL.fHeight);
+    
+    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+        NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
+    }
+    
+    if (fDevManager->isUsingGL()) {
+        W = fGL.fWidth;
+        H = fGL.fHeight;
+        CGRect rect = CGRectMake(0, 0, W, H);
+        fGLLayer.bounds = rect;
+    }
+    else {
+        CGRect rect = self.bounds;
+        W = (int)CGRectGetWidth(rect);
+        H = (int)CGRectGetHeight(rect);
+        fRasterLayer.bounds = rect;
+    }
+    
+    printf("---- layoutSubviews %d %d\n", W, H);
+    fWind->resize(W, H);
+    fWind->inval(NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+- (void)drawWithCanvas:(SkCanvas*)canvas {
+    fRedrawRequestPending = false;
+    fFPSState->startDraw();
+    fWind->draw(canvas);
+    fFPSState->endDraw();
+#ifdef FORCE_REDRAW
+    fWind->inval(NULL);
+#endif
+    fFPSState->flush(fWind);
+}
+
+- (void)drawInGL {
+    // This application only creates a single context which is already set current at this point.
+    // This call is redundant, but needed if dealing with multiple contexts.
+    [EAGLContext setCurrentContext:fGL.fContext];
+    
+    // This application only creates a single default framebuffer which is already bound at this point.
+    // This call is redundant, but needed if dealing with multiple framebuffers.
+    glBindFramebuffer(GL_FRAMEBUFFER, fGL.fFramebuffer);
+    
+    GLint scissorEnable;
+    glGetIntegerv(GL_SCISSOR_TEST, &scissorEnable);
+    glDisable(GL_SCISSOR_TEST);
+    glClearColor(0,0,0,0);
+    glClear(GL_COLOR_BUFFER_BIT);
+    if (scissorEnable) {
+        glEnable(GL_SCISSOR_TEST);
+    }
+    glViewport(0, 0, fGL.fWidth, fGL.fHeight);
+    
+   
+    SkAutoTUnref<SkCanvas> canvas(fWind->createCanvas());
+    // if we're not "retained", then we have to always redraw everything.
+    // This call forces us to ignore the fDirtyRgn, and draw everywhere.
+    // If we are "retained", we can skip this call (as the raster case does)
+    fWind->forceInvalAll();
+
+    [self drawWithCanvas:canvas];
+    
+    // This application only creates a single color renderbuffer which is already bound at this point.
+    // This call is redundant, but needed if dealing with multiple renderbuffers.
+    glBindRenderbuffer(GL_RENDERBUFFER, fGL.fRenderbuffer);
+    [fGL.fContext presentRenderbuffer:GL_RENDERBUFFER];
+    
+}
+
+- (void)drawInRaster {
+    SkAutoTUnref<SkCanvas> canvas(fWind->createCanvas());
+    [self drawWithCanvas:canvas];
+    CGImageRef cgimage = SkCreateCGImageRef(fWind->getBitmap());
+    fRasterLayer.contents = (id)cgimage;
+    CGImageRelease(cgimage);
+}
+
+- (void)forceRedraw {
+    if (fDevManager->isUsingGL())
+        [self drawInGL];
+    else 
+        [self drawInRaster];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+- (void)setSkTitle:(const char *)title {
+    NSString* text = [NSString stringWithUTF8String:title];
+    if ([text length] > 0)
+        self.fTitle = text;
+    
+    if (fTitleItem && fTitle) {
+        fTitleItem.title = [NSString stringWithFormat:@"%@%@", fTitle, 
+                            [NSString stringWithUTF8String:fFPSState->str.c_str()]];
+    }
+}
+
+- (void)postInvalWithRect:(const SkIRect*)r {
+    if (!fRedrawRequestPending) {
+        fRedrawRequestPending = true;
+        bool gl = fDevManager->isUsingGL();
+        [CATransaction begin];
+        [CATransaction setAnimationDuration:0];
+        fRasterLayer.hidden = gl;
+        fGLLayer.hidden = !gl;
+        [CATransaction commit];
+        if (gl) {
+            [self performSelector:@selector(drawInGL) withObject:nil afterDelay:0];
+        }
+        else {
+            [self performSelector:@selector(drawInRaster) withObject:nil afterDelay:0];
+            [self setNeedsDisplay];
+        }
+    }
+}
+
+@end
diff --git a/experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig b/experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig
new file mode 100644
index 0000000..a219e72
--- /dev/null
+++ b/experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig
@@ -0,0 +1,17 @@
+//
+//  SkiOSSampleApp-Base.xcconfig
+//  iOSSampleApp
+//
+//  Created by Yang Su on 6/30/11.
+//  Copyright 2011 Google Inc.
+//  Use of this source code is governed by a BSD-style license that can be
+//  found in the LICENSE file.
+//
+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
+
diff --git a/experimental/iOSSampleApp/SkiOSSampleApp-Debug.xcconfig b/experimental/iOSSampleApp/SkiOSSampleApp-Debug.xcconfig
new file mode 100644
index 0000000..eafd949
--- /dev/null
+++ b/experimental/iOSSampleApp/SkiOSSampleApp-Debug.xcconfig
@@ -0,0 +1,13 @@
+//
+//  SkiOSSampleApp.xcconfig
+//  iOSSampleApp
+//
+//  Created by Yang Su on 6/30/11.
+//  Copyright 2011 Google Inc.
+//  Use of this source code is governed by a BSD-style license that can be
+//  found in the LICENSE file.
+//
+#include "SkiOSSampleApp-Base"
+
+GCC_PREPROCESSOR_DEFINITIONS=SK_DEBUG SK_BUILD_FOR_IOS
+GCC_OPTIMIZATION_LEVEL=0
diff --git a/experimental/iOSSampleApp/SkiOSSampleApp-Release.xcconfig b/experimental/iOSSampleApp/SkiOSSampleApp-Release.xcconfig
new file mode 100644
index 0000000..1720318
--- /dev/null
+++ b/experimental/iOSSampleApp/SkiOSSampleApp-Release.xcconfig
@@ -0,0 +1,12 @@
+//
+//  SkiOSSampleApp-Release.xcconfig
+//  iOSSampleApp
+//
+//  Created by Yang Su on 6/30/11.
+//  Copyright 2011 Google Inc.
+//  Use of this source code is governed by a BSD-style license that can be
+//  found in the LICENSE file.
+//
+#include "SkiOSSampleApp-Base"
+GCC_PREPROCESSOR_DEFINITIONS=SK_RELEASE SK_BUILD_FOR_IOS
+GCC_OPTIMIZATION_LEVEL=s
diff --git a/experimental/iOSSampleApp/iOSSampleApp-Info.plist b/experimental/iOSSampleApp/iOSSampleApp-Info.plist
new file mode 100644
index 0000000..47a923f
--- /dev/null
+++ b/experimental/iOSSampleApp/iOSSampleApp-Info.plist
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleDisplayName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>com.google.SkiaSampleApp</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1.0</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>NSMainNibFile</key>
+	<string>MainWindow_iPhone</string>
+	<key>NSMainNibFile~ipad</key>
+	<string>MainWindow_iPad</string>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
diff --git a/experimental/iOSSampleApp/iOSSampleApp.xcodeproj/project.pbxproj b/experimental/iOSSampleApp/iOSSampleApp.xcodeproj/project.pbxproj
new file mode 100755
index 0000000..f4516cd
--- /dev/null
+++ b/experimental/iOSSampleApp/iOSSampleApp.xcodeproj/project.pbxproj
@@ -0,0 +1,3489 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
+		2605F44213F18B1B0044A072 /* DebuggerContentView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2605F43C13F18B1B0044A072 /* DebuggerContentView.cpp */; };
+		2605F44313F18B1B0044A072 /* DebuggerCommandsView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2605F43E13F18B1B0044A072 /* DebuggerCommandsView.cpp */; };
+		2605F44413F18B1B0044A072 /* SkDebugDumper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2605F43F13F18B1B0044A072 /* SkDebugDumper.cpp */; };
+		2605F44513F18B1B0044A072 /* DebuggerStateView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2605F44113F18B1B0044A072 /* DebuggerStateView.cpp */; };
+		2605F65D13F19D1D0044A072 /* SamplePicture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005913B11F5B0064D447 /* SamplePicture.cpp */; };
+		2605F66513F19EB80044A072 /* GrStencilBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2605F66413F19EB80044A072 /* GrStencilBuffer.cpp */; };
+		2605F83013F1AE4B0044A072 /* xfermodes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002213B11F5B0064D447 /* xfermodes.cpp */; };
+		2605F83113F1AE4C0044A072 /* tilemodes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002113B11F5B0064D447 /* tilemodes.cpp */; };
+		2605F83213F1AE4C0044A072 /* shapes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002013B11F5B0064D447 /* shapes.cpp */; };
+		2605F83313F1AE4D0044A072 /* shadows.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E001F13B11F5B0064D447 /* shadows.cpp */; };
+		2605F83413F1AE4D0044A072 /* shadertext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E001E13B11F5B0064D447 /* shadertext.cpp */; };
+		2605F83513F1AE4E0044A072 /* poly2poly.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E001D13B11F5B0064D447 /* poly2poly.cpp */; };
+		2605F83613F1AE4F0044A072 /* nocolorbleed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E001B13B11F5B0064D447 /* nocolorbleed.cpp */; };
+		2605F83713F1AE500044A072 /* points.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E001C13B11F5B0064D447 /* points.cpp */; };
+		2605F83813F1AE500044A072 /* gradients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E001A13B11F5B0064D447 /* gradients.cpp */; };
+		2605F83913F1AE510044A072 /* filltypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E001813B11F5B0064D447 /* filltypes.cpp */; };
+		2605F83A13F1AE520044A072 /* complexclip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E001713B11F5B0064D447 /* complexclip.cpp */; };
+		2605F83B13F1AE520044A072 /* blurs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E001613B11F5B0064D447 /* blurs.cpp */; };
+		2605F83C13F1AE530044A072 /* bitmapfilters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E001513B11F5B0064D447 /* bitmapfilters.cpp */; };
+		260E00E313B11F5B0064D447 /* OverView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002513B11F5B0064D447 /* OverView.cpp */; };
+		260E00E813B11F5B0064D447 /* SampleArc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002A13B11F5B0064D447 /* SampleArc.cpp */; };
+		260E00E913B11F5B0064D447 /* SampleAvoid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002B13B11F5B0064D447 /* SampleAvoid.cpp */; };
+		260E00EC13B11F5B0064D447 /* SampleBlur.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002E13B11F5B0064D447 /* SampleBlur.cpp */; };
+		260E00EE13B11F5B0064D447 /* SampleCircle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003013B11F5B0064D447 /* SampleCircle.cpp */; };
+		260E00F013B11F5B0064D447 /* SampleComplexClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003313B11F5B0064D447 /* SampleComplexClip.cpp */; };
+		260E00F213B11F5B0064D447 /* SampleCull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003513B11F5B0064D447 /* SampleCull.cpp */; };
+		260E00FB13B11F5B0064D447 /* SampleFillType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003E13B11F5B0064D447 /* SampleFillType.cpp */; };
+		260E010613B11F5B0064D447 /* SampleLCD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004913B11F5B0064D447 /* SampleLCD.cpp */; };
+		260E011313B11F5B0064D447 /* SamplePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005613B11F5B0064D447 /* SamplePath.cpp */; };
+		260E011413B11F5B0064D447 /* SamplePathClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005713B11F5B0064D447 /* SamplePathClip.cpp */; };
+		260E011513B11F5B0064D447 /* SamplePathEffects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005813B11F5B0064D447 /* SamplePathEffects.cpp */; };
+		260E011713B11F5B0064D447 /* SamplePoints.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005A13B11F5B0064D447 /* SamplePoints.cpp */; };
+		260E011813B11F5B0064D447 /* SamplePolyToPoly.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005B13B11F5B0064D447 /* SamplePolyToPoly.cpp */; };
+		260E011913B11F5B0064D447 /* SampleRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005C13B11F5B0064D447 /* SampleRegion.cpp */; };
+		260E011D13B11F5B0064D447 /* SampleShapes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006013B11F5B0064D447 /* SampleShapes.cpp */; };
+		260E011F13B11F5B0064D447 /* SampleSlides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006213B11F5B0064D447 /* SampleSlides.cpp */; };
+		260E012313B11F5B0064D447 /* SampleText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006613B11F5B0064D447 /* SampleText.cpp */; };
+		260E012413B11F5B0064D447 /* SampleTextAlpha.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006713B11F5B0064D447 /* SampleTextAlpha.cpp */; };
+		260E012513B11F5B0064D447 /* SampleTextBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006813B11F5B0064D447 /* SampleTextBox.cpp */; };
+		260E012613B11F5B0064D447 /* SampleTextEffects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006913B11F5B0064D447 /* SampleTextEffects.cpp */; };
+		260E012713B11F5B0064D447 /* SampleTextOnPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006A13B11F5B0064D447 /* SampleTextOnPath.cpp */; };
+		260E012B13B11F5B0064D447 /* SampleTriangles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006E13B11F5B0064D447 /* SampleTriangles.cpp */; };
+		260E012C13B11F5B0064D447 /* SampleTypeface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006F13B11F5B0064D447 /* SampleTypeface.cpp */; };
+		260E012D13B11F5B0064D447 /* SampleUnitMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E007013B11F5B0064D447 /* SampleUnitMapper.cpp */; };
+		260E012E13B11F5B0064D447 /* SampleVertices.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E007113B11F5B0064D447 /* SampleVertices.cpp */; };
+		260E013113B11F5B0064D447 /* SkGPipeRead.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E007513B11F5B0064D447 /* SkGPipeRead.cpp */; };
+		260E013213B11F5B0064D447 /* SkGPipeWrite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E007613B11F5B0064D447 /* SkGPipeWrite.cpp */; };
+		260E02A213B1225D0064D447 /* Sk64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E020C13B1225D0064D447 /* Sk64.cpp */; };
+		260E02A313B1225D0064D447 /* SkAdvancedTypefaceMetrics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E020D13B1225D0064D447 /* SkAdvancedTypefaceMetrics.cpp */; };
+		260E02A413B1225D0064D447 /* SkAlphaRuns.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E020E13B1225D0064D447 /* SkAlphaRuns.cpp */; };
+		260E02A513B1225D0064D447 /* SkBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E021013B1225D0064D447 /* SkBitmap.cpp */; };
+		260E02A613B1225D0064D447 /* SkBitmapProcShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E021113B1225D0064D447 /* SkBitmapProcShader.cpp */; };
+		260E02A713B1225D0064D447 /* SkBitmapProcState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E021313B1225D0064D447 /* SkBitmapProcState.cpp */; };
+		260E02A813B1225D0064D447 /* SkBitmapProcState_matrixProcs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E021613B1225D0064D447 /* SkBitmapProcState_matrixProcs.cpp */; };
+		260E02A913B1225D0064D447 /* SkBitmapSampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E021813B1225D0064D447 /* SkBitmapSampler.cpp */; };
+		260E02AA13B1225D0064D447 /* SkBitmap_scroll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E021D13B1225D0064D447 /* SkBitmap_scroll.cpp */; };
+		260E02AD13B1225D0064D447 /* SkBlitRow_D4444.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022113B1225D0064D447 /* SkBlitRow_D4444.cpp */; };
+		260E02AE13B1225D0064D447 /* SkBlitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022213B1225D0064D447 /* SkBlitter.cpp */; };
+		260E02B513B1225D0064D447 /* SkBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022913B1225D0064D447 /* SkBuffer.cpp */; };
+		260E02B613B1225D0064D447 /* SkCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022A13B1225D0064D447 /* SkCanvas.cpp */; };
+		260E02B713B1225D0064D447 /* SkChunkAlloc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022B13B1225D0064D447 /* SkChunkAlloc.cpp */; };
+		260E02B813B1225D0064D447 /* SkClampRange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022C13B1225D0064D447 /* SkClampRange.cpp */; };
+		260E02B913B1225D0064D447 /* SkClipStack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022D13B1225D0064D447 /* SkClipStack.cpp */; };
+		260E02BA13B1225D0064D447 /* SkColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022E13B1225D0064D447 /* SkColor.cpp */; };
+		260E02BB13B1225D0064D447 /* SkColorFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022F13B1225D0064D447 /* SkColorFilter.cpp */; };
+		260E02BC13B1225D0064D447 /* SkColorTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E023013B1225D0064D447 /* SkColorTable.cpp */; };
+		260E02BD13B1225D0064D447 /* SkComposeShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E023113B1225D0064D447 /* SkComposeShader.cpp */; };
+		260E02BE13B1225D0064D447 /* SkConcaveToTriangles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E023213B1225D0064D447 /* SkConcaveToTriangles.cpp */; };
+		260E02BF13B1225D0064D447 /* SkCordic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E023413B1225D0064D447 /* SkCordic.cpp */; };
+		260E02C013B1225D0064D447 /* SkCubicClipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E023713B1225D0064D447 /* SkCubicClipper.cpp */; };
+		260E02C213B1225D0064D447 /* SkDebug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E023A13B1225D0064D447 /* SkDebug.cpp */; };
+		260E02C313B1225D0064D447 /* SkDeque.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E023B13B1225D0064D447 /* SkDeque.cpp */; };
+		260E02C413B1225D0064D447 /* SkDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E023C13B1225D0064D447 /* SkDevice.cpp */; };
+		260E02C513B1225D0064D447 /* SkDither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E023D13B1225D0064D447 /* SkDither.cpp */; };
+		260E02C613B1225D0064D447 /* SkDraw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E023E13B1225D0064D447 /* SkDraw.cpp */; };
+		260E02C713B1225D0064D447 /* SkEdge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024013B1225D0064D447 /* SkEdge.cpp */; };
+		260E02C813B1225D0064D447 /* SkEdgeBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024213B1225D0064D447 /* SkEdgeBuilder.cpp */; };
+		260E02C913B1225D0064D447 /* SkEdgeClipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024313B1225D0064D447 /* SkEdgeClipper.cpp */; };
+		260E02CA13B1225D0064D447 /* SkFilterProc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024513B1225D0064D447 /* SkFilterProc.cpp */; };
+		260E02CB13B1225D0064D447 /* SkFlate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024713B1225D0064D447 /* SkFlate.cpp */; };
+		260E02CC13B1225D0064D447 /* SkFlattenable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024813B1225D0064D447 /* SkFlattenable.cpp */; };
+		260E02CD13B1225D0064D447 /* SkFloat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024913B1225D0064D447 /* SkFloat.cpp */; };
+		260E02CE13B1225D0064D447 /* SkFloatBits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024B13B1225D0064D447 /* SkFloatBits.cpp */; };
+		260E02CF13B1225D0064D447 /* SkFontHost.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024C13B1225D0064D447 /* SkFontHost.cpp */; };
+		260E02D013B1225D0064D447 /* SkGeometry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024D13B1225D0064D447 /* SkGeometry.cpp */; };
+		260E02D113B1225D0064D447 /* SkGlobals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024E13B1225D0064D447 /* SkGlobals.cpp */; };
+		260E02D213B1225D0064D447 /* SkGlyphCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E024F13B1225D0064D447 /* SkGlyphCache.cpp */; };
+		260E02D313B1225D0064D447 /* SkGraphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025113B1225D0064D447 /* SkGraphics.cpp */; };
+		260E02D413B1225D0064D447 /* SkLineClipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025213B1225D0064D447 /* SkLineClipper.cpp */; };
+		260E02D513B1225D0064D447 /* SkMMapStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025313B1225D0064D447 /* SkMMapStream.cpp */; };
+		260E02D613B1225D0064D447 /* SkMallocPixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025413B1225D0064D447 /* SkMallocPixelRef.cpp */; };
+		260E02D713B1225D0064D447 /* SkMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025513B1225D0064D447 /* SkMask.cpp */; };
+		260E02D813B1225D0064D447 /* SkMaskFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025613B1225D0064D447 /* SkMaskFilter.cpp */; };
+		260E02D913B1225D0064D447 /* SkMath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025713B1225D0064D447 /* SkMath.cpp */; };
+		260E02DA13B1225D0064D447 /* SkMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025813B1225D0064D447 /* SkMatrix.cpp */; };
+		260E02DB13B1225D0064D447 /* SkMetaData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025913B1225D0064D447 /* SkMetaData.cpp */; };
+		260E02DC13B1225D0064D447 /* SkPackBits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025A13B1225D0064D447 /* SkPackBits.cpp */; };
+		260E02DD13B1225D0064D447 /* SkPaint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025B13B1225D0064D447 /* SkPaint.cpp */; };
+		260E02DE13B1225D0064D447 /* SkPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025C13B1225D0064D447 /* SkPath.cpp */; };
+		260E02DF13B1225D0064D447 /* SkPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025D13B1225D0064D447 /* SkPathEffect.cpp */; };
+		260E02E013B1225D0064D447 /* SkPathHeap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E025E13B1225D0064D447 /* SkPathHeap.cpp */; };
+		260E02E113B1225D0064D447 /* SkPathMeasure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026013B1225D0064D447 /* SkPathMeasure.cpp */; };
+		260E02E213B1225D0064D447 /* SkPicture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026113B1225D0064D447 /* SkPicture.cpp */; };
+		260E02E313B1225D0064D447 /* SkPictureFlat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026213B1225D0064D447 /* SkPictureFlat.cpp */; };
+		260E02E413B1225D0064D447 /* SkPicturePlayback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026413B1225D0064D447 /* SkPicturePlayback.cpp */; };
+		260E02E513B1225D0064D447 /* SkPictureRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026613B1225D0064D447 /* SkPictureRecord.cpp */; };
+		260E02E613B1225D0064D447 /* SkPixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026813B1225D0064D447 /* SkPixelRef.cpp */; };
+		260E02E713B1225D0064D447 /* SkPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026913B1225D0064D447 /* SkPoint.cpp */; };
+		260E02E913B1225D0064D447 /* SkPtrRecorder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026B13B1225D0064D447 /* SkPtrRecorder.cpp */; };
+		260E02EA13B1225D0064D447 /* SkQuadClipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026C13B1225D0064D447 /* SkQuadClipper.cpp */; };
+		260E02EB13B1225D0064D447 /* SkRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026E13B1225D0064D447 /* SkRasterizer.cpp */; };
+		260E02EC13B1225D0064D447 /* SkRect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026F13B1225D0064D447 /* SkRect.cpp */; };
+		260E02ED13B1225D0064D447 /* SkRefDict.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027013B1225D0064D447 /* SkRefDict.cpp */; };
+		260E02EE13B1225D0064D447 /* SkRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027113B1225D0064D447 /* SkRegion.cpp */; };
+		260E02EF13B1225D0064D447 /* SkRegion_path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027313B1225D0064D447 /* SkRegion_path.cpp */; };
+		260E02F013B1225D0064D447 /* SkScalar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027413B1225D0064D447 /* SkScalar.cpp */; };
+		260E02F113B1225D0064D447 /* SkScalerContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027513B1225D0064D447 /* SkScalerContext.cpp */; };
+		260E02F213B1225D0064D447 /* SkScan.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027613B1225D0064D447 /* SkScan.cpp */; };
+		260E02F313B1225D0064D447 /* SkScan_AntiPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027813B1225D0064D447 /* SkScan_AntiPath.cpp */; };
+		260E02F413B1225D0064D447 /* SkScan_Antihair.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027913B1225D0064D447 /* SkScan_Antihair.cpp */; };
+		260E02F513B1225D0064D447 /* SkScan_Hairline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027A13B1225D0064D447 /* SkScan_Hairline.cpp */; };
+		260E02F613B1225D0064D447 /* SkScan_Path.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027B13B1225D0064D447 /* SkScan_Path.cpp */; };
+		260E02F713B1225D0064D447 /* SkShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027C13B1225D0064D447 /* SkShader.cpp */; };
+		260E02F813B1225D0064D447 /* SkShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E027D13B1225D0064D447 /* SkShape.cpp */; };
+		260E02FB13B1225D0064D447 /* SkStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E028313B1225D0064D447 /* SkStream.cpp */; };
+		260E02FC13B1225D0064D447 /* SkString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E028413B1225D0064D447 /* SkString.cpp */; };
+		260E02FD13B1225D0064D447 /* SkStroke.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E028513B1225D0064D447 /* SkStroke.cpp */; };
+		260E02FE13B1225D0064D447 /* SkStrokerPriv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E028613B1225D0064D447 /* SkStrokerPriv.cpp */; };
+		260E02FF13B1225D0064D447 /* SkTSearch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E028813B1225D0064D447 /* SkTSearch.cpp */; };
+		260E030013B1225D0064D447 /* SkTypeface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E028C13B1225D0064D447 /* SkTypeface.cpp */; };
+		260E030113B1225D0064D447 /* SkTypefaceCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E028D13B1225D0064D447 /* SkTypefaceCache.cpp */; };
+		260E030213B1225D0064D447 /* SkUnPreMultiply.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E028F13B1225D0064D447 /* SkUnPreMultiply.cpp */; };
+		260E030313B1225D0064D447 /* SkUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E029013B1225D0064D447 /* SkUtils.cpp */; };
+		260E030413B1225D0064D447 /* SkWriter32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E029113B1225D0064D447 /* SkWriter32.cpp */; };
+		260E030513B1225D0064D447 /* SkXfermode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E029213B1225D0064D447 /* SkXfermode.cpp */; };
+		260E030713B1225D0064D447 /* SkDebug_stdio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E029613B1225D0064D447 /* SkDebug_stdio.cpp */; };
+		260E030B13B1225D0064D447 /* SkGlobals_global.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E029A13B1225D0064D447 /* SkGlobals_global.cpp */; };
+		260E030C13B1225D0064D447 /* SkMemory_malloc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E029B13B1225D0064D447 /* SkMemory_malloc.cpp */; };
+		260E030E13B1225D0064D447 /* SkThread_pthread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E029D13B1225D0064D447 /* SkThread_pthread.cpp */; };
+		260E030F13B1225D0064D447 /* SkTime_Unix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E029E13B1225D0064D447 /* SkTime_Unix.cpp */; };
+		260E031113B1225D0064D447 /* SkXMLParser_empty.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E02A013B1225D0064D447 /* SkXMLParser_empty.cpp */; };
+		260E035413B122A30064D447 /* Sk1DPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E033713B122A30064D447 /* Sk1DPathEffect.cpp */; };
+		260E035513B122A30064D447 /* Sk2DPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E033813B122A30064D447 /* Sk2DPathEffect.cpp */; };
+		260E035613B122A30064D447 /* SkAvoidXfermode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E033913B122A30064D447 /* SkAvoidXfermode.cpp */; };
+		260E035713B122A30064D447 /* SkBitmapCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E033A13B122A30064D447 /* SkBitmapCache.cpp */; };
+		260E035813B122A30064D447 /* SkBlurDrawLooper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E033C13B122A30064D447 /* SkBlurDrawLooper.cpp */; };
+		260E035913B122A30064D447 /* SkBlurMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E033D13B122A30064D447 /* SkBlurMask.cpp */; };
+		260E035A13B122A30064D447 /* SkBlurMaskFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E033F13B122A30064D447 /* SkBlurMaskFilter.cpp */; };
+		260E035B13B122A30064D447 /* SkColorFilters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034013B122A30064D447 /* SkColorFilters.cpp */; };
+		260E035C13B122A30064D447 /* SkColorMatrixFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034113B122A30064D447 /* SkColorMatrixFilter.cpp */; };
+		260E035D13B122A30064D447 /* SkCornerPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034213B122A30064D447 /* SkCornerPathEffect.cpp */; };
+		260E035E13B122A30064D447 /* SkDashPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034313B122A30064D447 /* SkDashPathEffect.cpp */; };
+		260E035F13B122A30064D447 /* SkDiscretePathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034413B122A30064D447 /* SkDiscretePathEffect.cpp */; };
+		260E036013B122A30064D447 /* SkEmbossMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034513B122A30064D447 /* SkEmbossMask.cpp */; };
+		260E036113B122A30064D447 /* SkEmbossMaskFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034713B122A30064D447 /* SkEmbossMaskFilter.cpp */; };
+		260E036213B122A30064D447 /* SkGradientShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034913B122A30064D447 /* SkGradientShader.cpp */; };
+		260E036313B122A30064D447 /* SkGroupShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034A13B122A30064D447 /* SkGroupShape.cpp */; };
+		260E036413B122A30064D447 /* SkKernel33MaskFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034B13B122A30064D447 /* SkKernel33MaskFilter.cpp */; };
+		260E036513B122A30064D447 /* SkLayerDrawLooper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034C13B122A30064D447 /* SkLayerDrawLooper.cpp */; };
+		260E036613B122A30064D447 /* SkLayerRasterizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034D13B122A30064D447 /* SkLayerRasterizer.cpp */; };
+		260E036713B122A30064D447 /* SkPaintFlagsDrawFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034E13B122A30064D447 /* SkPaintFlagsDrawFilter.cpp */; };
+		260E036813B122A30064D447 /* SkPixelXorXfermode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E034F13B122A30064D447 /* SkPixelXorXfermode.cpp */; };
+		260E036913B122A30064D447 /* SkPorterDuff.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E035013B122A30064D447 /* SkPorterDuff.cpp */; };
+		260E036A13B122A30064D447 /* SkRectShape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E035213B122A30064D447 /* SkRectShape.cpp */; };
+		260E036B13B122A30064D447 /* SkTransparentShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E035313B122A30064D447 /* SkTransparentShader.cpp */; };
+		260E040C13B122D40064D447 /* GrAllocPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03D113B122D40064D447 /* GrAllocPool.cpp */; };
+		260E040D13B122D40064D447 /* GrAtlas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03D213B122D40064D447 /* GrAtlas.cpp */; };
+		260E040E13B122D40064D447 /* GrBufferAllocPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03D413B122D40064D447 /* GrBufferAllocPool.cpp */; };
+		260E040F13B122D40064D447 /* GrClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03D613B122D40064D447 /* GrClip.cpp */; };
+		260E041213B122D40064D447 /* GrDrawTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03D913B122D40064D447 /* GrDrawTarget.cpp */; };
+		260E041413B122D40064D447 /* GrGLIndexBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03DB13B122D40064D447 /* GrGLIndexBuffer.cpp */; };
+		260E041513B122D40064D447 /* GrGLInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03DC13B122D40064D447 /* GrGLInterface.cpp */; };
+		260E041613B122D40064D447 /* GrGLProgram.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03DD13B122D40064D447 /* GrGLProgram.cpp */; };
+		260E041813B122D40064D447 /* GrGLUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03E013B122D40064D447 /* GrGLUtil.cpp */; };
+		260E041913B122D40064D447 /* GrGLVertexBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03E113B122D40064D447 /* GrGLVertexBuffer.cpp */; };
+		260E041A13B122D40064D447 /* GrGpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03E213B122D40064D447 /* GrGpu.cpp */; };
+		260E041B13B122D40064D447 /* GrGpuFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03E313B122D40064D447 /* GrGpuFactory.cpp */; };
+		260E041C13B122D40064D447 /* GrGpuGL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03E413B122D40064D447 /* GrGpuGL.cpp */; };
+		260E041D13B122D40064D447 /* GrGpuGLFixed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03E613B122D40064D447 /* GrGpuGLFixed.cpp */; };
+		260E041E13B122D40064D447 /* GrGpuGLShaders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03E813B122D40064D447 /* GrGpuGLShaders.cpp */; };
+		260E041F13B122D40064D447 /* GrInOrderDrawBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03EA13B122D40064D447 /* GrInOrderDrawBuffer.cpp */; };
+		260E042013B122D40064D447 /* GrMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03EB13B122D40064D447 /* GrMatrix.cpp */; };
+		260E042113B122D40064D447 /* GrMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03EC13B122D40064D447 /* GrMemory.cpp */; };
+		260E042213B122D40064D447 /* GrPathRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03ED13B122D40064D447 /* GrPathRenderer.cpp */; };
+		260E042313B122D40064D447 /* GrPathUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03EE13B122D40064D447 /* GrPathUtils.cpp */; };
+		260E042413B122D40064D447 /* GrRectanizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03F013B122D40064D447 /* GrRectanizer.cpp */; };
+		260E042513B122D40064D447 /* GrResource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03F213B122D40064D447 /* GrResource.cpp */; };
+		260E042613B122D40064D447 /* GrStencil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03F313B122D40064D447 /* GrStencil.cpp */; };
+		260E042813B122D40064D447 /* GrTextContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03F513B122D40064D447 /* GrTextContext.cpp */; };
+		260E042913B122D40064D447 /* GrTextStrike.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03F613B122D40064D447 /* GrTextStrike.cpp */; };
+		260E042A13B122D40064D447 /* GrTexture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03F813B122D40064D447 /* GrTexture.cpp */; };
+		260E042C13B122D40064D447 /* gr_unittests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03FA13B122D40064D447 /* gr_unittests.cpp */; };
+		260E042D13B122D40064D447 /* GrPrintf_skia.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E040213B122D40064D447 /* GrPrintf_skia.cpp */; };
+		260E042E13B122D40064D447 /* SkGpuCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E040313B122D40064D447 /* SkGpuCanvas.cpp */; };
+		260E042F13B122D40064D447 /* SkGpuDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E040413B122D40064D447 /* SkGpuDevice.cpp */; };
+		260E043013B122D40064D447 /* SkGr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E040513B122D40064D447 /* SkGr.cpp */; };
+		260E043113B122D40064D447 /* SkGrFontScaler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E040613B122D40064D447 /* SkGrFontScaler.cpp */; };
+		260E043213B122D40064D447 /* SkGrTexturePixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E040713B122D40064D447 /* SkGrTexturePixelRef.cpp */; };
+		260E046613B1232F0064D447 /* SkCreateRLEPixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E044B13B1232F0064D447 /* SkCreateRLEPixelRef.cpp */; };
+		260E046713B1232F0064D447 /* SkFDStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E044C13B1232F0064D447 /* SkFDStream.cpp */; };
+		260E046813B1232F0064D447 /* SkFlipPixelRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E044D13B1232F0064D447 /* SkFlipPixelRef.cpp */; };
+		260E046913B1232F0064D447 /* SkImageDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E044E13B1232F0064D447 /* SkImageDecoder.cpp */; };
+		260E047113B1232F0064D447 /* SkImageEncoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E045613B1232F0064D447 /* SkImageEncoder.cpp */; };
+		260E050113B123840064D447 /* SkSVGCircle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04CE13B123840064D447 /* SkSVGCircle.cpp */; };
+		260E050213B123840064D447 /* SkSVGClipPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04D013B123840064D447 /* SkSVGClipPath.cpp */; };
+		260E050313B123840064D447 /* SkSVGDefs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04D213B123840064D447 /* SkSVGDefs.cpp */; };
+		260E050413B123840064D447 /* SkSVGElements.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04D413B123840064D447 /* SkSVGElements.cpp */; };
+		260E050513B123840064D447 /* SkSVGEllipse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04D613B123840064D447 /* SkSVGEllipse.cpp */; };
+		260E050613B123840064D447 /* SkSVGFeColorMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04D813B123840064D447 /* SkSVGFeColorMatrix.cpp */; };
+		260E050713B123840064D447 /* SkSVGFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04DA13B123840064D447 /* SkSVGFilter.cpp */; };
+		260E050813B123840064D447 /* SkSVGG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04DC13B123840064D447 /* SkSVGG.cpp */; };
+		260E050913B123840064D447 /* SkSVGGradient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04DE13B123840064D447 /* SkSVGGradient.cpp */; };
+		260E050A13B123840064D447 /* SkSVGGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04E013B123840064D447 /* SkSVGGroup.cpp */; };
+		260E050B13B123840064D447 /* SkSVGImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04E213B123840064D447 /* SkSVGImage.cpp */; };
+		260E050C13B123840064D447 /* SkSVGLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04E413B123840064D447 /* SkSVGLine.cpp */; };
+		260E050D13B123840064D447 /* SkSVGLinearGradient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04E613B123840064D447 /* SkSVGLinearGradient.cpp */; };
+		260E050E13B123840064D447 /* SkSVGMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04E813B123840064D447 /* SkSVGMask.cpp */; };
+		260E050F13B123840064D447 /* SkSVGMetadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04EA13B123840064D447 /* SkSVGMetadata.cpp */; };
+		260E051013B123840064D447 /* SkSVGPaintState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04EC13B123840064D447 /* SkSVGPaintState.cpp */; };
+		260E051113B123840064D447 /* SkSVGParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04ED13B123840064D447 /* SkSVGParser.cpp */; };
+		260E051213B123840064D447 /* SkSVGPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04EE13B123840064D447 /* SkSVGPath.cpp */; };
+		260E051313B123840064D447 /* SkSVGPolygon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04F013B123840064D447 /* SkSVGPolygon.cpp */; };
+		260E051413B123840064D447 /* SkSVGPolyline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04F213B123840064D447 /* SkSVGPolyline.cpp */; };
+		260E051513B123840064D447 /* SkSVGRadialGradient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04F413B123840064D447 /* SkSVGRadialGradient.cpp */; };
+		260E051613B123840064D447 /* SkSVGRect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04F613B123840064D447 /* SkSVGRect.cpp */; };
+		260E051713B123840064D447 /* SkSVGSVG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04F813B123840064D447 /* SkSVGSVG.cpp */; };
+		260E051813B123840064D447 /* SkSVGStop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04FA13B123840064D447 /* SkSVGStop.cpp */; };
+		260E051913B123840064D447 /* SkSVGSymbol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04FC13B123840064D447 /* SkSVGSymbol.cpp */; };
+		260E051A13B123840064D447 /* SkSVGText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E04FE13B123840064D447 /* SkSVGText.cpp */; };
+		260E051B13B123840064D447 /* SkSVGUse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E050013B123840064D447 /* SkSVGUse.cpp */; };
+		260E056C13B123DE0064D447 /* SkCreateCGImageRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E054913B123DE0064D447 /* SkCreateCGImageRef.cpp */; };
+		260E057713B123DE0064D447 /* SkBoundaryPatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E055713B123DE0064D447 /* SkBoundaryPatch.cpp */; };
+		260E057813B123DE0064D447 /* SkCamera.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E055813B123DE0064D447 /* SkCamera.cpp */; };
+		260E057913B123DE0064D447 /* SkColorMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E055913B123DE0064D447 /* SkColorMatrix.cpp */; };
+		260E057A13B123DE0064D447 /* SkCubicInterval.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E055A13B123DE0064D447 /* SkCubicInterval.cpp */; };
+		260E057B13B123DE0064D447 /* SkCullPoints.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E055B13B123DE0064D447 /* SkCullPoints.cpp */; };
+		260E057C13B123DE0064D447 /* SkDumpCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E055C13B123DE0064D447 /* SkDumpCanvas.cpp */; };
+		260E057E13B123DE0064D447 /* SkInterpolator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E055E13B123DE0064D447 /* SkInterpolator.cpp */; };
+		260E057F13B123DE0064D447 /* SkLayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E055F13B123DE0064D447 /* SkLayer.cpp */; };
+		260E058013B123DE0064D447 /* SkMatrix44.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E056013B123DE0064D447 /* SkMatrix44.cpp */; };
+		260E058113B123DE0064D447 /* SkMeshUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E056113B123DE0064D447 /* SkMeshUtils.cpp */; };
+		260E058213B123DE0064D447 /* SkNWayCanvas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E056213B123DE0064D447 /* SkNWayCanvas.cpp */; };
+		260E058313B123DE0064D447 /* SkNinePatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E056313B123DE0064D447 /* SkNinePatch.cpp */; };
+		260E058413B123DE0064D447 /* SkOSFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E056413B123DE0064D447 /* SkOSFile.cpp */; };
+		260E058513B123DE0064D447 /* SkParse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E056513B123DE0064D447 /* SkParse.cpp */; };
+		260E058613B123DE0064D447 /* SkParseColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E056613B123DE0064D447 /* SkParseColor.cpp */; };
+		260E058713B123DE0064D447 /* SkParsePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E056713B123DE0064D447 /* SkParsePath.cpp */; };
+		260E058913B123DE0064D447 /* SkSfntUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E056913B123DE0064D447 /* SkSfntUtils.cpp */; };
+		260E058A13B123DE0064D447 /* SkUnitMappers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E056A13B123DE0064D447 /* SkUnitMappers.cpp */; };
+		260E05C913B123E80064D447 /* SkBGViewArtist.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05AF13B123E80064D447 /* SkBGViewArtist.cpp */; };
+		260E05CB13B123E80064D447 /* SkEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05B113B123E80064D447 /* SkEvent.cpp */; };
+		260E05CC13B123E80064D447 /* SkEventSink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05B213B123E80064D447 /* SkEventSink.cpp */; };
+		260E05CD13B123E80064D447 /* SkImageView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05B313B123E80064D447 /* SkImageView.cpp */; };
+		260E05CE13B123E80064D447 /* SkListView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05B413B123E80064D447 /* SkListView.cpp */; };
+		260E05D013B123E80064D447 /* SkOSMenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05B613B123E80064D447 /* SkOSMenu.cpp */; };
+		260E05D113B123E80064D447 /* SkParsePaint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05B713B123E80064D447 /* SkParsePaint.cpp */; };
+		260E05D513B123E80064D447 /* SkStackViewLayout.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05BB13B123E80064D447 /* SkStackViewLayout.cpp */; };
+		260E05D613B123E80064D447 /* SkStaticTextView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05BC13B123E80064D447 /* SkStaticTextView.cpp */; };
+		260E05D713B123E80064D447 /* SkTagList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05BD13B123E80064D447 /* SkTagList.cpp */; };
+		260E05D813B123E80064D447 /* SkTextBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05BF13B123E80064D447 /* SkTextBox.cpp */; };
+		260E05D913B123E80064D447 /* SkTouchGesture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05C013B123E80064D447 /* SkTouchGesture.cpp */; };
+		260E05DA13B123E80064D447 /* SkView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05C113B123E80064D447 /* SkView.cpp */; };
+		260E05DB13B123E80064D447 /* SkViewInflate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05C213B123E80064D447 /* SkViewInflate.cpp */; };
+		260E05DC13B123E80064D447 /* SkViewPriv.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05C313B123E80064D447 /* SkViewPriv.cpp */; };
+		260E05DD13B123E80064D447 /* SkWidget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05C513B123E80064D447 /* SkWidget.cpp */; };
+		260E05DF13B123E80064D447 /* SkWidgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05C713B123E80064D447 /* SkWidgets.cpp */; };
+		260E05E013B123E80064D447 /* SkWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05C813B123E80064D447 /* SkWindow.cpp */; };
+		260E05FE13B124210064D447 /* SkDOM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05F713B124210064D447 /* SkDOM.cpp */; };
+		260E060113B124210064D447 /* SkXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E05FA13B124210064D447 /* SkXMLParser.cpp */; };
+		260E075313B127E00064D447 /* SkAnimateActive.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06BC13B127E00064D447 /* SkAnimateActive.cpp */; };
+		260E075413B127E00064D447 /* SkAnimateBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06BE13B127E00064D447 /* SkAnimateBase.cpp */; };
+		260E075513B127E00064D447 /* SkAnimateField.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06C013B127E00064D447 /* SkAnimateField.cpp */; };
+		260E075613B127E00064D447 /* SkAnimateMaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06C113B127E00064D447 /* SkAnimateMaker.cpp */; };
+		260E075713B127E00064D447 /* SkAnimateSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06C413B127E00064D447 /* SkAnimateSet.cpp */; };
+		260E075813B127E00064D447 /* SkAnimator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06C613B127E00064D447 /* SkAnimator.cpp */; };
+		260E075913B127E00064D447 /* SkAnimatorScript.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06C713B127E00064D447 /* SkAnimatorScript.cpp */; };
+		260E075A13B127E00064D447 /* SkBase64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06C913B127E00064D447 /* SkBase64.cpp */; };
+		260E075B13B127E00064D447 /* SkBoundable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06CB13B127E00064D447 /* SkBoundable.cpp */; };
+		260E075C13B127E00064D447 /* SkBuildCondensedInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06CD13B127E00064D447 /* SkBuildCondensedInfo.cpp */; };
+		260E075D13B127E00064D447 /* SkDisplayAdd.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06CE13B127E00064D447 /* SkDisplayAdd.cpp */; };
+		260E075E13B127E00064D447 /* SkDisplayApply.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06D013B127E00064D447 /* SkDisplayApply.cpp */; };
+		260E075F13B127E00064D447 /* SkDisplayBounds.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06D213B127E00064D447 /* SkDisplayBounds.cpp */; };
+		260E076013B127E00064D447 /* SkDisplayEvent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06D413B127E00064D447 /* SkDisplayEvent.cpp */; };
+		260E076113B127E00064D447 /* SkDisplayEvents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06D613B127E00064D447 /* SkDisplayEvents.cpp */; };
+		260E076213B127E00064D447 /* SkDisplayInclude.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06D813B127E00064D447 /* SkDisplayInclude.cpp */; };
+		260E076313B127E00064D447 /* SkDisplayInput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06DA13B127E00064D447 /* SkDisplayInput.cpp */; };
+		260E076413B127E00064D447 /* SkDisplayList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06DC13B127E00064D447 /* SkDisplayList.cpp */; };
+		260E076513B127E00064D447 /* SkDisplayMath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06DE13B127E00064D447 /* SkDisplayMath.cpp */; };
+		260E076613B127E00064D447 /* SkDisplayMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06E013B127E00064D447 /* SkDisplayMovie.cpp */; };
+		260E076713B127E00064D447 /* SkDisplayNumber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06E213B127E00064D447 /* SkDisplayNumber.cpp */; };
+		260E076813B127E00064D447 /* SkDisplayPost.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06E413B127E00064D447 /* SkDisplayPost.cpp */; };
+		260E076913B127E00064D447 /* SkDisplayRandom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06E613B127E00064D447 /* SkDisplayRandom.cpp */; };
+		260E076A13B127E00064D447 /* SkDisplayScreenplay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06E813B127E00064D447 /* SkDisplayScreenplay.cpp */; };
+		260E076B13B127E00064D447 /* SkDisplayType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06EA13B127E00064D447 /* SkDisplayType.cpp */; };
+		260E076C13B127E00064D447 /* SkDisplayTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06EC13B127E00064D447 /* SkDisplayTypes.cpp */; };
+		260E076D13B127E00064D447 /* SkDisplayXMLParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06EE13B127E00064D447 /* SkDisplayXMLParser.cpp */; };
+		260E076E13B127E00064D447 /* SkDisplayable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06F013B127E00064D447 /* SkDisplayable.cpp */; };
+		260E076F13B127E00064D447 /* SkDraw3D.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06F213B127E00064D447 /* SkDraw3D.cpp */; };
+		260E077013B127E00064D447 /* SkDrawBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06F413B127E00064D447 /* SkDrawBitmap.cpp */; };
+		260E077113B127E00064D447 /* SkDrawBlur.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06F613B127E00064D447 /* SkDrawBlur.cpp */; };
+		260E077213B127E00064D447 /* SkDrawClip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06F813B127E00064D447 /* SkDrawClip.cpp */; };
+		260E077313B127E00064D447 /* SkDrawColor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06FA13B127E00064D447 /* SkDrawColor.cpp */; };
+		260E077413B127E00064D447 /* SkDrawDash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06FC13B127E00064D447 /* SkDrawDash.cpp */; };
+		260E077513B127E00064D447 /* SkDrawDiscrete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E06FE13B127E00064D447 /* SkDrawDiscrete.cpp */; };
+		260E077613B127E00064D447 /* SkDrawEmboss.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E070013B127E00064D447 /* SkDrawEmboss.cpp */; };
+		260E077713B127E00064D447 /* SkDrawExtraPathEffect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E070213B127E00064D447 /* SkDrawExtraPathEffect.cpp */; };
+		260E077813B127E00064D447 /* SkDrawFull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E070313B127E00064D447 /* SkDrawFull.cpp */; };
+		260E077913B127E00064D447 /* SkDrawGradient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E070513B127E00064D447 /* SkDrawGradient.cpp */; };
+		260E077A13B127E00064D447 /* SkDrawGroup.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E070713B127E00064D447 /* SkDrawGroup.cpp */; };
+		260E077B13B127E00064D447 /* SkDrawLine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E070913B127E00064D447 /* SkDrawLine.cpp */; };
+		260E077C13B127E00064D447 /* SkDrawMatrix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E070B13B127E00064D447 /* SkDrawMatrix.cpp */; };
+		260E077D13B127E00064D447 /* SkDrawOval.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E070D13B127E00064D447 /* SkDrawOval.cpp */; };
+		260E077E13B127E00064D447 /* SkDrawPaint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E070F13B127E00064D447 /* SkDrawPaint.cpp */; };
+		260E077F13B127E00064D447 /* SkDrawPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E071113B127E00064D447 /* SkDrawPath.cpp */; };
+		260E078013B127E00064D447 /* SkDrawPoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E071313B127E00064D447 /* SkDrawPoint.cpp */; };
+		260E078113B127E00064D447 /* SkDrawRectangle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E071513B127E00064D447 /* SkDrawRectangle.cpp */; };
+		260E078213B127E00064D447 /* SkDrawSaveLayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E071713B127E00064D447 /* SkDrawSaveLayer.cpp */; };
+		260E078313B127E00064D447 /* SkDrawShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E071913B127E00064D447 /* SkDrawShader.cpp */; };
+		260E078413B127E00064D447 /* SkDrawText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E071B13B127E00064D447 /* SkDrawText.cpp */; };
+		260E078513B127E00064D447 /* SkDrawTextBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E071D13B127E00064D447 /* SkDrawTextBox.cpp */; };
+		260E078613B127E00064D447 /* SkDrawTo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E071F13B127E00064D447 /* SkDrawTo.cpp */; };
+		260E078713B127E00064D447 /* SkDrawTransparentShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E072113B127E00064D447 /* SkDrawTransparentShader.cpp */; };
+		260E078813B127E00064D447 /* SkDrawable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E072313B127E00064D447 /* SkDrawable.cpp */; };
+		260E078913B127E00064D447 /* SkDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E072513B127E00064D447 /* SkDump.cpp */; };
+		260E078A13B127E00064D447 /* SkGetCondensedInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E072813B127E00064D447 /* SkGetCondensedInfo.cpp */; };
+		260E078B13B127E00064D447 /* SkHitClear.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E072913B127E00064D447 /* SkHitClear.cpp */; };
+		260E078C13B127E00064D447 /* SkHitTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E072B13B127E00064D447 /* SkHitTest.cpp */; };
+		260E078D13B127E00064D447 /* SkMatrixParts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E072E13B127E00064D447 /* SkMatrixParts.cpp */; };
+		260E078E13B127E00064D447 /* SkMemberInfo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E073013B127E00064D447 /* SkMemberInfo.cpp */; };
+		260E078F13B127E00064D447 /* SkOpArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E073213B127E00064D447 /* SkOpArray.cpp */; };
+		260E079013B127E00064D447 /* SkOperandIterpolator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E073713B127E00064D447 /* SkOperandIterpolator.cpp */; };
+		260E079113B127E00064D447 /* SkPaintParts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E073813B127E00064D447 /* SkPaintParts.cpp */; };
+		260E079213B127E00064D447 /* SkParseSVGPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E073A13B127E00064D447 /* SkParseSVGPath.cpp */; };
+		260E079313B127E00064D447 /* SkPathParts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E073B13B127E00064D447 /* SkPathParts.cpp */; };
+		260E079413B127E00064D447 /* SkPostParts.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E073D13B127E00064D447 /* SkPostParts.cpp */; };
+		260E079513B127E00064D447 /* SkScript.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E073F13B127E00064D447 /* SkScript.cpp */; };
+		260E079613B127E00064D447 /* SkScriptDecompile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E074313B127E00064D447 /* SkScriptDecompile.cpp */; };
+		260E079713B127E00064D447 /* SkScriptRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E074413B127E00064D447 /* SkScriptRuntime.cpp */; };
+		260E079813B127E00064D447 /* SkScriptTokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E074613B127E00064D447 /* SkScriptTokenizer.cpp */; };
+		260E079913B127E00064D447 /* SkSnapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E074713B127E00064D447 /* SkSnapshot.cpp */; };
+		260E079A13B127E00064D447 /* SkTextOnPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E074A13B127E00064D447 /* SkTextOnPath.cpp */; };
+		260E079B13B127E00064D447 /* SkTextToPath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E074C13B127E00064D447 /* SkTextToPath.cpp */; };
+		260E079C13B127E00064D447 /* SkTime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E074E13B127E00064D447 /* SkTime.cpp */; };
+		260E079D13B127E00064D447 /* SkTypedArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E074F13B127E00064D447 /* SkTypedArray.cpp */; };
+		260E079E13B127E00064D447 /* SkXMLAnimatorWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E075113B127E00064D447 /* SkXMLAnimatorWriter.cpp */; };
+		260E07B313B128210064D447 /* SkXMLWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E07B213B128210064D447 /* SkXMLWriter.cpp */; };
+		260E07C513B128610064D447 /* SkMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E07C413B128610064D447 /* SkMovie.cpp */; };
+		260E07CA13B128740064D447 /* SkPageFlipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E07C913B128740064D447 /* SkPageFlipper.cpp */; };
+		260E07D113B128870064D447 /* SkImageRef.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E07CE13B128870064D447 /* SkImageRef.cpp */; };
+		260E07D213B128870064D447 /* SkImageRefPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E07CF13B128870064D447 /* SkImageRefPool.cpp */; };
+		260E07D713B1289C0064D447 /* SkImageRef_GlobalPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E07D613B1289C0064D447 /* SkImageRef_GlobalPool.cpp */; };
+		260E083D13B12A200064D447 /* SkImageDecoder_Factory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E083B13B12A200064D447 /* SkImageDecoder_Factory.cpp */; };
+		260E083E13B12A200064D447 /* SkImageEncoder_Factory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E083C13B12A200064D447 /* SkImageEncoder_Factory.cpp */; };
+		260E087F13B12B6F0064D447 /* SkFontHost_mac_coretext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E029813B1225D0064D447 /* SkFontHost_mac_coretext.cpp */; };
+		260E095713B134C90064D447 /* FlingState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E095513B134C90064D447 /* FlingState.cpp */; };
+		260E095813B134C90064D447 /* GrDrawMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E095613B134C90064D447 /* GrDrawMesh.cpp */; };
+		260E147913B2734E0064D447 /* SkUISplitViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 260E147813B2734E0064D447 /* SkUISplitViewController.mm */; };
+		260E16E613B2853F0064D447 /* SampleGM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004413B11F5B0064D447 /* SampleGM.cpp */; };
+		260E1DCD13B3AA490064D447 /* SkUINavigationController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 260E1DCB13B3AA490064D447 /* SkUINavigationController.mm */; };
+		260E1EA213B3B15A0064D447 /* SkPDFCatalog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1E9613B3B15A0064D447 /* SkPDFCatalog.cpp */; };
+		260E1EA313B3B15A0064D447 /* SkPDFDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1E9713B3B15A0064D447 /* SkPDFDevice.cpp */; };
+		260E1EA413B3B15A0064D447 /* SkPDFDocument.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1E9813B3B15A0064D447 /* SkPDFDocument.cpp */; };
+		260E1EA513B3B15A0064D447 /* SkPDFFont.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1E9913B3B15A0064D447 /* SkPDFFont.cpp */; };
+		260E1EA613B3B15A0064D447 /* SkPDFFormXObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1E9A13B3B15A0064D447 /* SkPDFFormXObject.cpp */; };
+		260E1EA713B3B15A0064D447 /* SkPDFGraphicState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1E9B13B3B15A0064D447 /* SkPDFGraphicState.cpp */; };
+		260E1EA813B3B15A0064D447 /* SkPDFImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1E9C13B3B15A0064D447 /* SkPDFImage.cpp */; };
+		260E1EA913B3B15A0064D447 /* SkPDFPage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1E9D13B3B15A0064D447 /* SkPDFPage.cpp */; };
+		260E1EAA13B3B15A0064D447 /* SkPDFShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1E9E13B3B15A0064D447 /* SkPDFShader.cpp */; };
+		260E1EAB13B3B15A0064D447 /* SkPDFStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1E9F13B3B15A0064D447 /* SkPDFStream.cpp */; };
+		260E1EAC13B3B15A0064D447 /* SkPDFTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1EA013B3B15A0064D447 /* SkPDFTypes.cpp */; };
+		260E1EAD13B3B15A0064D447 /* SkPDFUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E1EA113B3B15A0064D447 /* SkPDFUtils.cpp */; };
+		260EE9D513AFA7850064D447 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 260EE9D013AFA7850064D447 /* CoreFoundation.framework */; };
+		260EF18513AFD62E0064D447 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 260EF18413AFD62E0064D447 /* CoreText.framework */; };
+		260EF2B013AFDBD30064D447 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
+		2612C0D1140D2F9B001B6925 /* ClockFaceView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002413B11F5B0064D447 /* ClockFaceView.cpp */; };
+		2612C0D2140D2F9C001B6925 /* SampleAARects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002613B11F5B0064D447 /* SampleAARects.cpp */; };
+		2612C0D3140D2F9C001B6925 /* SampleAnimator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002813B11F5B0064D447 /* SampleAnimator.cpp */; };
+		2612C0D4140D2F9D001B6925 /* SampleAll.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002713B11F5B0064D447 /* SampleAll.cpp */; };
+		2612C0D5140D2F9F001B6925 /* SampleBigGradient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002C13B11F5B0064D447 /* SampleBigGradient.cpp */; };
+		2612C0D6140D2F9F001B6925 /* SampleBitmapRect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002D13B11F5B0064D447 /* SampleBitmapRect.cpp */; };
+		2612C0D7140D2FA1001B6925 /* SampleCamera.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E002F13B11F5B0064D447 /* SampleCamera.cpp */; };
+		2612C0D8140D2FA2001B6925 /* SampleColorFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003213B11F5B0064D447 /* SampleColorFilter.cpp */; };
+		2612C0D9140D2FA3001B6925 /* SampleConcavePaths.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003413B11F5B0064D447 /* SampleConcavePaths.cpp */; };
+		2612C0DA140D2FA4001B6925 /* SampleDecode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003613B11F5B0064D447 /* SampleDecode.cpp */; };
+		2612C0DB140D2FA5001B6925 /* SampleDither.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003713B11F5B0064D447 /* SampleDither.cpp */; };
+		2612C0DC140D2FA8001B6925 /* SampleDitherBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003813B11F5B0064D447 /* SampleDitherBitmap.cpp */; };
+		2612C0DD140D2FAA001B6925 /* SampleDrawLooper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003913B11F5B0064D447 /* SampleDrawLooper.cpp */; };
+		2612C0DE140D2FAA001B6925 /* SampleEffects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003A13B11F5B0064D447 /* SampleEffects.cpp */; };
+		2612C0DF140D2FAB001B6925 /* SampleEmboss.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003B13B11F5B0064D447 /* SampleEmboss.cpp */; };
+		2612C0E0140D2FAB001B6925 /* SampleEncode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003C13B11F5B0064D447 /* SampleEncode.cpp */; };
+		2612C0E1140D2FAC001B6925 /* SampleExtractAlpha.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003D13B11F5B0064D447 /* SampleExtractAlpha.cpp */; };
+		2612C0E2140D2FAD001B6925 /* SampleFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E003F13B11F5B0064D447 /* SampleFilter.cpp */; };
+		2612C0E3140D2FAE001B6925 /* SampleFilter2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004013B11F5B0064D447 /* SampleFilter2.cpp */; };
+		2612C0E4140D2FAE001B6925 /* SampleFontCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004113B11F5B0064D447 /* SampleFontCache.cpp */; };
+		2612C0E5140D2FAF001B6925 /* SampleFontScalerTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004213B11F5B0064D447 /* SampleFontScalerTest.cpp */; };
+		2612C0E6140D2FAF001B6925 /* SampleFuzz.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004313B11F5B0064D447 /* SampleFuzz.cpp */; };
+		2612C0E7140D2FB1001B6925 /* SampleGradients.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004513B11F5B0064D447 /* SampleGradients.cpp */; };
+		2612C0E8140D2FB1001B6925 /* SampleHairline.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004613B11F5B0064D447 /* SampleHairline.cpp */; };
+		2612C0E9140D2FB2001B6925 /* SampleImage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004713B11F5B0064D447 /* SampleImage.cpp */; };
+		2612C0EA140D2FB2001B6925 /* SampleImageDir.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004813B11F5B0064D447 /* SampleImageDir.cpp */; };
+		2612C0EB140D2FB3001B6925 /* SampleLayerMask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004A13B11F5B0064D447 /* SampleLayerMask.cpp */; };
+		2612C0EC140D2FB4001B6925 /* SampleLayers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004B13B11F5B0064D447 /* SampleLayers.cpp */; };
+		2612C0ED140D2FB4001B6925 /* SampleLineClipper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004C13B11F5B0064D447 /* SampleLineClipper.cpp */; };
+		2612C0EE140D2FB5001B6925 /* SampleLines.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004D13B11F5B0064D447 /* SampleLines.cpp */; };
+		2612C0EF140D2FB7001B6925 /* SampleMeasure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004E13B11F5B0064D447 /* SampleMeasure.cpp */; };
+		2612C0F0140D2FB8001B6925 /* SampleMipMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E004F13B11F5B0064D447 /* SampleMipMap.cpp */; };
+		2612C0F1140D2FB8001B6925 /* SampleMovie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005013B11F5B0064D447 /* SampleMovie.cpp */; };
+		2612C0F2140D2FB9001B6925 /* SampleNinePatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005113B11F5B0064D447 /* SampleNinePatch.cpp */; };
+		2612C0F3140D2FBA001B6925 /* SampleOvalTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005213B11F5B0064D447 /* SampleOvalTest.cpp */; };
+		2612C0F4140D2FBB001B6925 /* SampleOverflow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005313B11F5B0064D447 /* SampleOverflow.cpp */; };
+		2612C0F5140D2FBB001B6925 /* SamplePageFlip.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005413B11F5B0064D447 /* SamplePageFlip.cpp */; };
+		2612C0F6140D2FBC001B6925 /* SamplePatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005513B11F5B0064D447 /* SamplePatch.cpp */; };
+		2612C0F7140D2FBD001B6925 /* SampleRepeatTile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005D13B11F5B0064D447 /* SampleRepeatTile.cpp */; };
+		2612C0F8140D2FBE001B6925 /* SampleShaderText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005E13B11F5B0064D447 /* SampleShaderText.cpp */; };
+		2612C0F9140D2FBE001B6925 /* SampleShaders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E005F13B11F5B0064D447 /* SampleShaders.cpp */; };
+		2612C0FA140D2FC0001B6925 /* SampleSkLayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006113B11F5B0064D447 /* SampleSkLayer.cpp */; };
+		2612C0FB140D2FC1001B6925 /* SampleStrokePath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006313B11F5B0064D447 /* SampleStrokePath.cpp */; };
+		2612C0FC140D2FC2001B6925 /* SampleStrokeText.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006413B11F5B0064D447 /* SampleStrokeText.cpp */; };
+		2612C0FE140D2FC3001B6925 /* SampleTextureDomain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006B13B11F5B0064D447 /* SampleTextureDomain.cpp */; };
+		2612C0FF140D2FC4001B6925 /* SampleTiling.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006C13B11F5B0064D447 /* SampleTiling.cpp */; };
+		2612C100140D2FC5001B6925 /* SampleTinyBitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E006D13B11F5B0064D447 /* SampleTinyBitmap.cpp */; };
+		2612C101140D2FC6001B6925 /* SampleXfermodes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E007213B11F5B0064D447 /* SampleXfermodes.cpp */; };
+		2612C102140D2FC7001B6925 /* SampleXfermodesBlur.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E007313B11F5B0064D447 /* SampleXfermodesBlur.cpp */; };
+		2612C106140D2FE3001B6925 /* SkEventNotifier.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2612C105140D2FE3001B6925 /* SkEventNotifier.mm */; };
+		2612C110140D300B001B6925 /* GrDefaultPathRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2612C109140D300B001B6925 /* GrDefaultPathRenderer.cpp */; };
+		2612C111140D300B001B6925 /* GrGLStencilBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2612C10B140D300B001B6925 /* GrGLStencilBuffer.cpp */; };
+		2612C112140D300B001B6925 /* GrPathRendererChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2612C10E140D300B001B6925 /* GrPathRendererChain.cpp */; };
+		2612C122140D30B0001B6925 /* GrAddPathRenderers_none.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2612C121140D30B0001B6925 /* GrAddPathRenderers_none.cpp */; };
+		263BE75813CCC7BF00CCE991 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 263BE75713CCC7BF00CCE991 /* QuartzCore.framework */; };
+		26591EB913EB16EB000DA8A8 /* TransitionView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26591EB813EB16EB000DA8A8 /* TransitionView.cpp */; };
+		265C7DE313D75752008329F6 /* SkOptionListController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 265C7DE213D75752008329F6 /* SkOptionListController.mm */; };
+		2662AB7013BD067900CDE7E9 /* SkiOSSampleApp-Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 2662AB6F13BD067900CDE7E9 /* SkiOSSampleApp-Debug.xcconfig */; };
+		2662AB7613BD0C0D00CDE7E9 /* SkiOSSampleApp-Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 2662AB7513BD0C0D00CDE7E9 /* SkiOSSampleApp-Release.xcconfig */; };
+		2662AB7813BD0C1E00CDE7E9 /* SkiOSSampleApp-Base.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 2662AB7713BD0C1E00CDE7E9 /* SkiOSSampleApp-Base.xcconfig */; };
+		2663AC9413D5D8D400C20488 /* SkOptionsTableViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2663AC9313D5D8D400C20488 /* SkOptionsTableViewController.mm */; };
+		26677D6613B4C548009319B8 /* SkData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26677D6513B4C548009319B8 /* SkData.cpp */; };
+		26811E7913DEFAE8001A1609 /* SkBitSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26811E7813DEFAE8001A1609 /* SkBitSet.cpp */; };
+		268C50D613F022820003FF9A /* SkColorPalette.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 268C50D213F022820003FF9A /* SkColorPalette.cpp */; };
+		268C50D713F022820003FF9A /* SkNetPipeController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 268C50D413F022820003FF9A /* SkNetPipeController.cpp */; };
+		268C50E013F0230C0003FF9A /* SkSockets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 268C50DD13F0230C0003FF9A /* SkSockets.cpp */; };
+		26962B2313CDF6A00039B1FB /* SkOSFile_iOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 260EE8BB13AFA7790064D447 /* SkOSFile_iOS.mm */; };
+		26962C8013CE256E0039B1FB /* SkUIDetailViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26962C7913CE256E0039B1FB /* SkUIDetailViewController.mm */; };
+		26962C8113CE256E0039B1FB /* SkUIRootViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26962C7B13CE256E0039B1FB /* SkUIRootViewController.mm */; };
+		26962CA413CE265C0039B1FB /* SkOSWindow_iOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26962CA313CE265C0039B1FB /* SkOSWindow_iOS.mm */; };
+		26962CAB13CE268A0039B1FB /* SampleApp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26962CA913CE268A0039B1FB /* SampleApp.cpp */; };
+		26962D4F13CE2D780039B1FB /* GrGLDefaultInterface_iOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26962D4E13CE2D780039B1FB /* GrGLDefaultInterface_iOS.cpp */; };
+		26A8AFF313E05D7000A3C111 /* GrResourceCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A8AFF113E05D7000A3C111 /* GrResourceCache.cpp */; };
+		26D91223140D576B0036A311 /* SkSampleUIView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26D91222140D576B0036A311 /* SkSampleUIView.mm */; };
+		26D9122A140D57BD0036A311 /* skia_ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26D91229140D57BD0036A311 /* skia_ios.mm */; };
+		26E0E40A13B4E67800866555 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 260EE9D113AFA7850064D447 /* OpenGLES.framework */; };
+		26F548C213B918EC007CC564 /* SkBlitter_4444.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022313B1225D0064D447 /* SkBlitter_4444.cpp */; };
+		26F548C313B918ED007CC564 /* SkBlitter_A1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022413B1225D0064D447 /* SkBlitter_A1.cpp */; };
+		26F548C413B918ED007CC564 /* SkBlitter_A8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022513B1225D0064D447 /* SkBlitter_A8.cpp */; };
+		26F548C513B918EE007CC564 /* SkBlitter_ARGB32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022613B1225D0064D447 /* SkBlitter_ARGB32.cpp */; };
+		26F548C613B918EF007CC564 /* SkBlitter_RGB16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022713B1225D0064D447 /* SkBlitter_RGB16.cpp */; };
+		26F548C713B918EF007CC564 /* SkBlitter_Sprite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022813B1225D0064D447 /* SkBlitter_Sprite.cpp */; };
+		26F548C813B918F1007CC564 /* SkProcSpriteBlitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E026A13B1225D0064D447 /* SkProcSpriteBlitter.cpp */; };
+		26F548C913B918F2007CC564 /* SkSpriteBlitter_ARGB32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E028113B1225D0064D447 /* SkSpriteBlitter_ARGB32.cpp */; };
+		26F548CA13B918F2007CC564 /* SkSpriteBlitter_RGB16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E028213B1225D0064D447 /* SkSpriteBlitter_RGB16.cpp */; };
+		26F548DA13B9192E007CC564 /* SkBlitRow_D32.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E022013B1225D0064D447 /* SkBlitRow_D32.cpp */; };
+		26F548DB13B9192E007CC564 /* SkBlitRow_D16.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E021F13B1225D0064D447 /* SkBlitRow_D16.cpp */; };
+		26F548E913B91980007CC564 /* SkBitmapProcState_opts_arm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F548E013B91980007CC564 /* SkBitmapProcState_opts_arm.cpp */; };
+		26F548EC13B91980007CC564 /* SkBlitRow_opts_none.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F548E413B91980007CC564 /* SkBlitRow_opts_none.cpp */; };
+		26F548ED13B91980007CC564 /* SkUtils_opts_none.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26F548E613B91980007CC564 /* SkUtils_opts_none.cpp */; };
+		26FB129313E704AE001AFF6D /* GrGLTexture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03DF13B122D40064D447 /* GrGLTexture.cpp */; };
+		26FB129413E704B0001AFF6D /* GrContext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260E03D713B122D40064D447 /* GrContext.cpp */; };
+		26FB12B013E70D3B001AFF6D /* GrGLRenderTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26FB12AD13E70D3B001AFF6D /* GrGLRenderTarget.cpp */; };
+		26FB12B413E70D51001AFF6D /* GrRenderTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26FB12B313E70D51001AFF6D /* GrRenderTarget.cpp */; };
+		26FB98D313D0C87000ACBEA0 /* SkUIView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26FB98D213D0C87000ACBEA0 /* SkUIView.mm */; };
+		2860E328111B887F00E27156 /* AppDelegate_iPhone.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2860E326111B887F00E27156 /* AppDelegate_iPhone.mm */; };
+		2860E329111B887F00E27156 /* MainWindow_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2860E327111B887F00E27156 /* MainWindow_iPhone.xib */; };
+		2860E32E111B888700E27156 /* AppDelegate_iPad.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2860E32C111B888700E27156 /* AppDelegate_iPad.mm */; };
+		2860E32F111B888700E27156 /* MainWindow_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2860E32D111B888700E27156 /* MainWindow_iPad.xib */; };
+		288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+		1D6058910D05DD3D006BFB54 /* iOSSampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOSSampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+		2605F43C13F18B1B0044A072 /* DebuggerContentView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DebuggerContentView.cpp; sourceTree = "<group>"; };
+		2605F43D13F18B1B0044A072 /* DebuggerViews.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebuggerViews.h; sourceTree = "<group>"; };
+		2605F43E13F18B1B0044A072 /* DebuggerCommandsView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DebuggerCommandsView.cpp; sourceTree = "<group>"; };
+		2605F43F13F18B1B0044A072 /* SkDebugDumper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDebugDumper.cpp; sourceTree = "<group>"; };
+		2605F44013F18B1B0044A072 /* SkDebugDumper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDebugDumper.h; sourceTree = "<group>"; };
+		2605F44113F18B1B0044A072 /* DebuggerStateView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DebuggerStateView.cpp; sourceTree = "<group>"; };
+		2605F66413F19EB80044A072 /* GrStencilBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrStencilBuffer.cpp; sourceTree = "<group>"; };
+		260E001513B11F5B0064D447 /* bitmapfilters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitmapfilters.cpp; sourceTree = "<group>"; };
+		260E001613B11F5B0064D447 /* blurs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = blurs.cpp; sourceTree = "<group>"; };
+		260E001713B11F5B0064D447 /* complexclip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = complexclip.cpp; sourceTree = "<group>"; };
+		260E001813B11F5B0064D447 /* filltypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filltypes.cpp; sourceTree = "<group>"; };
+		260E001913B11F5B0064D447 /* gm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gm.h; sourceTree = "<group>"; };
+		260E001A13B11F5B0064D447 /* gradients.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gradients.cpp; sourceTree = "<group>"; };
+		260E001B13B11F5B0064D447 /* nocolorbleed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nocolorbleed.cpp; sourceTree = "<group>"; };
+		260E001C13B11F5B0064D447 /* points.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = points.cpp; sourceTree = "<group>"; };
+		260E001D13B11F5B0064D447 /* poly2poly.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = poly2poly.cpp; sourceTree = "<group>"; };
+		260E001E13B11F5B0064D447 /* shadertext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shadertext.cpp; sourceTree = "<group>"; };
+		260E001F13B11F5B0064D447 /* shadows.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shadows.cpp; sourceTree = "<group>"; };
+		260E002013B11F5B0064D447 /* shapes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shapes.cpp; sourceTree = "<group>"; };
+		260E002113B11F5B0064D447 /* tilemodes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tilemodes.cpp; sourceTree = "<group>"; };
+		260E002213B11F5B0064D447 /* xfermodes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xfermodes.cpp; sourceTree = "<group>"; };
+		260E002413B11F5B0064D447 /* ClockFaceView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClockFaceView.cpp; sourceTree = "<group>"; };
+		260E002513B11F5B0064D447 /* OverView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OverView.cpp; sourceTree = "<group>"; };
+		260E002613B11F5B0064D447 /* SampleAARects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleAARects.cpp; sourceTree = "<group>"; };
+		260E002713B11F5B0064D447 /* SampleAll.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleAll.cpp; sourceTree = "<group>"; };
+		260E002813B11F5B0064D447 /* SampleAnimator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleAnimator.cpp; sourceTree = "<group>"; };
+		260E002A13B11F5B0064D447 /* SampleArc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleArc.cpp; sourceTree = "<group>"; };
+		260E002B13B11F5B0064D447 /* SampleAvoid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleAvoid.cpp; sourceTree = "<group>"; };
+		260E002C13B11F5B0064D447 /* SampleBigGradient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleBigGradient.cpp; sourceTree = "<group>"; };
+		260E002D13B11F5B0064D447 /* SampleBitmapRect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleBitmapRect.cpp; sourceTree = "<group>"; };
+		260E002E13B11F5B0064D447 /* SampleBlur.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleBlur.cpp; sourceTree = "<group>"; };
+		260E002F13B11F5B0064D447 /* SampleCamera.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleCamera.cpp; sourceTree = "<group>"; };
+		260E003013B11F5B0064D447 /* SampleCircle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleCircle.cpp; sourceTree = "<group>"; };
+		260E003113B11F5B0064D447 /* SampleCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleCode.h; sourceTree = "<group>"; };
+		260E003213B11F5B0064D447 /* SampleColorFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleColorFilter.cpp; sourceTree = "<group>"; };
+		260E003313B11F5B0064D447 /* SampleComplexClip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleComplexClip.cpp; sourceTree = "<group>"; };
+		260E003413B11F5B0064D447 /* SampleConcavePaths.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleConcavePaths.cpp; sourceTree = "<group>"; };
+		260E003513B11F5B0064D447 /* SampleCull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleCull.cpp; sourceTree = "<group>"; };
+		260E003613B11F5B0064D447 /* SampleDecode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleDecode.cpp; sourceTree = "<group>"; };
+		260E003713B11F5B0064D447 /* SampleDither.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleDither.cpp; sourceTree = "<group>"; };
+		260E003813B11F5B0064D447 /* SampleDitherBitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleDitherBitmap.cpp; sourceTree = "<group>"; };
+		260E003913B11F5B0064D447 /* SampleDrawLooper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleDrawLooper.cpp; sourceTree = "<group>"; };
+		260E003A13B11F5B0064D447 /* SampleEffects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleEffects.cpp; sourceTree = "<group>"; };
+		260E003B13B11F5B0064D447 /* SampleEmboss.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleEmboss.cpp; sourceTree = "<group>"; };
+		260E003C13B11F5B0064D447 /* SampleEncode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleEncode.cpp; sourceTree = "<group>"; };
+		260E003D13B11F5B0064D447 /* SampleExtractAlpha.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleExtractAlpha.cpp; sourceTree = "<group>"; };
+		260E003E13B11F5B0064D447 /* SampleFillType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFillType.cpp; sourceTree = "<group>"; };
+		260E003F13B11F5B0064D447 /* SampleFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFilter.cpp; sourceTree = "<group>"; };
+		260E004013B11F5B0064D447 /* SampleFilter2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFilter2.cpp; sourceTree = "<group>"; };
+		260E004113B11F5B0064D447 /* SampleFontCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFontCache.cpp; sourceTree = "<group>"; };
+		260E004213B11F5B0064D447 /* SampleFontScalerTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFontScalerTest.cpp; sourceTree = "<group>"; };
+		260E004313B11F5B0064D447 /* SampleFuzz.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleFuzz.cpp; sourceTree = "<group>"; };
+		260E004413B11F5B0064D447 /* SampleGM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleGM.cpp; sourceTree = "<group>"; };
+		260E004513B11F5B0064D447 /* SampleGradients.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleGradients.cpp; sourceTree = "<group>"; };
+		260E004613B11F5B0064D447 /* SampleHairline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleHairline.cpp; sourceTree = "<group>"; };
+		260E004713B11F5B0064D447 /* SampleImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleImage.cpp; sourceTree = "<group>"; };
+		260E004813B11F5B0064D447 /* SampleImageDir.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleImageDir.cpp; sourceTree = "<group>"; };
+		260E004913B11F5B0064D447 /* SampleLCD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleLCD.cpp; sourceTree = "<group>"; };
+		260E004A13B11F5B0064D447 /* SampleLayerMask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleLayerMask.cpp; sourceTree = "<group>"; };
+		260E004B13B11F5B0064D447 /* SampleLayers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleLayers.cpp; sourceTree = "<group>"; };
+		260E004C13B11F5B0064D447 /* SampleLineClipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleLineClipper.cpp; sourceTree = "<group>"; };
+		260E004D13B11F5B0064D447 /* SampleLines.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleLines.cpp; sourceTree = "<group>"; };
+		260E004E13B11F5B0064D447 /* SampleMeasure.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleMeasure.cpp; sourceTree = "<group>"; };
+		260E004F13B11F5B0064D447 /* SampleMipMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleMipMap.cpp; sourceTree = "<group>"; };
+		260E005013B11F5B0064D447 /* SampleMovie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleMovie.cpp; sourceTree = "<group>"; };
+		260E005113B11F5B0064D447 /* SampleNinePatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleNinePatch.cpp; sourceTree = "<group>"; };
+		260E005213B11F5B0064D447 /* SampleOvalTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleOvalTest.cpp; sourceTree = "<group>"; };
+		260E005313B11F5B0064D447 /* SampleOverflow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleOverflow.cpp; sourceTree = "<group>"; };
+		260E005413B11F5B0064D447 /* SamplePageFlip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplePageFlip.cpp; sourceTree = "<group>"; };
+		260E005513B11F5B0064D447 /* SamplePatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplePatch.cpp; sourceTree = "<group>"; };
+		260E005613B11F5B0064D447 /* SamplePath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplePath.cpp; sourceTree = "<group>"; };
+		260E005713B11F5B0064D447 /* SamplePathClip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplePathClip.cpp; sourceTree = "<group>"; };
+		260E005813B11F5B0064D447 /* SamplePathEffects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplePathEffects.cpp; sourceTree = "<group>"; };
+		260E005913B11F5B0064D447 /* SamplePicture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplePicture.cpp; sourceTree = "<group>"; };
+		260E005A13B11F5B0064D447 /* SamplePoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplePoints.cpp; sourceTree = "<group>"; };
+		260E005B13B11F5B0064D447 /* SamplePolyToPoly.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SamplePolyToPoly.cpp; sourceTree = "<group>"; };
+		260E005C13B11F5B0064D447 /* SampleRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleRegion.cpp; sourceTree = "<group>"; };
+		260E005D13B11F5B0064D447 /* SampleRepeatTile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleRepeatTile.cpp; sourceTree = "<group>"; };
+		260E005E13B11F5B0064D447 /* SampleShaderText.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleShaderText.cpp; sourceTree = "<group>"; };
+		260E005F13B11F5B0064D447 /* SampleShaders.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleShaders.cpp; sourceTree = "<group>"; };
+		260E006013B11F5B0064D447 /* SampleShapes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleShapes.cpp; sourceTree = "<group>"; };
+		260E006113B11F5B0064D447 /* SampleSkLayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleSkLayer.cpp; sourceTree = "<group>"; };
+		260E006213B11F5B0064D447 /* SampleSlides.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleSlides.cpp; sourceTree = "<group>"; };
+		260E006313B11F5B0064D447 /* SampleStrokePath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleStrokePath.cpp; sourceTree = "<group>"; };
+		260E006413B11F5B0064D447 /* SampleStrokeText.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleStrokeText.cpp; sourceTree = "<group>"; };
+		260E006613B11F5B0064D447 /* SampleText.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleText.cpp; sourceTree = "<group>"; };
+		260E006713B11F5B0064D447 /* SampleTextAlpha.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleTextAlpha.cpp; sourceTree = "<group>"; };
+		260E006813B11F5B0064D447 /* SampleTextBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleTextBox.cpp; sourceTree = "<group>"; };
+		260E006913B11F5B0064D447 /* SampleTextEffects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleTextEffects.cpp; sourceTree = "<group>"; };
+		260E006A13B11F5B0064D447 /* SampleTextOnPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleTextOnPath.cpp; sourceTree = "<group>"; };
+		260E006B13B11F5B0064D447 /* SampleTextureDomain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleTextureDomain.cpp; sourceTree = "<group>"; };
+		260E006C13B11F5B0064D447 /* SampleTiling.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleTiling.cpp; sourceTree = "<group>"; };
+		260E006D13B11F5B0064D447 /* SampleTinyBitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleTinyBitmap.cpp; sourceTree = "<group>"; };
+		260E006E13B11F5B0064D447 /* SampleTriangles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleTriangles.cpp; sourceTree = "<group>"; };
+		260E006F13B11F5B0064D447 /* SampleTypeface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleTypeface.cpp; sourceTree = "<group>"; };
+		260E007013B11F5B0064D447 /* SampleUnitMapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleUnitMapper.cpp; sourceTree = "<group>"; };
+		260E007113B11F5B0064D447 /* SampleVertices.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleVertices.cpp; sourceTree = "<group>"; };
+		260E007213B11F5B0064D447 /* SampleXfermodes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleXfermodes.cpp; sourceTree = "<group>"; };
+		260E007313B11F5B0064D447 /* SampleXfermodesBlur.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SampleXfermodesBlur.cpp; sourceTree = "<group>"; };
+		260E007513B11F5B0064D447 /* SkGPipeRead.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGPipeRead.cpp; sourceTree = "<group>"; };
+		260E007613B11F5B0064D447 /* SkGPipeWrite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGPipeWrite.cpp; sourceTree = "<group>"; };
+		260E01B213B1225D0064D447 /* Sk64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sk64.h; path = core/Sk64.h; sourceTree = "<group>"; };
+		260E01B313B1225D0064D447 /* SkAdvancedTypefaceMetrics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkAdvancedTypefaceMetrics.h; path = core/SkAdvancedTypefaceMetrics.h; sourceTree = "<group>"; };
+		260E01B413B1225D0064D447 /* SkAutoKern.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkAutoKern.h; path = core/SkAutoKern.h; sourceTree = "<group>"; };
+		260E01B513B1225D0064D447 /* SkBitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitmap.h; path = core/SkBitmap.h; sourceTree = "<group>"; };
+		260E01B613B1225D0064D447 /* SkBlitRow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBlitRow.h; path = core/SkBlitRow.h; sourceTree = "<group>"; };
+		260E01B713B1225D0064D447 /* SkBlitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBlitter.h; path = core/SkBlitter.h; sourceTree = "<group>"; };
+		260E01B813B1225D0064D447 /* SkBounder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBounder.h; path = core/SkBounder.h; sourceTree = "<group>"; };
+		260E01B913B1225D0064D447 /* SkBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBuffer.h; path = core/SkBuffer.h; sourceTree = "<group>"; };
+		260E01BA13B1225D0064D447 /* SkCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkCanvas.h; path = core/SkCanvas.h; sourceTree = "<group>"; };
+		260E01BB13B1225D0064D447 /* SkChunkAlloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkChunkAlloc.h; path = core/SkChunkAlloc.h; sourceTree = "<group>"; };
+		260E01BC13B1225D0064D447 /* SkClampRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkClampRange.h; path = core/SkClampRange.h; sourceTree = "<group>"; };
+		260E01BD13B1225D0064D447 /* SkClipStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkClipStack.h; path = core/SkClipStack.h; sourceTree = "<group>"; };
+		260E01BE13B1225D0064D447 /* SkColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkColor.h; path = core/SkColor.h; sourceTree = "<group>"; };
+		260E01BF13B1225D0064D447 /* SkColorFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkColorFilter.h; path = core/SkColorFilter.h; sourceTree = "<group>"; };
+		260E01C013B1225D0064D447 /* SkColorPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkColorPriv.h; path = core/SkColorPriv.h; sourceTree = "<group>"; };
+		260E01C113B1225D0064D447 /* SkColorShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkColorShader.h; path = core/SkColorShader.h; sourceTree = "<group>"; };
+		260E01C213B1225D0064D447 /* SkComposeShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkComposeShader.h; path = core/SkComposeShader.h; sourceTree = "<group>"; };
+		260E01C313B1225D0064D447 /* SkDeque.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDeque.h; path = core/SkDeque.h; sourceTree = "<group>"; };
+		260E01C413B1225D0064D447 /* SkDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDescriptor.h; path = core/SkDescriptor.h; sourceTree = "<group>"; };
+		260E01C513B1225D0064D447 /* SkDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDevice.h; path = core/SkDevice.h; sourceTree = "<group>"; };
+		260E01C613B1225D0064D447 /* SkDither.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDither.h; path = core/SkDither.h; sourceTree = "<group>"; };
+		260E01C713B1225D0064D447 /* SkDraw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDraw.h; path = core/SkDraw.h; sourceTree = "<group>"; };
+		260E01C813B1225D0064D447 /* SkDrawFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDrawFilter.h; path = core/SkDrawFilter.h; sourceTree = "<group>"; };
+		260E01C913B1225D0064D447 /* SkDrawLooper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkDrawLooper.h; path = core/SkDrawLooper.h; sourceTree = "<group>"; };
+		260E01CA13B1225D0064D447 /* SkEndian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkEndian.h; path = core/SkEndian.h; sourceTree = "<group>"; };
+		260E01CB13B1225D0064D447 /* SkFDot6.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFDot6.h; path = core/SkFDot6.h; sourceTree = "<group>"; };
+		260E01CC13B1225D0064D447 /* SkFixed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFixed.h; path = core/SkFixed.h; sourceTree = "<group>"; };
+		260E01CD13B1225D0064D447 /* SkFlate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFlate.h; path = core/SkFlate.h; sourceTree = "<group>"; };
+		260E01CE13B1225D0064D447 /* SkFlattenable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFlattenable.h; path = core/SkFlattenable.h; sourceTree = "<group>"; };
+		260E01CF13B1225D0064D447 /* SkFloatBits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFloatBits.h; path = core/SkFloatBits.h; sourceTree = "<group>"; };
+		260E01D013B1225D0064D447 /* SkFloatingPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFloatingPoint.h; path = core/SkFloatingPoint.h; sourceTree = "<group>"; };
+		260E01D113B1225D0064D447 /* SkFontHost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkFontHost.h; path = core/SkFontHost.h; sourceTree = "<group>"; };
+		260E01D213B1225D0064D447 /* SkGeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkGeometry.h; path = core/SkGeometry.h; sourceTree = "<group>"; };
+		260E01D313B1225D0064D447 /* SkGlobals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkGlobals.h; path = core/SkGlobals.h; sourceTree = "<group>"; };
+		260E01D413B1225D0064D447 /* SkGraphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkGraphics.h; path = core/SkGraphics.h; sourceTree = "<group>"; };
+		260E01D513B1225D0064D447 /* SkMMapStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMMapStream.h; path = core/SkMMapStream.h; sourceTree = "<group>"; };
+		260E01D613B1225D0064D447 /* SkMallocPixelRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMallocPixelRef.h; path = core/SkMallocPixelRef.h; sourceTree = "<group>"; };
+		260E01D713B1225D0064D447 /* SkMask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMask.h; path = core/SkMask.h; sourceTree = "<group>"; };
+		260E01D813B1225D0064D447 /* SkMaskFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMaskFilter.h; path = core/SkMaskFilter.h; sourceTree = "<group>"; };
+		260E01D913B1225D0064D447 /* SkMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMath.h; path = core/SkMath.h; sourceTree = "<group>"; };
+		260E01DA13B1225D0064D447 /* SkMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMatrix.h; path = core/SkMatrix.h; sourceTree = "<group>"; };
+		260E01DB13B1225D0064D447 /* SkMetaData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkMetaData.h; path = core/SkMetaData.h; sourceTree = "<group>"; };
+		260E01DC13B1225D0064D447 /* SkOSFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkOSFile.h; path = core/SkOSFile.h; sourceTree = "<group>"; };
+		260E01DD13B1225D0064D447 /* SkPackBits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPackBits.h; path = core/SkPackBits.h; sourceTree = "<group>"; };
+		260E01DE13B1225D0064D447 /* SkPaint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPaint.h; path = core/SkPaint.h; sourceTree = "<group>"; };
+		260E01DF13B1225D0064D447 /* SkPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPath.h; path = core/SkPath.h; sourceTree = "<group>"; };
+		260E01E013B1225D0064D447 /* SkPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPathEffect.h; path = core/SkPathEffect.h; sourceTree = "<group>"; };
+		260E01E113B1225D0064D447 /* SkPathMeasure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPathMeasure.h; path = core/SkPathMeasure.h; sourceTree = "<group>"; };
+		260E01E213B1225D0064D447 /* SkPerspIter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPerspIter.h; path = core/SkPerspIter.h; sourceTree = "<group>"; };
+		260E01E313B1225D0064D447 /* SkPicture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPicture.h; path = core/SkPicture.h; sourceTree = "<group>"; };
+		260E01E413B1225D0064D447 /* SkPixelRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPixelRef.h; path = core/SkPixelRef.h; sourceTree = "<group>"; };
+		260E01E513B1225D0064D447 /* SkPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPoint.h; path = core/SkPoint.h; sourceTree = "<group>"; };
+		260E01E613B1225D0064D447 /* SkPtrRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPtrRecorder.h; path = core/SkPtrRecorder.h; sourceTree = "<group>"; };
+		260E01E713B1225D0064D447 /* SkRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRandom.h; path = core/SkRandom.h; sourceTree = "<group>"; };
+		260E01E813B1225D0064D447 /* SkRasterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRasterizer.h; path = core/SkRasterizer.h; sourceTree = "<group>"; };
+		260E01E913B1225D0064D447 /* SkReader32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkReader32.h; path = core/SkReader32.h; sourceTree = "<group>"; };
+		260E01EA13B1225D0064D447 /* SkRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRect.h; path = core/SkRect.h; sourceTree = "<group>"; };
+		260E01EB13B1225D0064D447 /* SkRefCnt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRefCnt.h; path = core/SkRefCnt.h; sourceTree = "<group>"; };
+		260E01EC13B1225D0064D447 /* SkRefDict.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRefDict.h; path = core/SkRefDict.h; sourceTree = "<group>"; };
+		260E01ED13B1225D0064D447 /* SkRegion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRegion.h; path = core/SkRegion.h; sourceTree = "<group>"; };
+		260E01EE13B1225D0064D447 /* SkScalar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkScalar.h; path = core/SkScalar.h; sourceTree = "<group>"; };
+		260E01EF13B1225D0064D447 /* SkScalarCompare.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkScalarCompare.h; path = core/SkScalarCompare.h; sourceTree = "<group>"; };
+		260E01F013B1225D0064D447 /* SkScalerContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkScalerContext.h; path = core/SkScalerContext.h; sourceTree = "<group>"; };
+		260E01F113B1225D0064D447 /* SkScan.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkScan.h; path = core/SkScan.h; sourceTree = "<group>"; };
+		260E01F213B1225D0064D447 /* SkShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkShader.h; path = core/SkShader.h; sourceTree = "<group>"; };
+		260E01F313B1225D0064D447 /* SkStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkStream.h; path = core/SkStream.h; sourceTree = "<group>"; };
+		260E01F413B1225D0064D447 /* SkString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkString.h; path = core/SkString.h; sourceTree = "<group>"; };
+		260E01F513B1225D0064D447 /* SkStroke.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkStroke.h; path = core/SkStroke.h; sourceTree = "<group>"; };
+		260E01F613B1225D0064D447 /* SkTDArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTDArray.h; path = core/SkTDArray.h; sourceTree = "<group>"; };
+		260E01F713B1225D0064D447 /* SkTDStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTDStack.h; path = core/SkTDStack.h; sourceTree = "<group>"; };
+		260E01F813B1225D0064D447 /* SkTDict.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTDict.h; path = core/SkTDict.h; sourceTree = "<group>"; };
+		260E01F913B1225D0064D447 /* SkTRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTRegistry.h; path = core/SkTRegistry.h; sourceTree = "<group>"; };
+		260E01FA13B1225D0064D447 /* SkTScopedPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTScopedPtr.h; path = core/SkTScopedPtr.h; sourceTree = "<group>"; };
+		260E01FB13B1225D0064D447 /* SkTSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTSearch.h; path = core/SkTSearch.h; sourceTree = "<group>"; };
+		260E01FC13B1225D0064D447 /* SkTemplates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTemplates.h; path = core/SkTemplates.h; sourceTree = "<group>"; };
+		260E01FD13B1225D0064D447 /* SkThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkThread.h; path = core/SkThread.h; sourceTree = "<group>"; };
+		260E01FE13B1225D0064D447 /* SkThread_platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkThread_platform.h; path = core/SkThread_platform.h; sourceTree = "<group>"; };
+		260E01FF13B1225D0064D447 /* SkTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTime.h; path = core/SkTime.h; sourceTree = "<group>"; };
+		260E020013B1225D0064D447 /* SkTypeface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTypeface.h; path = core/SkTypeface.h; sourceTree = "<group>"; };
+		260E020113B1225D0064D447 /* SkTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTypes.h; path = core/SkTypes.h; sourceTree = "<group>"; };
+		260E020213B1225D0064D447 /* SkUnPreMultiply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUnPreMultiply.h; path = core/SkUnPreMultiply.h; sourceTree = "<group>"; };
+		260E020313B1225D0064D447 /* SkUnitMapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUnitMapper.h; path = core/SkUnitMapper.h; sourceTree = "<group>"; };
+		260E020413B1225D0064D447 /* SkUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUtils.h; path = core/SkUtils.h; sourceTree = "<group>"; };
+		260E020513B1225D0064D447 /* SkWriter32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkWriter32.h; path = core/SkWriter32.h; sourceTree = "<group>"; };
+		260E020613B1225D0064D447 /* SkXfermode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkXfermode.h; path = core/SkXfermode.h; sourceTree = "<group>"; };
+		260E020813B1225D0064D447 /* SkCGUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCGUtils.h; sourceTree = "<group>"; };
+		260E020B13B1225D0064D447 /* ARGB32_Clamp_Bilinear_BitmapShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARGB32_Clamp_Bilinear_BitmapShader.h; sourceTree = "<group>"; };
+		260E020C13B1225D0064D447 /* Sk64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sk64.cpp; sourceTree = "<group>"; };
+		260E020D13B1225D0064D447 /* SkAdvancedTypefaceMetrics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAdvancedTypefaceMetrics.cpp; sourceTree = "<group>"; };
+		260E020E13B1225D0064D447 /* SkAlphaRuns.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAlphaRuns.cpp; sourceTree = "<group>"; };
+		260E020F13B1225D0064D447 /* SkAntiRun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAntiRun.h; sourceTree = "<group>"; };
+		260E021013B1225D0064D447 /* SkBitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmap.cpp; sourceTree = "<group>"; };
+		260E021113B1225D0064D447 /* SkBitmapProcShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapProcShader.cpp; sourceTree = "<group>"; };
+		260E021213B1225D0064D447 /* SkBitmapProcShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapProcShader.h; sourceTree = "<group>"; };
+		260E021313B1225D0064D447 /* SkBitmapProcState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapProcState.cpp; sourceTree = "<group>"; };
+		260E021413B1225D0064D447 /* SkBitmapProcState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapProcState.h; sourceTree = "<group>"; };
+		260E021513B1225D0064D447 /* SkBitmapProcState_matrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapProcState_matrix.h; sourceTree = "<group>"; };
+		260E021613B1225D0064D447 /* SkBitmapProcState_matrixProcs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapProcState_matrixProcs.cpp; sourceTree = "<group>"; };
+		260E021713B1225D0064D447 /* SkBitmapProcState_sample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapProcState_sample.h; sourceTree = "<group>"; };
+		260E021813B1225D0064D447 /* SkBitmapSampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapSampler.cpp; sourceTree = "<group>"; };
+		260E021913B1225D0064D447 /* SkBitmapSampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapSampler.h; sourceTree = "<group>"; };
+		260E021A13B1225D0064D447 /* SkBitmapSamplerTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapSamplerTemplate.h; sourceTree = "<group>"; };
+		260E021B13B1225D0064D447 /* SkBitmapShader16BilerpTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapShader16BilerpTemplate.h; sourceTree = "<group>"; };
+		260E021C13B1225D0064D447 /* SkBitmapShaderTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapShaderTemplate.h; sourceTree = "<group>"; };
+		260E021D13B1225D0064D447 /* SkBitmap_scroll.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmap_scroll.cpp; sourceTree = "<group>"; };
+		260E021E13B1225D0064D447 /* SkBlitBWMaskTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBlitBWMaskTemplate.h; sourceTree = "<group>"; };
+		260E021F13B1225D0064D447 /* SkBlitRow_D16.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitRow_D16.cpp; sourceTree = "<group>"; };
+		260E022013B1225D0064D447 /* SkBlitRow_D32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitRow_D32.cpp; sourceTree = "<group>"; };
+		260E022113B1225D0064D447 /* SkBlitRow_D4444.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitRow_D4444.cpp; sourceTree = "<group>"; };
+		260E022213B1225D0064D447 /* SkBlitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter.cpp; sourceTree = "<group>"; };
+		260E022313B1225D0064D447 /* SkBlitter_4444.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_4444.cpp; sourceTree = "<group>"; };
+		260E022413B1225D0064D447 /* SkBlitter_A1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_A1.cpp; sourceTree = "<group>"; };
+		260E022513B1225D0064D447 /* SkBlitter_A8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_A8.cpp; sourceTree = "<group>"; };
+		260E022613B1225D0064D447 /* SkBlitter_ARGB32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_ARGB32.cpp; sourceTree = "<group>"; };
+		260E022713B1225D0064D447 /* SkBlitter_RGB16.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_RGB16.cpp; sourceTree = "<group>"; };
+		260E022813B1225D0064D447 /* SkBlitter_Sprite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitter_Sprite.cpp; sourceTree = "<group>"; };
+		260E022913B1225D0064D447 /* SkBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBuffer.cpp; sourceTree = "<group>"; };
+		260E022A13B1225D0064D447 /* SkCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCanvas.cpp; sourceTree = "<group>"; };
+		260E022B13B1225D0064D447 /* SkChunkAlloc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkChunkAlloc.cpp; sourceTree = "<group>"; };
+		260E022C13B1225D0064D447 /* SkClampRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkClampRange.cpp; sourceTree = "<group>"; };
+		260E022D13B1225D0064D447 /* SkClipStack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkClipStack.cpp; sourceTree = "<group>"; };
+		260E022E13B1225D0064D447 /* SkColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColor.cpp; sourceTree = "<group>"; };
+		260E022F13B1225D0064D447 /* SkColorFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColorFilter.cpp; sourceTree = "<group>"; };
+		260E023013B1225D0064D447 /* SkColorTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColorTable.cpp; sourceTree = "<group>"; };
+		260E023113B1225D0064D447 /* SkComposeShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkComposeShader.cpp; sourceTree = "<group>"; };
+		260E023213B1225D0064D447 /* SkConcaveToTriangles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkConcaveToTriangles.cpp; sourceTree = "<group>"; };
+		260E023313B1225D0064D447 /* SkConcaveToTriangles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkConcaveToTriangles.h; sourceTree = "<group>"; };
+		260E023413B1225D0064D447 /* SkCordic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCordic.cpp; sourceTree = "<group>"; };
+		260E023513B1225D0064D447 /* SkCordic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCordic.h; sourceTree = "<group>"; };
+		260E023613B1225D0064D447 /* SkCoreBlitters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCoreBlitters.h; sourceTree = "<group>"; };
+		260E023713B1225D0064D447 /* SkCubicClipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCubicClipper.cpp; sourceTree = "<group>"; };
+		260E023813B1225D0064D447 /* SkCubicClipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCubicClipper.h; sourceTree = "<group>"; };
+		260E023A13B1225D0064D447 /* SkDebug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDebug.cpp; sourceTree = "<group>"; };
+		260E023B13B1225D0064D447 /* SkDeque.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDeque.cpp; sourceTree = "<group>"; };
+		260E023C13B1225D0064D447 /* SkDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDevice.cpp; sourceTree = "<group>"; };
+		260E023D13B1225D0064D447 /* SkDither.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDither.cpp; sourceTree = "<group>"; };
+		260E023E13B1225D0064D447 /* SkDraw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDraw.cpp; sourceTree = "<group>"; };
+		260E023F13B1225D0064D447 /* SkDrawProcs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawProcs.h; sourceTree = "<group>"; };
+		260E024013B1225D0064D447 /* SkEdge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEdge.cpp; sourceTree = "<group>"; };
+		260E024113B1225D0064D447 /* SkEdge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEdge.h; sourceTree = "<group>"; };
+		260E024213B1225D0064D447 /* SkEdgeBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEdgeBuilder.cpp; sourceTree = "<group>"; };
+		260E024313B1225D0064D447 /* SkEdgeClipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEdgeClipper.cpp; sourceTree = "<group>"; };
+		260E024413B1225D0064D447 /* SkFP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkFP.h; sourceTree = "<group>"; };
+		260E024513B1225D0064D447 /* SkFilterProc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFilterProc.cpp; sourceTree = "<group>"; };
+		260E024613B1225D0064D447 /* SkFilterProc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkFilterProc.h; sourceTree = "<group>"; };
+		260E024713B1225D0064D447 /* SkFlate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFlate.cpp; sourceTree = "<group>"; };
+		260E024813B1225D0064D447 /* SkFlattenable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFlattenable.cpp; sourceTree = "<group>"; };
+		260E024913B1225D0064D447 /* SkFloat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFloat.cpp; sourceTree = "<group>"; };
+		260E024A13B1225D0064D447 /* SkFloat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkFloat.h; sourceTree = "<group>"; };
+		260E024B13B1225D0064D447 /* SkFloatBits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFloatBits.cpp; sourceTree = "<group>"; };
+		260E024C13B1225D0064D447 /* SkFontHost.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFontHost.cpp; sourceTree = "<group>"; };
+		260E024D13B1225D0064D447 /* SkGeometry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGeometry.cpp; sourceTree = "<group>"; };
+		260E024E13B1225D0064D447 /* SkGlobals.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGlobals.cpp; sourceTree = "<group>"; };
+		260E024F13B1225D0064D447 /* SkGlyphCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGlyphCache.cpp; sourceTree = "<group>"; };
+		260E025013B1225D0064D447 /* SkGlyphCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGlyphCache.h; sourceTree = "<group>"; };
+		260E025113B1225D0064D447 /* SkGraphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGraphics.cpp; sourceTree = "<group>"; };
+		260E025213B1225D0064D447 /* SkLineClipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkLineClipper.cpp; sourceTree = "<group>"; };
+		260E025313B1225D0064D447 /* SkMMapStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMMapStream.cpp; sourceTree = "<group>"; };
+		260E025413B1225D0064D447 /* SkMallocPixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMallocPixelRef.cpp; sourceTree = "<group>"; };
+		260E025513B1225D0064D447 /* SkMask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMask.cpp; sourceTree = "<group>"; };
+		260E025613B1225D0064D447 /* SkMaskFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMaskFilter.cpp; sourceTree = "<group>"; };
+		260E025713B1225D0064D447 /* SkMath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMath.cpp; sourceTree = "<group>"; };
+		260E025813B1225D0064D447 /* SkMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMatrix.cpp; sourceTree = "<group>"; };
+		260E025913B1225D0064D447 /* SkMetaData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMetaData.cpp; sourceTree = "<group>"; };
+		260E025A13B1225D0064D447 /* SkPackBits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPackBits.cpp; sourceTree = "<group>"; };
+		260E025B13B1225D0064D447 /* SkPaint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPaint.cpp; sourceTree = "<group>"; };
+		260E025C13B1225D0064D447 /* SkPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPath.cpp; sourceTree = "<group>"; };
+		260E025D13B1225D0064D447 /* SkPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPathEffect.cpp; sourceTree = "<group>"; };
+		260E025E13B1225D0064D447 /* SkPathHeap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPathHeap.cpp; sourceTree = "<group>"; };
+		260E025F13B1225D0064D447 /* SkPathHeap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPathHeap.h; sourceTree = "<group>"; };
+		260E026013B1225D0064D447 /* SkPathMeasure.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPathMeasure.cpp; sourceTree = "<group>"; };
+		260E026113B1225D0064D447 /* SkPicture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPicture.cpp; sourceTree = "<group>"; };
+		260E026213B1225D0064D447 /* SkPictureFlat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPictureFlat.cpp; sourceTree = "<group>"; };
+		260E026313B1225D0064D447 /* SkPictureFlat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPictureFlat.h; sourceTree = "<group>"; };
+		260E026413B1225D0064D447 /* SkPicturePlayback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPicturePlayback.cpp; sourceTree = "<group>"; };
+		260E026513B1225D0064D447 /* SkPicturePlayback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPicturePlayback.h; sourceTree = "<group>"; };
+		260E026613B1225D0064D447 /* SkPictureRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPictureRecord.cpp; sourceTree = "<group>"; };
+		260E026713B1225D0064D447 /* SkPictureRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPictureRecord.h; sourceTree = "<group>"; };
+		260E026813B1225D0064D447 /* SkPixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPixelRef.cpp; sourceTree = "<group>"; };
+		260E026913B1225D0064D447 /* SkPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPoint.cpp; sourceTree = "<group>"; };
+		260E026A13B1225D0064D447 /* SkProcSpriteBlitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkProcSpriteBlitter.cpp; sourceTree = "<group>"; };
+		260E026B13B1225D0064D447 /* SkPtrRecorder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPtrRecorder.cpp; sourceTree = "<group>"; };
+		260E026C13B1225D0064D447 /* SkQuadClipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkQuadClipper.cpp; sourceTree = "<group>"; };
+		260E026D13B1225D0064D447 /* SkQuadClipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkQuadClipper.h; sourceTree = "<group>"; };
+		260E026E13B1225D0064D447 /* SkRasterizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRasterizer.cpp; sourceTree = "<group>"; };
+		260E026F13B1225D0064D447 /* SkRect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRect.cpp; sourceTree = "<group>"; };
+		260E027013B1225D0064D447 /* SkRefDict.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRefDict.cpp; sourceTree = "<group>"; };
+		260E027113B1225D0064D447 /* SkRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRegion.cpp; sourceTree = "<group>"; };
+		260E027213B1225D0064D447 /* SkRegionPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkRegionPriv.h; sourceTree = "<group>"; };
+		260E027313B1225D0064D447 /* SkRegion_path.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRegion_path.cpp; sourceTree = "<group>"; };
+		260E027413B1225D0064D447 /* SkScalar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScalar.cpp; sourceTree = "<group>"; };
+		260E027513B1225D0064D447 /* SkScalerContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScalerContext.cpp; sourceTree = "<group>"; };
+		260E027613B1225D0064D447 /* SkScan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScan.cpp; sourceTree = "<group>"; };
+		260E027713B1225D0064D447 /* SkScanPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScanPriv.h; sourceTree = "<group>"; };
+		260E027813B1225D0064D447 /* SkScan_AntiPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScan_AntiPath.cpp; sourceTree = "<group>"; };
+		260E027913B1225D0064D447 /* SkScan_Antihair.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScan_Antihair.cpp; sourceTree = "<group>"; };
+		260E027A13B1225D0064D447 /* SkScan_Hairline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScan_Hairline.cpp; sourceTree = "<group>"; };
+		260E027B13B1225D0064D447 /* SkScan_Path.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScan_Path.cpp; sourceTree = "<group>"; };
+		260E027C13B1225D0064D447 /* SkShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkShader.cpp; sourceTree = "<group>"; };
+		260E027D13B1225D0064D447 /* SkShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkShape.cpp; sourceTree = "<group>"; };
+		260E027E13B1225D0064D447 /* SkSinTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSinTable.h; sourceTree = "<group>"; };
+		260E027F13B1225D0064D447 /* SkSpriteBlitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSpriteBlitter.h; sourceTree = "<group>"; };
+		260E028013B1225D0064D447 /* SkSpriteBlitterTemplate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSpriteBlitterTemplate.h; sourceTree = "<group>"; };
+		260E028113B1225D0064D447 /* SkSpriteBlitter_ARGB32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSpriteBlitter_ARGB32.cpp; sourceTree = "<group>"; };
+		260E028213B1225D0064D447 /* SkSpriteBlitter_RGB16.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSpriteBlitter_RGB16.cpp; sourceTree = "<group>"; };
+		260E028313B1225D0064D447 /* SkStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkStream.cpp; sourceTree = "<group>"; };
+		260E028413B1225D0064D447 /* SkString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkString.cpp; sourceTree = "<group>"; };
+		260E028513B1225D0064D447 /* SkStroke.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkStroke.cpp; sourceTree = "<group>"; };
+		260E028613B1225D0064D447 /* SkStrokerPriv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkStrokerPriv.cpp; sourceTree = "<group>"; };
+		260E028713B1225D0064D447 /* SkStrokerPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkStrokerPriv.h; sourceTree = "<group>"; };
+		260E028813B1225D0064D447 /* SkTSearch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTSearch.cpp; sourceTree = "<group>"; };
+		260E028913B1225D0064D447 /* SkTSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTSort.h; sourceTree = "<group>"; };
+		260E028A13B1225D0064D447 /* SkTemplatesPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTemplatesPriv.h; sourceTree = "<group>"; };
+		260E028B13B1225D0064D447 /* SkTextFormatParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTextFormatParams.h; sourceTree = "<group>"; };
+		260E028C13B1225D0064D447 /* SkTypeface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTypeface.cpp; sourceTree = "<group>"; };
+		260E028D13B1225D0064D447 /* SkTypefaceCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTypefaceCache.cpp; sourceTree = "<group>"; };
+		260E028E13B1225D0064D447 /* SkTypefaceCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTypefaceCache.h; sourceTree = "<group>"; };
+		260E028F13B1225D0064D447 /* SkUnPreMultiply.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkUnPreMultiply.cpp; sourceTree = "<group>"; };
+		260E029013B1225D0064D447 /* SkUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkUtils.cpp; sourceTree = "<group>"; };
+		260E029113B1225D0064D447 /* SkWriter32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkWriter32.cpp; sourceTree = "<group>"; };
+		260E029213B1225D0064D447 /* SkXfermode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkXfermode.cpp; sourceTree = "<group>"; };
+		260E029613B1225D0064D447 /* SkDebug_stdio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDebug_stdio.cpp; sourceTree = "<group>"; };
+		260E029813B1225D0064D447 /* SkFontHost_mac_coretext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFontHost_mac_coretext.cpp; sourceTree = "<group>"; };
+		260E029A13B1225D0064D447 /* SkGlobals_global.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGlobals_global.cpp; sourceTree = "<group>"; };
+		260E029B13B1225D0064D447 /* SkMemory_malloc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMemory_malloc.cpp; sourceTree = "<group>"; };
+		260E029D13B1225D0064D447 /* SkThread_pthread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkThread_pthread.cpp; sourceTree = "<group>"; };
+		260E029E13B1225D0064D447 /* SkTime_Unix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTime_Unix.cpp; sourceTree = "<group>"; };
+		260E02A013B1225D0064D447 /* SkXMLParser_empty.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkXMLParser_empty.cpp; sourceTree = "<group>"; };
+		260E02A113B1225D0064D447 /* sk_predefined_gamma.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sk_predefined_gamma.h; sourceTree = "<group>"; };
+		260E032013B122A30064D447 /* Sk1DPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sk1DPathEffect.h; sourceTree = "<group>"; };
+		260E032113B122A30064D447 /* Sk2DPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Sk2DPathEffect.h; sourceTree = "<group>"; };
+		260E032213B122A30064D447 /* SkAvoidXfermode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAvoidXfermode.h; sourceTree = "<group>"; };
+		260E032313B122A30064D447 /* SkBlurDrawLooper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBlurDrawLooper.h; sourceTree = "<group>"; };
+		260E032413B122A30064D447 /* SkBlurMaskFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBlurMaskFilter.h; sourceTree = "<group>"; };
+		260E032513B122A30064D447 /* SkColorMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkColorMatrix.h; sourceTree = "<group>"; };
+		260E032613B122A30064D447 /* SkColorMatrixFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkColorMatrixFilter.h; sourceTree = "<group>"; };
+		260E032713B122A30064D447 /* SkCornerPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCornerPathEffect.h; sourceTree = "<group>"; };
+		260E032813B122A30064D447 /* SkDashPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDashPathEffect.h; sourceTree = "<group>"; };
+		260E032913B122A30064D447 /* SkDiscretePathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDiscretePathEffect.h; sourceTree = "<group>"; };
+		260E032A13B122A30064D447 /* SkDrawExtraPathEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawExtraPathEffect.h; sourceTree = "<group>"; };
+		260E032B13B122A30064D447 /* SkEmbossMaskFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEmbossMaskFilter.h; sourceTree = "<group>"; };
+		260E032C13B122A30064D447 /* SkGradientShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGradientShader.h; sourceTree = "<group>"; };
+		260E032D13B122A30064D447 /* SkGroupShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGroupShape.h; sourceTree = "<group>"; };
+		260E032E13B122A30064D447 /* SkKernel33MaskFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkKernel33MaskFilter.h; sourceTree = "<group>"; };
+		260E032F13B122A30064D447 /* SkLayerDrawLooper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkLayerDrawLooper.h; sourceTree = "<group>"; };
+		260E033013B122A30064D447 /* SkLayerRasterizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkLayerRasterizer.h; sourceTree = "<group>"; };
+		260E033113B122A30064D447 /* SkPaintFlagsDrawFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPaintFlagsDrawFilter.h; sourceTree = "<group>"; };
+		260E033213B122A30064D447 /* SkPixelXorXfermode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPixelXorXfermode.h; sourceTree = "<group>"; };
+		260E033313B122A30064D447 /* SkPorterDuff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPorterDuff.h; sourceTree = "<group>"; };
+		260E033413B122A30064D447 /* SkRectShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkRectShape.h; sourceTree = "<group>"; };
+		260E033513B122A30064D447 /* SkTransparentShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTransparentShader.h; sourceTree = "<group>"; };
+		260E033713B122A30064D447 /* Sk1DPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sk1DPathEffect.cpp; sourceTree = "<group>"; };
+		260E033813B122A30064D447 /* Sk2DPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Sk2DPathEffect.cpp; sourceTree = "<group>"; };
+		260E033913B122A30064D447 /* SkAvoidXfermode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAvoidXfermode.cpp; sourceTree = "<group>"; };
+		260E033A13B122A30064D447 /* SkBitmapCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapCache.cpp; sourceTree = "<group>"; };
+		260E033B13B122A30064D447 /* SkBitmapCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmapCache.h; sourceTree = "<group>"; };
+		260E033C13B122A30064D447 /* SkBlurDrawLooper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlurDrawLooper.cpp; sourceTree = "<group>"; };
+		260E033D13B122A30064D447 /* SkBlurMask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlurMask.cpp; sourceTree = "<group>"; };
+		260E033E13B122A30064D447 /* SkBlurMask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBlurMask.h; sourceTree = "<group>"; };
+		260E033F13B122A30064D447 /* SkBlurMaskFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlurMaskFilter.cpp; sourceTree = "<group>"; };
+		260E034013B122A30064D447 /* SkColorFilters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColorFilters.cpp; sourceTree = "<group>"; };
+		260E034113B122A30064D447 /* SkColorMatrixFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColorMatrixFilter.cpp; sourceTree = "<group>"; };
+		260E034213B122A30064D447 /* SkCornerPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCornerPathEffect.cpp; sourceTree = "<group>"; };
+		260E034313B122A30064D447 /* SkDashPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDashPathEffect.cpp; sourceTree = "<group>"; };
+		260E034413B122A30064D447 /* SkDiscretePathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDiscretePathEffect.cpp; sourceTree = "<group>"; };
+		260E034513B122A30064D447 /* SkEmbossMask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEmbossMask.cpp; sourceTree = "<group>"; };
+		260E034613B122A30064D447 /* SkEmbossMask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEmbossMask.h; sourceTree = "<group>"; };
+		260E034713B122A30064D447 /* SkEmbossMaskFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEmbossMaskFilter.cpp; sourceTree = "<group>"; };
+		260E034813B122A30064D447 /* SkEmbossMask_Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEmbossMask_Table.h; sourceTree = "<group>"; };
+		260E034913B122A30064D447 /* SkGradientShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGradientShader.cpp; sourceTree = "<group>"; };
+		260E034A13B122A30064D447 /* SkGroupShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGroupShape.cpp; sourceTree = "<group>"; };
+		260E034B13B122A30064D447 /* SkKernel33MaskFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkKernel33MaskFilter.cpp; sourceTree = "<group>"; };
+		260E034C13B122A30064D447 /* SkLayerDrawLooper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkLayerDrawLooper.cpp; sourceTree = "<group>"; };
+		260E034D13B122A30064D447 /* SkLayerRasterizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkLayerRasterizer.cpp; sourceTree = "<group>"; };
+		260E034E13B122A30064D447 /* SkPaintFlagsDrawFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPaintFlagsDrawFilter.cpp; sourceTree = "<group>"; };
+		260E034F13B122A30064D447 /* SkPixelXorXfermode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPixelXorXfermode.cpp; sourceTree = "<group>"; };
+		260E035013B122A30064D447 /* SkPorterDuff.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPorterDuff.cpp; sourceTree = "<group>"; };
+		260E035113B122A30064D447 /* SkRadialGradient_Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkRadialGradient_Table.h; sourceTree = "<group>"; };
+		260E035213B122A30064D447 /* SkRectShape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkRectShape.cpp; sourceTree = "<group>"; };
+		260E035313B122A30064D447 /* SkTransparentShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTransparentShader.cpp; sourceTree = "<group>"; };
+		260E038C13B122D40064D447 /* GrAllocPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrAllocPool.h; sourceTree = "<group>"; };
+		260E038D13B122D40064D447 /* GrAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrAllocator.h; sourceTree = "<group>"; };
+		260E038E13B122D40064D447 /* GrAtlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrAtlas.h; sourceTree = "<group>"; };
+		260E038F13B122D40064D447 /* GrClip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrClip.h; sourceTree = "<group>"; };
+		260E039013B122D40064D447 /* GrClipIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrClipIterator.h; sourceTree = "<group>"; };
+		260E039113B122D40064D447 /* GrColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrColor.h; sourceTree = "<group>"; };
+		260E039213B122D40064D447 /* GrConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrConfig.h; sourceTree = "<group>"; };
+		260E039313B122D40064D447 /* GrContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrContext.h; sourceTree = "<group>"; };
+		260E039413B122D40064D447 /* GrContext_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrContext_impl.h; sourceTree = "<group>"; };
+		260E039513B122D40064D447 /* GrDrawTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrDrawTarget.h; sourceTree = "<group>"; };
+		260E039613B122D40064D447 /* GrFontScaler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrFontScaler.h; sourceTree = "<group>"; };
+		260E039713B122D40064D447 /* GrGLConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLConfig.h; sourceTree = "<group>"; };
+		260E039813B122D40064D447 /* GrGLConfig_chrome.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLConfig_chrome.h; sourceTree = "<group>"; };
+		260E039B13B122D40064D447 /* GrGLInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLInterface.h; sourceTree = "<group>"; };
+		260E039E13B122D40064D447 /* GrGeometryBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGeometryBuffer.h; sourceTree = "<group>"; };
+		260E039F13B122D40064D447 /* GrGlyph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGlyph.h; sourceTree = "<group>"; };
+		260E03A013B122D40064D447 /* GrGpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGpu.h; sourceTree = "<group>"; };
+		260E03A113B122D40064D447 /* GrGpuVertex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGpuVertex.h; sourceTree = "<group>"; };
+		260E03A213B122D40064D447 /* GrIPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrIPoint.h; sourceTree = "<group>"; };
+		260E03A313B122D40064D447 /* GrInOrderDrawBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrInOrderDrawBuffer.h; sourceTree = "<group>"; };
+		260E03A413B122D40064D447 /* GrIndexBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrIndexBuffer.h; sourceTree = "<group>"; };
+		260E03A513B122D40064D447 /* GrInstanceCounter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrInstanceCounter.h; sourceTree = "<group>"; };
+		260E03A613B122D40064D447 /* GrKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrKey.h; sourceTree = "<group>"; };
+		260E03A713B122D40064D447 /* GrMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrMatrix.h; sourceTree = "<group>"; };
+		260E03A913B122D40064D447 /* GrMesh.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrMesh.h; sourceTree = "<group>"; };
+		260E03AA13B122D40064D447 /* GrNoncopyable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrNoncopyable.h; sourceTree = "<group>"; };
+		260E03AB13B122D40064D447 /* GrPaint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPaint.h; sourceTree = "<group>"; };
+		260E03AC13B122D40064D447 /* GrPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPath.h; sourceTree = "<group>"; };
+		260E03AD13B122D40064D447 /* GrPathRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPathRenderer.h; sourceTree = "<group>"; };
+		260E03AE13B122D40064D447 /* GrPathSink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPathSink.h; sourceTree = "<group>"; };
+		260E03AF13B122D40064D447 /* GrPlotMgr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPlotMgr.h; sourceTree = "<group>"; };
+		260E03B013B122D40064D447 /* GrPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPoint.h; sourceTree = "<group>"; };
+		260E03B113B122D40064D447 /* GrRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrRandom.h; sourceTree = "<group>"; };
+		260E03B213B122D40064D447 /* GrRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrRect.h; sourceTree = "<group>"; };
+		260E03B313B122D40064D447 /* GrRectanizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrRectanizer.h; sourceTree = "<group>"; };
+		260E03B413B122D40064D447 /* GrRefCnt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrRefCnt.h; sourceTree = "<group>"; };
+		260E03B513B122D40064D447 /* GrResource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrResource.h; sourceTree = "<group>"; };
+		260E03B613B122D40064D447 /* GrSamplerState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrSamplerState.h; sourceTree = "<group>"; };
+		260E03B713B122D40064D447 /* GrScalar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrScalar.h; sourceTree = "<group>"; };
+		260E03B813B122D40064D447 /* GrStencil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrStencil.h; sourceTree = "<group>"; };
+		260E03B913B122D40064D447 /* GrStopwatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrStopwatch.h; sourceTree = "<group>"; };
+		260E03BA13B122D40064D447 /* GrStringBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrStringBuilder.h; sourceTree = "<group>"; };
+		260E03BB13B122D40064D447 /* GrTArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTArray.h; sourceTree = "<group>"; };
+		260E03BC13B122D40064D447 /* GrTBSearch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTBSearch.h; sourceTree = "<group>"; };
+		260E03BD13B122D40064D447 /* GrTDArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTDArray.h; sourceTree = "<group>"; };
+		260E03BE13B122D40064D447 /* GrTHashCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTHashCache.h; sourceTree = "<group>"; };
+		260E03BF13B122D40064D447 /* GrTLList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTLList.h; sourceTree = "<group>"; };
+		260E03C013B122D40064D447 /* GrTesselatedPathRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTesselatedPathRenderer.h; sourceTree = "<group>"; };
+		260E03C113B122D40064D447 /* GrTextContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTextContext.h; sourceTree = "<group>"; };
+		260E03C213B122D40064D447 /* GrTextStrike.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTextStrike.h; sourceTree = "<group>"; };
+		260E03C313B122D40064D447 /* GrTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTexture.h; sourceTree = "<group>"; };
+		260E03C513B122D40064D447 /* GrTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTypes.h; sourceTree = "<group>"; };
+		260E03C613B122D40064D447 /* GrUserConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrUserConfig.h; sourceTree = "<group>"; };
+		260E03C713B122D40064D447 /* GrVertexBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrVertexBuffer.h; sourceTree = "<group>"; };
+		260E03D113B122D40064D447 /* GrAllocPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrAllocPool.cpp; sourceTree = "<group>"; };
+		260E03D213B122D40064D447 /* GrAtlas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrAtlas.cpp; sourceTree = "<group>"; };
+		260E03D313B122D40064D447 /* GrBinHashKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrBinHashKey.h; sourceTree = "<group>"; };
+		260E03D413B122D40064D447 /* GrBufferAllocPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrBufferAllocPool.cpp; sourceTree = "<group>"; };
+		260E03D513B122D40064D447 /* GrBufferAllocPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrBufferAllocPool.h; sourceTree = "<group>"; };
+		260E03D613B122D40064D447 /* GrClip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrClip.cpp; sourceTree = "<group>"; };
+		260E03D713B122D40064D447 /* GrContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrContext.cpp; sourceTree = "<group>"; };
+		260E03D913B122D40064D447 /* GrDrawTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrDrawTarget.cpp; sourceTree = "<group>"; };
+		260E03DA13B122D40064D447 /* GrGLDefaultInterface_none.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLDefaultInterface_none.cpp; sourceTree = "<group>"; };
+		260E03DB13B122D40064D447 /* GrGLIndexBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLIndexBuffer.cpp; sourceTree = "<group>"; };
+		260E03DC13B122D40064D447 /* GrGLInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLInterface.cpp; sourceTree = "<group>"; };
+		260E03DD13B122D40064D447 /* GrGLProgram.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLProgram.cpp; sourceTree = "<group>"; };
+		260E03DE13B122D40064D447 /* GrGLProgram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLProgram.h; sourceTree = "<group>"; };
+		260E03DF13B122D40064D447 /* GrGLTexture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLTexture.cpp; sourceTree = "<group>"; };
+		260E03E013B122D40064D447 /* GrGLUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLUtil.cpp; sourceTree = "<group>"; };
+		260E03E113B122D40064D447 /* GrGLVertexBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLVertexBuffer.cpp; sourceTree = "<group>"; };
+		260E03E213B122D40064D447 /* GrGpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGpu.cpp; sourceTree = "<group>"; };
+		260E03E313B122D40064D447 /* GrGpuFactory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGpuFactory.cpp; sourceTree = "<group>"; };
+		260E03E413B122D40064D447 /* GrGpuGL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGpuGL.cpp; sourceTree = "<group>"; };
+		260E03E513B122D40064D447 /* GrGpuGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGpuGL.h; sourceTree = "<group>"; };
+		260E03E613B122D40064D447 /* GrGpuGLFixed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGpuGLFixed.cpp; sourceTree = "<group>"; };
+		260E03E713B122D40064D447 /* GrGpuGLFixed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGpuGLFixed.h; sourceTree = "<group>"; };
+		260E03E813B122D40064D447 /* GrGpuGLShaders.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGpuGLShaders.cpp; sourceTree = "<group>"; };
+		260E03E913B122D40064D447 /* GrGpuGLShaders.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGpuGLShaders.h; sourceTree = "<group>"; };
+		260E03EA13B122D40064D447 /* GrInOrderDrawBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrInOrderDrawBuffer.cpp; sourceTree = "<group>"; };
+		260E03EB13B122D40064D447 /* GrMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrMatrix.cpp; sourceTree = "<group>"; };
+		260E03EC13B122D40064D447 /* GrMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrMemory.cpp; sourceTree = "<group>"; };
+		260E03ED13B122D40064D447 /* GrPathRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrPathRenderer.cpp; sourceTree = "<group>"; };
+		260E03EE13B122D40064D447 /* GrPathUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrPathUtils.cpp; sourceTree = "<group>"; };
+		260E03EF13B122D40064D447 /* GrPathUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPathUtils.h; sourceTree = "<group>"; };
+		260E03F013B122D40064D447 /* GrRectanizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrRectanizer.cpp; sourceTree = "<group>"; };
+		260E03F113B122D40064D447 /* GrRedBlackTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrRedBlackTree.h; sourceTree = "<group>"; };
+		260E03F213B122D40064D447 /* GrResource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrResource.cpp; sourceTree = "<group>"; };
+		260E03F313B122D40064D447 /* GrStencil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrStencil.cpp; sourceTree = "<group>"; };
+		260E03F513B122D40064D447 /* GrTextContext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrTextContext.cpp; sourceTree = "<group>"; };
+		260E03F613B122D40064D447 /* GrTextStrike.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrTextStrike.cpp; sourceTree = "<group>"; };
+		260E03F713B122D40064D447 /* GrTextStrike_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTextStrike_impl.h; sourceTree = "<group>"; };
+		260E03F813B122D40064D447 /* GrTexture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrTexture.cpp; sourceTree = "<group>"; };
+		260E03FA13B122D40064D447 /* gr_unittests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gr_unittests.cpp; sourceTree = "<group>"; };
+		260E03FC13B122D40064D447 /* SkGpuCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGpuCanvas.h; sourceTree = "<group>"; };
+		260E03FD13B122D40064D447 /* SkGpuDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGpuDevice.h; sourceTree = "<group>"; };
+		260E03FF13B122D40064D447 /* SkGr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGr.h; sourceTree = "<group>"; };
+		260E040013B122D40064D447 /* SkGrTexturePixelRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGrTexturePixelRef.h; sourceTree = "<group>"; };
+		260E040213B122D40064D447 /* GrPrintf_skia.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrPrintf_skia.cpp; sourceTree = "<group>"; };
+		260E040313B122D40064D447 /* SkGpuCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGpuCanvas.cpp; sourceTree = "<group>"; };
+		260E040413B122D40064D447 /* SkGpuDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGpuDevice.cpp; sourceTree = "<group>"; };
+		260E040513B122D40064D447 /* SkGr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGr.cpp; sourceTree = "<group>"; };
+		260E040613B122D40064D447 /* SkGrFontScaler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGrFontScaler.cpp; sourceTree = "<group>"; };
+		260E040713B122D40064D447 /* SkGrTexturePixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGrTexturePixelRef.cpp; sourceTree = "<group>"; };
+		260E044113B1232F0064D447 /* SkImageDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageDecoder.h; sourceTree = "<group>"; };
+		260E044213B1232F0064D447 /* SkImageEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageEncoder.h; sourceTree = "<group>"; };
+		260E044A13B1232F0064D447 /* SkBitmap_RLEPixels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBitmap_RLEPixels.h; sourceTree = "<group>"; };
+		260E044B13B1232F0064D447 /* SkCreateRLEPixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCreateRLEPixelRef.cpp; sourceTree = "<group>"; };
+		260E044C13B1232F0064D447 /* SkFDStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFDStream.cpp; sourceTree = "<group>"; };
+		260E044D13B1232F0064D447 /* SkFlipPixelRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkFlipPixelRef.cpp; sourceTree = "<group>"; };
+		260E044E13B1232F0064D447 /* SkImageDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageDecoder.cpp; sourceTree = "<group>"; };
+		260E045613B1232F0064D447 /* SkImageEncoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageEncoder.cpp; sourceTree = "<group>"; };
+		260E04C813B123840064D447 /* SkSVGAttribute.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGAttribute.h; sourceTree = "<group>"; };
+		260E04C913B123840064D447 /* SkSVGBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGBase.h; sourceTree = "<group>"; };
+		260E04CA13B123840064D447 /* SkSVGPaintState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGPaintState.h; sourceTree = "<group>"; };
+		260E04CB13B123840064D447 /* SkSVGParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGParser.h; sourceTree = "<group>"; };
+		260E04CC13B123840064D447 /* SkSVGTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGTypes.h; sourceTree = "<group>"; };
+		260E04CE13B123840064D447 /* SkSVGCircle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGCircle.cpp; sourceTree = "<group>"; };
+		260E04CF13B123840064D447 /* SkSVGCircle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGCircle.h; sourceTree = "<group>"; };
+		260E04D013B123840064D447 /* SkSVGClipPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGClipPath.cpp; sourceTree = "<group>"; };
+		260E04D113B123840064D447 /* SkSVGClipPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGClipPath.h; sourceTree = "<group>"; };
+		260E04D213B123840064D447 /* SkSVGDefs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGDefs.cpp; sourceTree = "<group>"; };
+		260E04D313B123840064D447 /* SkSVGDefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGDefs.h; sourceTree = "<group>"; };
+		260E04D413B123840064D447 /* SkSVGElements.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGElements.cpp; sourceTree = "<group>"; };
+		260E04D513B123840064D447 /* SkSVGElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGElements.h; sourceTree = "<group>"; };
+		260E04D613B123840064D447 /* SkSVGEllipse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGEllipse.cpp; sourceTree = "<group>"; };
+		260E04D713B123840064D447 /* SkSVGEllipse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGEllipse.h; sourceTree = "<group>"; };
+		260E04D813B123840064D447 /* SkSVGFeColorMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGFeColorMatrix.cpp; sourceTree = "<group>"; };
+		260E04D913B123840064D447 /* SkSVGFeColorMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGFeColorMatrix.h; sourceTree = "<group>"; };
+		260E04DA13B123840064D447 /* SkSVGFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGFilter.cpp; sourceTree = "<group>"; };
+		260E04DB13B123840064D447 /* SkSVGFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGFilter.h; sourceTree = "<group>"; };
+		260E04DC13B123840064D447 /* SkSVGG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGG.cpp; sourceTree = "<group>"; };
+		260E04DD13B123840064D447 /* SkSVGG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGG.h; sourceTree = "<group>"; };
+		260E04DE13B123840064D447 /* SkSVGGradient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGGradient.cpp; sourceTree = "<group>"; };
+		260E04DF13B123840064D447 /* SkSVGGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGGradient.h; sourceTree = "<group>"; };
+		260E04E013B123840064D447 /* SkSVGGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGGroup.cpp; sourceTree = "<group>"; };
+		260E04E113B123840064D447 /* SkSVGGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGGroup.h; sourceTree = "<group>"; };
+		260E04E213B123840064D447 /* SkSVGImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGImage.cpp; sourceTree = "<group>"; };
+		260E04E313B123840064D447 /* SkSVGImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGImage.h; sourceTree = "<group>"; };
+		260E04E413B123840064D447 /* SkSVGLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGLine.cpp; sourceTree = "<group>"; };
+		260E04E513B123840064D447 /* SkSVGLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGLine.h; sourceTree = "<group>"; };
+		260E04E613B123840064D447 /* SkSVGLinearGradient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGLinearGradient.cpp; sourceTree = "<group>"; };
+		260E04E713B123840064D447 /* SkSVGLinearGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGLinearGradient.h; sourceTree = "<group>"; };
+		260E04E813B123840064D447 /* SkSVGMask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGMask.cpp; sourceTree = "<group>"; };
+		260E04E913B123840064D447 /* SkSVGMask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGMask.h; sourceTree = "<group>"; };
+		260E04EA13B123840064D447 /* SkSVGMetadata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGMetadata.cpp; sourceTree = "<group>"; };
+		260E04EB13B123840064D447 /* SkSVGMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGMetadata.h; sourceTree = "<group>"; };
+		260E04EC13B123840064D447 /* SkSVGPaintState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGPaintState.cpp; sourceTree = "<group>"; };
+		260E04ED13B123840064D447 /* SkSVGParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGParser.cpp; sourceTree = "<group>"; };
+		260E04EE13B123840064D447 /* SkSVGPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGPath.cpp; sourceTree = "<group>"; };
+		260E04EF13B123840064D447 /* SkSVGPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGPath.h; sourceTree = "<group>"; };
+		260E04F013B123840064D447 /* SkSVGPolygon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGPolygon.cpp; sourceTree = "<group>"; };
+		260E04F113B123840064D447 /* SkSVGPolygon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGPolygon.h; sourceTree = "<group>"; };
+		260E04F213B123840064D447 /* SkSVGPolyline.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGPolyline.cpp; sourceTree = "<group>"; };
+		260E04F313B123840064D447 /* SkSVGPolyline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGPolyline.h; sourceTree = "<group>"; };
+		260E04F413B123840064D447 /* SkSVGRadialGradient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGRadialGradient.cpp; sourceTree = "<group>"; };
+		260E04F513B123840064D447 /* SkSVGRadialGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGRadialGradient.h; sourceTree = "<group>"; };
+		260E04F613B123840064D447 /* SkSVGRect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGRect.cpp; sourceTree = "<group>"; };
+		260E04F713B123840064D447 /* SkSVGRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGRect.h; sourceTree = "<group>"; };
+		260E04F813B123840064D447 /* SkSVGSVG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGSVG.cpp; sourceTree = "<group>"; };
+		260E04F913B123840064D447 /* SkSVGSVG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGSVG.h; sourceTree = "<group>"; };
+		260E04FA13B123840064D447 /* SkSVGStop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGStop.cpp; sourceTree = "<group>"; };
+		260E04FB13B123840064D447 /* SkSVGStop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGStop.h; sourceTree = "<group>"; };
+		260E04FC13B123840064D447 /* SkSVGSymbol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGSymbol.cpp; sourceTree = "<group>"; };
+		260E04FD13B123840064D447 /* SkSVGSymbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGSymbol.h; sourceTree = "<group>"; };
+		260E04FE13B123840064D447 /* SkSVGText.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGText.cpp; sourceTree = "<group>"; };
+		260E04FF13B123840064D447 /* SkSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSVGText.h; sourceTree = "<group>"; };
+		260E050013B123840064D447 /* SkSVGUse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSVGUse.cpp; sourceTree = "<group>"; };
+		260E053013B123DE0064D447 /* SkCGUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCGUtils.h; sourceTree = "<group>"; };
+		260E053113B123DE0064D447 /* SkBoundaryPatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBoundaryPatch.h; sourceTree = "<group>"; };
+		260E053213B123DE0064D447 /* SkCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCamera.h; sourceTree = "<group>"; };
+		260E053313B123DE0064D447 /* SkCubicInterval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCubicInterval.h; sourceTree = "<group>"; };
+		260E053413B123DE0064D447 /* SkCullPoints.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkCullPoints.h; sourceTree = "<group>"; };
+		260E053513B123DE0064D447 /* SkDumpCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDumpCanvas.h; sourceTree = "<group>"; };
+		260E053613B123DE0064D447 /* SkEGLContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEGLContext.h; sourceTree = "<group>"; };
+		260E053713B123DE0064D447 /* SkGLCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkGLCanvas.h; sourceTree = "<group>"; };
+		260E053813B123DE0064D447 /* SkInterpolator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkInterpolator.h; sourceTree = "<group>"; };
+		260E053913B123DE0064D447 /* SkLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkLayer.h; sourceTree = "<group>"; };
+		260E053A13B123DE0064D447 /* SkMatrix44.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkMatrix44.h; sourceTree = "<group>"; };
+		260E053B13B123DE0064D447 /* SkMeshUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkMeshUtils.h; sourceTree = "<group>"; };
+		260E053C13B123DE0064D447 /* SkNWayCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkNWayCanvas.h; sourceTree = "<group>"; };
+		260E053D13B123DE0064D447 /* SkNinePatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkNinePatch.h; sourceTree = "<group>"; };
+		260E053E13B123DE0064D447 /* SkParse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkParse.h; sourceTree = "<group>"; };
+		260E053F13B123DE0064D447 /* SkParsePaint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkParsePaint.h; sourceTree = "<group>"; };
+		260E054013B123DE0064D447 /* SkParsePath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkParsePath.h; sourceTree = "<group>"; };
+		260E054113B123DE0064D447 /* SkProxyCanvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkProxyCanvas.h; sourceTree = "<group>"; };
+		260E054213B123DE0064D447 /* SkSfntUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSfntUtils.h; sourceTree = "<group>"; };
+		260E054313B123DE0064D447 /* SkTextBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTextBox.h; sourceTree = "<group>"; };
+		260E054413B123DE0064D447 /* SkUnitMappers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkUnitMappers.h; sourceTree = "<group>"; };
+		260E054913B123DE0064D447 /* SkCreateCGImageRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCreateCGImageRef.cpp; sourceTree = "<group>"; };
+		260E055713B123DE0064D447 /* SkBoundaryPatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBoundaryPatch.cpp; sourceTree = "<group>"; };
+		260E055813B123DE0064D447 /* SkCamera.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCamera.cpp; sourceTree = "<group>"; };
+		260E055913B123DE0064D447 /* SkColorMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkColorMatrix.cpp; sourceTree = "<group>"; };
+		260E055A13B123DE0064D447 /* SkCubicInterval.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCubicInterval.cpp; sourceTree = "<group>"; };
+		260E055B13B123DE0064D447 /* SkCullPoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkCullPoints.cpp; sourceTree = "<group>"; };
+		260E055C13B123DE0064D447 /* SkDumpCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDumpCanvas.cpp; sourceTree = "<group>"; };
+		260E055E13B123DE0064D447 /* SkInterpolator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkInterpolator.cpp; sourceTree = "<group>"; };
+		260E055F13B123DE0064D447 /* SkLayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkLayer.cpp; sourceTree = "<group>"; };
+		260E056013B123DE0064D447 /* SkMatrix44.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMatrix44.cpp; sourceTree = "<group>"; };
+		260E056113B123DE0064D447 /* SkMeshUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMeshUtils.cpp; sourceTree = "<group>"; };
+		260E056213B123DE0064D447 /* SkNWayCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkNWayCanvas.cpp; sourceTree = "<group>"; };
+		260E056313B123DE0064D447 /* SkNinePatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkNinePatch.cpp; sourceTree = "<group>"; };
+		260E056413B123DE0064D447 /* SkOSFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkOSFile.cpp; sourceTree = "<group>"; };
+		260E056513B123DE0064D447 /* SkParse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkParse.cpp; sourceTree = "<group>"; };
+		260E056613B123DE0064D447 /* SkParseColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkParseColor.cpp; sourceTree = "<group>"; };
+		260E056713B123DE0064D447 /* SkParsePath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkParsePath.cpp; sourceTree = "<group>"; };
+		260E056813B123DE0064D447 /* SkProxyCanvas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkProxyCanvas.cpp; sourceTree = "<group>"; };
+		260E056913B123DE0064D447 /* SkSfntUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSfntUtils.cpp; sourceTree = "<group>"; };
+		260E056A13B123DE0064D447 /* SkUnitMappers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkUnitMappers.cpp; sourceTree = "<group>"; };
+		260E059813B123E80064D447 /* SkApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkApplication.h; sourceTree = "<group>"; };
+		260E059913B123E80064D447 /* SkBGViewArtist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBGViewArtist.h; sourceTree = "<group>"; };
+		260E059A13B123E80064D447 /* SkBorderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBorderView.h; sourceTree = "<group>"; };
+		260E059B13B123E80064D447 /* SkEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEvent.h; sourceTree = "<group>"; };
+		260E059C13B123E80064D447 /* SkEventSink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkEventSink.h; sourceTree = "<group>"; };
+		260E059D13B123E80064D447 /* SkImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageView.h; sourceTree = "<group>"; };
+		260E059E13B123E80064D447 /* SkKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkKey.h; sourceTree = "<group>"; };
+		260E059F13B123E80064D447 /* SkOSMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkOSMenu.h; sourceTree = "<group>"; };
+		260E05A413B123E80064D447 /* SkProgressBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkProgressBarView.h; sourceTree = "<group>"; };
+		260E05A513B123E80064D447 /* SkScrollBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScrollBarView.h; sourceTree = "<group>"; };
+		260E05A613B123E80064D447 /* SkStackViewLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkStackViewLayout.h; sourceTree = "<group>"; };
+		260E05A713B123E80064D447 /* SkSystemEventTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSystemEventTypes.h; sourceTree = "<group>"; };
+		260E05A813B123E80064D447 /* SkTouchGesture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTouchGesture.h; sourceTree = "<group>"; };
+		260E05A913B123E80064D447 /* SkView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkView.h; sourceTree = "<group>"; };
+		260E05AA13B123E80064D447 /* SkViewInflate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkViewInflate.h; sourceTree = "<group>"; };
+		260E05AB13B123E80064D447 /* SkWidget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkWidget.h; sourceTree = "<group>"; };
+		260E05AC13B123E80064D447 /* SkWidgetViews.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkWidgetViews.h; sourceTree = "<group>"; };
+		260E05AD13B123E80064D447 /* SkWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkWindow.h; sourceTree = "<group>"; };
+		260E05AF13B123E80064D447 /* SkBGViewArtist.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBGViewArtist.cpp; sourceTree = "<group>"; };
+		260E05B013B123E80064D447 /* SkBorderView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBorderView.cpp; sourceTree = "<group>"; };
+		260E05B113B123E80064D447 /* SkEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEvent.cpp; sourceTree = "<group>"; };
+		260E05B213B123E80064D447 /* SkEventSink.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkEventSink.cpp; sourceTree = "<group>"; };
+		260E05B313B123E80064D447 /* SkImageView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageView.cpp; sourceTree = "<group>"; };
+		260E05B413B123E80064D447 /* SkListView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkListView.cpp; sourceTree = "<group>"; };
+		260E05B513B123E80064D447 /* SkListWidget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkListWidget.cpp; sourceTree = "<group>"; };
+		260E05B613B123E80064D447 /* SkOSMenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkOSMenu.cpp; sourceTree = "<group>"; };
+		260E05B713B123E80064D447 /* SkParsePaint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkParsePaint.cpp; sourceTree = "<group>"; };
+		260E05B813B123E80064D447 /* SkProgressBarView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkProgressBarView.cpp; sourceTree = "<group>"; };
+		260E05B913B123E80064D447 /* SkProgressView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkProgressView.cpp; sourceTree = "<group>"; };
+		260E05BA13B123E80064D447 /* SkScrollBarView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScrollBarView.cpp; sourceTree = "<group>"; };
+		260E05BB13B123E80064D447 /* SkStackViewLayout.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkStackViewLayout.cpp; sourceTree = "<group>"; };
+		260E05BC13B123E80064D447 /* SkStaticTextView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkStaticTextView.cpp; sourceTree = "<group>"; };
+		260E05BD13B123E80064D447 /* SkTagList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTagList.cpp; sourceTree = "<group>"; };
+		260E05BE13B123E80064D447 /* SkTagList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTagList.h; sourceTree = "<group>"; };
+		260E05BF13B123E80064D447 /* SkTextBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTextBox.cpp; sourceTree = "<group>"; };
+		260E05C013B123E80064D447 /* SkTouchGesture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTouchGesture.cpp; sourceTree = "<group>"; };
+		260E05C113B123E80064D447 /* SkView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkView.cpp; sourceTree = "<group>"; };
+		260E05C213B123E80064D447 /* SkViewInflate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkViewInflate.cpp; sourceTree = "<group>"; };
+		260E05C313B123E80064D447 /* SkViewPriv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkViewPriv.cpp; sourceTree = "<group>"; };
+		260E05C413B123E80064D447 /* SkViewPriv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkViewPriv.h; sourceTree = "<group>"; };
+		260E05C513B123E80064D447 /* SkWidget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkWidget.cpp; sourceTree = "<group>"; };
+		260E05C613B123E80064D447 /* SkWidgetViews.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkWidgetViews.cpp; sourceTree = "<group>"; };
+		260E05C713B123E80064D447 /* SkWidgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkWidgets.cpp; sourceTree = "<group>"; };
+		260E05C813B123E80064D447 /* SkWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkWindow.cpp; sourceTree = "<group>"; };
+		260E05EE13B124210064D447 /* SkBML_WXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBML_WXMLParser.h; sourceTree = "<group>"; };
+		260E05EF13B124210064D447 /* SkBML_XMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBML_XMLParser.h; sourceTree = "<group>"; };
+		260E05F013B124210064D447 /* SkDOM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDOM.h; sourceTree = "<group>"; };
+		260E05F113B124210064D447 /* SkJS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkJS.h; sourceTree = "<group>"; };
+		260E05F213B124210064D447 /* SkXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkXMLParser.h; sourceTree = "<group>"; };
+		260E05F313B124210064D447 /* SkXMLWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkXMLWriter.h; sourceTree = "<group>"; };
+		260E05F713B124210064D447 /* SkDOM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDOM.cpp; sourceTree = "<group>"; };
+		260E05FA13B124210064D447 /* SkXMLParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkXMLParser.cpp; sourceTree = "<group>"; };
+		260E06B813B127E00064D447 /* SkAnimator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimator.h; sourceTree = "<group>"; };
+		260E06B913B127E00064D447 /* SkAnimatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimatorView.h; sourceTree = "<group>"; };
+		260E06BB13B127E00064D447 /* SkAnimate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimate.h; sourceTree = "<group>"; };
+		260E06BC13B127E00064D447 /* SkAnimateActive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimateActive.cpp; sourceTree = "<group>"; };
+		260E06BD13B127E00064D447 /* SkAnimateActive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimateActive.h; sourceTree = "<group>"; };
+		260E06BE13B127E00064D447 /* SkAnimateBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimateBase.cpp; sourceTree = "<group>"; };
+		260E06BF13B127E00064D447 /* SkAnimateBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimateBase.h; sourceTree = "<group>"; };
+		260E06C013B127E00064D447 /* SkAnimateField.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimateField.cpp; sourceTree = "<group>"; };
+		260E06C113B127E00064D447 /* SkAnimateMaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimateMaker.cpp; sourceTree = "<group>"; };
+		260E06C213B127E00064D447 /* SkAnimateMaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimateMaker.h; sourceTree = "<group>"; };
+		260E06C313B127E00064D447 /* SkAnimateProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimateProperties.h; sourceTree = "<group>"; };
+		260E06C413B127E00064D447 /* SkAnimateSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimateSet.cpp; sourceTree = "<group>"; };
+		260E06C513B127E00064D447 /* SkAnimateSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimateSet.h; sourceTree = "<group>"; };
+		260E06C613B127E00064D447 /* SkAnimator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimator.cpp; sourceTree = "<group>"; };
+		260E06C713B127E00064D447 /* SkAnimatorScript.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkAnimatorScript.cpp; sourceTree = "<group>"; };
+		260E06C813B127E00064D447 /* SkAnimatorScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkAnimatorScript.h; sourceTree = "<group>"; };
+		260E06C913B127E00064D447 /* SkBase64.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBase64.cpp; sourceTree = "<group>"; };
+		260E06CA13B127E00064D447 /* SkBase64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBase64.h; sourceTree = "<group>"; };
+		260E06CB13B127E00064D447 /* SkBoundable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBoundable.cpp; sourceTree = "<group>"; };
+		260E06CC13B127E00064D447 /* SkBoundable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBoundable.h; sourceTree = "<group>"; };
+		260E06CD13B127E00064D447 /* SkBuildCondensedInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBuildCondensedInfo.cpp; sourceTree = "<group>"; };
+		260E06CE13B127E00064D447 /* SkDisplayAdd.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayAdd.cpp; sourceTree = "<group>"; };
+		260E06CF13B127E00064D447 /* SkDisplayAdd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayAdd.h; sourceTree = "<group>"; };
+		260E06D013B127E00064D447 /* SkDisplayApply.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayApply.cpp; sourceTree = "<group>"; };
+		260E06D113B127E00064D447 /* SkDisplayApply.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayApply.h; sourceTree = "<group>"; };
+		260E06D213B127E00064D447 /* SkDisplayBounds.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayBounds.cpp; sourceTree = "<group>"; };
+		260E06D313B127E00064D447 /* SkDisplayBounds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayBounds.h; sourceTree = "<group>"; };
+		260E06D413B127E00064D447 /* SkDisplayEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayEvent.cpp; sourceTree = "<group>"; };
+		260E06D513B127E00064D447 /* SkDisplayEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayEvent.h; sourceTree = "<group>"; };
+		260E06D613B127E00064D447 /* SkDisplayEvents.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayEvents.cpp; sourceTree = "<group>"; };
+		260E06D713B127E00064D447 /* SkDisplayEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayEvents.h; sourceTree = "<group>"; };
+		260E06D813B127E00064D447 /* SkDisplayInclude.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayInclude.cpp; sourceTree = "<group>"; };
+		260E06D913B127E00064D447 /* SkDisplayInclude.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayInclude.h; sourceTree = "<group>"; };
+		260E06DA13B127E00064D447 /* SkDisplayInput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayInput.cpp; sourceTree = "<group>"; };
+		260E06DB13B127E00064D447 /* SkDisplayInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayInput.h; sourceTree = "<group>"; };
+		260E06DC13B127E00064D447 /* SkDisplayList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayList.cpp; sourceTree = "<group>"; };
+		260E06DD13B127E00064D447 /* SkDisplayList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayList.h; sourceTree = "<group>"; };
+		260E06DE13B127E00064D447 /* SkDisplayMath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayMath.cpp; sourceTree = "<group>"; };
+		260E06DF13B127E00064D447 /* SkDisplayMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayMath.h; sourceTree = "<group>"; };
+		260E06E013B127E00064D447 /* SkDisplayMovie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayMovie.cpp; sourceTree = "<group>"; };
+		260E06E113B127E00064D447 /* SkDisplayMovie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayMovie.h; sourceTree = "<group>"; };
+		260E06E213B127E00064D447 /* SkDisplayNumber.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayNumber.cpp; sourceTree = "<group>"; };
+		260E06E313B127E00064D447 /* SkDisplayNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayNumber.h; sourceTree = "<group>"; };
+		260E06E413B127E00064D447 /* SkDisplayPost.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayPost.cpp; sourceTree = "<group>"; };
+		260E06E513B127E00064D447 /* SkDisplayPost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayPost.h; sourceTree = "<group>"; };
+		260E06E613B127E00064D447 /* SkDisplayRandom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayRandom.cpp; sourceTree = "<group>"; };
+		260E06E713B127E00064D447 /* SkDisplayRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayRandom.h; sourceTree = "<group>"; };
+		260E06E813B127E00064D447 /* SkDisplayScreenplay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayScreenplay.cpp; sourceTree = "<group>"; };
+		260E06E913B127E00064D447 /* SkDisplayScreenplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayScreenplay.h; sourceTree = "<group>"; };
+		260E06EA13B127E00064D447 /* SkDisplayType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayType.cpp; sourceTree = "<group>"; };
+		260E06EB13B127E00064D447 /* SkDisplayType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayType.h; sourceTree = "<group>"; };
+		260E06EC13B127E00064D447 /* SkDisplayTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayTypes.cpp; sourceTree = "<group>"; };
+		260E06ED13B127E00064D447 /* SkDisplayTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayTypes.h; sourceTree = "<group>"; };
+		260E06EE13B127E00064D447 /* SkDisplayXMLParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayXMLParser.cpp; sourceTree = "<group>"; };
+		260E06EF13B127E00064D447 /* SkDisplayXMLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayXMLParser.h; sourceTree = "<group>"; };
+		260E06F013B127E00064D447 /* SkDisplayable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDisplayable.cpp; sourceTree = "<group>"; };
+		260E06F113B127E00064D447 /* SkDisplayable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDisplayable.h; sourceTree = "<group>"; };
+		260E06F213B127E00064D447 /* SkDraw3D.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDraw3D.cpp; sourceTree = "<group>"; };
+		260E06F313B127E00064D447 /* SkDraw3D.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDraw3D.h; sourceTree = "<group>"; };
+		260E06F413B127E00064D447 /* SkDrawBitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawBitmap.cpp; sourceTree = "<group>"; };
+		260E06F513B127E00064D447 /* SkDrawBitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawBitmap.h; sourceTree = "<group>"; };
+		260E06F613B127E00064D447 /* SkDrawBlur.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawBlur.cpp; sourceTree = "<group>"; };
+		260E06F713B127E00064D447 /* SkDrawBlur.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawBlur.h; sourceTree = "<group>"; };
+		260E06F813B127E00064D447 /* SkDrawClip.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawClip.cpp; sourceTree = "<group>"; };
+		260E06F913B127E00064D447 /* SkDrawClip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawClip.h; sourceTree = "<group>"; };
+		260E06FA13B127E00064D447 /* SkDrawColor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawColor.cpp; sourceTree = "<group>"; };
+		260E06FB13B127E00064D447 /* SkDrawColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawColor.h; sourceTree = "<group>"; };
+		260E06FC13B127E00064D447 /* SkDrawDash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawDash.cpp; sourceTree = "<group>"; };
+		260E06FD13B127E00064D447 /* SkDrawDash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawDash.h; sourceTree = "<group>"; };
+		260E06FE13B127E00064D447 /* SkDrawDiscrete.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawDiscrete.cpp; sourceTree = "<group>"; };
+		260E06FF13B127E00064D447 /* SkDrawDiscrete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawDiscrete.h; sourceTree = "<group>"; };
+		260E070013B127E00064D447 /* SkDrawEmboss.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawEmboss.cpp; sourceTree = "<group>"; };
+		260E070113B127E00064D447 /* SkDrawEmboss.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawEmboss.h; sourceTree = "<group>"; };
+		260E070213B127E00064D447 /* SkDrawExtraPathEffect.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawExtraPathEffect.cpp; sourceTree = "<group>"; };
+		260E070313B127E00064D447 /* SkDrawFull.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawFull.cpp; sourceTree = "<group>"; };
+		260E070413B127E00064D447 /* SkDrawFull.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawFull.h; sourceTree = "<group>"; };
+		260E070513B127E00064D447 /* SkDrawGradient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawGradient.cpp; sourceTree = "<group>"; };
+		260E070613B127E00064D447 /* SkDrawGradient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawGradient.h; sourceTree = "<group>"; };
+		260E070713B127E00064D447 /* SkDrawGroup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawGroup.cpp; sourceTree = "<group>"; };
+		260E070813B127E00064D447 /* SkDrawGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawGroup.h; sourceTree = "<group>"; };
+		260E070913B127E00064D447 /* SkDrawLine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawLine.cpp; sourceTree = "<group>"; };
+		260E070A13B127E00064D447 /* SkDrawLine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawLine.h; sourceTree = "<group>"; };
+		260E070B13B127E00064D447 /* SkDrawMatrix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawMatrix.cpp; sourceTree = "<group>"; };
+		260E070C13B127E00064D447 /* SkDrawMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawMatrix.h; sourceTree = "<group>"; };
+		260E070D13B127E00064D447 /* SkDrawOval.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawOval.cpp; sourceTree = "<group>"; };
+		260E070E13B127E00064D447 /* SkDrawOval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawOval.h; sourceTree = "<group>"; };
+		260E070F13B127E00064D447 /* SkDrawPaint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawPaint.cpp; sourceTree = "<group>"; };
+		260E071013B127E00064D447 /* SkDrawPaint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawPaint.h; sourceTree = "<group>"; };
+		260E071113B127E00064D447 /* SkDrawPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawPath.cpp; sourceTree = "<group>"; };
+		260E071213B127E00064D447 /* SkDrawPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawPath.h; sourceTree = "<group>"; };
+		260E071313B127E00064D447 /* SkDrawPoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawPoint.cpp; sourceTree = "<group>"; };
+		260E071413B127E00064D447 /* SkDrawPoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawPoint.h; sourceTree = "<group>"; };
+		260E071513B127E00064D447 /* SkDrawRectangle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawRectangle.cpp; sourceTree = "<group>"; };
+		260E071613B127E00064D447 /* SkDrawRectangle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawRectangle.h; sourceTree = "<group>"; };
+		260E071713B127E00064D447 /* SkDrawSaveLayer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawSaveLayer.cpp; sourceTree = "<group>"; };
+		260E071813B127E00064D447 /* SkDrawSaveLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawSaveLayer.h; sourceTree = "<group>"; };
+		260E071913B127E00064D447 /* SkDrawShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawShader.cpp; sourceTree = "<group>"; };
+		260E071A13B127E00064D447 /* SkDrawShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawShader.h; sourceTree = "<group>"; };
+		260E071B13B127E00064D447 /* SkDrawText.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawText.cpp; sourceTree = "<group>"; };
+		260E071C13B127E00064D447 /* SkDrawText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawText.h; sourceTree = "<group>"; };
+		260E071D13B127E00064D447 /* SkDrawTextBox.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawTextBox.cpp; sourceTree = "<group>"; };
+		260E071E13B127E00064D447 /* SkDrawTextBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawTextBox.h; sourceTree = "<group>"; };
+		260E071F13B127E00064D447 /* SkDrawTo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawTo.cpp; sourceTree = "<group>"; };
+		260E072013B127E00064D447 /* SkDrawTo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawTo.h; sourceTree = "<group>"; };
+		260E072113B127E00064D447 /* SkDrawTransparentShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawTransparentShader.cpp; sourceTree = "<group>"; };
+		260E072213B127E00064D447 /* SkDrawTransparentShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawTransparentShader.h; sourceTree = "<group>"; };
+		260E072313B127E00064D447 /* SkDrawable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDrawable.cpp; sourceTree = "<group>"; };
+		260E072413B127E00064D447 /* SkDrawable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDrawable.h; sourceTree = "<group>"; };
+		260E072513B127E00064D447 /* SkDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkDump.cpp; sourceTree = "<group>"; };
+		260E072613B127E00064D447 /* SkDump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkDump.h; sourceTree = "<group>"; };
+		260E072713B127E00064D447 /* SkExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkExtras.h; sourceTree = "<group>"; };
+		260E072813B127E00064D447 /* SkGetCondensedInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkGetCondensedInfo.cpp; sourceTree = "<group>"; };
+		260E072913B127E00064D447 /* SkHitClear.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkHitClear.cpp; sourceTree = "<group>"; };
+		260E072A13B127E00064D447 /* SkHitClear.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkHitClear.h; sourceTree = "<group>"; };
+		260E072B13B127E00064D447 /* SkHitTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkHitTest.cpp; sourceTree = "<group>"; };
+		260E072C13B127E00064D447 /* SkHitTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkHitTest.h; sourceTree = "<group>"; };
+		260E072D13B127E00064D447 /* SkIntArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkIntArray.h; sourceTree = "<group>"; };
+		260E072E13B127E00064D447 /* SkMatrixParts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMatrixParts.cpp; sourceTree = "<group>"; };
+		260E072F13B127E00064D447 /* SkMatrixParts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkMatrixParts.h; sourceTree = "<group>"; };
+		260E073013B127E00064D447 /* SkMemberInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMemberInfo.cpp; sourceTree = "<group>"; };
+		260E073113B127E00064D447 /* SkMemberInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkMemberInfo.h; sourceTree = "<group>"; };
+		260E073213B127E00064D447 /* SkOpArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkOpArray.cpp; sourceTree = "<group>"; };
+		260E073313B127E00064D447 /* SkOpArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkOpArray.h; sourceTree = "<group>"; };
+		260E073413B127E00064D447 /* SkOperand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkOperand.h; sourceTree = "<group>"; };
+		260E073513B127E00064D447 /* SkOperand2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkOperand2.h; sourceTree = "<group>"; };
+		260E073613B127E00064D447 /* SkOperandInterpolator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkOperandInterpolator.h; sourceTree = "<group>"; };
+		260E073713B127E00064D447 /* SkOperandIterpolator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkOperandIterpolator.cpp; sourceTree = "<group>"; };
+		260E073813B127E00064D447 /* SkPaintParts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPaintParts.cpp; sourceTree = "<group>"; };
+		260E073913B127E00064D447 /* SkPaintParts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPaintParts.h; sourceTree = "<group>"; };
+		260E073A13B127E00064D447 /* SkParseSVGPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkParseSVGPath.cpp; sourceTree = "<group>"; };
+		260E073B13B127E00064D447 /* SkPathParts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPathParts.cpp; sourceTree = "<group>"; };
+		260E073C13B127E00064D447 /* SkPathParts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPathParts.h; sourceTree = "<group>"; };
+		260E073D13B127E00064D447 /* SkPostParts.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPostParts.cpp; sourceTree = "<group>"; };
+		260E073E13B127E00064D447 /* SkPostParts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPostParts.h; sourceTree = "<group>"; };
+		260E073F13B127E00064D447 /* SkScript.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScript.cpp; sourceTree = "<group>"; };
+		260E074013B127E00064D447 /* SkScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScript.h; sourceTree = "<group>"; };
+		260E074113B127E00064D447 /* SkScript2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScript2.h; sourceTree = "<group>"; };
+		260E074213B127E00064D447 /* SkScriptCallBack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScriptCallBack.h; sourceTree = "<group>"; };
+		260E074313B127E00064D447 /* SkScriptDecompile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScriptDecompile.cpp; sourceTree = "<group>"; };
+		260E074413B127E00064D447 /* SkScriptRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScriptRuntime.cpp; sourceTree = "<group>"; };
+		260E074513B127E00064D447 /* SkScriptRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkScriptRuntime.h; sourceTree = "<group>"; };
+		260E074613B127E00064D447 /* SkScriptTokenizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkScriptTokenizer.cpp; sourceTree = "<group>"; };
+		260E074713B127E00064D447 /* SkSnapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkSnapshot.cpp; sourceTree = "<group>"; };
+		260E074813B127E00064D447 /* SkSnapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSnapshot.h; sourceTree = "<group>"; };
+		260E074913B127E00064D447 /* SkTDArray_Experimental.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTDArray_Experimental.h; sourceTree = "<group>"; };
+		260E074A13B127E00064D447 /* SkTextOnPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTextOnPath.cpp; sourceTree = "<group>"; };
+		260E074B13B127E00064D447 /* SkTextOnPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTextOnPath.h; sourceTree = "<group>"; };
+		260E074C13B127E00064D447 /* SkTextToPath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTextToPath.cpp; sourceTree = "<group>"; };
+		260E074D13B127E00064D447 /* SkTextToPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTextToPath.h; sourceTree = "<group>"; };
+		260E074E13B127E00064D447 /* SkTime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTime.cpp; sourceTree = "<group>"; };
+		260E074F13B127E00064D447 /* SkTypedArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkTypedArray.cpp; sourceTree = "<group>"; };
+		260E075013B127E00064D447 /* SkTypedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkTypedArray.h; sourceTree = "<group>"; };
+		260E075113B127E00064D447 /* SkXMLAnimatorWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkXMLAnimatorWriter.cpp; sourceTree = "<group>"; };
+		260E075213B127E00064D447 /* SkXMLAnimatorWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkXMLAnimatorWriter.h; sourceTree = "<group>"; };
+		260E07B213B128210064D447 /* SkXMLWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkXMLWriter.cpp; sourceTree = "<group>"; };
+		260E07C413B128610064D447 /* SkMovie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkMovie.cpp; sourceTree = "<group>"; };
+		260E07C913B128740064D447 /* SkPageFlipper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkPageFlipper.cpp; sourceTree = "<group>"; };
+		260E07CE13B128870064D447 /* SkImageRef.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageRef.cpp; sourceTree = "<group>"; };
+		260E07CF13B128870064D447 /* SkImageRefPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageRefPool.cpp; sourceTree = "<group>"; };
+		260E07D013B128870064D447 /* SkImageRefPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageRefPool.h; sourceTree = "<group>"; };
+		260E07D613B1289C0064D447 /* SkImageRef_GlobalPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageRef_GlobalPool.cpp; sourceTree = "<group>"; };
+		260E080E13B1294E0064D447 /* SkImageDecoder_CG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageDecoder_CG.cpp; sourceTree = "<group>"; };
+		260E083B13B12A200064D447 /* SkImageDecoder_Factory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageDecoder_Factory.cpp; sourceTree = "<group>"; };
+		260E083C13B12A200064D447 /* SkImageEncoder_Factory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkImageEncoder_Factory.cpp; sourceTree = "<group>"; };
+		260E08D013B12DBE0064D447 /* SkStream_NSData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkStream_NSData.h; path = ../../include/utils/ios/SkStream_NSData.h; sourceTree = SOURCE_ROOT; };
+		260E095513B134C90064D447 /* FlingState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FlingState.cpp; sourceTree = "<group>"; };
+		260E095613B134C90064D447 /* GrDrawMesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrDrawMesh.cpp; sourceTree = "<group>"; };
+		260E147713B2734E0064D447 /* SkUISplitViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkUISplitViewController.h; sourceTree = "<group>"; };
+		260E147813B2734E0064D447 /* SkUISplitViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SkUISplitViewController.mm; sourceTree = "<group>"; };
+		260E1DCA13B3AA490064D447 /* SkUINavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkUINavigationController.h; sourceTree = "<group>"; };
+		260E1DCB13B3AA490064D447 /* SkUINavigationController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SkUINavigationController.mm; sourceTree = "<group>"; };
+		260E1E8913B3B13B0064D447 /* SkPDFCatalog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFCatalog.h; path = ../../include/pdf/SkPDFCatalog.h; sourceTree = SOURCE_ROOT; };
+		260E1E8A13B3B13B0064D447 /* SkPDFDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFDevice.h; path = ../../include/pdf/SkPDFDevice.h; sourceTree = SOURCE_ROOT; };
+		260E1E8B13B3B13B0064D447 /* SkPDFDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFDocument.h; path = ../../include/pdf/SkPDFDocument.h; sourceTree = SOURCE_ROOT; };
+		260E1E8C13B3B13B0064D447 /* SkPDFFont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFFont.h; path = ../../include/pdf/SkPDFFont.h; sourceTree = SOURCE_ROOT; };
+		260E1E8D13B3B13B0064D447 /* SkPDFFormXObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFFormXObject.h; path = ../../include/pdf/SkPDFFormXObject.h; sourceTree = SOURCE_ROOT; };
+		260E1E8E13B3B13B0064D447 /* SkPDFGraphicState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFGraphicState.h; path = ../../include/pdf/SkPDFGraphicState.h; sourceTree = SOURCE_ROOT; };
+		260E1E8F13B3B13B0064D447 /* SkPDFImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFImage.h; path = ../../include/pdf/SkPDFImage.h; sourceTree = SOURCE_ROOT; };
+		260E1E9013B3B13B0064D447 /* SkPDFPage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFPage.h; path = ../../include/pdf/SkPDFPage.h; sourceTree = SOURCE_ROOT; };
+		260E1E9113B3B13B0064D447 /* SkPDFShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFShader.h; path = ../../include/pdf/SkPDFShader.h; sourceTree = SOURCE_ROOT; };
+		260E1E9213B3B13B0064D447 /* SkPDFStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFStream.h; path = ../../include/pdf/SkPDFStream.h; sourceTree = SOURCE_ROOT; };
+		260E1E9313B3B13B0064D447 /* SkPDFTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFTypes.h; path = ../../include/pdf/SkPDFTypes.h; sourceTree = SOURCE_ROOT; };
+		260E1E9413B3B13B0064D447 /* SkPDFUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPDFUtils.h; path = ../../include/pdf/SkPDFUtils.h; sourceTree = SOURCE_ROOT; };
+		260E1E9613B3B15A0064D447 /* SkPDFCatalog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFCatalog.cpp; path = ../../src/pdf/SkPDFCatalog.cpp; sourceTree = SOURCE_ROOT; };
+		260E1E9713B3B15A0064D447 /* SkPDFDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFDevice.cpp; path = ../../src/pdf/SkPDFDevice.cpp; sourceTree = SOURCE_ROOT; };
+		260E1E9813B3B15A0064D447 /* SkPDFDocument.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFDocument.cpp; path = ../../src/pdf/SkPDFDocument.cpp; sourceTree = SOURCE_ROOT; };
+		260E1E9913B3B15A0064D447 /* SkPDFFont.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFFont.cpp; path = ../../src/pdf/SkPDFFont.cpp; sourceTree = SOURCE_ROOT; };
+		260E1E9A13B3B15A0064D447 /* SkPDFFormXObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFFormXObject.cpp; path = ../../src/pdf/SkPDFFormXObject.cpp; sourceTree = SOURCE_ROOT; };
+		260E1E9B13B3B15A0064D447 /* SkPDFGraphicState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFGraphicState.cpp; path = ../../src/pdf/SkPDFGraphicState.cpp; sourceTree = SOURCE_ROOT; };
+		260E1E9C13B3B15A0064D447 /* SkPDFImage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFImage.cpp; path = ../../src/pdf/SkPDFImage.cpp; sourceTree = SOURCE_ROOT; };
+		260E1E9D13B3B15A0064D447 /* SkPDFPage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFPage.cpp; path = ../../src/pdf/SkPDFPage.cpp; sourceTree = SOURCE_ROOT; };
+		260E1E9E13B3B15A0064D447 /* SkPDFShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFShader.cpp; path = ../../src/pdf/SkPDFShader.cpp; sourceTree = SOURCE_ROOT; };
+		260E1E9F13B3B15A0064D447 /* SkPDFStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFStream.cpp; path = ../../src/pdf/SkPDFStream.cpp; sourceTree = SOURCE_ROOT; };
+		260E1EA013B3B15A0064D447 /* SkPDFTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFTypes.cpp; path = ../../src/pdf/SkPDFTypes.cpp; sourceTree = SOURCE_ROOT; };
+		260E1EA113B3B15A0064D447 /* SkPDFUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkPDFUtils.cpp; path = ../../src/pdf/SkPDFUtils.cpp; sourceTree = SOURCE_ROOT; };
+		260EE8BA13AFA7790064D447 /* SkFontHost_iOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkFontHost_iOS.mm; path = ../../src/utils/ios/SkFontHost_iOS.mm; sourceTree = SOURCE_ROOT; };
+		260EE8BB13AFA7790064D447 /* SkOSFile_iOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkOSFile_iOS.mm; path = ../../src/utils/ios/SkOSFile_iOS.mm; sourceTree = SOURCE_ROOT; };
+		260EE8BF13AFA7790064D447 /* SkStream_NSData.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkStream_NSData.mm; path = ../../src/utils/ios/SkStream_NSData.mm; sourceTree = SOURCE_ROOT; };
+		260EE9D013AFA7850064D447 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+		260EE9D113AFA7850064D447 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
+		260EF18413AFD62E0064D447 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
+		2612C104140D2FE3001B6925 /* SkEventNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkEventNotifier.h; path = ../../src/utils/mac/SkEventNotifier.h; sourceTree = SOURCE_ROOT; };
+		2612C105140D2FE3001B6925 /* SkEventNotifier.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkEventNotifier.mm; path = ../../src/utils/mac/SkEventNotifier.mm; sourceTree = SOURCE_ROOT; };
+		2612C109140D300B001B6925 /* GrDefaultPathRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrDefaultPathRenderer.cpp; sourceTree = "<group>"; };
+		2612C10A140D300B001B6925 /* GrDefaultPathRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrDefaultPathRenderer.h; sourceTree = "<group>"; };
+		2612C10B140D300B001B6925 /* GrGLStencilBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLStencilBuffer.cpp; sourceTree = "<group>"; };
+		2612C10C140D300B001B6925 /* GrGLStencilBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLStencilBuffer.h; sourceTree = "<group>"; };
+		2612C10D140D300B001B6925 /* GrPathRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPathRenderer.h; sourceTree = "<group>"; };
+		2612C10E140D300B001B6925 /* GrPathRendererChain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrPathRendererChain.cpp; sourceTree = "<group>"; };
+		2612C10F140D300B001B6925 /* GrPathRendererChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrPathRendererChain.h; sourceTree = "<group>"; };
+		2612C121140D30B0001B6925 /* GrAddPathRenderers_none.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrAddPathRenderers_none.cpp; sourceTree = "<group>"; };
+		263BE75713CCC7BF00CCE991 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
+		26591EB813EB16EB000DA8A8 /* TransitionView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TransitionView.cpp; sourceTree = "<group>"; };
+		265C7DE113D75752008329F6 /* SkOptionListController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkOptionListController.h; path = Shared/SkOptionListController.h; sourceTree = "<group>"; };
+		265C7DE213D75752008329F6 /* SkOptionListController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkOptionListController.mm; path = Shared/SkOptionListController.mm; sourceTree = "<group>"; };
+		2662AB6F13BD067900CDE7E9 /* SkiOSSampleApp-Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "SkiOSSampleApp-Debug.xcconfig"; sourceTree = "<group>"; };
+		2662AB7513BD0C0D00CDE7E9 /* SkiOSSampleApp-Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "SkiOSSampleApp-Release.xcconfig"; sourceTree = "<group>"; };
+		2662AB7713BD0C1E00CDE7E9 /* SkiOSSampleApp-Base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = "SkiOSSampleApp-Base.xcconfig"; sourceTree = "<group>"; };
+		2663AC9213D5D8D400C20488 /* SkOptionsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkOptionsTableViewController.h; path = Shared/SkOptionsTableViewController.h; sourceTree = "<group>"; };
+		2663AC9313D5D8D400C20488 /* SkOptionsTableViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkOptionsTableViewController.mm; path = Shared/SkOptionsTableViewController.mm; sourceTree = "<group>"; };
+		26677D6413B4C53E009319B8 /* SkData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkData.h; path = core/SkData.h; sourceTree = "<group>"; };
+		26677D6513B4C548009319B8 /* SkData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkData.cpp; path = core/SkData.cpp; sourceTree = "<group>"; };
+		26811E7813DEFAE8001A1609 /* SkBitSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkBitSet.cpp; path = ../../src/pdf/SkBitSet.cpp; sourceTree = SOURCE_ROOT; };
+		26811E7A13DEFAF7001A1609 /* SkBitSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkBitSet.h; path = ../../include/pdf/SkBitSet.h; sourceTree = SOURCE_ROOT; };
+		268C50D213F022820003FF9A /* SkColorPalette.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkColorPalette.cpp; path = ../DrawingBoard/SkColorPalette.cpp; sourceTree = SOURCE_ROOT; };
+		268C50D313F022820003FF9A /* SkColorPalette.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkColorPalette.h; path = ../DrawingBoard/SkColorPalette.h; sourceTree = SOURCE_ROOT; };
+		268C50D413F022820003FF9A /* SkNetPipeController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkNetPipeController.cpp; path = ../DrawingBoard/SkNetPipeController.cpp; sourceTree = SOURCE_ROOT; };
+		268C50D513F022820003FF9A /* SkNetPipeController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkNetPipeController.h; path = ../DrawingBoard/SkNetPipeController.h; sourceTree = SOURCE_ROOT; };
+		268C50D813F022AF0003FF9A /* SampleDrawingClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleDrawingClient.cpp; path = ../DrawingBoard/SampleDrawingClient.cpp; sourceTree = SOURCE_ROOT; };
+		268C50D913F022AF0003FF9A /* SampleDrawingServer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleDrawingServer.cpp; path = ../DrawingBoard/SampleDrawingServer.cpp; sourceTree = SOURCE_ROOT; };
+		268C50DC13F0230C0003FF9A /* SampleNetPipeReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleNetPipeReader.cpp; path = ../Networking/SampleNetPipeReader.cpp; sourceTree = SOURCE_ROOT; };
+		268C50DD13F0230C0003FF9A /* SkSockets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SkSockets.cpp; path = ../Networking/SkSockets.cpp; sourceTree = SOURCE_ROOT; };
+		268C50DE13F0230C0003FF9A /* SkSockets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkSockets.h; path = ../Networking/SkSockets.h; sourceTree = SOURCE_ROOT; };
+		26962C7813CE256E0039B1FB /* SkUIDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUIDetailViewController.h; path = Shared/SkUIDetailViewController.h; sourceTree = "<group>"; };
+		26962C7913CE256E0039B1FB /* SkUIDetailViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkUIDetailViewController.mm; path = Shared/SkUIDetailViewController.mm; sourceTree = "<group>"; };
+		26962C7A13CE256E0039B1FB /* SkUIRootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUIRootViewController.h; path = Shared/SkUIRootViewController.h; sourceTree = "<group>"; };
+		26962C7B13CE256E0039B1FB /* SkUIRootViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkUIRootViewController.mm; path = Shared/SkUIRootViewController.mm; sourceTree = "<group>"; };
+		26962C8E13CE25D60039B1FB /* iOSSampleApp_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iOSSampleApp_Prefix.pch; sourceTree = "<group>"; };
+		26962C8F13CE25D60039B1FB /* iOSSampleApp-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "iOSSampleApp-Info.plist"; sourceTree = "<group>"; };
+		26962CA313CE265C0039B1FB /* SkOSWindow_iOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkOSWindow_iOS.mm; path = ../../src/utils/ios/SkOSWindow_iOS.mm; sourceTree = SOURCE_ROOT; };
+		26962CA513CE26730039B1FB /* SkOSWindow_iOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkOSWindow_iOS.h; path = ../../include/views/SkOSWindow_iOS.h; sourceTree = SOURCE_ROOT; };
+		26962CA913CE268A0039B1FB /* SampleApp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SampleApp.cpp; path = ../../samplecode/SampleApp.cpp; sourceTree = SOURCE_ROOT; };
+		26962CAA13CE268A0039B1FB /* SampleApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SampleApp.h; path = ../../samplecode/SampleApp.h; sourceTree = SOURCE_ROOT; };
+		26962D4E13CE2D780039B1FB /* GrGLDefaultInterface_iOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GrGLDefaultInterface_iOS.cpp; path = ../../gpu/src/ios/GrGLDefaultInterface_iOS.cpp; sourceTree = SOURCE_ROOT; };
+		26A8AFF113E05D7000A3C111 /* GrResourceCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrResourceCache.cpp; sourceTree = "<group>"; };
+		26A8AFF213E05D7000A3C111 /* GrResourceCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrResourceCache.h; sourceTree = "<group>"; };
+		26D91221140D576B0036A311 /* SkSampleUIView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkSampleUIView.h; sourceTree = "<group>"; };
+		26D91222140D576B0036A311 /* SkSampleUIView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SkSampleUIView.mm; sourceTree = "<group>"; };
+		26D91229140D57BD0036A311 /* skia_ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = skia_ios.mm; path = Shared/skia_ios.mm; sourceTree = "<group>"; };
+		26E0E5C913B5299E00866555 /* SkGPipe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkGPipe.h; path = ../../include/pipe/SkGPipe.h; sourceTree = SOURCE_ROOT; };
+		26F548E013B91980007CC564 /* SkBitmapProcState_opts_arm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBitmapProcState_opts_arm.cpp; sourceTree = "<group>"; };
+		26F548E413B91980007CC564 /* SkBlitRow_opts_none.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkBlitRow_opts_none.cpp; sourceTree = "<group>"; };
+		26F548E513B91980007CC564 /* SkBlitRow_opts_SSE2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkBlitRow_opts_SSE2.h; sourceTree = "<group>"; };
+		26F548E613B91980007CC564 /* SkUtils_opts_none.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SkUtils_opts_none.cpp; sourceTree = "<group>"; };
+		26F548E713B91980007CC564 /* SkUtils_opts_SSE2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkUtils_opts_SSE2.h; sourceTree = "<group>"; };
+		26F5490B13B91B73007CC564 /* SkUserConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUserConfig.h; path = ../../include/config/SkUserConfig.h; sourceTree = SOURCE_ROOT; };
+		26F5491013B91BDB007CC564 /* SkEdgeClipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkEdgeClipper.h; path = core/SkEdgeClipper.h; sourceTree = "<group>"; };
+		26F5491113B91BDB007CC564 /* SkLineClipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkLineClipper.h; path = core/SkLineClipper.h; sourceTree = "<group>"; };
+		26F5491213B91BDB007CC564 /* SkPostConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPostConfig.h; path = core/SkPostConfig.h; sourceTree = "<group>"; };
+		26F5491313B91BDB007CC564 /* SkPreConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkPreConfig.h; path = core/SkPreConfig.h; sourceTree = "<group>"; };
+		26F5491413B91BDB007CC564 /* SkRelay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkRelay.h; path = core/SkRelay.h; sourceTree = "<group>"; };
+		26F5491513B91BDB007CC564 /* SkShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkShape.h; path = core/SkShape.h; sourceTree = "<group>"; };
+		26F5491613B91BDB007CC564 /* SkSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkSize.h; path = core/SkSize.h; sourceTree = "<group>"; };
+		26F5491713B91BDB007CC564 /* SkTLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTLazy.h; path = core/SkTLazy.h; sourceTree = "<group>"; };
+		26F5491A13B91C22007CC564 /* SkFlipPixelRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkFlipPixelRef.h; sourceTree = "<group>"; };
+		26F5491B13B91C22007CC564 /* SkImageRef_GlobalPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageRef_GlobalPool.h; sourceTree = "<group>"; };
+		26F5491C13B91C22007CC564 /* SkImageRef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkImageRef.h; sourceTree = "<group>"; };
+		26F5491D13B91C22007CC564 /* SkJpegUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkJpegUtility.h; sourceTree = "<group>"; };
+		26F5491E13B91C22007CC564 /* SkMovie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkMovie.h; sourceTree = "<group>"; };
+		26F5491F13B91C22007CC564 /* SkPageFlipper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkPageFlipper.h; sourceTree = "<group>"; };
+		26F5492213B91C51007CC564 /* FlingState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FlingState.h; sourceTree = "<group>"; };
+		26F5492313B91C51007CC564 /* GrGLDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLDefines.h; sourceTree = "<group>"; };
+		26F5492413B91C51007CC564 /* GrTemplates.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrTemplates.h; sourceTree = "<group>"; };
+		26F5492713B91CA1007CC564 /* SkTypeface_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkTypeface_mac.h; path = include/ports/SkTypeface_mac.h; sourceTree = "<group>"; };
+		26FB12AD13E70D3B001AFF6D /* GrGLRenderTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrGLRenderTarget.cpp; sourceTree = "<group>"; };
+		26FB12AE13E70D3B001AFF6D /* GrGLRenderTarget.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLRenderTarget.h; sourceTree = "<group>"; };
+		26FB12AF13E70D3B001AFF6D /* GrGLTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrGLTexture.h; sourceTree = "<group>"; };
+		26FB12B313E70D51001AFF6D /* GrRenderTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GrRenderTarget.cpp; sourceTree = "<group>"; };
+		26FB98D113D0C87000ACBEA0 /* SkUIView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SkUIView.h; path = Shared/SkUIView.h; sourceTree = "<group>"; };
+		26FB98D213D0C87000ACBEA0 /* SkUIView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SkUIView.mm; path = Shared/SkUIView.mm; sourceTree = "<group>"; };
+		2860E325111B887F00E27156 /* AppDelegate_iPhone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate_iPhone.h; sourceTree = "<group>"; };
+		2860E326111B887F00E27156 /* AppDelegate_iPhone.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate_iPhone.mm; sourceTree = "<group>"; };
+		2860E327111B887F00E27156 /* MainWindow_iPhone.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow_iPhone.xib; sourceTree = "<group>"; };
+		2860E32B111B888700E27156 /* AppDelegate_iPad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate_iPad.h; sourceTree = "<group>"; };
+		2860E32C111B888700E27156 /* AppDelegate_iPad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate_iPad.mm; sourceTree = "<group>"; };
+		2860E32D111B888700E27156 /* MainWindow_iPad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow_iPad.xib; sourceTree = "<group>"; };
+		288765FC0DF74451002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		1D60588F0D05DD3D006BFB54 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */,
+				288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */,
+				260EE9D513AFA7850064D447 /* CoreFoundation.framework in Frameworks */,
+				260EF18513AFD62E0064D447 /* CoreText.framework in Frameworks */,
+				260EF2B013AFDBD30064D447 /* Foundation.framework in Frameworks */,
+				26E0E40A13B4E67800866555 /* OpenGLES.framework in Frameworks */,
+				263BE75813CCC7BF00CCE991 /* QuartzCore.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		19C28FACFE9D520D11CA2CBB /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				1D6058910D05DD3D006BFB54 /* iOSSampleApp.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		2605F43B13F18B1B0044A072 /* Debugger */ = {
+			isa = PBXGroup;
+			children = (
+				2605F43C13F18B1B0044A072 /* DebuggerContentView.cpp */,
+				2605F43D13F18B1B0044A072 /* DebuggerViews.h */,
+				2605F43E13F18B1B0044A072 /* DebuggerCommandsView.cpp */,
+				2605F43F13F18B1B0044A072 /* SkDebugDumper.cpp */,
+				2605F44013F18B1B0044A072 /* SkDebugDumper.h */,
+				2605F44113F18B1B0044A072 /* DebuggerStateView.cpp */,
+			);
+			name = Debugger;
+			path = ../Debugger;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E001413B11F5B0064D447 /* gm */ = {
+			isa = PBXGroup;
+			children = (
+				260E001513B11F5B0064D447 /* bitmapfilters.cpp */,
+				260E001613B11F5B0064D447 /* blurs.cpp */,
+				260E001713B11F5B0064D447 /* complexclip.cpp */,
+				260E001813B11F5B0064D447 /* filltypes.cpp */,
+				260E001913B11F5B0064D447 /* gm.h */,
+				260E001A13B11F5B0064D447 /* gradients.cpp */,
+				260E001B13B11F5B0064D447 /* nocolorbleed.cpp */,
+				260E001C13B11F5B0064D447 /* points.cpp */,
+				260E001D13B11F5B0064D447 /* poly2poly.cpp */,
+				260E001E13B11F5B0064D447 /* shadertext.cpp */,
+				260E001F13B11F5B0064D447 /* shadows.cpp */,
+				260E002013B11F5B0064D447 /* shapes.cpp */,
+				260E002113B11F5B0064D447 /* tilemodes.cpp */,
+				260E002213B11F5B0064D447 /* xfermodes.cpp */,
+			);
+			name = gm;
+			path = ../../gm;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E002313B11F5B0064D447 /* samplecode */ = {
+			isa = PBXGroup;
+			children = (
+				26591EB813EB16EB000DA8A8 /* TransitionView.cpp */,
+				260E002413B11F5B0064D447 /* ClockFaceView.cpp */,
+				260E002513B11F5B0064D447 /* OverView.cpp */,
+				260E002613B11F5B0064D447 /* SampleAARects.cpp */,
+				260E002713B11F5B0064D447 /* SampleAll.cpp */,
+				260E002813B11F5B0064D447 /* SampleAnimator.cpp */,
+				260E002A13B11F5B0064D447 /* SampleArc.cpp */,
+				260E002B13B11F5B0064D447 /* SampleAvoid.cpp */,
+				260E002C13B11F5B0064D447 /* SampleBigGradient.cpp */,
+				260E002D13B11F5B0064D447 /* SampleBitmapRect.cpp */,
+				260E002E13B11F5B0064D447 /* SampleBlur.cpp */,
+				260E002F13B11F5B0064D447 /* SampleCamera.cpp */,
+				260E003013B11F5B0064D447 /* SampleCircle.cpp */,
+				260E003113B11F5B0064D447 /* SampleCode.h */,
+				260E003213B11F5B0064D447 /* SampleColorFilter.cpp */,
+				260E003313B11F5B0064D447 /* SampleComplexClip.cpp */,
+				260E003413B11F5B0064D447 /* SampleConcavePaths.cpp */,
+				260E003513B11F5B0064D447 /* SampleCull.cpp */,
+				260E003613B11F5B0064D447 /* SampleDecode.cpp */,
+				260E003713B11F5B0064D447 /* SampleDither.cpp */,
+				260E003813B11F5B0064D447 /* SampleDitherBitmap.cpp */,
+				260E003913B11F5B0064D447 /* SampleDrawLooper.cpp */,
+				260E003A13B11F5B0064D447 /* SampleEffects.cpp */,
+				260E003B13B11F5B0064D447 /* SampleEmboss.cpp */,
+				260E003C13B11F5B0064D447 /* SampleEncode.cpp */,
+				260E003D13B11F5B0064D447 /* SampleExtractAlpha.cpp */,
+				260E003E13B11F5B0064D447 /* SampleFillType.cpp */,
+				260E003F13B11F5B0064D447 /* SampleFilter.cpp */,
+				260E004013B11F5B0064D447 /* SampleFilter2.cpp */,
+				260E004113B11F5B0064D447 /* SampleFontCache.cpp */,
+				260E004213B11F5B0064D447 /* SampleFontScalerTest.cpp */,
+				260E004313B11F5B0064D447 /* SampleFuzz.cpp */,
+				260E004413B11F5B0064D447 /* SampleGM.cpp */,
+				260E004513B11F5B0064D447 /* SampleGradients.cpp */,
+				260E004613B11F5B0064D447 /* SampleHairline.cpp */,
+				260E004713B11F5B0064D447 /* SampleImage.cpp */,
+				260E004813B11F5B0064D447 /* SampleImageDir.cpp */,
+				260E004913B11F5B0064D447 /* SampleLCD.cpp */,
+				260E004A13B11F5B0064D447 /* SampleLayerMask.cpp */,
+				260E004B13B11F5B0064D447 /* SampleLayers.cpp */,
+				260E004C13B11F5B0064D447 /* SampleLineClipper.cpp */,
+				260E004D13B11F5B0064D447 /* SampleLines.cpp */,
+				260E004E13B11F5B0064D447 /* SampleMeasure.cpp */,
+				260E004F13B11F5B0064D447 /* SampleMipMap.cpp */,
+				260E005013B11F5B0064D447 /* SampleMovie.cpp */,
+				260E005113B11F5B0064D447 /* SampleNinePatch.cpp */,
+				260E005213B11F5B0064D447 /* SampleOvalTest.cpp */,
+				260E005313B11F5B0064D447 /* SampleOverflow.cpp */,
+				260E005413B11F5B0064D447 /* SamplePageFlip.cpp */,
+				260E005513B11F5B0064D447 /* SamplePatch.cpp */,
+				260E005613B11F5B0064D447 /* SamplePath.cpp */,
+				260E005713B11F5B0064D447 /* SamplePathClip.cpp */,
+				260E005813B11F5B0064D447 /* SamplePathEffects.cpp */,
+				260E005913B11F5B0064D447 /* SamplePicture.cpp */,
+				260E005A13B11F5B0064D447 /* SamplePoints.cpp */,
+				260E005B13B11F5B0064D447 /* SamplePolyToPoly.cpp */,
+				260E005C13B11F5B0064D447 /* SampleRegion.cpp */,
+				260E005D13B11F5B0064D447 /* SampleRepeatTile.cpp */,
+				260E005E13B11F5B0064D447 /* SampleShaderText.cpp */,
+				260E005F13B11F5B0064D447 /* SampleShaders.cpp */,
+				260E006013B11F5B0064D447 /* SampleShapes.cpp */,
+				260E006113B11F5B0064D447 /* SampleSkLayer.cpp */,
+				260E006213B11F5B0064D447 /* SampleSlides.cpp */,
+				260E006313B11F5B0064D447 /* SampleStrokePath.cpp */,
+				260E006413B11F5B0064D447 /* SampleStrokeText.cpp */,
+				260E006613B11F5B0064D447 /* SampleText.cpp */,
+				260E006713B11F5B0064D447 /* SampleTextAlpha.cpp */,
+				260E006813B11F5B0064D447 /* SampleTextBox.cpp */,
+				260E006913B11F5B0064D447 /* SampleTextEffects.cpp */,
+				260E006A13B11F5B0064D447 /* SampleTextOnPath.cpp */,
+				260E006B13B11F5B0064D447 /* SampleTextureDomain.cpp */,
+				260E006C13B11F5B0064D447 /* SampleTiling.cpp */,
+				260E006D13B11F5B0064D447 /* SampleTinyBitmap.cpp */,
+				260E006E13B11F5B0064D447 /* SampleTriangles.cpp */,
+				260E006F13B11F5B0064D447 /* SampleTypeface.cpp */,
+				260E007013B11F5B0064D447 /* SampleUnitMapper.cpp */,
+				260E007113B11F5B0064D447 /* SampleVertices.cpp */,
+				260E007213B11F5B0064D447 /* SampleXfermodes.cpp */,
+				260E007313B11F5B0064D447 /* SampleXfermodesBlur.cpp */,
+			);
+			name = samplecode;
+			path = ../../samplecode;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E007413B11F5B0064D447 /* pipe */ = {
+			isa = PBXGroup;
+			children = (
+				26E0E5C913B5299E00866555 /* SkGPipe.h */,
+				260E007513B11F5B0064D447 /* SkGPipeRead.cpp */,
+				260E007613B11F5B0064D447 /* SkGPipeWrite.cpp */,
+			);
+			name = pipe;
+			path = ../../src/pipe;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E013413B11F7A0064D447 /* SampleApp */ = {
+			isa = PBXGroup;
+			children = (
+				26962CAA13CE268A0039B1FB /* SampleApp.h */,
+				26962CA913CE268A0039B1FB /* SampleApp.cpp */,
+				260E001413B11F5B0064D447 /* gm */,
+				260E002313B11F5B0064D447 /* samplecode */,
+			);
+			name = SampleApp;
+			sourceTree = "<group>";
+		};
+		260E01AF13B1225D0064D447 /* core */ = {
+			isa = PBXGroup;
+			children = (
+				26F5492713B91CA1007CC564 /* SkTypeface_mac.h */,
+				260E01B013B1225D0064D447 /* include */,
+				260E020913B1225D0064D447 /* src */,
+			);
+			name = core;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E01B013B1225D0064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				260E01B213B1225D0064D447 /* Sk64.h */,
+				260E01B313B1225D0064D447 /* SkAdvancedTypefaceMetrics.h */,
+				260E01B413B1225D0064D447 /* SkAutoKern.h */,
+				260E01B513B1225D0064D447 /* SkBitmap.h */,
+				260E01B613B1225D0064D447 /* SkBlitRow.h */,
+				260E01B713B1225D0064D447 /* SkBlitter.h */,
+				260E01B813B1225D0064D447 /* SkBounder.h */,
+				260E01B913B1225D0064D447 /* SkBuffer.h */,
+				260E01BA13B1225D0064D447 /* SkCanvas.h */,
+				260E01BB13B1225D0064D447 /* SkChunkAlloc.h */,
+				260E01BC13B1225D0064D447 /* SkClampRange.h */,
+				260E01BD13B1225D0064D447 /* SkClipStack.h */,
+				260E01BE13B1225D0064D447 /* SkColor.h */,
+				260E01BF13B1225D0064D447 /* SkColorFilter.h */,
+				260E01C013B1225D0064D447 /* SkColorPriv.h */,
+				260E01C113B1225D0064D447 /* SkColorShader.h */,
+				260E01C213B1225D0064D447 /* SkComposeShader.h */,
+				260E01C313B1225D0064D447 /* SkDeque.h */,
+				260E01C413B1225D0064D447 /* SkDescriptor.h */,
+				260E01C513B1225D0064D447 /* SkDevice.h */,
+				260E01C613B1225D0064D447 /* SkDither.h */,
+				260E01C713B1225D0064D447 /* SkDraw.h */,
+				260E01C813B1225D0064D447 /* SkDrawFilter.h */,
+				260E01C913B1225D0064D447 /* SkDrawLooper.h */,
+				260E01CA13B1225D0064D447 /* SkEndian.h */,
+				260E01CB13B1225D0064D447 /* SkFDot6.h */,
+				260E01CC13B1225D0064D447 /* SkFixed.h */,
+				260E01CD13B1225D0064D447 /* SkFlate.h */,
+				260E01CE13B1225D0064D447 /* SkFlattenable.h */,
+				260E01CF13B1225D0064D447 /* SkFloatBits.h */,
+				260E01D013B1225D0064D447 /* SkFloatingPoint.h */,
+				260E01D113B1225D0064D447 /* SkFontHost.h */,
+				260E01D213B1225D0064D447 /* SkGeometry.h */,
+				260E01D313B1225D0064D447 /* SkGlobals.h */,
+				260E01D413B1225D0064D447 /* SkGraphics.h */,
+				260E01D513B1225D0064D447 /* SkMMapStream.h */,
+				260E01D613B1225D0064D447 /* SkMallocPixelRef.h */,
+				260E01D713B1225D0064D447 /* SkMask.h */,
+				260E01D813B1225D0064D447 /* SkMaskFilter.h */,
+				260E01D913B1225D0064D447 /* SkMath.h */,
+				260E01DA13B1225D0064D447 /* SkMatrix.h */,
+				260E01DB13B1225D0064D447 /* SkMetaData.h */,
+				260E01DC13B1225D0064D447 /* SkOSFile.h */,
+				260E01DD13B1225D0064D447 /* SkPackBits.h */,
+				260E01DE13B1225D0064D447 /* SkPaint.h */,
+				260E01DF13B1225D0064D447 /* SkPath.h */,
+				260E01E013B1225D0064D447 /* SkPathEffect.h */,
+				260E01E113B1225D0064D447 /* SkPathMeasure.h */,
+				260E01E213B1225D0064D447 /* SkPerspIter.h */,
+				260E01E313B1225D0064D447 /* SkPicture.h */,
+				260E01E413B1225D0064D447 /* SkPixelRef.h */,
+				260E01E513B1225D0064D447 /* SkPoint.h */,
+				260E01E613B1225D0064D447 /* SkPtrRecorder.h */,
+				260E01E713B1225D0064D447 /* SkRandom.h */,
+				260E01E813B1225D0064D447 /* SkRasterizer.h */,
+				260E01E913B1225D0064D447 /* SkReader32.h */,
+				260E01EA13B1225D0064D447 /* SkRect.h */,
+				260E01EB13B1225D0064D447 /* SkRefCnt.h */,
+				260E01EC13B1225D0064D447 /* SkRefDict.h */,
+				260E01ED13B1225D0064D447 /* SkRegion.h */,
+				260E01EE13B1225D0064D447 /* SkScalar.h */,
+				260E01EF13B1225D0064D447 /* SkScalarCompare.h */,
+				260E01F013B1225D0064D447 /* SkScalerContext.h */,
+				260E01F113B1225D0064D447 /* SkScan.h */,
+				260E01F213B1225D0064D447 /* SkShader.h */,
+				260E01F313B1225D0064D447 /* SkStream.h */,
+				260E01F413B1225D0064D447 /* SkString.h */,
+				260E01F513B1225D0064D447 /* SkStroke.h */,
+				260E01F613B1225D0064D447 /* SkTDArray.h */,
+				260E01F713B1225D0064D447 /* SkTDStack.h */,
+				260E01F813B1225D0064D447 /* SkTDict.h */,
+				260E01F913B1225D0064D447 /* SkTRegistry.h */,
+				260E01FA13B1225D0064D447 /* SkTScopedPtr.h */,
+				260E01FB13B1225D0064D447 /* SkTSearch.h */,
+				260E01FC13B1225D0064D447 /* SkTemplates.h */,
+				260E01FD13B1225D0064D447 /* SkThread.h */,
+				260E01FE13B1225D0064D447 /* SkThread_platform.h */,
+				260E01FF13B1225D0064D447 /* SkTime.h */,
+				260E020013B1225D0064D447 /* SkTypeface.h */,
+				260E020113B1225D0064D447 /* SkTypes.h */,
+				260E020213B1225D0064D447 /* SkUnPreMultiply.h */,
+				260E020313B1225D0064D447 /* SkUnitMapper.h */,
+				260E020413B1225D0064D447 /* SkUtils.h */,
+				260E020513B1225D0064D447 /* SkWriter32.h */,
+				260E020613B1225D0064D447 /* SkXfermode.h */,
+				26F5491013B91BDB007CC564 /* SkEdgeClipper.h */,
+				26F5491113B91BDB007CC564 /* SkLineClipper.h */,
+				26F5491213B91BDB007CC564 /* SkPostConfig.h */,
+				26F5491313B91BDB007CC564 /* SkPreConfig.h */,
+				26F5491413B91BDB007CC564 /* SkRelay.h */,
+				26F5491513B91BDB007CC564 /* SkShape.h */,
+				26F5491613B91BDB007CC564 /* SkSize.h */,
+				26F5491713B91BDB007CC564 /* SkTLazy.h */,
+				26677D6413B4C53E009319B8 /* SkData.h */,
+				260E020713B1225D0064D447 /* mac */,
+			);
+			path = include;
+			sourceTree = "<group>";
+		};
+		260E020713B1225D0064D447 /* mac */ = {
+			isa = PBXGroup;
+			children = (
+				260E020813B1225D0064D447 /* SkCGUtils.h */,
+			);
+			name = mac;
+			path = utils/mac;
+			sourceTree = "<group>";
+		};
+		260E020913B1225D0064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26677D6513B4C548009319B8 /* SkData.cpp */,
+				260E020A13B1225D0064D447 /* core */,
+				260E029513B1225D0064D447 /* ports */,
+			);
+			path = src;
+			sourceTree = "<group>";
+		};
+		260E020A13B1225D0064D447 /* core */ = {
+			isa = PBXGroup;
+			children = (
+				260E020B13B1225D0064D447 /* ARGB32_Clamp_Bilinear_BitmapShader.h */,
+				260E020C13B1225D0064D447 /* Sk64.cpp */,
+				260E020D13B1225D0064D447 /* SkAdvancedTypefaceMetrics.cpp */,
+				260E020E13B1225D0064D447 /* SkAlphaRuns.cpp */,
+				260E020F13B1225D0064D447 /* SkAntiRun.h */,
+				260E021013B1225D0064D447 /* SkBitmap.cpp */,
+				260E021113B1225D0064D447 /* SkBitmapProcShader.cpp */,
+				260E021213B1225D0064D447 /* SkBitmapProcShader.h */,
+				260E021313B1225D0064D447 /* SkBitmapProcState.cpp */,
+				260E021413B1225D0064D447 /* SkBitmapProcState.h */,
+				260E021513B1225D0064D447 /* SkBitmapProcState_matrix.h */,
+				260E021613B1225D0064D447 /* SkBitmapProcState_matrixProcs.cpp */,
+				260E021713B1225D0064D447 /* SkBitmapProcState_sample.h */,
+				260E021813B1225D0064D447 /* SkBitmapSampler.cpp */,
+				260E021913B1225D0064D447 /* SkBitmapSampler.h */,
+				260E021A13B1225D0064D447 /* SkBitmapSamplerTemplate.h */,
+				260E021B13B1225D0064D447 /* SkBitmapShader16BilerpTemplate.h */,
+				260E021C13B1225D0064D447 /* SkBitmapShaderTemplate.h */,
+				260E021D13B1225D0064D447 /* SkBitmap_scroll.cpp */,
+				260E021E13B1225D0064D447 /* SkBlitBWMaskTemplate.h */,
+				260E021F13B1225D0064D447 /* SkBlitRow_D16.cpp */,
+				260E022013B1225D0064D447 /* SkBlitRow_D32.cpp */,
+				260E022113B1225D0064D447 /* SkBlitRow_D4444.cpp */,
+				260E022213B1225D0064D447 /* SkBlitter.cpp */,
+				260E022313B1225D0064D447 /* SkBlitter_4444.cpp */,
+				260E022413B1225D0064D447 /* SkBlitter_A1.cpp */,
+				260E022513B1225D0064D447 /* SkBlitter_A8.cpp */,
+				260E022613B1225D0064D447 /* SkBlitter_ARGB32.cpp */,
+				260E022713B1225D0064D447 /* SkBlitter_RGB16.cpp */,
+				260E022813B1225D0064D447 /* SkBlitter_Sprite.cpp */,
+				260E022913B1225D0064D447 /* SkBuffer.cpp */,
+				260E022A13B1225D0064D447 /* SkCanvas.cpp */,
+				260E022B13B1225D0064D447 /* SkChunkAlloc.cpp */,
+				260E022C13B1225D0064D447 /* SkClampRange.cpp */,
+				260E022D13B1225D0064D447 /* SkClipStack.cpp */,
+				260E022E13B1225D0064D447 /* SkColor.cpp */,
+				260E022F13B1225D0064D447 /* SkColorFilter.cpp */,
+				260E023013B1225D0064D447 /* SkColorTable.cpp */,
+				260E023113B1225D0064D447 /* SkComposeShader.cpp */,
+				260E023213B1225D0064D447 /* SkConcaveToTriangles.cpp */,
+				260E023313B1225D0064D447 /* SkConcaveToTriangles.h */,
+				260E023413B1225D0064D447 /* SkCordic.cpp */,
+				260E023513B1225D0064D447 /* SkCordic.h */,
+				260E023613B1225D0064D447 /* SkCoreBlitters.h */,
+				260E023713B1225D0064D447 /* SkCubicClipper.cpp */,
+				260E023813B1225D0064D447 /* SkCubicClipper.h */,
+				260E023A13B1225D0064D447 /* SkDebug.cpp */,
+				260E023B13B1225D0064D447 /* SkDeque.cpp */,
+				260E023C13B1225D0064D447 /* SkDevice.cpp */,
+				260E023D13B1225D0064D447 /* SkDither.cpp */,
+				260E023E13B1225D0064D447 /* SkDraw.cpp */,
+				260E023F13B1225D0064D447 /* SkDrawProcs.h */,
+				260E024013B1225D0064D447 /* SkEdge.cpp */,
+				260E024113B1225D0064D447 /* SkEdge.h */,
+				260E024213B1225D0064D447 /* SkEdgeBuilder.cpp */,
+				260E024313B1225D0064D447 /* SkEdgeClipper.cpp */,
+				260E024413B1225D0064D447 /* SkFP.h */,
+				260E024513B1225D0064D447 /* SkFilterProc.cpp */,
+				260E024613B1225D0064D447 /* SkFilterProc.h */,
+				260E024713B1225D0064D447 /* SkFlate.cpp */,
+				260E024813B1225D0064D447 /* SkFlattenable.cpp */,
+				260E024913B1225D0064D447 /* SkFloat.cpp */,
+				260E024A13B1225D0064D447 /* SkFloat.h */,
+				260E024B13B1225D0064D447 /* SkFloatBits.cpp */,
+				260E024C13B1225D0064D447 /* SkFontHost.cpp */,
+				260E024D13B1225D0064D447 /* SkGeometry.cpp */,
+				260E024E13B1225D0064D447 /* SkGlobals.cpp */,
+				260E024F13B1225D0064D447 /* SkGlyphCache.cpp */,
+				260E025013B1225D0064D447 /* SkGlyphCache.h */,
+				260E025113B1225D0064D447 /* SkGraphics.cpp */,
+				260E025213B1225D0064D447 /* SkLineClipper.cpp */,
+				260E025313B1225D0064D447 /* SkMMapStream.cpp */,
+				260E025413B1225D0064D447 /* SkMallocPixelRef.cpp */,
+				260E025513B1225D0064D447 /* SkMask.cpp */,
+				260E025613B1225D0064D447 /* SkMaskFilter.cpp */,
+				260E025713B1225D0064D447 /* SkMath.cpp */,
+				260E025813B1225D0064D447 /* SkMatrix.cpp */,
+				260E025913B1225D0064D447 /* SkMetaData.cpp */,
+				260E025A13B1225D0064D447 /* SkPackBits.cpp */,
+				260E025B13B1225D0064D447 /* SkPaint.cpp */,
+				260E025C13B1225D0064D447 /* SkPath.cpp */,
+				260E025D13B1225D0064D447 /* SkPathEffect.cpp */,
+				260E025E13B1225D0064D447 /* SkPathHeap.cpp */,
+				260E025F13B1225D0064D447 /* SkPathHeap.h */,
+				260E026013B1225D0064D447 /* SkPathMeasure.cpp */,
+				260E026113B1225D0064D447 /* SkPicture.cpp */,
+				260E026213B1225D0064D447 /* SkPictureFlat.cpp */,
+				260E026313B1225D0064D447 /* SkPictureFlat.h */,
+				260E026413B1225D0064D447 /* SkPicturePlayback.cpp */,
+				260E026513B1225D0064D447 /* SkPicturePlayback.h */,
+				260E026613B1225D0064D447 /* SkPictureRecord.cpp */,
+				260E026713B1225D0064D447 /* SkPictureRecord.h */,
+				260E026813B1225D0064D447 /* SkPixelRef.cpp */,
+				260E026913B1225D0064D447 /* SkPoint.cpp */,
+				260E026A13B1225D0064D447 /* SkProcSpriteBlitter.cpp */,
+				260E026B13B1225D0064D447 /* SkPtrRecorder.cpp */,
+				260E026C13B1225D0064D447 /* SkQuadClipper.cpp */,
+				260E026D13B1225D0064D447 /* SkQuadClipper.h */,
+				260E026E13B1225D0064D447 /* SkRasterizer.cpp */,
+				260E026F13B1225D0064D447 /* SkRect.cpp */,
+				260E027013B1225D0064D447 /* SkRefDict.cpp */,
+				260E027113B1225D0064D447 /* SkRegion.cpp */,
+				260E027213B1225D0064D447 /* SkRegionPriv.h */,
+				260E027313B1225D0064D447 /* SkRegion_path.cpp */,
+				260E027413B1225D0064D447 /* SkScalar.cpp */,
+				260E027513B1225D0064D447 /* SkScalerContext.cpp */,
+				260E027613B1225D0064D447 /* SkScan.cpp */,
+				260E027713B1225D0064D447 /* SkScanPriv.h */,
+				260E027813B1225D0064D447 /* SkScan_AntiPath.cpp */,
+				260E027913B1225D0064D447 /* SkScan_Antihair.cpp */,
+				260E027A13B1225D0064D447 /* SkScan_Hairline.cpp */,
+				260E027B13B1225D0064D447 /* SkScan_Path.cpp */,
+				260E027C13B1225D0064D447 /* SkShader.cpp */,
+				260E027D13B1225D0064D447 /* SkShape.cpp */,
+				260E027E13B1225D0064D447 /* SkSinTable.h */,
+				260E027F13B1225D0064D447 /* SkSpriteBlitter.h */,
+				260E028013B1225D0064D447 /* SkSpriteBlitterTemplate.h */,
+				260E028113B1225D0064D447 /* SkSpriteBlitter_ARGB32.cpp */,
+				260E028213B1225D0064D447 /* SkSpriteBlitter_RGB16.cpp */,
+				260E028313B1225D0064D447 /* SkStream.cpp */,
+				260E028413B1225D0064D447 /* SkString.cpp */,
+				260E028513B1225D0064D447 /* SkStroke.cpp */,
+				260E028613B1225D0064D447 /* SkStrokerPriv.cpp */,
+				260E028713B1225D0064D447 /* SkStrokerPriv.h */,
+				260E028813B1225D0064D447 /* SkTSearch.cpp */,
+				260E028913B1225D0064D447 /* SkTSort.h */,
+				260E028A13B1225D0064D447 /* SkTemplatesPriv.h */,
+				260E028B13B1225D0064D447 /* SkTextFormatParams.h */,
+				260E028C13B1225D0064D447 /* SkTypeface.cpp */,
+				260E028D13B1225D0064D447 /* SkTypefaceCache.cpp */,
+				260E028E13B1225D0064D447 /* SkTypefaceCache.h */,
+				260E028F13B1225D0064D447 /* SkUnPreMultiply.cpp */,
+				260E029013B1225D0064D447 /* SkUtils.cpp */,
+				260E029113B1225D0064D447 /* SkWriter32.cpp */,
+				260E029213B1225D0064D447 /* SkXfermode.cpp */,
+			);
+			path = core;
+			sourceTree = "<group>";
+		};
+		260E029513B1225D0064D447 /* ports */ = {
+			isa = PBXGroup;
+			children = (
+				260E029613B1225D0064D447 /* SkDebug_stdio.cpp */,
+				260E029813B1225D0064D447 /* SkFontHost_mac_coretext.cpp */,
+				260E029A13B1225D0064D447 /* SkGlobals_global.cpp */,
+				260E029B13B1225D0064D447 /* SkMemory_malloc.cpp */,
+				260E029D13B1225D0064D447 /* SkThread_pthread.cpp */,
+				260E029E13B1225D0064D447 /* SkTime_Unix.cpp */,
+				260E02A013B1225D0064D447 /* SkXMLParser_empty.cpp */,
+				260E02A113B1225D0064D447 /* sk_predefined_gamma.h */,
+			);
+			path = ports;
+			sourceTree = "<group>";
+		};
+		260E031E13B122A30064D447 /* effects */ = {
+			isa = PBXGroup;
+			children = (
+				260E031F13B122A30064D447 /* include */,
+				260E033613B122A30064D447 /* src */,
+			);
+			name = effects;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E031F13B122A30064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				260E032013B122A30064D447 /* Sk1DPathEffect.h */,
+				260E032113B122A30064D447 /* Sk2DPathEffect.h */,
+				260E032213B122A30064D447 /* SkAvoidXfermode.h */,
+				260E032313B122A30064D447 /* SkBlurDrawLooper.h */,
+				260E032413B122A30064D447 /* SkBlurMaskFilter.h */,
+				260E032513B122A30064D447 /* SkColorMatrix.h */,
+				260E032613B122A30064D447 /* SkColorMatrixFilter.h */,
+				260E032713B122A30064D447 /* SkCornerPathEffect.h */,
+				260E032813B122A30064D447 /* SkDashPathEffect.h */,
+				260E032913B122A30064D447 /* SkDiscretePathEffect.h */,
+				260E032A13B122A30064D447 /* SkDrawExtraPathEffect.h */,
+				260E032B13B122A30064D447 /* SkEmbossMaskFilter.h */,
+				260E032C13B122A30064D447 /* SkGradientShader.h */,
+				260E032D13B122A30064D447 /* SkGroupShape.h */,
+				260E032E13B122A30064D447 /* SkKernel33MaskFilter.h */,
+				260E032F13B122A30064D447 /* SkLayerDrawLooper.h */,
+				260E033013B122A30064D447 /* SkLayerRasterizer.h */,
+				260E033113B122A30064D447 /* SkPaintFlagsDrawFilter.h */,
+				260E033213B122A30064D447 /* SkPixelXorXfermode.h */,
+				260E033313B122A30064D447 /* SkPorterDuff.h */,
+				260E033413B122A30064D447 /* SkRectShape.h */,
+				260E033513B122A30064D447 /* SkTransparentShader.h */,
+			);
+			name = include;
+			path = include/effects;
+			sourceTree = "<group>";
+		};
+		260E033613B122A30064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				260E033713B122A30064D447 /* Sk1DPathEffect.cpp */,
+				260E033813B122A30064D447 /* Sk2DPathEffect.cpp */,
+				260E033913B122A30064D447 /* SkAvoidXfermode.cpp */,
+				260E033A13B122A30064D447 /* SkBitmapCache.cpp */,
+				260E033B13B122A30064D447 /* SkBitmapCache.h */,
+				260E033C13B122A30064D447 /* SkBlurDrawLooper.cpp */,
+				260E033D13B122A30064D447 /* SkBlurMask.cpp */,
+				260E033E13B122A30064D447 /* SkBlurMask.h */,
+				260E033F13B122A30064D447 /* SkBlurMaskFilter.cpp */,
+				260E034013B122A30064D447 /* SkColorFilters.cpp */,
+				260E034113B122A30064D447 /* SkColorMatrixFilter.cpp */,
+				260E034213B122A30064D447 /* SkCornerPathEffect.cpp */,
+				260E034313B122A30064D447 /* SkDashPathEffect.cpp */,
+				260E034413B122A30064D447 /* SkDiscretePathEffect.cpp */,
+				260E034513B122A30064D447 /* SkEmbossMask.cpp */,
+				260E034613B122A30064D447 /* SkEmbossMask.h */,
+				260E034713B122A30064D447 /* SkEmbossMaskFilter.cpp */,
+				260E034813B122A30064D447 /* SkEmbossMask_Table.h */,
+				260E034913B122A30064D447 /* SkGradientShader.cpp */,
+				260E034A13B122A30064D447 /* SkGroupShape.cpp */,
+				260E034B13B122A30064D447 /* SkKernel33MaskFilter.cpp */,
+				260E034C13B122A30064D447 /* SkLayerDrawLooper.cpp */,
+				260E034D13B122A30064D447 /* SkLayerRasterizer.cpp */,
+				260E034E13B122A30064D447 /* SkPaintFlagsDrawFilter.cpp */,
+				260E034F13B122A30064D447 /* SkPixelXorXfermode.cpp */,
+				260E035013B122A30064D447 /* SkPorterDuff.cpp */,
+				260E035113B122A30064D447 /* SkRadialGradient_Table.h */,
+				260E035213B122A30064D447 /* SkRectShape.cpp */,
+				260E035313B122A30064D447 /* SkTransparentShader.cpp */,
+			);
+			name = src;
+			path = src/effects;
+			sourceTree = "<group>";
+		};
+		260E038913B122D40064D447 /* gpu */ = {
+			isa = PBXGroup;
+			children = (
+				260E038A13B122D40064D447 /* gpu */,
+				260E03FB13B122D40064D447 /* include */,
+				260E040113B122D40064D447 /* src */,
+			);
+			name = gpu;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E038A13B122D40064D447 /* gpu */ = {
+			isa = PBXGroup;
+			children = (
+				260E038B13B122D40064D447 /* include */,
+				260E03C813B122D40064D447 /* src */,
+			);
+			path = gpu;
+			sourceTree = "<group>";
+		};
+		260E038B13B122D40064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26F5492213B91C51007CC564 /* FlingState.h */,
+				26F5492313B91C51007CC564 /* GrGLDefines.h */,
+				26F5492413B91C51007CC564 /* GrTemplates.h */,
+				260E038C13B122D40064D447 /* GrAllocPool.h */,
+				260E038D13B122D40064D447 /* GrAllocator.h */,
+				260E038E13B122D40064D447 /* GrAtlas.h */,
+				260E038F13B122D40064D447 /* GrClip.h */,
+				260E039013B122D40064D447 /* GrClipIterator.h */,
+				260E039113B122D40064D447 /* GrColor.h */,
+				260E039213B122D40064D447 /* GrConfig.h */,
+				260E039313B122D40064D447 /* GrContext.h */,
+				260E039413B122D40064D447 /* GrContext_impl.h */,
+				260E039513B122D40064D447 /* GrDrawTarget.h */,
+				260E039613B122D40064D447 /* GrFontScaler.h */,
+				260E039713B122D40064D447 /* GrGLConfig.h */,
+				260E039813B122D40064D447 /* GrGLConfig_chrome.h */,
+				260E039B13B122D40064D447 /* GrGLInterface.h */,
+				260E039E13B122D40064D447 /* GrGeometryBuffer.h */,
+				260E039F13B122D40064D447 /* GrGlyph.h */,
+				260E03A013B122D40064D447 /* GrGpu.h */,
+				260E03A113B122D40064D447 /* GrGpuVertex.h */,
+				260E03A213B122D40064D447 /* GrIPoint.h */,
+				260E03A313B122D40064D447 /* GrInOrderDrawBuffer.h */,
+				260E03A413B122D40064D447 /* GrIndexBuffer.h */,
+				260E03A513B122D40064D447 /* GrInstanceCounter.h */,
+				260E03A613B122D40064D447 /* GrKey.h */,
+				260E03A713B122D40064D447 /* GrMatrix.h */,
+				260E03A913B122D40064D447 /* GrMesh.h */,
+				260E03AA13B122D40064D447 /* GrNoncopyable.h */,
+				260E03AB13B122D40064D447 /* GrPaint.h */,
+				260E03AC13B122D40064D447 /* GrPath.h */,
+				260E03AD13B122D40064D447 /* GrPathRenderer.h */,
+				260E03AE13B122D40064D447 /* GrPathSink.h */,
+				260E03AF13B122D40064D447 /* GrPlotMgr.h */,
+				260E03B013B122D40064D447 /* GrPoint.h */,
+				260E03B113B122D40064D447 /* GrRandom.h */,
+				260E03B213B122D40064D447 /* GrRect.h */,
+				260E03B313B122D40064D447 /* GrRectanizer.h */,
+				260E03B413B122D40064D447 /* GrRefCnt.h */,
+				260E03B513B122D40064D447 /* GrResource.h */,
+				260E03B613B122D40064D447 /* GrSamplerState.h */,
+				260E03B713B122D40064D447 /* GrScalar.h */,
+				260E03B813B122D40064D447 /* GrStencil.h */,
+				260E03B913B122D40064D447 /* GrStopwatch.h */,
+				260E03BA13B122D40064D447 /* GrStringBuilder.h */,
+				260E03BB13B122D40064D447 /* GrTArray.h */,
+				260E03BC13B122D40064D447 /* GrTBSearch.h */,
+				260E03BD13B122D40064D447 /* GrTDArray.h */,
+				260E03BE13B122D40064D447 /* GrTHashCache.h */,
+				260E03BF13B122D40064D447 /* GrTLList.h */,
+				260E03C013B122D40064D447 /* GrTesselatedPathRenderer.h */,
+				260E03C113B122D40064D447 /* GrTextContext.h */,
+				260E03C213B122D40064D447 /* GrTextStrike.h */,
+				260E03C313B122D40064D447 /* GrTexture.h */,
+				260E03C513B122D40064D447 /* GrTypes.h */,
+				260E03C613B122D40064D447 /* GrUserConfig.h */,
+				260E03C713B122D40064D447 /* GrVertexBuffer.h */,
+			);
+			path = include;
+			sourceTree = "<group>";
+		};
+		260E03C813B122D40064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				2612C121140D30B0001B6925 /* GrAddPathRenderers_none.cpp */,
+				2612C109140D300B001B6925 /* GrDefaultPathRenderer.cpp */,
+				2612C10A140D300B001B6925 /* GrDefaultPathRenderer.h */,
+				2612C10B140D300B001B6925 /* GrGLStencilBuffer.cpp */,
+				2612C10C140D300B001B6925 /* GrGLStencilBuffer.h */,
+				2612C10D140D300B001B6925 /* GrPathRenderer.h */,
+				2612C10E140D300B001B6925 /* GrPathRendererChain.cpp */,
+				2612C10F140D300B001B6925 /* GrPathRendererChain.h */,
+				2605F66413F19EB80044A072 /* GrStencilBuffer.cpp */,
+				26FB12B313E70D51001AFF6D /* GrRenderTarget.cpp */,
+				26FB12AD13E70D3B001AFF6D /* GrGLRenderTarget.cpp */,
+				26FB12AE13E70D3B001AFF6D /* GrGLRenderTarget.h */,
+				26FB12AF13E70D3B001AFF6D /* GrGLTexture.h */,
+				26A8AFF113E05D7000A3C111 /* GrResourceCache.cpp */,
+				26A8AFF213E05D7000A3C111 /* GrResourceCache.h */,
+				260E095513B134C90064D447 /* FlingState.cpp */,
+				260E095613B134C90064D447 /* GrDrawMesh.cpp */,
+				260E03D113B122D40064D447 /* GrAllocPool.cpp */,
+				260E03D213B122D40064D447 /* GrAtlas.cpp */,
+				260E03D313B122D40064D447 /* GrBinHashKey.h */,
+				260E03D413B122D40064D447 /* GrBufferAllocPool.cpp */,
+				260E03D513B122D40064D447 /* GrBufferAllocPool.h */,
+				260E03D613B122D40064D447 /* GrClip.cpp */,
+				260E03D713B122D40064D447 /* GrContext.cpp */,
+				260E03D913B122D40064D447 /* GrDrawTarget.cpp */,
+				260E03DA13B122D40064D447 /* GrGLDefaultInterface_none.cpp */,
+				260E03DB13B122D40064D447 /* GrGLIndexBuffer.cpp */,
+				260E03DC13B122D40064D447 /* GrGLInterface.cpp */,
+				260E03DD13B122D40064D447 /* GrGLProgram.cpp */,
+				260E03DE13B122D40064D447 /* GrGLProgram.h */,
+				260E03DF13B122D40064D447 /* GrGLTexture.cpp */,
+				260E03E013B122D40064D447 /* GrGLUtil.cpp */,
+				260E03E113B122D40064D447 /* GrGLVertexBuffer.cpp */,
+				260E03E213B122D40064D447 /* GrGpu.cpp */,
+				260E03E313B122D40064D447 /* GrGpuFactory.cpp */,
+				260E03E413B122D40064D447 /* GrGpuGL.cpp */,
+				260E03E513B122D40064D447 /* GrGpuGL.h */,
+				260E03E613B122D40064D447 /* GrGpuGLFixed.cpp */,
+				260E03E713B122D40064D447 /* GrGpuGLFixed.h */,
+				260E03E813B122D40064D447 /* GrGpuGLShaders.cpp */,
+				260E03E913B122D40064D447 /* GrGpuGLShaders.h */,
+				260E03EA13B122D40064D447 /* GrInOrderDrawBuffer.cpp */,
+				260E03EB13B122D40064D447 /* GrMatrix.cpp */,
+				260E03EC13B122D40064D447 /* GrMemory.cpp */,
+				260E03ED13B122D40064D447 /* GrPathRenderer.cpp */,
+				260E03EE13B122D40064D447 /* GrPathUtils.cpp */,
+				260E03EF13B122D40064D447 /* GrPathUtils.h */,
+				260E03F013B122D40064D447 /* GrRectanizer.cpp */,
+				260E03F113B122D40064D447 /* GrRedBlackTree.h */,
+				260E03F213B122D40064D447 /* GrResource.cpp */,
+				260E03F313B122D40064D447 /* GrStencil.cpp */,
+				260E03F513B122D40064D447 /* GrTextContext.cpp */,
+				260E03F613B122D40064D447 /* GrTextStrike.cpp */,
+				260E03F713B122D40064D447 /* GrTextStrike_impl.h */,
+				260E03F813B122D40064D447 /* GrTexture.cpp */,
+				260E03FA13B122D40064D447 /* gr_unittests.cpp */,
+			);
+			path = src;
+			sourceTree = "<group>";
+		};
+		260E03FB13B122D40064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				260E03FC13B122D40064D447 /* SkGpuCanvas.h */,
+				260E03FD13B122D40064D447 /* SkGpuDevice.h */,
+				260E03FF13B122D40064D447 /* SkGr.h */,
+				260E040013B122D40064D447 /* SkGrTexturePixelRef.h */,
+			);
+			name = include;
+			path = include/gpu;
+			sourceTree = "<group>";
+		};
+		260E040113B122D40064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				260E040213B122D40064D447 /* GrPrintf_skia.cpp */,
+				260E040313B122D40064D447 /* SkGpuCanvas.cpp */,
+				260E040413B122D40064D447 /* SkGpuDevice.cpp */,
+				260E040513B122D40064D447 /* SkGr.cpp */,
+				260E040613B122D40064D447 /* SkGrFontScaler.cpp */,
+				260E040713B122D40064D447 /* SkGrTexturePixelRef.cpp */,
+			);
+			name = src;
+			path = src/gpu;
+			sourceTree = "<group>";
+		};
+		260E043E13B1232F0064D447 /* images */ = {
+			isa = PBXGroup;
+			children = (
+				260E043F13B1232F0064D447 /* include */,
+				260E044813B1232F0064D447 /* src */,
+			);
+			name = images;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E043F13B1232F0064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26F5491A13B91C22007CC564 /* SkFlipPixelRef.h */,
+				26F5491B13B91C22007CC564 /* SkImageRef_GlobalPool.h */,
+				26F5491C13B91C22007CC564 /* SkImageRef.h */,
+				26F5491D13B91C22007CC564 /* SkJpegUtility.h */,
+				26F5491E13B91C22007CC564 /* SkMovie.h */,
+				26F5491F13B91C22007CC564 /* SkPageFlipper.h */,
+				260E044113B1232F0064D447 /* SkImageDecoder.h */,
+				260E044213B1232F0064D447 /* SkImageEncoder.h */,
+			);
+			name = include;
+			path = include/images;
+			sourceTree = "<group>";
+		};
+		260E044813B1232F0064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				260E044913B1232F0064D447 /* images */,
+				260E046413B1232F0064D447 /* ports */,
+			);
+			path = src;
+			sourceTree = "<group>";
+		};
+		260E044913B1232F0064D447 /* images */ = {
+			isa = PBXGroup;
+			children = (
+				260E07C413B128610064D447 /* SkMovie.cpp */,
+				260E044A13B1232F0064D447 /* SkBitmap_RLEPixels.h */,
+				260E044B13B1232F0064D447 /* SkCreateRLEPixelRef.cpp */,
+				260E044C13B1232F0064D447 /* SkFDStream.cpp */,
+				260E044D13B1232F0064D447 /* SkFlipPixelRef.cpp */,
+				260E044E13B1232F0064D447 /* SkImageDecoder.cpp */,
+				260E045613B1232F0064D447 /* SkImageEncoder.cpp */,
+				260E083B13B12A200064D447 /* SkImageDecoder_Factory.cpp */,
+				260E083C13B12A200064D447 /* SkImageEncoder_Factory.cpp */,
+				260E07C913B128740064D447 /* SkPageFlipper.cpp */,
+				260E07CE13B128870064D447 /* SkImageRef.cpp */,
+				260E07CF13B128870064D447 /* SkImageRefPool.cpp */,
+				260E07D013B128870064D447 /* SkImageRefPool.h */,
+				260E07D613B1289C0064D447 /* SkImageRef_GlobalPool.cpp */,
+			);
+			path = images;
+			sourceTree = "<group>";
+		};
+		260E046413B1232F0064D447 /* ports */ = {
+			isa = PBXGroup;
+			children = (
+				260E080E13B1294E0064D447 /* SkImageDecoder_CG.cpp */,
+			);
+			path = ports;
+			sourceTree = "<group>";
+		};
+		260E04B413B123730064D447 /* opts */ = {
+			isa = PBXGroup;
+			children = (
+				26F548E013B91980007CC564 /* SkBitmapProcState_opts_arm.cpp */,
+				26F548E413B91980007CC564 /* SkBlitRow_opts_none.cpp */,
+				26F548E513B91980007CC564 /* SkBlitRow_opts_SSE2.h */,
+				26F548E613B91980007CC564 /* SkUtils_opts_none.cpp */,
+				26F548E713B91980007CC564 /* SkUtils_opts_SSE2.h */,
+			);
+			name = opts;
+			path = ../../src/opts;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E04C613B123840064D447 /* svg */ = {
+			isa = PBXGroup;
+			children = (
+				260E04C713B123840064D447 /* include */,
+				260E04CD13B123840064D447 /* src */,
+			);
+			name = svg;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E04C713B123840064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				260E04C813B123840064D447 /* SkSVGAttribute.h */,
+				260E04C913B123840064D447 /* SkSVGBase.h */,
+				260E04CA13B123840064D447 /* SkSVGPaintState.h */,
+				260E04CB13B123840064D447 /* SkSVGParser.h */,
+				260E04CC13B123840064D447 /* SkSVGTypes.h */,
+			);
+			name = include;
+			path = include/svg;
+			sourceTree = "<group>";
+		};
+		260E04CD13B123840064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				260E04CE13B123840064D447 /* SkSVGCircle.cpp */,
+				260E04CF13B123840064D447 /* SkSVGCircle.h */,
+				260E04D013B123840064D447 /* SkSVGClipPath.cpp */,
+				260E04D113B123840064D447 /* SkSVGClipPath.h */,
+				260E04D213B123840064D447 /* SkSVGDefs.cpp */,
+				260E04D313B123840064D447 /* SkSVGDefs.h */,
+				260E04D413B123840064D447 /* SkSVGElements.cpp */,
+				260E04D513B123840064D447 /* SkSVGElements.h */,
+				260E04D613B123840064D447 /* SkSVGEllipse.cpp */,
+				260E04D713B123840064D447 /* SkSVGEllipse.h */,
+				260E04D813B123840064D447 /* SkSVGFeColorMatrix.cpp */,
+				260E04D913B123840064D447 /* SkSVGFeColorMatrix.h */,
+				260E04DA13B123840064D447 /* SkSVGFilter.cpp */,
+				260E04DB13B123840064D447 /* SkSVGFilter.h */,
+				260E04DC13B123840064D447 /* SkSVGG.cpp */,
+				260E04DD13B123840064D447 /* SkSVGG.h */,
+				260E04DE13B123840064D447 /* SkSVGGradient.cpp */,
+				260E04DF13B123840064D447 /* SkSVGGradient.h */,
+				260E04E013B123840064D447 /* SkSVGGroup.cpp */,
+				260E04E113B123840064D447 /* SkSVGGroup.h */,
+				260E04E213B123840064D447 /* SkSVGImage.cpp */,
+				260E04E313B123840064D447 /* SkSVGImage.h */,
+				260E04E413B123840064D447 /* SkSVGLine.cpp */,
+				260E04E513B123840064D447 /* SkSVGLine.h */,
+				260E04E613B123840064D447 /* SkSVGLinearGradient.cpp */,
+				260E04E713B123840064D447 /* SkSVGLinearGradient.h */,
+				260E04E813B123840064D447 /* SkSVGMask.cpp */,
+				260E04E913B123840064D447 /* SkSVGMask.h */,
+				260E04EA13B123840064D447 /* SkSVGMetadata.cpp */,
+				260E04EB13B123840064D447 /* SkSVGMetadata.h */,
+				260E04EC13B123840064D447 /* SkSVGPaintState.cpp */,
+				260E04ED13B123840064D447 /* SkSVGParser.cpp */,
+				260E04EE13B123840064D447 /* SkSVGPath.cpp */,
+				260E04EF13B123840064D447 /* SkSVGPath.h */,
+				260E04F013B123840064D447 /* SkSVGPolygon.cpp */,
+				260E04F113B123840064D447 /* SkSVGPolygon.h */,
+				260E04F213B123840064D447 /* SkSVGPolyline.cpp */,
+				260E04F313B123840064D447 /* SkSVGPolyline.h */,
+				260E04F413B123840064D447 /* SkSVGRadialGradient.cpp */,
+				260E04F513B123840064D447 /* SkSVGRadialGradient.h */,
+				260E04F613B123840064D447 /* SkSVGRect.cpp */,
+				260E04F713B123840064D447 /* SkSVGRect.h */,
+				260E04F813B123840064D447 /* SkSVGSVG.cpp */,
+				260E04F913B123840064D447 /* SkSVGSVG.h */,
+				260E04FA13B123840064D447 /* SkSVGStop.cpp */,
+				260E04FB13B123840064D447 /* SkSVGStop.h */,
+				260E04FC13B123840064D447 /* SkSVGSymbol.cpp */,
+				260E04FD13B123840064D447 /* SkSVGSymbol.h */,
+				260E04FE13B123840064D447 /* SkSVGText.cpp */,
+				260E04FF13B123840064D447 /* SkSVGText.h */,
+				260E050013B123840064D447 /* SkSVGUse.cpp */,
+			);
+			name = src;
+			path = src/svg;
+			sourceTree = "<group>";
+		};
+		260E052D13B123DE0064D447 /* utils */ = {
+			isa = PBXGroup;
+			children = (
+				260E052E13B123DE0064D447 /* include */,
+				260E054513B123DE0064D447 /* src */,
+			);
+			name = utils;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E052E13B123DE0064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				260E052F13B123DE0064D447 /* mac */,
+				260E053113B123DE0064D447 /* SkBoundaryPatch.h */,
+				260E053213B123DE0064D447 /* SkCamera.h */,
+				260E053313B123DE0064D447 /* SkCubicInterval.h */,
+				260E053413B123DE0064D447 /* SkCullPoints.h */,
+				260E053513B123DE0064D447 /* SkDumpCanvas.h */,
+				260E053613B123DE0064D447 /* SkEGLContext.h */,
+				260E053713B123DE0064D447 /* SkGLCanvas.h */,
+				260E053813B123DE0064D447 /* SkInterpolator.h */,
+				260E053913B123DE0064D447 /* SkLayer.h */,
+				260E053A13B123DE0064D447 /* SkMatrix44.h */,
+				260E053B13B123DE0064D447 /* SkMeshUtils.h */,
+				260E053C13B123DE0064D447 /* SkNWayCanvas.h */,
+				260E053D13B123DE0064D447 /* SkNinePatch.h */,
+				260E053E13B123DE0064D447 /* SkParse.h */,
+				260E053F13B123DE0064D447 /* SkParsePaint.h */,
+				260E054013B123DE0064D447 /* SkParsePath.h */,
+				260E054113B123DE0064D447 /* SkProxyCanvas.h */,
+				260E054213B123DE0064D447 /* SkSfntUtils.h */,
+				260E054313B123DE0064D447 /* SkTextBox.h */,
+				260E054413B123DE0064D447 /* SkUnitMappers.h */,
+			);
+			name = include;
+			path = include/utils;
+			sourceTree = "<group>";
+		};
+		260E052F13B123DE0064D447 /* mac */ = {
+			isa = PBXGroup;
+			children = (
+				260E053013B123DE0064D447 /* SkCGUtils.h */,
+			);
+			path = mac;
+			sourceTree = "<group>";
+		};
+		260E054513B123DE0064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				260E054813B123DE0064D447 /* mac */,
+				260E055713B123DE0064D447 /* SkBoundaryPatch.cpp */,
+				260E055813B123DE0064D447 /* SkCamera.cpp */,
+				260E055913B123DE0064D447 /* SkColorMatrix.cpp */,
+				260E055A13B123DE0064D447 /* SkCubicInterval.cpp */,
+				260E055B13B123DE0064D447 /* SkCullPoints.cpp */,
+				260E055C13B123DE0064D447 /* SkDumpCanvas.cpp */,
+				260E055E13B123DE0064D447 /* SkInterpolator.cpp */,
+				260E055F13B123DE0064D447 /* SkLayer.cpp */,
+				260E056013B123DE0064D447 /* SkMatrix44.cpp */,
+				260E056113B123DE0064D447 /* SkMeshUtils.cpp */,
+				260E056213B123DE0064D447 /* SkNWayCanvas.cpp */,
+				260E056313B123DE0064D447 /* SkNinePatch.cpp */,
+				260E056413B123DE0064D447 /* SkOSFile.cpp */,
+				260E056513B123DE0064D447 /* SkParse.cpp */,
+				260E056613B123DE0064D447 /* SkParseColor.cpp */,
+				260E056713B123DE0064D447 /* SkParsePath.cpp */,
+				260E056813B123DE0064D447 /* SkProxyCanvas.cpp */,
+				260E056913B123DE0064D447 /* SkSfntUtils.cpp */,
+				260E056A13B123DE0064D447 /* SkUnitMappers.cpp */,
+			);
+			name = src;
+			path = src/utils;
+			sourceTree = "<group>";
+		};
+		260E054813B123DE0064D447 /* mac */ = {
+			isa = PBXGroup;
+			children = (
+				260E054913B123DE0064D447 /* SkCreateCGImageRef.cpp */,
+			);
+			path = mac;
+			sourceTree = "<group>";
+		};
+		260E059613B123E80064D447 /* view */ = {
+			isa = PBXGroup;
+			children = (
+				260E059713B123E80064D447 /* include */,
+				260E05AE13B123E80064D447 /* src */,
+			);
+			name = view;
+			path = ../..;
+			sourceTree = "<group>";
+		};
+		260E059713B123E80064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				260E059813B123E80064D447 /* SkApplication.h */,
+				260E059913B123E80064D447 /* SkBGViewArtist.h */,
+				260E059A13B123E80064D447 /* SkBorderView.h */,
+				260E059B13B123E80064D447 /* SkEvent.h */,
+				260E059C13B123E80064D447 /* SkEventSink.h */,
+				260E059D13B123E80064D447 /* SkImageView.h */,
+				260E059E13B123E80064D447 /* SkKey.h */,
+				260E059F13B123E80064D447 /* SkOSMenu.h */,
+				260E05A413B123E80064D447 /* SkProgressBarView.h */,
+				260E05A513B123E80064D447 /* SkScrollBarView.h */,
+				260E05A613B123E80064D447 /* SkStackViewLayout.h */,
+				260E05A713B123E80064D447 /* SkSystemEventTypes.h */,
+				260E05A813B123E80064D447 /* SkTouchGesture.h */,
+				260E05A913B123E80064D447 /* SkView.h */,
+				260E05AA13B123E80064D447 /* SkViewInflate.h */,
+				260E05AB13B123E80064D447 /* SkWidget.h */,
+				260E05AC13B123E80064D447 /* SkWidgetViews.h */,
+				260E05AD13B123E80064D447 /* SkWindow.h */,
+			);
+			name = include;
+			path = include/views;
+			sourceTree = "<group>";
+		};
+		260E05AE13B123E80064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				260E05AF13B123E80064D447 /* SkBGViewArtist.cpp */,
+				260E05B113B123E80064D447 /* SkEvent.cpp */,
+				260E05B213B123E80064D447 /* SkEventSink.cpp */,
+				260E05B313B123E80064D447 /* SkImageView.cpp */,
+				260E05B413B123E80064D447 /* SkListView.cpp */,
+				260E05B513B123E80064D447 /* SkListWidget.cpp */,
+				260E05B613B123E80064D447 /* SkOSMenu.cpp */,
+				260E05B713B123E80064D447 /* SkParsePaint.cpp */,
+				260E05B813B123E80064D447 /* SkProgressBarView.cpp */,
+				260E05B913B123E80064D447 /* SkProgressView.cpp */,
+				260E05BA13B123E80064D447 /* SkScrollBarView.cpp */,
+				260E05B013B123E80064D447 /* SkBorderView.cpp */,
+				260E05BB13B123E80064D447 /* SkStackViewLayout.cpp */,
+				260E05BC13B123E80064D447 /* SkStaticTextView.cpp */,
+				260E05BD13B123E80064D447 /* SkTagList.cpp */,
+				260E05BE13B123E80064D447 /* SkTagList.h */,
+				260E05BF13B123E80064D447 /* SkTextBox.cpp */,
+				260E05C013B123E80064D447 /* SkTouchGesture.cpp */,
+				260E05C113B123E80064D447 /* SkView.cpp */,
+				260E05C213B123E80064D447 /* SkViewInflate.cpp */,
+				260E05C313B123E80064D447 /* SkViewPriv.cpp */,
+				260E05C413B123E80064D447 /* SkViewPriv.h */,
+				260E05C513B123E80064D447 /* SkWidget.cpp */,
+				260E05C613B123E80064D447 /* SkWidgetViews.cpp */,
+				260E05C713B123E80064D447 /* SkWidgets.cpp */,
+				260E05C813B123E80064D447 /* SkWindow.cpp */,
+			);
+			name = src;
+			path = src/views;
+			sourceTree = "<group>";
+		};
+		260E05EC13B124210064D447 /* xml */ = {
+			isa = PBXGroup;
+			children = (
+				260E05ED13B124210064D447 /* include */,
+				260E05F413B124210064D447 /* src */,
+			);
+			name = xml;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E05ED13B124210064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				260E05EE13B124210064D447 /* SkBML_WXMLParser.h */,
+				260E05EF13B124210064D447 /* SkBML_XMLParser.h */,
+				260E05F013B124210064D447 /* SkDOM.h */,
+				260E05F113B124210064D447 /* SkJS.h */,
+				260E05F213B124210064D447 /* SkXMLParser.h */,
+				260E05F313B124210064D447 /* SkXMLWriter.h */,
+			);
+			name = include;
+			path = include/xml;
+			sourceTree = "<group>";
+		};
+		260E05F413B124210064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				260E05F713B124210064D447 /* SkDOM.cpp */,
+				260E05FA13B124210064D447 /* SkXMLParser.cpp */,
+				260E07B213B128210064D447 /* SkXMLWriter.cpp */,
+			);
+			name = src;
+			path = src/xml;
+			sourceTree = "<group>";
+		};
+		260E06B613B127E00064D447 /* animator */ = {
+			isa = PBXGroup;
+			children = (
+				260E06B713B127E00064D447 /* include */,
+				260E06BA13B127E00064D447 /* src */,
+			);
+			name = animator;
+			path = ../..;
+			sourceTree = SOURCE_ROOT;
+		};
+		260E06B713B127E00064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				260E06B813B127E00064D447 /* SkAnimator.h */,
+				260E06B913B127E00064D447 /* SkAnimatorView.h */,
+			);
+			name = include;
+			path = include/animator;
+			sourceTree = "<group>";
+		};
+		260E06BA13B127E00064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				260E06BB13B127E00064D447 /* SkAnimate.h */,
+				260E06BC13B127E00064D447 /* SkAnimateActive.cpp */,
+				260E06BD13B127E00064D447 /* SkAnimateActive.h */,
+				260E06BE13B127E00064D447 /* SkAnimateBase.cpp */,
+				260E06BF13B127E00064D447 /* SkAnimateBase.h */,
+				260E06C013B127E00064D447 /* SkAnimateField.cpp */,
+				260E06C113B127E00064D447 /* SkAnimateMaker.cpp */,
+				260E06C213B127E00064D447 /* SkAnimateMaker.h */,
+				260E06C313B127E00064D447 /* SkAnimateProperties.h */,
+				260E06C413B127E00064D447 /* SkAnimateSet.cpp */,
+				260E06C513B127E00064D447 /* SkAnimateSet.h */,
+				260E06C613B127E00064D447 /* SkAnimator.cpp */,
+				260E06C713B127E00064D447 /* SkAnimatorScript.cpp */,
+				260E06C813B127E00064D447 /* SkAnimatorScript.h */,
+				260E06C913B127E00064D447 /* SkBase64.cpp */,
+				260E06CA13B127E00064D447 /* SkBase64.h */,
+				260E06CB13B127E00064D447 /* SkBoundable.cpp */,
+				260E06CC13B127E00064D447 /* SkBoundable.h */,
+				260E06CD13B127E00064D447 /* SkBuildCondensedInfo.cpp */,
+				260E06CE13B127E00064D447 /* SkDisplayAdd.cpp */,
+				260E06CF13B127E00064D447 /* SkDisplayAdd.h */,
+				260E06D013B127E00064D447 /* SkDisplayApply.cpp */,
+				260E06D113B127E00064D447 /* SkDisplayApply.h */,
+				260E06D213B127E00064D447 /* SkDisplayBounds.cpp */,
+				260E06D313B127E00064D447 /* SkDisplayBounds.h */,
+				260E06D413B127E00064D447 /* SkDisplayEvent.cpp */,
+				260E06D513B127E00064D447 /* SkDisplayEvent.h */,
+				260E06D613B127E00064D447 /* SkDisplayEvents.cpp */,
+				260E06D713B127E00064D447 /* SkDisplayEvents.h */,
+				260E06D813B127E00064D447 /* SkDisplayInclude.cpp */,
+				260E06D913B127E00064D447 /* SkDisplayInclude.h */,
+				260E06DA13B127E00064D447 /* SkDisplayInput.cpp */,
+				260E06DB13B127E00064D447 /* SkDisplayInput.h */,
+				260E06DC13B127E00064D447 /* SkDisplayList.cpp */,
+				260E06DD13B127E00064D447 /* SkDisplayList.h */,
+				260E06DE13B127E00064D447 /* SkDisplayMath.cpp */,
+				260E06DF13B127E00064D447 /* SkDisplayMath.h */,
+				260E06E013B127E00064D447 /* SkDisplayMovie.cpp */,
+				260E06E113B127E00064D447 /* SkDisplayMovie.h */,
+				260E06E213B127E00064D447 /* SkDisplayNumber.cpp */,
+				260E06E313B127E00064D447 /* SkDisplayNumber.h */,
+				260E06E413B127E00064D447 /* SkDisplayPost.cpp */,
+				260E06E513B127E00064D447 /* SkDisplayPost.h */,
+				260E06E613B127E00064D447 /* SkDisplayRandom.cpp */,
+				260E06E713B127E00064D447 /* SkDisplayRandom.h */,
+				260E06E813B127E00064D447 /* SkDisplayScreenplay.cpp */,
+				260E06E913B127E00064D447 /* SkDisplayScreenplay.h */,
+				260E06EA13B127E00064D447 /* SkDisplayType.cpp */,
+				260E06EB13B127E00064D447 /* SkDisplayType.h */,
+				260E06EC13B127E00064D447 /* SkDisplayTypes.cpp */,
+				260E06ED13B127E00064D447 /* SkDisplayTypes.h */,
+				260E06EE13B127E00064D447 /* SkDisplayXMLParser.cpp */,
+				260E06EF13B127E00064D447 /* SkDisplayXMLParser.h */,
+				260E06F013B127E00064D447 /* SkDisplayable.cpp */,
+				260E06F113B127E00064D447 /* SkDisplayable.h */,
+				260E06F213B127E00064D447 /* SkDraw3D.cpp */,
+				260E06F313B127E00064D447 /* SkDraw3D.h */,
+				260E06F413B127E00064D447 /* SkDrawBitmap.cpp */,
+				260E06F513B127E00064D447 /* SkDrawBitmap.h */,
+				260E06F613B127E00064D447 /* SkDrawBlur.cpp */,
+				260E06F713B127E00064D447 /* SkDrawBlur.h */,
+				260E06F813B127E00064D447 /* SkDrawClip.cpp */,
+				260E06F913B127E00064D447 /* SkDrawClip.h */,
+				260E06FA13B127E00064D447 /* SkDrawColor.cpp */,
+				260E06FB13B127E00064D447 /* SkDrawColor.h */,
+				260E06FC13B127E00064D447 /* SkDrawDash.cpp */,
+				260E06FD13B127E00064D447 /* SkDrawDash.h */,
+				260E06FE13B127E00064D447 /* SkDrawDiscrete.cpp */,
+				260E06FF13B127E00064D447 /* SkDrawDiscrete.h */,
+				260E070013B127E00064D447 /* SkDrawEmboss.cpp */,
+				260E070113B127E00064D447 /* SkDrawEmboss.h */,
+				260E070213B127E00064D447 /* SkDrawExtraPathEffect.cpp */,
+				260E070313B127E00064D447 /* SkDrawFull.cpp */,
+				260E070413B127E00064D447 /* SkDrawFull.h */,
+				260E070513B127E00064D447 /* SkDrawGradient.cpp */,
+				260E070613B127E00064D447 /* SkDrawGradient.h */,
+				260E070713B127E00064D447 /* SkDrawGroup.cpp */,
+				260E070813B127E00064D447 /* SkDrawGroup.h */,
+				260E070913B127E00064D447 /* SkDrawLine.cpp */,
+				260E070A13B127E00064D447 /* SkDrawLine.h */,
+				260E070B13B127E00064D447 /* SkDrawMatrix.cpp */,
+				260E070C13B127E00064D447 /* SkDrawMatrix.h */,
+				260E070D13B127E00064D447 /* SkDrawOval.cpp */,
+				260E070E13B127E00064D447 /* SkDrawOval.h */,
+				260E070F13B127E00064D447 /* SkDrawPaint.cpp */,
+				260E071013B127E00064D447 /* SkDrawPaint.h */,
+				260E071113B127E00064D447 /* SkDrawPath.cpp */,
+				260E071213B127E00064D447 /* SkDrawPath.h */,
+				260E071313B127E00064D447 /* SkDrawPoint.cpp */,
+				260E071413B127E00064D447 /* SkDrawPoint.h */,
+				260E071513B127E00064D447 /* SkDrawRectangle.cpp */,
+				260E071613B127E00064D447 /* SkDrawRectangle.h */,
+				260E071713B127E00064D447 /* SkDrawSaveLayer.cpp */,
+				260E071813B127E00064D447 /* SkDrawSaveLayer.h */,
+				260E071913B127E00064D447 /* SkDrawShader.cpp */,
+				260E071A13B127E00064D447 /* SkDrawShader.h */,
+				260E071B13B127E00064D447 /* SkDrawText.cpp */,
+				260E071C13B127E00064D447 /* SkDrawText.h */,
+				260E071D13B127E00064D447 /* SkDrawTextBox.cpp */,
+				260E071E13B127E00064D447 /* SkDrawTextBox.h */,
+				260E071F13B127E00064D447 /* SkDrawTo.cpp */,
+				260E072013B127E00064D447 /* SkDrawTo.h */,
+				260E072113B127E00064D447 /* SkDrawTransparentShader.cpp */,
+				260E072213B127E00064D447 /* SkDrawTransparentShader.h */,
+				260E072313B127E00064D447 /* SkDrawable.cpp */,
+				260E072413B127E00064D447 /* SkDrawable.h */,
+				260E072513B127E00064D447 /* SkDump.cpp */,
+				260E072613B127E00064D447 /* SkDump.h */,
+				260E072713B127E00064D447 /* SkExtras.h */,
+				260E072813B127E00064D447 /* SkGetCondensedInfo.cpp */,
+				260E072913B127E00064D447 /* SkHitClear.cpp */,
+				260E072A13B127E00064D447 /* SkHitClear.h */,
+				260E072B13B127E00064D447 /* SkHitTest.cpp */,
+				260E072C13B127E00064D447 /* SkHitTest.h */,
+				260E072D13B127E00064D447 /* SkIntArray.h */,
+				260E072E13B127E00064D447 /* SkMatrixParts.cpp */,
+				260E072F13B127E00064D447 /* SkMatrixParts.h */,
+				260E073013B127E00064D447 /* SkMemberInfo.cpp */,
+				260E073113B127E00064D447 /* SkMemberInfo.h */,
+				260E073213B127E00064D447 /* SkOpArray.cpp */,
+				260E073313B127E00064D447 /* SkOpArray.h */,
+				260E073413B127E00064D447 /* SkOperand.h */,
+				260E073513B127E00064D447 /* SkOperand2.h */,
+				260E073613B127E00064D447 /* SkOperandInterpolator.h */,
+				260E073713B127E00064D447 /* SkOperandIterpolator.cpp */,
+				260E073813B127E00064D447 /* SkPaintParts.cpp */,
+				260E073913B127E00064D447 /* SkPaintParts.h */,
+				260E073A13B127E00064D447 /* SkParseSVGPath.cpp */,
+				260E073B13B127E00064D447 /* SkPathParts.cpp */,
+				260E073C13B127E00064D447 /* SkPathParts.h */,
+				260E073D13B127E00064D447 /* SkPostParts.cpp */,
+				260E073E13B127E00064D447 /* SkPostParts.h */,
+				260E073F13B127E00064D447 /* SkScript.cpp */,
+				260E074013B127E00064D447 /* SkScript.h */,
+				260E074113B127E00064D447 /* SkScript2.h */,
+				260E074213B127E00064D447 /* SkScriptCallBack.h */,
+				260E074313B127E00064D447 /* SkScriptDecompile.cpp */,
+				260E074413B127E00064D447 /* SkScriptRuntime.cpp */,
+				260E074513B127E00064D447 /* SkScriptRuntime.h */,
+				260E074613B127E00064D447 /* SkScriptTokenizer.cpp */,
+				260E074713B127E00064D447 /* SkSnapshot.cpp */,
+				260E074813B127E00064D447 /* SkSnapshot.h */,
+				260E074913B127E00064D447 /* SkTDArray_Experimental.h */,
+				260E074A13B127E00064D447 /* SkTextOnPath.cpp */,
+				260E074B13B127E00064D447 /* SkTextOnPath.h */,
+				260E074C13B127E00064D447 /* SkTextToPath.cpp */,
+				260E074D13B127E00064D447 /* SkTextToPath.h */,
+				260E074E13B127E00064D447 /* SkTime.cpp */,
+				260E074F13B127E00064D447 /* SkTypedArray.cpp */,
+				260E075013B127E00064D447 /* SkTypedArray.h */,
+				260E075113B127E00064D447 /* SkXMLAnimatorWriter.cpp */,
+				260E075213B127E00064D447 /* SkXMLAnimatorWriter.h */,
+			);
+			name = src;
+			path = src/animator;
+			sourceTree = "<group>";
+		};
+		260E1E8813B3B10B0064D447 /* pdf */ = {
+			isa = PBXGroup;
+			children = (
+				260E1E9513B3B13F0064D447 /* include */,
+				260E1EAE13B3B1620064D447 /* src */,
+			);
+			name = pdf;
+			sourceTree = "<group>";
+		};
+		260E1E9513B3B13F0064D447 /* include */ = {
+			isa = PBXGroup;
+			children = (
+				26811E7A13DEFAF7001A1609 /* SkBitSet.h */,
+				260E1E8913B3B13B0064D447 /* SkPDFCatalog.h */,
+				260E1E8A13B3B13B0064D447 /* SkPDFDevice.h */,
+				260E1E8B13B3B13B0064D447 /* SkPDFDocument.h */,
+				260E1E8C13B3B13B0064D447 /* SkPDFFont.h */,
+				260E1E8D13B3B13B0064D447 /* SkPDFFormXObject.h */,
+				260E1E8E13B3B13B0064D447 /* SkPDFGraphicState.h */,
+				260E1E8F13B3B13B0064D447 /* SkPDFImage.h */,
+				260E1E9013B3B13B0064D447 /* SkPDFPage.h */,
+				260E1E9113B3B13B0064D447 /* SkPDFShader.h */,
+				260E1E9213B3B13B0064D447 /* SkPDFStream.h */,
+				260E1E9313B3B13B0064D447 /* SkPDFTypes.h */,
+				260E1E9413B3B13B0064D447 /* SkPDFUtils.h */,
+			);
+			name = include;
+			sourceTree = "<group>";
+		};
+		260E1EAE13B3B1620064D447 /* src */ = {
+			isa = PBXGroup;
+			children = (
+				26811E7813DEFAE8001A1609 /* SkBitSet.cpp */,
+				260E1E9613B3B15A0064D447 /* SkPDFCatalog.cpp */,
+				260E1E9713B3B15A0064D447 /* SkPDFDevice.cpp */,
+				260E1E9813B3B15A0064D447 /* SkPDFDocument.cpp */,
+				260E1E9913B3B15A0064D447 /* SkPDFFont.cpp */,
+				260E1E9A13B3B15A0064D447 /* SkPDFFormXObject.cpp */,
+				260E1E9B13B3B15A0064D447 /* SkPDFGraphicState.cpp */,
+				260E1E9C13B3B15A0064D447 /* SkPDFImage.cpp */,
+				260E1E9D13B3B15A0064D447 /* SkPDFPage.cpp */,
+				260E1E9E13B3B15A0064D447 /* SkPDFShader.cpp */,
+				260E1E9F13B3B15A0064D447 /* SkPDFStream.cpp */,
+				260E1EA013B3B15A0064D447 /* SkPDFTypes.cpp */,
+				260E1EA113B3B15A0064D447 /* SkPDFUtils.cpp */,
+			);
+			name = src;
+			sourceTree = "<group>";
+		};
+		260EE81F13AFA7790064D447 /* Skia */ = {
+			isa = PBXGroup;
+			children = (
+				26F5490B13B91B73007CC564 /* SkUserConfig.h */,
+				260E06B613B127E00064D447 /* animator */,
+				260E01AF13B1225D0064D447 /* core */,
+				260E031E13B122A30064D447 /* effects */,
+				260E038913B122D40064D447 /* gpu */,
+				260E043E13B1232F0064D447 /* images */,
+				260E04B413B123730064D447 /* opts */,
+				260E007413B11F5B0064D447 /* pipe */,
+				260E1E8813B3B10B0064D447 /* pdf */,
+				260E04C613B123840064D447 /* svg */,
+				260E052D13B123DE0064D447 /* utils */,
+				260E059613B123E80064D447 /* view */,
+				260E05EC13B124210064D447 /* xml */,
+			);
+			name = Skia;
+			sourceTree = "<group>";
+		};
+		260EE8B913AFA7790064D447 /* iOS */ = {
+			isa = PBXGroup;
+			children = (
+				26D91229140D57BD0036A311 /* skia_ios.mm */,
+				26FB98D113D0C87000ACBEA0 /* SkUIView.h */,
+				26FB98D213D0C87000ACBEA0 /* SkUIView.mm */,
+				26962D4E13CE2D780039B1FB /* GrGLDefaultInterface_iOS.cpp */,
+				26962CA513CE26730039B1FB /* SkOSWindow_iOS.h */,
+				26962CA313CE265C0039B1FB /* SkOSWindow_iOS.mm */,
+				260EE8BB13AFA7790064D447 /* SkOSFile_iOS.mm */,
+				260EE8BA13AFA7790064D447 /* SkFontHost_iOS.mm */,
+				260E08D013B12DBE0064D447 /* SkStream_NSData.h */,
+				260EE8BF13AFA7790064D447 /* SkStream_NSData.mm */,
+			);
+			name = iOS;
+			sourceTree = "<group>";
+		};
+		26962CC813CE27390039B1FB /* xcconfig */ = {
+			isa = PBXGroup;
+			children = (
+				2662AB6F13BD067900CDE7E9 /* SkiOSSampleApp-Debug.xcconfig */,
+				2662AB7513BD0C0D00CDE7E9 /* SkiOSSampleApp-Release.xcconfig */,
+				2662AB7713BD0C1E00CDE7E9 /* SkiOSSampleApp-Base.xcconfig */,
+			);
+			name = xcconfig;
+			sourceTree = "<group>";
+		};
+		26962CE713CE29120039B1FB /* DrawingBoard */ = {
+			isa = PBXGroup;
+			children = (
+				268C50D813F022AF0003FF9A /* SampleDrawingClient.cpp */,
+				268C50D913F022AF0003FF9A /* SampleDrawingServer.cpp */,
+				268C50D213F022820003FF9A /* SkColorPalette.cpp */,
+				268C50D313F022820003FF9A /* SkColorPalette.h */,
+				268C50D413F022820003FF9A /* SkNetPipeController.cpp */,
+				268C50D513F022820003FF9A /* SkNetPipeController.h */,
+			);
+			name = DrawingBoard;
+			sourceTree = "<group>";
+		};
+		26F67B2A13CB3564005DDCD2 /* Networking */ = {
+			isa = PBXGroup;
+			children = (
+				268C50DC13F0230C0003FF9A /* SampleNetPipeReader.cpp */,
+				268C50DD13F0230C0003FF9A /* SkSockets.cpp */,
+				268C50DE13F0230C0003FF9A /* SkSockets.h */,
+			);
+			name = Networking;
+			sourceTree = "<group>";
+		};
+		2860E324111B887F00E27156 /* iPhone */ = {
+			isa = PBXGroup;
+			children = (
+				2860E325111B887F00E27156 /* AppDelegate_iPhone.h */,
+				2860E326111B887F00E27156 /* AppDelegate_iPhone.mm */,
+				2860E327111B887F00E27156 /* MainWindow_iPhone.xib */,
+				260E1DCA13B3AA490064D447 /* SkUINavigationController.h */,
+				260E1DCB13B3AA490064D447 /* SkUINavigationController.mm */,
+			);
+			path = iPhone;
+			sourceTree = "<group>";
+		};
+		2860E32A111B888700E27156 /* iPad */ = {
+			isa = PBXGroup;
+			children = (
+				2860E32B111B888700E27156 /* AppDelegate_iPad.h */,
+				2860E32C111B888700E27156 /* AppDelegate_iPad.mm */,
+				2860E32D111B888700E27156 /* MainWindow_iPad.xib */,
+				260E147713B2734E0064D447 /* SkUISplitViewController.h */,
+				260E147813B2734E0064D447 /* SkUISplitViewController.mm */,
+			);
+			path = iPad;
+			sourceTree = "<group>";
+		};
+		28EEBF621118D79A00187D67 /* Shared */ = {
+			isa = PBXGroup;
+			children = (
+				2612C104140D2FE3001B6925 /* SkEventNotifier.h */,
+				2612C105140D2FE3001B6925 /* SkEventNotifier.mm */,
+				26962C7813CE256E0039B1FB /* SkUIDetailViewController.h */,
+				26962C7913CE256E0039B1FB /* SkUIDetailViewController.mm */,
+				26962C7A13CE256E0039B1FB /* SkUIRootViewController.h */,
+				26962C7B13CE256E0039B1FB /* SkUIRootViewController.mm */,
+				2663AC9213D5D8D400C20488 /* SkOptionsTableViewController.h */,
+				2663AC9313D5D8D400C20488 /* SkOptionsTableViewController.mm */,
+				265C7DE113D75752008329F6 /* SkOptionListController.h */,
+				265C7DE213D75752008329F6 /* SkOptionListController.mm */,
+			);
+			name = Shared;
+			sourceTree = "<group>";
+		};
+		29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
+			isa = PBXGroup;
+			children = (
+				2860E32A111B888700E27156 /* iPad */,
+				2860E324111B887F00E27156 /* iPhone */,
+				28EEBF621118D79A00187D67 /* Shared */,
+				29B97315FDCFA39411CA2CEA /* Other Sources */,
+				29B97323FDCFA39411CA2CEA /* Frameworks */,
+				19C28FACFE9D520D11CA2CBB /* Products */,
+			);
+			name = CustomTemplate;
+			sourceTree = "<group>";
+		};
+		29B97315FDCFA39411CA2CEA /* Other Sources */ = {
+			isa = PBXGroup;
+			children = (
+				26D91221140D576B0036A311 /* SkSampleUIView.h */,
+				26D91222140D576B0036A311 /* SkSampleUIView.mm */,
+				26962C8E13CE25D60039B1FB /* iOSSampleApp_Prefix.pch */,
+				26962C8F13CE25D60039B1FB /* iOSSampleApp-Info.plist */,
+				26962CC813CE27390039B1FB /* xcconfig */,
+				2605F43B13F18B1B0044A072 /* Debugger */,
+				26F67B2A13CB3564005DDCD2 /* Networking */,
+				26962CE713CE29120039B1FB /* DrawingBoard */,
+				260EE8B913AFA7790064D447 /* iOS */,
+				260E013413B11F7A0064D447 /* SampleApp */,
+				260EE81F13AFA7790064D447 /* Skia */,
+			);
+			name = "Other Sources";
+			sourceTree = "<group>";
+		};
+		29B97323FDCFA39411CA2CEA /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				260EE9D113AFA7850064D447 /* OpenGLES.framework */,
+				1DF5F4DF0D08C38300B7A737 /* UIKit.framework */,
+				1D30AB110D05D00D00671497 /* Foundation.framework */,
+				260EE9D013AFA7850064D447 /* CoreFoundation.framework */,
+				288765FC0DF74451002DB57D /* CoreGraphics.framework */,
+				260EF18413AFD62E0064D447 /* CoreText.framework */,
+				263BE75713CCC7BF00CCE991 /* QuartzCore.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		1D6058900D05DD3D006BFB54 /* iOSSampleApp */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "iOSSampleApp" */;
+			buildPhases = (
+				1D60588D0D05DD3D006BFB54 /* Resources */,
+				1D60588E0D05DD3D006BFB54 /* Sources */,
+				1D60588F0D05DD3D006BFB54 /* Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = iOSSampleApp;
+			productName = iOSShell;
+			productReference = 1D6058910D05DD3D006BFB54 /* iOSSampleApp.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		29B97313FDCFA39411CA2CEA /* Project object */ = {
+			isa = PBXProject;
+			buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "iOSSampleApp" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 1;
+			knownRegions = (
+				English,
+				Japanese,
+				French,
+				German,
+			);
+			mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				1D6058900D05DD3D006BFB54 /* iOSSampleApp */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		1D60588D0D05DD3D006BFB54 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				2860E329111B887F00E27156 /* MainWindow_iPhone.xib in Resources */,
+				2860E32F111B888700E27156 /* MainWindow_iPad.xib in Resources */,
+				2662AB7013BD067900CDE7E9 /* SkiOSSampleApp-Debug.xcconfig in Resources */,
+				2662AB7613BD0C0D00CDE7E9 /* SkiOSSampleApp-Release.xcconfig in Resources */,
+				2662AB7813BD0C1E00CDE7E9 /* SkiOSSampleApp-Base.xcconfig in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		1D60588E0D05DD3D006BFB54 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				2860E328111B887F00E27156 /* AppDelegate_iPhone.mm in Sources */,
+				2860E32E111B888700E27156 /* AppDelegate_iPad.mm in Sources */,
+				260E00E313B11F5B0064D447 /* OverView.cpp in Sources */,
+				260E00E813B11F5B0064D447 /* SampleArc.cpp in Sources */,
+				260E00E913B11F5B0064D447 /* SampleAvoid.cpp in Sources */,
+				260E00EC13B11F5B0064D447 /* SampleBlur.cpp in Sources */,
+				260E00EE13B11F5B0064D447 /* SampleCircle.cpp in Sources */,
+				260E00F013B11F5B0064D447 /* SampleComplexClip.cpp in Sources */,
+				260E00F213B11F5B0064D447 /* SampleCull.cpp in Sources */,
+				260E00FB13B11F5B0064D447 /* SampleFillType.cpp in Sources */,
+				260E010613B11F5B0064D447 /* SampleLCD.cpp in Sources */,
+				260E011313B11F5B0064D447 /* SamplePath.cpp in Sources */,
+				260E011413B11F5B0064D447 /* SamplePathClip.cpp in Sources */,
+				260E011513B11F5B0064D447 /* SamplePathEffects.cpp in Sources */,
+				260E011713B11F5B0064D447 /* SamplePoints.cpp in Sources */,
+				260E011813B11F5B0064D447 /* SamplePolyToPoly.cpp in Sources */,
+				260E011913B11F5B0064D447 /* SampleRegion.cpp in Sources */,
+				260E011D13B11F5B0064D447 /* SampleShapes.cpp in Sources */,
+				260E011F13B11F5B0064D447 /* SampleSlides.cpp in Sources */,
+				260E012313B11F5B0064D447 /* SampleText.cpp in Sources */,
+				260E012413B11F5B0064D447 /* SampleTextAlpha.cpp in Sources */,
+				260E012513B11F5B0064D447 /* SampleTextBox.cpp in Sources */,
+				260E012613B11F5B0064D447 /* SampleTextEffects.cpp in Sources */,
+				260E012713B11F5B0064D447 /* SampleTextOnPath.cpp in Sources */,
+				260E012B13B11F5B0064D447 /* SampleTriangles.cpp in Sources */,
+				260E012C13B11F5B0064D447 /* SampleTypeface.cpp in Sources */,
+				260E012D13B11F5B0064D447 /* SampleUnitMapper.cpp in Sources */,
+				260E012E13B11F5B0064D447 /* SampleVertices.cpp in Sources */,
+				260E013113B11F5B0064D447 /* SkGPipeRead.cpp in Sources */,
+				260E013213B11F5B0064D447 /* SkGPipeWrite.cpp in Sources */,
+				260E02A213B1225D0064D447 /* Sk64.cpp in Sources */,
+				260E02A313B1225D0064D447 /* SkAdvancedTypefaceMetrics.cpp in Sources */,
+				260E02A413B1225D0064D447 /* SkAlphaRuns.cpp in Sources */,
+				260E02A513B1225D0064D447 /* SkBitmap.cpp in Sources */,
+				260E02A613B1225D0064D447 /* SkBitmapProcShader.cpp in Sources */,
+				260E02A713B1225D0064D447 /* SkBitmapProcState.cpp in Sources */,
+				260E02A813B1225D0064D447 /* SkBitmapProcState_matrixProcs.cpp in Sources */,
+				260E02A913B1225D0064D447 /* SkBitmapSampler.cpp in Sources */,
+				260E02AA13B1225D0064D447 /* SkBitmap_scroll.cpp in Sources */,
+				260E02AD13B1225D0064D447 /* SkBlitRow_D4444.cpp in Sources */,
+				260E02AE13B1225D0064D447 /* SkBlitter.cpp in Sources */,
+				260E02B513B1225D0064D447 /* SkBuffer.cpp in Sources */,
+				260E02B613B1225D0064D447 /* SkCanvas.cpp in Sources */,
+				260E02B713B1225D0064D447 /* SkChunkAlloc.cpp in Sources */,
+				260E02B813B1225D0064D447 /* SkClampRange.cpp in Sources */,
+				260E02B913B1225D0064D447 /* SkClipStack.cpp in Sources */,
+				260E02BA13B1225D0064D447 /* SkColor.cpp in Sources */,
+				260E02BB13B1225D0064D447 /* SkColorFilter.cpp in Sources */,
+				260E02BC13B1225D0064D447 /* SkColorTable.cpp in Sources */,
+				260E02BD13B1225D0064D447 /* SkComposeShader.cpp in Sources */,
+				260E02BE13B1225D0064D447 /* SkConcaveToTriangles.cpp in Sources */,
+				260E02BF13B1225D0064D447 /* SkCordic.cpp in Sources */,
+				260E02C013B1225D0064D447 /* SkCubicClipper.cpp in Sources */,
+				260E02C213B1225D0064D447 /* SkDebug.cpp in Sources */,
+				260E02C313B1225D0064D447 /* SkDeque.cpp in Sources */,
+				260E02C413B1225D0064D447 /* SkDevice.cpp in Sources */,
+				260E02C513B1225D0064D447 /* SkDither.cpp in Sources */,
+				260E02C613B1225D0064D447 /* SkDraw.cpp in Sources */,
+				260E02C713B1225D0064D447 /* SkEdge.cpp in Sources */,
+				260E02C813B1225D0064D447 /* SkEdgeBuilder.cpp in Sources */,
+				260E02C913B1225D0064D447 /* SkEdgeClipper.cpp in Sources */,
+				260E02CA13B1225D0064D447 /* SkFilterProc.cpp in Sources */,
+				260E02CB13B1225D0064D447 /* SkFlate.cpp in Sources */,
+				260E02CC13B1225D0064D447 /* SkFlattenable.cpp in Sources */,
+				260E02CD13B1225D0064D447 /* SkFloat.cpp in Sources */,
+				260E02CE13B1225D0064D447 /* SkFloatBits.cpp in Sources */,
+				260E02CF13B1225D0064D447 /* SkFontHost.cpp in Sources */,
+				260E02D013B1225D0064D447 /* SkGeometry.cpp in Sources */,
+				260E02D113B1225D0064D447 /* SkGlobals.cpp in Sources */,
+				260E02D213B1225D0064D447 /* SkGlyphCache.cpp in Sources */,
+				260E02D313B1225D0064D447 /* SkGraphics.cpp in Sources */,
+				260E02D413B1225D0064D447 /* SkLineClipper.cpp in Sources */,
+				260E02D513B1225D0064D447 /* SkMMapStream.cpp in Sources */,
+				260E02D613B1225D0064D447 /* SkMallocPixelRef.cpp in Sources */,
+				260E02D713B1225D0064D447 /* SkMask.cpp in Sources */,
+				260E02D813B1225D0064D447 /* SkMaskFilter.cpp in Sources */,
+				260E02D913B1225D0064D447 /* SkMath.cpp in Sources */,
+				260E02DA13B1225D0064D447 /* SkMatrix.cpp in Sources */,
+				260E02DB13B1225D0064D447 /* SkMetaData.cpp in Sources */,
+				260E02DC13B1225D0064D447 /* SkPackBits.cpp in Sources */,
+				260E02DD13B1225D0064D447 /* SkPaint.cpp in Sources */,
+				260E02DE13B1225D0064D447 /* SkPath.cpp in Sources */,
+				260E02DF13B1225D0064D447 /* SkPathEffect.cpp in Sources */,
+				260E02E013B1225D0064D447 /* SkPathHeap.cpp in Sources */,
+				260E02E113B1225D0064D447 /* SkPathMeasure.cpp in Sources */,
+				260E02E213B1225D0064D447 /* SkPicture.cpp in Sources */,
+				260E02E313B1225D0064D447 /* SkPictureFlat.cpp in Sources */,
+				260E02E413B1225D0064D447 /* SkPicturePlayback.cpp in Sources */,
+				260E02E513B1225D0064D447 /* SkPictureRecord.cpp in Sources */,
+				260E02E613B1225D0064D447 /* SkPixelRef.cpp in Sources */,
+				260E02E713B1225D0064D447 /* SkPoint.cpp in Sources */,
+				260E02E913B1225D0064D447 /* SkPtrRecorder.cpp in Sources */,
+				260E02EA13B1225D0064D447 /* SkQuadClipper.cpp in Sources */,
+				260E02EB13B1225D0064D447 /* SkRasterizer.cpp in Sources */,
+				260E02EC13B1225D0064D447 /* SkRect.cpp in Sources */,
+				260E02ED13B1225D0064D447 /* SkRefDict.cpp in Sources */,
+				260E02EE13B1225D0064D447 /* SkRegion.cpp in Sources */,
+				260E02EF13B1225D0064D447 /* SkRegion_path.cpp in Sources */,
+				260E02F013B1225D0064D447 /* SkScalar.cpp in Sources */,
+				260E02F113B1225D0064D447 /* SkScalerContext.cpp in Sources */,
+				260E02F213B1225D0064D447 /* SkScan.cpp in Sources */,
+				260E02F313B1225D0064D447 /* SkScan_AntiPath.cpp in Sources */,
+				260E02F413B1225D0064D447 /* SkScan_Antihair.cpp in Sources */,
+				260E02F513B1225D0064D447 /* SkScan_Hairline.cpp in Sources */,
+				260E02F613B1225D0064D447 /* SkScan_Path.cpp in Sources */,
+				260E02F713B1225D0064D447 /* SkShader.cpp in Sources */,
+				260E02F813B1225D0064D447 /* SkShape.cpp in Sources */,
+				260E02FB13B1225D0064D447 /* SkStream.cpp in Sources */,
+				260E02FC13B1225D0064D447 /* SkString.cpp in Sources */,
+				260E02FD13B1225D0064D447 /* SkStroke.cpp in Sources */,
+				260E02FE13B1225D0064D447 /* SkStrokerPriv.cpp in Sources */,
+				260E02FF13B1225D0064D447 /* SkTSearch.cpp in Sources */,
+				260E030013B1225D0064D447 /* SkTypeface.cpp in Sources */,
+				260E030113B1225D0064D447 /* SkTypefaceCache.cpp in Sources */,
+				260E030213B1225D0064D447 /* SkUnPreMultiply.cpp in Sources */,
+				260E030313B1225D0064D447 /* SkUtils.cpp in Sources */,
+				260E030413B1225D0064D447 /* SkWriter32.cpp in Sources */,
+				260E030513B1225D0064D447 /* SkXfermode.cpp in Sources */,
+				260E030713B1225D0064D447 /* SkDebug_stdio.cpp in Sources */,
+				260E030B13B1225D0064D447 /* SkGlobals_global.cpp in Sources */,
+				260E030C13B1225D0064D447 /* SkMemory_malloc.cpp in Sources */,
+				260E030E13B1225D0064D447 /* SkThread_pthread.cpp in Sources */,
+				260E030F13B1225D0064D447 /* SkTime_Unix.cpp in Sources */,
+				260E031113B1225D0064D447 /* SkXMLParser_empty.cpp in Sources */,
+				260E035413B122A30064D447 /* Sk1DPathEffect.cpp in Sources */,
+				260E035513B122A30064D447 /* Sk2DPathEffect.cpp in Sources */,
+				260E035613B122A30064D447 /* SkAvoidXfermode.cpp in Sources */,
+				260E035713B122A30064D447 /* SkBitmapCache.cpp in Sources */,
+				260E035813B122A30064D447 /* SkBlurDrawLooper.cpp in Sources */,
+				260E035913B122A30064D447 /* SkBlurMask.cpp in Sources */,
+				260E035A13B122A30064D447 /* SkBlurMaskFilter.cpp in Sources */,
+				260E035B13B122A30064D447 /* SkColorFilters.cpp in Sources */,
+				260E035C13B122A30064D447 /* SkColorMatrixFilter.cpp in Sources */,
+				260E035D13B122A30064D447 /* SkCornerPathEffect.cpp in Sources */,
+				260E035E13B122A30064D447 /* SkDashPathEffect.cpp in Sources */,
+				260E035F13B122A30064D447 /* SkDiscretePathEffect.cpp in Sources */,
+				260E036013B122A30064D447 /* SkEmbossMask.cpp in Sources */,
+				260E036113B122A30064D447 /* SkEmbossMaskFilter.cpp in Sources */,
+				260E036213B122A30064D447 /* SkGradientShader.cpp in Sources */,
+				260E036313B122A30064D447 /* SkGroupShape.cpp in Sources */,
+				260E036413B122A30064D447 /* SkKernel33MaskFilter.cpp in Sources */,
+				260E036513B122A30064D447 /* SkLayerDrawLooper.cpp in Sources */,
+				260E036613B122A30064D447 /* SkLayerRasterizer.cpp in Sources */,
+				260E036713B122A30064D447 /* SkPaintFlagsDrawFilter.cpp in Sources */,
+				260E036813B122A30064D447 /* SkPixelXorXfermode.cpp in Sources */,
+				260E036913B122A30064D447 /* SkPorterDuff.cpp in Sources */,
+				260E036A13B122A30064D447 /* SkRectShape.cpp in Sources */,
+				260E036B13B122A30064D447 /* SkTransparentShader.cpp in Sources */,
+				260E040C13B122D40064D447 /* GrAllocPool.cpp in Sources */,
+				260E040D13B122D40064D447 /* GrAtlas.cpp in Sources */,
+				260E040E13B122D40064D447 /* GrBufferAllocPool.cpp in Sources */,
+				260E040F13B122D40064D447 /* GrClip.cpp in Sources */,
+				260E041213B122D40064D447 /* GrDrawTarget.cpp in Sources */,
+				260E041413B122D40064D447 /* GrGLIndexBuffer.cpp in Sources */,
+				260E041513B122D40064D447 /* GrGLInterface.cpp in Sources */,
+				260E041613B122D40064D447 /* GrGLProgram.cpp in Sources */,
+				260E041813B122D40064D447 /* GrGLUtil.cpp in Sources */,
+				260E041913B122D40064D447 /* GrGLVertexBuffer.cpp in Sources */,
+				260E041A13B122D40064D447 /* GrGpu.cpp in Sources */,
+				260E041B13B122D40064D447 /* GrGpuFactory.cpp in Sources */,
+				260E041C13B122D40064D447 /* GrGpuGL.cpp in Sources */,
+				260E041D13B122D40064D447 /* GrGpuGLFixed.cpp in Sources */,
+				260E041E13B122D40064D447 /* GrGpuGLShaders.cpp in Sources */,
+				260E041F13B122D40064D447 /* GrInOrderDrawBuffer.cpp in Sources */,
+				260E042013B122D40064D447 /* GrMatrix.cpp in Sources */,
+				260E042113B122D40064D447 /* GrMemory.cpp in Sources */,
+				260E042213B122D40064D447 /* GrPathRenderer.cpp in Sources */,
+				260E042313B122D40064D447 /* GrPathUtils.cpp in Sources */,
+				260E042413B122D40064D447 /* GrRectanizer.cpp in Sources */,
+				260E042513B122D40064D447 /* GrResource.cpp in Sources */,
+				260E042613B122D40064D447 /* GrStencil.cpp in Sources */,
+				260E042813B122D40064D447 /* GrTextContext.cpp in Sources */,
+				260E042913B122D40064D447 /* GrTextStrike.cpp in Sources */,
+				260E042A13B122D40064D447 /* GrTexture.cpp in Sources */,
+				260E042C13B122D40064D447 /* gr_unittests.cpp in Sources */,
+				260E042D13B122D40064D447 /* GrPrintf_skia.cpp in Sources */,
+				260E042E13B122D40064D447 /* SkGpuCanvas.cpp in Sources */,
+				260E042F13B122D40064D447 /* SkGpuDevice.cpp in Sources */,
+				260E043013B122D40064D447 /* SkGr.cpp in Sources */,
+				260E043113B122D40064D447 /* SkGrFontScaler.cpp in Sources */,
+				260E043213B122D40064D447 /* SkGrTexturePixelRef.cpp in Sources */,
+				260E046613B1232F0064D447 /* SkCreateRLEPixelRef.cpp in Sources */,
+				260E046713B1232F0064D447 /* SkFDStream.cpp in Sources */,
+				260E046813B1232F0064D447 /* SkFlipPixelRef.cpp in Sources */,
+				260E046913B1232F0064D447 /* SkImageDecoder.cpp in Sources */,
+				260E047113B1232F0064D447 /* SkImageEncoder.cpp in Sources */,
+				260E050113B123840064D447 /* SkSVGCircle.cpp in Sources */,
+				260E050213B123840064D447 /* SkSVGClipPath.cpp in Sources */,
+				260E050313B123840064D447 /* SkSVGDefs.cpp in Sources */,
+				260E050413B123840064D447 /* SkSVGElements.cpp in Sources */,
+				260E050513B123840064D447 /* SkSVGEllipse.cpp in Sources */,
+				260E050613B123840064D447 /* SkSVGFeColorMatrix.cpp in Sources */,
+				260E050713B123840064D447 /* SkSVGFilter.cpp in Sources */,
+				260E050813B123840064D447 /* SkSVGG.cpp in Sources */,
+				260E050913B123840064D447 /* SkSVGGradient.cpp in Sources */,
+				260E050A13B123840064D447 /* SkSVGGroup.cpp in Sources */,
+				260E050B13B123840064D447 /* SkSVGImage.cpp in Sources */,
+				260E050C13B123840064D447 /* SkSVGLine.cpp in Sources */,
+				260E050D13B123840064D447 /* SkSVGLinearGradient.cpp in Sources */,
+				260E050E13B123840064D447 /* SkSVGMask.cpp in Sources */,
+				260E050F13B123840064D447 /* SkSVGMetadata.cpp in Sources */,
+				260E051013B123840064D447 /* SkSVGPaintState.cpp in Sources */,
+				260E051113B123840064D447 /* SkSVGParser.cpp in Sources */,
+				260E051213B123840064D447 /* SkSVGPath.cpp in Sources */,
+				260E051313B123840064D447 /* SkSVGPolygon.cpp in Sources */,
+				260E051413B123840064D447 /* SkSVGPolyline.cpp in Sources */,
+				260E051513B123840064D447 /* SkSVGRadialGradient.cpp in Sources */,
+				260E051613B123840064D447 /* SkSVGRect.cpp in Sources */,
+				260E051713B123840064D447 /* SkSVGSVG.cpp in Sources */,
+				260E051813B123840064D447 /* SkSVGStop.cpp in Sources */,
+				260E051913B123840064D447 /* SkSVGSymbol.cpp in Sources */,
+				260E051A13B123840064D447 /* SkSVGText.cpp in Sources */,
+				260E051B13B123840064D447 /* SkSVGUse.cpp in Sources */,
+				260E056C13B123DE0064D447 /* SkCreateCGImageRef.cpp in Sources */,
+				260E057713B123DE0064D447 /* SkBoundaryPatch.cpp in Sources */,
+				260E057813B123DE0064D447 /* SkCamera.cpp in Sources */,
+				260E057913B123DE0064D447 /* SkColorMatrix.cpp in Sources */,
+				260E057A13B123DE0064D447 /* SkCubicInterval.cpp in Sources */,
+				260E057B13B123DE0064D447 /* SkCullPoints.cpp in Sources */,
+				260E057C13B123DE0064D447 /* SkDumpCanvas.cpp in Sources */,
+				260E057E13B123DE0064D447 /* SkInterpolator.cpp in Sources */,
+				260E057F13B123DE0064D447 /* SkLayer.cpp in Sources */,
+				260E058013B123DE0064D447 /* SkMatrix44.cpp in Sources */,
+				260E058113B123DE0064D447 /* SkMeshUtils.cpp in Sources */,
+				260E058213B123DE0064D447 /* SkNWayCanvas.cpp in Sources */,
+				260E058313B123DE0064D447 /* SkNinePatch.cpp in Sources */,
+				260E058413B123DE0064D447 /* SkOSFile.cpp in Sources */,
+				260E058513B123DE0064D447 /* SkParse.cpp in Sources */,
+				260E058613B123DE0064D447 /* SkParseColor.cpp in Sources */,
+				260E058713B123DE0064D447 /* SkParsePath.cpp in Sources */,
+				260E058913B123DE0064D447 /* SkSfntUtils.cpp in Sources */,
+				260E058A13B123DE0064D447 /* SkUnitMappers.cpp in Sources */,
+				260E05C913B123E80064D447 /* SkBGViewArtist.cpp in Sources */,
+				260E05CB13B123E80064D447 /* SkEvent.cpp in Sources */,
+				260E05CC13B123E80064D447 /* SkEventSink.cpp in Sources */,
+				260E05CD13B123E80064D447 /* SkImageView.cpp in Sources */,
+				260E05CE13B123E80064D447 /* SkListView.cpp in Sources */,
+				260E05D013B123E80064D447 /* SkOSMenu.cpp in Sources */,
+				260E05D113B123E80064D447 /* SkParsePaint.cpp in Sources */,
+				260E05D513B123E80064D447 /* SkStackViewLayout.cpp in Sources */,
+				260E05D613B123E80064D447 /* SkStaticTextView.cpp in Sources */,
+				260E05D713B123E80064D447 /* SkTagList.cpp in Sources */,
+				260E05D813B123E80064D447 /* SkTextBox.cpp in Sources */,
+				260E05D913B123E80064D447 /* SkTouchGesture.cpp in Sources */,
+				260E05DA13B123E80064D447 /* SkView.cpp in Sources */,
+				260E05DB13B123E80064D447 /* SkViewInflate.cpp in Sources */,
+				260E05DC13B123E80064D447 /* SkViewPriv.cpp in Sources */,
+				260E05DD13B123E80064D447 /* SkWidget.cpp in Sources */,
+				260E05DF13B123E80064D447 /* SkWidgets.cpp in Sources */,
+				260E05E013B123E80064D447 /* SkWindow.cpp in Sources */,
+				260E05FE13B124210064D447 /* SkDOM.cpp in Sources */,
+				260E060113B124210064D447 /* SkXMLParser.cpp in Sources */,
+				260E075313B127E00064D447 /* SkAnimateActive.cpp in Sources */,
+				260E075413B127E00064D447 /* SkAnimateBase.cpp in Sources */,
+				260E075513B127E00064D447 /* SkAnimateField.cpp in Sources */,
+				260E075613B127E00064D447 /* SkAnimateMaker.cpp in Sources */,
+				260E075713B127E00064D447 /* SkAnimateSet.cpp in Sources */,
+				260E075813B127E00064D447 /* SkAnimator.cpp in Sources */,
+				260E075913B127E00064D447 /* SkAnimatorScript.cpp in Sources */,
+				260E075A13B127E00064D447 /* SkBase64.cpp in Sources */,
+				260E075B13B127E00064D447 /* SkBoundable.cpp in Sources */,
+				260E075C13B127E00064D447 /* SkBuildCondensedInfo.cpp in Sources */,
+				260E075D13B127E00064D447 /* SkDisplayAdd.cpp in Sources */,
+				260E075E13B127E00064D447 /* SkDisplayApply.cpp in Sources */,
+				260E075F13B127E00064D447 /* SkDisplayBounds.cpp in Sources */,
+				260E076013B127E00064D447 /* SkDisplayEvent.cpp in Sources */,
+				260E076113B127E00064D447 /* SkDisplayEvents.cpp in Sources */,
+				260E076213B127E00064D447 /* SkDisplayInclude.cpp in Sources */,
+				260E076313B127E00064D447 /* SkDisplayInput.cpp in Sources */,
+				260E076413B127E00064D447 /* SkDisplayList.cpp in Sources */,
+				260E076513B127E00064D447 /* SkDisplayMath.cpp in Sources */,
+				260E076613B127E00064D447 /* SkDisplayMovie.cpp in Sources */,
+				260E076713B127E00064D447 /* SkDisplayNumber.cpp in Sources */,
+				260E076813B127E00064D447 /* SkDisplayPost.cpp in Sources */,
+				260E076913B127E00064D447 /* SkDisplayRandom.cpp in Sources */,
+				260E076A13B127E00064D447 /* SkDisplayScreenplay.cpp in Sources */,
+				260E076B13B127E00064D447 /* SkDisplayType.cpp in Sources */,
+				260E076C13B127E00064D447 /* SkDisplayTypes.cpp in Sources */,
+				260E076D13B127E00064D447 /* SkDisplayXMLParser.cpp in Sources */,
+				260E076E13B127E00064D447 /* SkDisplayable.cpp in Sources */,
+				260E076F13B127E00064D447 /* SkDraw3D.cpp in Sources */,
+				260E077013B127E00064D447 /* SkDrawBitmap.cpp in Sources */,
+				260E077113B127E00064D447 /* SkDrawBlur.cpp in Sources */,
+				260E077213B127E00064D447 /* SkDrawClip.cpp in Sources */,
+				260E077313B127E00064D447 /* SkDrawColor.cpp in Sources */,
+				260E077413B127E00064D447 /* SkDrawDash.cpp in Sources */,
+				260E077513B127E00064D447 /* SkDrawDiscrete.cpp in Sources */,
+				260E077613B127E00064D447 /* SkDrawEmboss.cpp in Sources */,
+				260E077713B127E00064D447 /* SkDrawExtraPathEffect.cpp in Sources */,
+				260E077813B127E00064D447 /* SkDrawFull.cpp in Sources */,
+				260E077913B127E00064D447 /* SkDrawGradient.cpp in Sources */,
+				260E077A13B127E00064D447 /* SkDrawGroup.cpp in Sources */,
+				260E077B13B127E00064D447 /* SkDrawLine.cpp in Sources */,
+				260E077C13B127E00064D447 /* SkDrawMatrix.cpp in Sources */,
+				260E077D13B127E00064D447 /* SkDrawOval.cpp in Sources */,
+				260E077E13B127E00064D447 /* SkDrawPaint.cpp in Sources */,
+				260E077F13B127E00064D447 /* SkDrawPath.cpp in Sources */,
+				260E078013B127E00064D447 /* SkDrawPoint.cpp in Sources */,
+				260E078113B127E00064D447 /* SkDrawRectangle.cpp in Sources */,
+				260E078213B127E00064D447 /* SkDrawSaveLayer.cpp in Sources */,
+				260E078313B127E00064D447 /* SkDrawShader.cpp in Sources */,
+				260E078413B127E00064D447 /* SkDrawText.cpp in Sources */,
+				260E078513B127E00064D447 /* SkDrawTextBox.cpp in Sources */,
+				260E078613B127E00064D447 /* SkDrawTo.cpp in Sources */,
+				260E078713B127E00064D447 /* SkDrawTransparentShader.cpp in Sources */,
+				260E078813B127E00064D447 /* SkDrawable.cpp in Sources */,
+				260E078913B127E00064D447 /* SkDump.cpp in Sources */,
+				260E078A13B127E00064D447 /* SkGetCondensedInfo.cpp in Sources */,
+				260E078B13B127E00064D447 /* SkHitClear.cpp in Sources */,
+				260E078C13B127E00064D447 /* SkHitTest.cpp in Sources */,
+				260E078D13B127E00064D447 /* SkMatrixParts.cpp in Sources */,
+				260E078E13B127E00064D447 /* SkMemberInfo.cpp in Sources */,
+				260E078F13B127E00064D447 /* SkOpArray.cpp in Sources */,
+				260E079013B127E00064D447 /* SkOperandIterpolator.cpp in Sources */,
+				260E079113B127E00064D447 /* SkPaintParts.cpp in Sources */,
+				260E079213B127E00064D447 /* SkParseSVGPath.cpp in Sources */,
+				260E079313B127E00064D447 /* SkPathParts.cpp in Sources */,
+				260E079413B127E00064D447 /* SkPostParts.cpp in Sources */,
+				260E079513B127E00064D447 /* SkScript.cpp in Sources */,
+				260E079613B127E00064D447 /* SkScriptDecompile.cpp in Sources */,
+				260E079713B127E00064D447 /* SkScriptRuntime.cpp in Sources */,
+				260E079813B127E00064D447 /* SkScriptTokenizer.cpp in Sources */,
+				260E079913B127E00064D447 /* SkSnapshot.cpp in Sources */,
+				260E079A13B127E00064D447 /* SkTextOnPath.cpp in Sources */,
+				260E079B13B127E00064D447 /* SkTextToPath.cpp in Sources */,
+				260E079C13B127E00064D447 /* SkTime.cpp in Sources */,
+				260E079D13B127E00064D447 /* SkTypedArray.cpp in Sources */,
+				260E079E13B127E00064D447 /* SkXMLAnimatorWriter.cpp in Sources */,
+				260E07B313B128210064D447 /* SkXMLWriter.cpp in Sources */,
+				260E07C513B128610064D447 /* SkMovie.cpp in Sources */,
+				260E07CA13B128740064D447 /* SkPageFlipper.cpp in Sources */,
+				260E07D113B128870064D447 /* SkImageRef.cpp in Sources */,
+				260E07D213B128870064D447 /* SkImageRefPool.cpp in Sources */,
+				260E07D713B1289C0064D447 /* SkImageRef_GlobalPool.cpp in Sources */,
+				260E083D13B12A200064D447 /* SkImageDecoder_Factory.cpp in Sources */,
+				260E083E13B12A200064D447 /* SkImageEncoder_Factory.cpp in Sources */,
+				260E087F13B12B6F0064D447 /* SkFontHost_mac_coretext.cpp in Sources */,
+				260E095713B134C90064D447 /* FlingState.cpp in Sources */,
+				260E095813B134C90064D447 /* GrDrawMesh.cpp in Sources */,
+				260E147913B2734E0064D447 /* SkUISplitViewController.mm in Sources */,
+				260E16E613B2853F0064D447 /* SampleGM.cpp in Sources */,
+				260E1DCD13B3AA490064D447 /* SkUINavigationController.mm in Sources */,
+				260E1EA213B3B15A0064D447 /* SkPDFCatalog.cpp in Sources */,
+				260E1EA313B3B15A0064D447 /* SkPDFDevice.cpp in Sources */,
+				260E1EA413B3B15A0064D447 /* SkPDFDocument.cpp in Sources */,
+				260E1EA513B3B15A0064D447 /* SkPDFFont.cpp in Sources */,
+				260E1EA613B3B15A0064D447 /* SkPDFFormXObject.cpp in Sources */,
+				260E1EA713B3B15A0064D447 /* SkPDFGraphicState.cpp in Sources */,
+				260E1EA813B3B15A0064D447 /* SkPDFImage.cpp in Sources */,
+				260E1EA913B3B15A0064D447 /* SkPDFPage.cpp in Sources */,
+				260E1EAA13B3B15A0064D447 /* SkPDFShader.cpp in Sources */,
+				260E1EAB13B3B15A0064D447 /* SkPDFStream.cpp in Sources */,
+				260E1EAC13B3B15A0064D447 /* SkPDFTypes.cpp in Sources */,
+				260E1EAD13B3B15A0064D447 /* SkPDFUtils.cpp in Sources */,
+				26677D6613B4C548009319B8 /* SkData.cpp in Sources */,
+				26F548C213B918EC007CC564 /* SkBlitter_4444.cpp in Sources */,
+				26F548C313B918ED007CC564 /* SkBlitter_A1.cpp in Sources */,
+				26F548C413B918ED007CC564 /* SkBlitter_A8.cpp in Sources */,
+				26F548C513B918EE007CC564 /* SkBlitter_ARGB32.cpp in Sources */,
+				26F548C613B918EF007CC564 /* SkBlitter_RGB16.cpp in Sources */,
+				26F548C713B918EF007CC564 /* SkBlitter_Sprite.cpp in Sources */,
+				26F548C813B918F1007CC564 /* SkProcSpriteBlitter.cpp in Sources */,
+				26F548C913B918F2007CC564 /* SkSpriteBlitter_ARGB32.cpp in Sources */,
+				26F548CA13B918F2007CC564 /* SkSpriteBlitter_RGB16.cpp in Sources */,
+				26F548DA13B9192E007CC564 /* SkBlitRow_D32.cpp in Sources */,
+				26F548DB13B9192E007CC564 /* SkBlitRow_D16.cpp in Sources */,
+				26F548E913B91980007CC564 /* SkBitmapProcState_opts_arm.cpp in Sources */,
+				26F548EC13B91980007CC564 /* SkBlitRow_opts_none.cpp in Sources */,
+				26F548ED13B91980007CC564 /* SkUtils_opts_none.cpp in Sources */,
+				26962B2313CDF6A00039B1FB /* SkOSFile_iOS.mm in Sources */,
+				26962C8013CE256E0039B1FB /* SkUIDetailViewController.mm in Sources */,
+				26962C8113CE256E0039B1FB /* SkUIRootViewController.mm in Sources */,
+				26962CA413CE265C0039B1FB /* SkOSWindow_iOS.mm in Sources */,
+				26962CAB13CE268A0039B1FB /* SampleApp.cpp in Sources */,
+				26962D4F13CE2D780039B1FB /* GrGLDefaultInterface_iOS.cpp in Sources */,
+				26FB98D313D0C87000ACBEA0 /* SkUIView.mm in Sources */,
+				2663AC9413D5D8D400C20488 /* SkOptionsTableViewController.mm in Sources */,
+				265C7DE313D75752008329F6 /* SkOptionListController.mm in Sources */,
+				26811E7913DEFAE8001A1609 /* SkBitSet.cpp in Sources */,
+				26A8AFF313E05D7000A3C111 /* GrResourceCache.cpp in Sources */,
+				26FB129313E704AE001AFF6D /* GrGLTexture.cpp in Sources */,
+				26FB129413E704B0001AFF6D /* GrContext.cpp in Sources */,
+				26FB12B013E70D3B001AFF6D /* GrGLRenderTarget.cpp in Sources */,
+				26FB12B413E70D51001AFF6D /* GrRenderTarget.cpp in Sources */,
+				26591EB913EB16EB000DA8A8 /* TransitionView.cpp in Sources */,
+				268C50D613F022820003FF9A /* SkColorPalette.cpp in Sources */,
+				268C50D713F022820003FF9A /* SkNetPipeController.cpp in Sources */,
+				268C50E013F0230C0003FF9A /* SkSockets.cpp in Sources */,
+				2605F44213F18B1B0044A072 /* DebuggerContentView.cpp in Sources */,
+				2605F44313F18B1B0044A072 /* DebuggerCommandsView.cpp in Sources */,
+				2605F44413F18B1B0044A072 /* SkDebugDumper.cpp in Sources */,
+				2605F44513F18B1B0044A072 /* DebuggerStateView.cpp in Sources */,
+				2605F65D13F19D1D0044A072 /* SamplePicture.cpp in Sources */,
+				2605F66513F19EB80044A072 /* GrStencilBuffer.cpp in Sources */,
+				2605F83013F1AE4B0044A072 /* xfermodes.cpp in Sources */,
+				2605F83113F1AE4C0044A072 /* tilemodes.cpp in Sources */,
+				2605F83213F1AE4C0044A072 /* shapes.cpp in Sources */,
+				2605F83313F1AE4D0044A072 /* shadows.cpp in Sources */,
+				2605F83413F1AE4D0044A072 /* shadertext.cpp in Sources */,
+				2605F83513F1AE4E0044A072 /* poly2poly.cpp in Sources */,
+				2605F83613F1AE4F0044A072 /* nocolorbleed.cpp in Sources */,
+				2605F83713F1AE500044A072 /* points.cpp in Sources */,
+				2605F83813F1AE500044A072 /* gradients.cpp in Sources */,
+				2605F83913F1AE510044A072 /* filltypes.cpp in Sources */,
+				2605F83A13F1AE520044A072 /* complexclip.cpp in Sources */,
+				2605F83B13F1AE520044A072 /* blurs.cpp in Sources */,
+				2605F83C13F1AE530044A072 /* bitmapfilters.cpp in Sources */,
+				2612C0D1140D2F9B001B6925 /* ClockFaceView.cpp in Sources */,
+				2612C0D2140D2F9C001B6925 /* SampleAARects.cpp in Sources */,
+				2612C0D3140D2F9C001B6925 /* SampleAnimator.cpp in Sources */,
+				2612C0D4140D2F9D001B6925 /* SampleAll.cpp in Sources */,
+				2612C0D5140D2F9F001B6925 /* SampleBigGradient.cpp in Sources */,
+				2612C0D6140D2F9F001B6925 /* SampleBitmapRect.cpp in Sources */,
+				2612C0D7140D2FA1001B6925 /* SampleCamera.cpp in Sources */,
+				2612C0D8140D2FA2001B6925 /* SampleColorFilter.cpp in Sources */,
+				2612C0D9140D2FA3001B6925 /* SampleConcavePaths.cpp in Sources */,
+				2612C0DA140D2FA4001B6925 /* SampleDecode.cpp in Sources */,
+				2612C0DB140D2FA5001B6925 /* SampleDither.cpp in Sources */,
+				2612C0DC140D2FA8001B6925 /* SampleDitherBitmap.cpp in Sources */,
+				2612C0DD140D2FAA001B6925 /* SampleDrawLooper.cpp in Sources */,
+				2612C0DE140D2FAA001B6925 /* SampleEffects.cpp in Sources */,
+				2612C0DF140D2FAB001B6925 /* SampleEmboss.cpp in Sources */,
+				2612C0E0140D2FAB001B6925 /* SampleEncode.cpp in Sources */,
+				2612C0E1140D2FAC001B6925 /* SampleExtractAlpha.cpp in Sources */,
+				2612C0E2140D2FAD001B6925 /* SampleFilter.cpp in Sources */,
+				2612C0E3140D2FAE001B6925 /* SampleFilter2.cpp in Sources */,
+				2612C0E4140D2FAE001B6925 /* SampleFontCache.cpp in Sources */,
+				2612C0E5140D2FAF001B6925 /* SampleFontScalerTest.cpp in Sources */,
+				2612C0E6140D2FAF001B6925 /* SampleFuzz.cpp in Sources */,
+				2612C0E7140D2FB1001B6925 /* SampleGradients.cpp in Sources */,
+				2612C0E8140D2FB1001B6925 /* SampleHairline.cpp in Sources */,
+				2612C0E9140D2FB2001B6925 /* SampleImage.cpp in Sources */,
+				2612C0EA140D2FB2001B6925 /* SampleImageDir.cpp in Sources */,
+				2612C0EB140D2FB3001B6925 /* SampleLayerMask.cpp in Sources */,
+				2612C0EC140D2FB4001B6925 /* SampleLayers.cpp in Sources */,
+				2612C0ED140D2FB4001B6925 /* SampleLineClipper.cpp in Sources */,
+				2612C0EE140D2FB5001B6925 /* SampleLines.cpp in Sources */,
+				2612C0EF140D2FB7001B6925 /* SampleMeasure.cpp in Sources */,
+				2612C0F0140D2FB8001B6925 /* SampleMipMap.cpp in Sources */,
+				2612C0F1140D2FB8001B6925 /* SampleMovie.cpp in Sources */,
+				2612C0F2140D2FB9001B6925 /* SampleNinePatch.cpp in Sources */,
+				2612C0F3140D2FBA001B6925 /* SampleOvalTest.cpp in Sources */,
+				2612C0F4140D2FBB001B6925 /* SampleOverflow.cpp in Sources */,
+				2612C0F5140D2FBB001B6925 /* SamplePageFlip.cpp in Sources */,
+				2612C0F6140D2FBC001B6925 /* SamplePatch.cpp in Sources */,
+				2612C0F7140D2FBD001B6925 /* SampleRepeatTile.cpp in Sources */,
+				2612C0F8140D2FBE001B6925 /* SampleShaderText.cpp in Sources */,
+				2612C0F9140D2FBE001B6925 /* SampleShaders.cpp in Sources */,
+				2612C0FA140D2FC0001B6925 /* SampleSkLayer.cpp in Sources */,
+				2612C0FB140D2FC1001B6925 /* SampleStrokePath.cpp in Sources */,
+				2612C0FC140D2FC2001B6925 /* SampleStrokeText.cpp in Sources */,
+				2612C0FE140D2FC3001B6925 /* SampleTextureDomain.cpp in Sources */,
+				2612C0FF140D2FC4001B6925 /* SampleTiling.cpp in Sources */,
+				2612C100140D2FC5001B6925 /* SampleTinyBitmap.cpp in Sources */,
+				2612C101140D2FC6001B6925 /* SampleXfermodes.cpp in Sources */,
+				2612C102140D2FC7001B6925 /* SampleXfermodesBlur.cpp in Sources */,
+				2612C106140D2FE3001B6925 /* SkEventNotifier.mm in Sources */,
+				2612C110140D300B001B6925 /* GrDefaultPathRenderer.cpp in Sources */,
+				2612C111140D300B001B6925 /* GrGLStencilBuffer.cpp in Sources */,
+				2612C112140D300B001B6925 /* GrPathRendererChain.cpp in Sources */,
+				2612C122140D30B0001B6925 /* GrAddPathRenderers_none.cpp in Sources */,
+				26D91223140D576B0036A311 /* SkSampleUIView.mm in Sources */,
+				26D9122A140D57BD0036A311 /* skia_ios.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		1D6058940D05DD3E006BFB54 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = iOSSampleApp_Prefix.pch;
+				INFOPLIST_FILE = "iOSSampleApp-Info.plist";
+				PRODUCT_NAME = iOSSampleApp;
+			};
+			name = Debug;
+		};
+		1D6058950D05DD3E006BFB54 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				COPY_PHASE_STRIP = YES;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = iOSSampleApp_Prefix.pch;
+				INFOPLIST_FILE = "iOSSampleApp-Info.plist";
+				PRODUCT_NAME = iOSSampleApp;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		C01FCF4F08A954540054247B /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 2662AB6F13BD067900CDE7E9 /* SkiOSSampleApp-Debug.xcconfig */;
+			buildSettings = {
+				ARCHS = "$(ARCHS_UNIVERSAL_IPHONE_OS)";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					SK_BUILD_FOR_IOS,
+					SK_DEBUG,
+				);
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = NO;
+				PREBINDING = NO;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				USER_HEADER_SEARCH_PATHS = "../../gpu/include/** ../../include/**";
+			};
+			name = Debug;
+		};
+		C01FCF5008A954540054247B /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 2662AB7513BD0C0D00CDE7E9 /* SkiOSSampleApp-Release.xcconfig */;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					SK_RELEASE,
+					SK_BUILD_FOR_IOS,
+				);
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = NO;
+				IPHONEOS_DEPLOYMENT_TARGET = 4.2;
+				OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+				PREBINDING = NO;
+				SDKROOT = iphoneos4.3;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "iOSSampleApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1D6058940D05DD3E006BFB54 /* Debug */,
+				1D6058950D05DD3E006BFB54 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		C01FCF4E08A954540054247B /* Build configuration list for PBXProject "iOSSampleApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				C01FCF4F08A954540054247B /* Debug */,
+				C01FCF5008A954540054247B /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
+}
diff --git a/experimental/iOSSampleApp/iOSSampleApp_Prefix.pch b/experimental/iOSSampleApp/iOSSampleApp_Prefix.pch
new file mode 100644
index 0000000..1c479f4
--- /dev/null
+++ b/experimental/iOSSampleApp/iOSSampleApp_Prefix.pch
@@ -0,0 +1,8 @@
+//
+// Prefix header for all source files of the 'iOSShell' target in the 'iOSShell' project
+//
+
+#ifdef __OBJC__
+    #import <Foundation/Foundation.h>
+    #import <UIKit/UIKit.h>
+#endif
diff --git a/experimental/iOSSampleApp/iPad/AppDelegate_iPad.h b/experimental/iOSSampleApp/iPad/AppDelegate_iPad.h
new file mode 100644
index 0000000..fbc3eac
--- /dev/null
+++ b/experimental/iOSSampleApp/iPad/AppDelegate_iPad.h
@@ -0,0 +1,20 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#import <UIKit/UIKit.h>
+#import "SkUISplitViewController.h"
+
+@interface AppDelegate_iPad : NSObject <UIApplicationDelegate> {
+@private;
+    UIWindow* window;
+    SkUISplitViewController* splitViewController;
+}
+@property (nonatomic, retain) IBOutlet UIWindow* window;
+@property (nonatomic, retain) IBOutlet SkUISplitViewController* splitViewController;
+
+@end
+
diff --git a/experimental/iOSSampleApp/iPad/AppDelegate_iPad.mm b/experimental/iOSSampleApp/iPad/AppDelegate_iPad.mm
new file mode 100644
index 0000000..d0aeaf2
--- /dev/null
+++ b/experimental/iOSSampleApp/iPad/AppDelegate_iPad.mm
@@ -0,0 +1,19 @@
+#import "AppDelegate_iPad.h"
+
+@implementation AppDelegate_iPad
+
+@synthesize window, splitViewController;
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+    [window addSubview:[splitViewController view]];
+    [window makeKeyAndVisible];
+    return YES;
+}
+
+- (void)dealloc {
+    [window release];
+    [splitViewController release];
+    [super dealloc];
+}
+
+@end
diff --git a/experimental/iOSSampleApp/iPad/MainWindow_iPad.xib b/experimental/iOSSampleApp/iPad/MainWindow_iPad.xib
new file mode 100644
index 0000000..6b197d0
--- /dev/null
+++ b/experimental/iOSSampleApp/iPad/MainWindow_iPad.xib
@@ -0,0 +1,565 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="7.10">
+	<data>
+		<int key="IBDocument.SystemTarget">1296</int>
+		<string key="IBDocument.SystemVersion">12B19</string>
+		<string key="IBDocument.InterfaceBuilderVersion">2549</string>
+		<string key="IBDocument.AppKitVersion">1187</string>
+		<string key="IBDocument.HIToolboxVersion">624.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+			<string key="NS.object.0">1498</string>
+		</object>
+		<object class="NSArray" key="IBDocument.IntegratedClassDependencies">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<string>IBProxyObject</string>
+			<string>IBUICustomObject</string>
+			<string>IBUINavigationBar</string>
+			<string>IBUINavigationController</string>
+			<string>IBUINavigationItem</string>
+			<string>IBUISplitViewController</string>
+			<string>IBUITableView</string>
+			<string>IBUITableViewController</string>
+			<string>IBUIView</string>
+			<string>IBUIViewController</string>
+			<string>IBUIWindow</string>
+		</object>
+		<object class="NSArray" key="IBDocument.PluginDependencies">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+			<integer value="1" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="IBProxyObject" id="841351856">
+				<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+			</object>
+			<object class="IBProxyObject" id="606714003">
+				<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+			</object>
+			<object class="IBUICustomObject" id="250404236">
+				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+			</object>
+			<object class="IBUIWindow" id="62075450">
+				<reference key="NSNextResponder"/>
+				<int key="NSvFlags">292</int>
+				<string key="NSFrameSize">{768, 1024}</string>
+				<reference key="NSSuperview"/>
+				<reference key="NSWindow"/>
+				<object class="NSColor" key="IBUIBackgroundColor">
+					<int key="NSColorSpace">1</int>
+					<bytes key="NSRGB">MSAxIDEAA</bytes>
+				</object>
+				<bool key="IBUIOpaque">NO</bool>
+				<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
+				<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
+					<int key="IBUIStatusBarStyle">2</int>
+				</object>
+				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+				<bool key="IBUIResizesToFullScreen">YES</bool>
+			</object>
+			<object class="IBUISplitViewController" id="143532475">
+				<object class="NSArray" key="IBUIToolbarItems" id="0">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+				<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
+					<int key="IBUIStatusBarStyle">2</int>
+				</object>
+				<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+					<int key="IBUIInterfaceOrientation">1</int>
+					<int key="interfaceOrientation">1</int>
+				</object>
+				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+				<bool key="IBUIHorizontal">NO</bool>
+				<object class="IBUINavigationController" key="IBUIMasterViewController" id="524408385">
+					<reference key="IBUIParentViewController" ref="143532475"/>
+					<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
+						<int key="IBUIStatusBarStyle">2</int>
+					</object>
+					<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+						<int key="IBUIInterfaceOrientation">1</int>
+						<int key="interfaceOrientation">1</int>
+					</object>
+					<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+					<bool key="IBUIHorizontal">NO</bool>
+					<object class="IBUINavigationBar" key="IBUINavigationBar" id="593802069">
+						<nil key="NSNextResponder"/>
+						<int key="NSvFlags">256</int>
+						<string key="NSFrameSize">{0, 0}</string>
+						<bool key="IBUIClipsSubviews">YES</bool>
+						<bool key="IBUIMultipleTouchEnabled">YES</bool>
+						<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+						<int key="IBUIBarStyle">1</int>
+					</object>
+					<object class="NSMutableArray" key="IBUIViewControllers">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="IBUITableViewController" id="714382558">
+							<object class="IBUITableView" key="IBUIView" id="805122470">
+								<nil key="NSNextResponder"/>
+								<int key="NSvFlags">274</int>
+								<string key="NSFrameSize">{320, 960}</string>
+								<object class="NSColor" key="IBUIBackgroundColor" id="933040628">
+									<int key="NSColorSpace">3</int>
+									<bytes key="NSWhite">MQA</bytes>
+								</object>
+								<bool key="IBUIClipsSubviews">YES</bool>
+								<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+								<bool key="IBUIAlwaysBounceVertical">YES</bool>
+								<int key="IBUISeparatorStyle">1</int>
+								<int key="IBUISectionIndexMinimumDisplayRowCount">0</int>
+								<bool key="IBUIShowsSelectionImmediatelyOnTouchBegin">YES</bool>
+								<float key="IBUIRowHeight">44</float>
+								<float key="IBUISectionHeaderHeight">22</float>
+								<float key="IBUISectionFooterHeight">22</float>
+							</object>
+							<object class="IBUINavigationItem" key="IBUINavigationItem" id="136024681">
+								<string key="IBUITitle">Samples</string>
+								<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+							</object>
+							<reference key="IBUIParentViewController" ref="524408385"/>
+							<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
+								<int key="IBUIStatusBarStyle">2</int>
+							</object>
+							<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+								<int key="IBUIInterfaceOrientation">1</int>
+								<int key="interfaceOrientation">1</int>
+							</object>
+							<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+							<bool key="IBUIHorizontal">NO</bool>
+							<bool key="IBUIClearsSelectionOnViewWillAppear">NO</bool>
+						</object>
+					</object>
+				</object>
+				<object class="IBUINavigationController" key="IBUIDetailViewController" id="1006871283">
+					<reference key="IBUIParentViewController" ref="143532475"/>
+					<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
+						<int key="IBUIStatusBarStyle">2</int>
+					</object>
+					<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+						<int key="IBUIInterfaceOrientation">1</int>
+						<int key="interfaceOrientation">1</int>
+					</object>
+					<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+					<bool key="IBUIHorizontal">NO</bool>
+					<object class="IBUINavigationBar" key="IBUINavigationBar" id="210980145">
+						<nil key="NSNextResponder"/>
+						<int key="NSvFlags">256</int>
+						<string key="NSFrameSize">{0, 0}</string>
+						<bool key="IBUIClipsSubviews">YES</bool>
+						<bool key="IBUIMultipleTouchEnabled">YES</bool>
+						<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+						<int key="IBUIBarStyle">1</int>
+					</object>
+					<object class="NSMutableArray" key="IBUIViewControllers">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="IBUIViewController" id="659859393">
+							<object class="IBUIView" key="IBUIView" id="879616490">
+								<nil key="NSNextResponder"/>
+								<int key="NSvFlags">274</int>
+								<string key="NSFrameSize">{768, 960}</string>
+								<reference key="IBUIBackgroundColor" ref="933040628"/>
+								<bool key="IBUIMultipleTouchEnabled">YES</bool>
+								<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+							</object>
+							<reference key="IBUIToolbarItems" ref="0"/>
+							<object class="IBUINavigationItem" key="IBUINavigationItem" id="245890386">
+								<string key="IBUITitle">Title</string>
+								<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+							</object>
+							<reference key="IBUIParentViewController" ref="1006871283"/>
+							<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+								<int key="IBUIInterfaceOrientation">1</int>
+								<int key="interfaceOrientation">1</int>
+							</object>
+							<string key="targetRuntimeIdentifier">IBIPadFramework</string>
+							<bool key="IBUIHorizontal">NO</bool>
+						</object>
+					</object>
+				</object>
+			</object>
+		</object>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<object class="NSMutableArray" key="connectionRecords">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="841351856"/>
+						<reference key="destination" ref="250404236"/>
+					</object>
+					<int key="connectionID">8</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">window</string>
+						<reference key="source" ref="250404236"/>
+						<reference key="destination" ref="62075450"/>
+					</object>
+					<int key="connectionID">7</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">splitViewController</string>
+						<reference key="source" ref="250404236"/>
+						<reference key="destination" ref="143532475"/>
+					</object>
+					<int key="connectionID">69</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">fRoot</string>
+						<reference key="source" ref="143532475"/>
+						<reference key="destination" ref="714382558"/>
+					</object>
+					<int key="connectionID">85</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">fDetail</string>
+						<reference key="source" ref="143532475"/>
+						<reference key="destination" ref="659859393"/>
+					</object>
+					<int key="connectionID">172</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">dataSource</string>
+						<reference key="source" ref="805122470"/>
+						<reference key="destination" ref="714382558"/>
+					</object>
+					<int key="connectionID">91</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="805122470"/>
+						<reference key="destination" ref="143532475"/>
+					</object>
+					<int key="connectionID">93</int>
+				</object>
+			</object>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<object class="NSArray" key="orderedObjects">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<reference key="object" ref="0"/>
+						<reference key="children" ref="1000"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="841351856"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="606714003"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">2</int>
+						<reference key="object" ref="62075450"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">6</int>
+						<reference key="object" ref="250404236"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">52</int>
+						<reference key="object" ref="143532475"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="524408385"/>
+							<reference ref="1006871283"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">53</int>
+						<reference key="object" ref="524408385"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="593802069"/>
+							<reference ref="714382558"/>
+						</object>
+						<reference key="parent" ref="143532475"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">55</int>
+						<reference key="object" ref="714382558"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="136024681"/>
+							<reference ref="805122470"/>
+						</object>
+						<reference key="parent" ref="524408385"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">56</int>
+						<reference key="object" ref="593802069"/>
+						<reference key="parent" ref="524408385"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">57</int>
+						<reference key="object" ref="136024681"/>
+						<reference key="parent" ref="714382558"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">89</int>
+						<reference key="object" ref="805122470"/>
+						<reference key="parent" ref="714382558"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">138</int>
+						<reference key="object" ref="1006871283"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="659859393"/>
+							<reference ref="210980145"/>
+						</object>
+						<reference key="parent" ref="143532475"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">142</int>
+						<reference key="object" ref="659859393"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="245890386"/>
+							<reference ref="879616490"/>
+						</object>
+						<reference key="parent" ref="1006871283"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">140</int>
+						<reference key="object" ref="210980145"/>
+						<reference key="parent" ref="1006871283"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">162</int>
+						<reference key="object" ref="245890386"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="parent" ref="659859393"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">143</int>
+						<reference key="object" ref="879616490"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="parent" ref="659859393"/>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="flattenedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="NSArray" key="dict.sortedKeys">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>-1.CustomClassName</string>
+					<string>-1.IBPluginDependency</string>
+					<string>-2.CustomClassName</string>
+					<string>-2.IBPluginDependency</string>
+					<string>138.IBPluginDependency</string>
+					<string>140.IBPluginDependency</string>
+					<string>142.CustomClassName</string>
+					<string>142.IBPluginDependency</string>
+					<string>143.CustomClassName</string>
+					<string>143.IBPluginDependency</string>
+					<string>162.IBPluginDependency</string>
+					<string>2.IBPluginDependency</string>
+					<string>52.CustomClassName</string>
+					<string>52.IBPluginDependency</string>
+					<string>53.IBPluginDependency</string>
+					<string>55.CustomClassName</string>
+					<string>55.IBPluginDependency</string>
+					<string>56.IBPluginDependency</string>
+					<string>57.IBPluginDependency</string>
+					<string>6.CustomClassName</string>
+					<string>6.IBPluginDependency</string>
+					<string>89.IBPluginDependency</string>
+				</object>
+				<object class="NSArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>UIApplication</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>UIResponder</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>SkUIDetailViewController</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>SkSampleUIView</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>SkUISplitViewController</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>SkUIRootViewController</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>AppDelegate_iPad</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="unlocalizedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<reference key="dict.values" ref="0"/>
+			</object>
+			<nil key="activeLocalization"/>
+			<object class="NSMutableDictionary" key="localizations">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<reference key="dict.values" ref="0"/>
+			</object>
+			<nil key="sourceID"/>
+			<int key="maxID">197</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">AppDelegate_iPad</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>splitViewController</string>
+							<string>window</string>
+						</object>
+						<object class="NSArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>SkUISplitViewController</string>
+							<string>UIWindow</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>splitViewController</string>
+							<string>window</string>
+						</object>
+						<object class="NSArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<object class="IBToOneOutletInfo">
+								<string key="name">splitViewController</string>
+								<string key="candidateClassName">SkUISplitViewController</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">window</string>
+								<string key="candidateClassName">UIWindow</string>
+							</object>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">./Classes/AppDelegate_iPad.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkSampleUIView</string>
+					<string key="superclassName">SkUIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">./Classes/SkSampleUIView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkUIDetailViewController</string>
+					<string key="superclassName">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">./Classes/SkUIDetailViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkUIRootViewController</string>
+					<string key="superclassName">UITableViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">./Classes/SkUIRootViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkUISplitViewController</string>
+					<string key="superclassName">UISplitViewController</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>fDetail</string>
+							<string>fRoot</string>
+						</object>
+						<object class="NSArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>SkUIDetailViewController</string>
+							<string>SkUIRootViewController</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>fDetail</string>
+							<string>fRoot</string>
+						</object>
+						<object class="NSArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<object class="IBToOneOutletInfo">
+								<string key="name">fDetail</string>
+								<string key="candidateClassName">SkUIDetailViewController</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">fRoot</string>
+								<string key="candidateClassName">SkUIRootViewController</string>
+							</object>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">./Classes/SkUISplitViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkUIView</string>
+					<string key="superclassName">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">./Classes/SkUIView.h</string>
+					</object>
+				</object>
+			</object>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBIPadFramework</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
+			<real value="1296" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
+			<integer value="3100" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<string key="IBCocoaTouchPluginVersion">1498</string>
+	</data>
+</archive>
diff --git a/experimental/iOSSampleApp/iPad/SkUISplitViewController.h b/experimental/iOSSampleApp/iPad/SkUISplitViewController.h
new file mode 100644
index 0000000..c333bed
--- /dev/null
+++ b/experimental/iOSSampleApp/iPad/SkUISplitViewController.h
@@ -0,0 +1,20 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#import <UIKit/UIKit.h>
+#import "SkUIRootViewController.h"
+#import "SkUIDetailViewController.h"
+
+@interface SkUISplitViewController : UISplitViewController <UITableViewDelegate, UISplitViewControllerDelegate> {
+@private
+    SkUIRootViewController* fRoot;
+    SkUIDetailViewController* fDetail;
+}
+@property (nonatomic, retain) IBOutlet SkUIRootViewController* fRoot;
+@property (nonatomic, retain) IBOutlet SkUIDetailViewController* fDetail;
+
+@end
diff --git a/experimental/iOSSampleApp/iPad/SkUISplitViewController.mm b/experimental/iOSSampleApp/iPad/SkUISplitViewController.mm
new file mode 100644
index 0000000..ceb7356
--- /dev/null
+++ b/experimental/iOSSampleApp/iPad/SkUISplitViewController.mm
@@ -0,0 +1,50 @@
+#import "SkUISplitViewController.h"
+
+@implementation SkUISplitViewController
+@synthesize fRoot, fDetail;
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+    return YES; //Auto Rotation for all orientations
+}
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    self.delegate = self;
+    [fDetail populateRoot:fRoot];
+}
+
+- (void)dealloc {
+    [fRoot release];
+    [fDetail release];
+    [super dealloc];
+}
+
+//Table View Delegate Methods
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+    [fDetail goToItem:indexPath.row];
+    if (fRoot.popoverController != nil) {
+        [fRoot.popoverController dismissPopoverAnimated:YES];
+    }
+}
+
+//Split View Controller Delegate
+- (void)splitViewController:(UISplitViewController*)svc
+     willHideViewController:(UIViewController *)aViewController
+          withBarButtonItem:(UIBarButtonItem*)barButtonItem
+       forPopoverController:(UIPopoverController*)pc {
+
+    barButtonItem.title = @"Samples";
+    fRoot.popoverController = pc;
+    fRoot.popoverButtonItem = barButtonItem;
+    [fDetail showRootPopoverButtonItem:fRoot.popoverButtonItem];
+}
+
+- (void)splitViewController:(UISplitViewController*)svc
+     willShowViewController:(UIViewController *)aViewController
+  invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem {
+    [fDetail invalidateRootPopoverButtonItem:fRoot.popoverButtonItem];
+    fRoot.popoverController = nil;
+    fRoot.popoverButtonItem = nil;
+}
+
+@end
diff --git a/experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h b/experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h
new file mode 100644
index 0000000..b5829b2
--- /dev/null
+++ b/experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h
@@ -0,0 +1,20 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#import <UIKit/UIKit.h>
+#import "SkUINavigationController.h"
+
+@interface AppDelegate_iPhone : NSObject <UIApplicationDelegate> {
+@private
+    UIWindow *window;
+    SkUINavigationController* fRoot;
+}
+@property (nonatomic, retain) IBOutlet UIWindow *window;
+@property (nonatomic, retain) IBOutlet SkUINavigationController* fRoot;
+
+@end
+
diff --git a/experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.mm b/experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.mm
new file mode 100644
index 0000000..80c5a44
--- /dev/null
+++ b/experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.mm
@@ -0,0 +1,18 @@
+#import "AppDelegate_iPhone.h"
+
+@implementation AppDelegate_iPhone
+@synthesize window, fRoot;
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+    [window addSubview:fRoot.view];
+    [window makeKeyAndVisible];
+    return YES;
+}
+
+- (void)dealloc {
+    [window release];
+    [fRoot release];
+    [super dealloc];
+}
+
+@end
diff --git a/experimental/iOSSampleApp/iPhone/MainWindow_iPhone.xib b/experimental/iOSSampleApp/iPhone/MainWindow_iPhone.xib
new file mode 100644
index 0000000..a270390
--- /dev/null
+++ b/experimental/iOSSampleApp/iPhone/MainWindow_iPhone.xib
@@ -0,0 +1,831 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
+	<data>
+		<int key="IBDocument.SystemTarget">1056</int>
+		<string key="IBDocument.SystemVersion">10K540</string>
+		<string key="IBDocument.InterfaceBuilderVersion">851</string>
+		<string key="IBDocument.AppKitVersion">1038.36</string>
+		<string key="IBDocument.HIToolboxVersion">461.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+			<string key="NS.object.0">141</string>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<integer value="94"/>
+			<integer value="10"/>
+		</object>
+		<object class="NSArray" key="IBDocument.PluginDependencies">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="NSArray" key="dict.sortedKeys" id="0">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+			</object>
+			<object class="NSMutableArray" key="dict.values">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+			</object>
+		</object>
+		<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
+			<bool key="EncodedWithXMLCoder">YES</bool>
+			<object class="IBProxyObject" id="841351856">
+				<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
+				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+			</object>
+			<object class="IBProxyObject" id="450319686">
+				<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
+				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+			</object>
+			<object class="IBUICustomObject" id="987256611">
+				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+			</object>
+			<object class="IBUIWindow" id="380026005">
+				<nil key="NSNextResponder"/>
+				<int key="NSvFlags">1316</int>
+				<object class="NSPSMatrix" key="NSFrameMatrix"/>
+				<string key="NSFrameSize">{320, 480}</string>
+				<object class="NSColor" key="IBUIBackgroundColor">
+					<int key="NSColorSpace">1</int>
+					<bytes key="NSRGB">MSAxIDEAA</bytes>
+				</object>
+				<bool key="IBUIOpaque">NO</bool>
+				<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
+				<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+				<bool key="IBUIResizesToFullScreen">YES</bool>
+			</object>
+			<object class="IBUINavigationController" id="490735104">
+				<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
+					<int key="IBUIStatusBarStyle">2</int>
+				</object>
+				<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+					<int key="interfaceOrientation">1</int>
+				</object>
+				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+				<bool key="IBUIHorizontal">NO</bool>
+				<object class="IBUINavigationBar" key="IBUINavigationBar" id="1017823495">
+					<nil key="NSNextResponder"/>
+					<int key="NSvFlags">256</int>
+					<string key="NSFrameSize">{0, 0}</string>
+					<bool key="IBUIOpaque">NO</bool>
+					<bool key="IBUIClipsSubviews">YES</bool>
+					<bool key="IBUIMultipleTouchEnabled">YES</bool>
+					<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+					<int key="IBUIBarStyle">1</int>
+				</object>
+				<object class="NSMutableArray" key="IBUIViewControllers">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBUIViewController" id="386778494">
+						<object class="IBUIView" key="IBUIView" id="456730278">
+							<reference key="NSNextResponder"/>
+							<int key="NSvFlags">274</int>
+							<string key="NSFrameSize">{320, 416}</string>
+							<reference key="NSSuperview"/>
+							<object class="NSColor" key="IBUIBackgroundColor" id="535258798">
+								<int key="NSColorSpace">3</int>
+								<bytes key="NSWhite">MQA</bytes>
+							</object>
+							<bool key="IBUIMultipleTouchEnabled">YES</bool>
+							<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+						</object>
+						<reference key="IBUIToolbarItems" ref="0"/>
+						<object class="IBUINavigationItem" key="IBUINavigationItem" id="694217933">
+							<reference key="IBUINavigationBar"/>
+							<string key="IBUITitle">Item</string>
+							<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+						</object>
+						<reference key="IBUIParentViewController" ref="490735104"/>
+						<object class="IBUISimulatedNavigationBarMetrics" key="IBUISimulatedTopBarMetrics" id="18577453">
+							<int key="IBUIBarStyle">1</int>
+							<bool key="IBUIPrompted">NO</bool>
+						</object>
+						<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+							<int key="interfaceOrientation">1</int>
+						</object>
+						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+						<bool key="IBUIHorizontal">NO</bool>
+					</object>
+				</object>
+			</object>
+			<object class="IBUINavigationController" id="922975573">
+				<reference key="IBUISimulatedTopBarMetrics" ref="18577453"/>
+				<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+					<int key="interfaceOrientation">1</int>
+				</object>
+				<bool key="IBUIWantsFullScreenLayout">YES</bool>
+				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+				<bool key="IBUIHorizontal">NO</bool>
+				<object class="IBUINavigationBar" key="IBUINavigationBar" id="499920774">
+					<nil key="NSNextResponder"/>
+					<int key="NSvFlags">256</int>
+					<string key="NSFrame">{{0, -44}, {0, 44}}</string>
+					<bool key="IBUIOpaque">NO</bool>
+					<bool key="IBUIClipsSubviews">YES</bool>
+					<bool key="IBUIMultipleTouchEnabled">YES</bool>
+					<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+					<int key="IBUIBarStyle">1</int>
+				</object>
+				<object class="NSMutableArray" key="IBUIViewControllers">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBUITableViewController" id="711816508">
+						<object class="IBUITableView" key="IBUIView" id="301135056">
+							<reference key="NSNextResponder"/>
+							<int key="NSvFlags">274</int>
+							<string key="NSFrameSize">{320, 436}</string>
+							<reference key="NSSuperview"/>
+							<reference key="IBUIBackgroundColor" ref="535258798"/>
+							<bool key="IBUIClipsSubviews">YES</bool>
+							<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+							<bool key="IBUIAlwaysBounceVertical">YES</bool>
+							<int key="IBUISeparatorStyle">1</int>
+							<int key="IBUISectionIndexMinimumDisplayRowCount">0</int>
+							<bool key="IBUIShowsSelectionImmediatelyOnTouchBegin">YES</bool>
+							<float key="IBUIRowHeight">44</float>
+							<float key="IBUISectionHeaderHeight">22</float>
+							<float key="IBUISectionFooterHeight">22</float>
+						</object>
+						<object class="IBUINavigationItem" key="IBUINavigationItem" id="948852329">
+							<reference key="IBUINavigationBar"/>
+							<string key="IBUITitle">Samples</string>
+							<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+						</object>
+						<reference key="IBUIParentViewController" ref="922975573"/>
+						<object class="IBUISimulatedNavigationBarMetrics" key="IBUISimulatedTopBarMetrics">
+							<bool key="IBUIPrompted">NO</bool>
+						</object>
+						<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
+						<object class="IBUISimulatedOrientationMetrics" key="IBUISimulatedOrientationMetrics">
+							<int key="interfaceOrientation">1</int>
+						</object>
+						<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
+						<bool key="IBUIHorizontal">NO</bool>
+					</object>
+				</object>
+			</object>
+		</object>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<object class="NSMutableArray" key="connectionRecords">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="841351856"/>
+						<reference key="destination" ref="987256611"/>
+					</object>
+					<int key="connectionID">5</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">window</string>
+						<reference key="source" ref="987256611"/>
+						<reference key="destination" ref="380026005"/>
+					</object>
+					<int key="connectionID">6</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">fDetail</string>
+						<reference key="source" ref="922975573"/>
+						<reference key="destination" ref="386778494"/>
+					</object>
+					<int key="connectionID">39</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">fRoot</string>
+						<reference key="source" ref="987256611"/>
+						<reference key="destination" ref="922975573"/>
+					</object>
+					<int key="connectionID">48</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">fRoot</string>
+						<reference key="source" ref="922975573"/>
+						<reference key="destination" ref="711816508"/>
+					</object>
+					<int key="connectionID">53</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">dataSource</string>
+						<reference key="source" ref="301135056"/>
+						<reference key="destination" ref="711816508"/>
+					</object>
+					<int key="connectionID">56</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBCocoaTouchOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="301135056"/>
+						<reference key="destination" ref="922975573"/>
+					</object>
+					<int key="connectionID">78</int>
+				</object>
+			</object>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<object class="NSArray" key="orderedObjects">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<reference key="object" ref="0"/>
+						<reference key="children" ref="1000"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">2</int>
+						<reference key="object" ref="380026005"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="841351856"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">4</int>
+						<reference key="object" ref="987256611"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">App Delegate</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="450319686"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">10</int>
+						<reference key="object" ref="922975573"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="499920774"/>
+							<reference ref="711816508"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">12</int>
+						<reference key="object" ref="499920774"/>
+						<reference key="parent" ref="922975573"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">28</int>
+						<reference key="object" ref="711816508"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="301135056"/>
+							<reference ref="948852329"/>
+						</object>
+						<reference key="parent" ref="922975573"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">54</int>
+						<reference key="object" ref="301135056"/>
+						<reference key="parent" ref="711816508"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">83</int>
+						<reference key="object" ref="948852329"/>
+						<reference key="parent" ref="711816508"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">94</int>
+						<reference key="object" ref="490735104"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="1017823495"/>
+							<reference ref="386778494"/>
+						</object>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">96</int>
+						<reference key="object" ref="1017823495"/>
+						<reference key="parent" ref="490735104"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">11</int>
+						<reference key="object" ref="386778494"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<reference ref="456730278"/>
+							<reference ref="694217933"/>
+						</object>
+						<reference key="parent" ref="490735104"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">35</int>
+						<reference key="object" ref="456730278"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="parent" ref="386778494"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">98</int>
+						<reference key="object" ref="694217933"/>
+						<object class="NSMutableArray" key="children">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+						<reference key="parent" ref="386778494"/>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="flattenedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="NSArray" key="dict.sortedKeys">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>-1.CustomClassName</string>
+					<string>-2.CustomClassName</string>
+					<string>10.CustomClassName</string>
+					<string>10.IBEditorWindowLastContentRect</string>
+					<string>10.IBPluginDependency</string>
+					<string>11.CustomClassName</string>
+					<string>11.IBEditorWindowLastContentRect</string>
+					<string>11.IBPluginDependency</string>
+					<string>12.IBPluginDependency</string>
+					<string>2.IBAttributePlaceholdersKey</string>
+					<string>2.IBEditorWindowLastContentRect</string>
+					<string>2.IBPluginDependency</string>
+					<string>2.UIWindow.visibleAtLaunch</string>
+					<string>28.CustomClassName</string>
+					<string>28.IBEditorWindowLastContentRect</string>
+					<string>28.IBPluginDependency</string>
+					<string>35.CustomClassName</string>
+					<string>35.IBPluginDependency</string>
+					<string>4.CustomClassName</string>
+					<string>4.IBPluginDependency</string>
+					<string>54.IBPluginDependency</string>
+					<string>54.IBViewBoundsToFrameTransform</string>
+					<string>83.IBPluginDependency</string>
+					<string>94.IBEditorWindowLastContentRect</string>
+					<string>94.IBPluginDependency</string>
+					<string>96.IBPluginDependency</string>
+				</object>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+					<string>UIApplication</string>
+					<string>UIResponder</string>
+					<string>SkUINavigationController</string>
+					<string>{{351, 526}, {320, 480}}</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>SkUIDetailViewController</string>
+					<string>{{445, 495}, {320, 480}}</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<object class="NSMutableDictionary">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<reference key="dict.sortedKeys" ref="0"/>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+						</object>
+					</object>
+					<string>{{520, 376}, {320, 480}}</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<integer value="1"/>
+					<string>SkUIRootViewController</string>
+					<string>{{360, 1385}, {320, 480}}</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>SkSampleUIView</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>AppDelegate_iPhone</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<object class="NSAffineTransform">
+						<bytes key="NSTransformStruct">P4AAAL+AAAAAAAAAw9kAAA</bytes>
+					</object>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>{{1356, 526}, {320, 480}}</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+					<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
+				</object>
+			</object>
+			<object class="NSMutableDictionary" key="unlocalizedProperties">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="activeLocalization"/>
+			<object class="NSMutableDictionary" key="localizations">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<reference key="dict.sortedKeys" ref="0"/>
+				<object class="NSMutableArray" key="dict.values">
+					<bool key="EncodedWithXMLCoder">YES</bool>
+				</object>
+			</object>
+			<nil key="sourceID"/>
+			<int key="maxID">99</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<object class="NSMutableArray" key="referencedPartialClassDescriptions">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">AppDelegate_iPhone</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>fRoot</string>
+							<string>window</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>SkUINavigationController</string>
+							<string>UIWindow</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>fRoot</string>
+							<string>window</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<object class="IBToOneOutletInfo">
+								<string key="name">fRoot</string>
+								<string key="candidateClassName">SkUINavigationController</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">window</string>
+								<string key="candidateClassName">UIWindow</string>
+							</object>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">iPhone/AppDelegate_iPhone.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkSampleUIView</string>
+					<string key="superclassName">SkUIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">SkSampleUIView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkUIDetailViewController</string>
+					<string key="superclassName">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">Shared/SkUIDetailViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkUINavigationController</string>
+					<string key="superclassName">UINavigationController</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>fDetail</string>
+							<string>fRoot</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>SkUIDetailViewController</string>
+							<string>SkUIRootViewController</string>
+						</object>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<bool key="EncodedWithXMLCoder">YES</bool>
+						<object class="NSArray" key="dict.sortedKeys">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<string>fDetail</string>
+							<string>fRoot</string>
+						</object>
+						<object class="NSMutableArray" key="dict.values">
+							<bool key="EncodedWithXMLCoder">YES</bool>
+							<object class="IBToOneOutletInfo">
+								<string key="name">fDetail</string>
+								<string key="candidateClassName">SkUIDetailViewController</string>
+							</object>
+							<object class="IBToOneOutletInfo">
+								<string key="name">fRoot</string>
+								<string key="candidateClassName">SkUIRootViewController</string>
+							</object>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">iPhone/SkUINavigationController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkUIRootViewController</string>
+					<string key="superclassName">UITableViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">Shared/SkUIRootViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkUIView</string>
+					<string key="superclassName">UIView</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">fOptionsDelegate</string>
+						<string key="NS.object.0">id</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">fOptionsDelegate</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">fOptionsDelegate</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">Shared/SkUIView.h</string>
+					</object>
+				</object>
+			</object>
+			<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+				<bool key="EncodedWithXMLCoder">YES</bool>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">QuartzCore.framework/Headers/CAAnimation.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">QuartzCore.framework/Headers/CALayer.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="565734826">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIApplication</string>
+					<string key="superclassName">UIResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIApplication.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIBarButtonItem</string>
+					<string key="superclassName">UIBarItem</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIBarButtonItem.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIBarItem</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIBarItem.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UINavigationBar</string>
+					<string key="superclassName">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="443745583">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UINavigationBar.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UINavigationController</string>
+					<string key="superclassName">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier" id="868507592">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UINavigationItem</string>
+					<string key="superclassName">NSObject</string>
+					<reference key="sourceIdentifier" ref="443745583"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIResponder</string>
+					<string key="superclassName">NSObject</string>
+					<reference key="sourceIdentifier" ref="565734826"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIScrollView</string>
+					<string key="superclassName">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIScrollView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UISearchBar</string>
+					<string key="superclassName">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UISearchDisplayController</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UITableView</string>
+					<string key="superclassName">UIScrollView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UITableView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UITableViewController</string>
+					<string key="superclassName">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UITableViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIPrintFormatter.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIView</string>
+					<string key="superclassName">UIResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<reference key="sourceIdentifier" ref="868507592"/>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIViewController</string>
+					<string key="superclassName">UIResponder</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIViewController.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">UIWindow</string>
+					<string key="superclassName">UIView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">UIKit.framework/Headers/UIWindow.h</string>
+					</object>
+				</object>
+			</object>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
+			<integer value="1056" key="NS.object.0"/>
+		</object>
+		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
+			<integer value="3100" key="NS.object.0"/>
+		</object>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<string key="IBDocument.LastKnownRelativeProjectPath">../iOSSampleApp.xcodeproj</string>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<string key="IBCocoaTouchPluginVersion">141</string>
+	</data>
+</archive>
diff --git a/experimental/iOSSampleApp/iPhone/SkUINavigationController.h b/experimental/iOSSampleApp/iPhone/SkUINavigationController.h
new file mode 100644
index 0000000..1078a33
--- /dev/null
+++ b/experimental/iOSSampleApp/iPhone/SkUINavigationController.h
@@ -0,0 +1,20 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#import <UIKit/UIKit.h>
+#import "SkUIRootViewController.h"
+#import "SkUIDetailViewController.h"
+
+@interface SkUINavigationController : UINavigationController <UITableViewDelegate, UINavigationBarDelegate> {
+@private
+    SkUIRootViewController* fRoot;
+    SkUIDetailViewController* fDetail;
+}
+@property (nonatomic, retain) IBOutlet SkUIRootViewController* fRoot;
+@property (nonatomic, retain) IBOutlet SkUIDetailViewController* fDetail;
+
+@end
diff --git a/experimental/iOSSampleApp/iPhone/SkUINavigationController.mm b/experimental/iOSSampleApp/iPhone/SkUINavigationController.mm
new file mode 100644
index 0000000..b430560
--- /dev/null
+++ b/experimental/iOSSampleApp/iPhone/SkUINavigationController.mm
@@ -0,0 +1,28 @@
+#import "SkUINavigationController.h"
+
+@implementation SkUINavigationController
+@synthesize fRoot, fDetail;
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    [fDetail populateRoot:fRoot];
+    [self pushViewController:fDetail animated:NO];
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
+    return YES; //Allow auto rotation for all orientations
+}
+
+- (void)dealloc {
+    [fRoot release];
+    [fDetail release];
+    [super dealloc];
+}
+
+//Table View Delegate Methods
+- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+    [fDetail goToItem:indexPath.row];
+    [self pushViewController:fDetail animated:YES];
+}
+
+@end
\ No newline at end of file
diff --git a/experimental/pixman/Pixman-version.h b/experimental/pixman/Pixman-version.h
new file mode 100644
index 0000000..d4ea7c7
--- /dev/null
+++ b/experimental/pixman/Pixman-version.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2008 Red Hat, Inc.
+ *
+ * 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 and this permission notice 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * Author: Carl D. Worth <cworth@cworth.org>
+ */
+
+#ifndef PIXMAN_VERSION_H__
+#define PIXMAN_VERSION_H__
+
+#ifndef PIXMAN_H__
+#  error pixman-version.h should only be included by pixman.h
+#endif
+
+#define PIXMAN_VERSION_MAJOR 0
+#define PIXMAN_VERSION_MINOR 27
+#define PIXMAN_VERSION_MICRO 33
+
+#define PIXMAN_VERSION_STRING "@PIXMAN_VERSION_MAJOR@.@PIXMAN_VERSION_MINOR@.@PIXMAN_VERSION_MICRO@"
+
+#define PIXMAN_VERSION_ENCODE(major, minor, micro) (	\
+	  ((major) * 10000)				\
+	+ ((minor) *   100)				\
+	+ ((micro) *     1))
+
+#define PIXMAN_VERSION PIXMAN_VERSION_ENCODE(	\
+	PIXMAN_VERSION_MAJOR,			\
+	PIXMAN_VERSION_MINOR,			\
+	PIXMAN_VERSION_MICRO)
+
+#endif /* PIXMAN_VERSION_H__ */
diff --git a/experimental/pixman/config.h b/experimental/pixman/config.h
new file mode 100644
index 0000000..4546edc
--- /dev/null
+++ b/experimental/pixman/config.h
@@ -0,0 +1,3 @@
+#define PACKAGE
+#define HAVE_PTHREAD_SETSPECIFIC
+#define PREFIX(a) a
diff --git a/experimental/pixman/junk.cpp b/experimental/pixman/junk.cpp
new file mode 100644
index 0000000..6a93bbd
--- /dev/null
+++ b/experimental/pixman/junk.cpp
@@ -0,0 +1,108 @@
+
+extern "C" {
+#include <stdio.h>
+#include <stdlib.h>
+#include <config.h>
+#include "pixman-private.h"
+#include "utils.h"
+#include "gtk-utils.h"
+
+}
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkPaint.h"
+#import "SkWindow.h"
+
+bool DrawPixman(SkCanvas* canvas, int step, bool useOld);
+SkCanvas* canvas;
+
+extern "C" {
+
+void*
+pixbuf_from_argb32 (uint32_t *bits,
+		    int width,
+		    int height,
+		    int stride)
+{
+    SkBitmap* bitmap = new SkBitmap;
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    bitmap->allocPixels();
+
+    int p_stride = bitmap->rowBytes();
+    uint32_t *p_bits = bitmap->getAddr32(0, 0);
+    int i;
+
+    for (i = 0; i < height; ++i)
+    {
+	uint32_t *src_row = &bits[i * (stride / 4)];
+	uint32_t *dst_row = p_bits + i * (p_stride / 4);
+
+	a8r8g8b8_to_rgba_np (dst_row, src_row, width);
+    }
+    return (void*) bitmap;
+}
+
+
+void show_image (pixman_image_t *image) {
+    int width, height;
+    pixman_format_code_t format;
+    pixman_image_t *copy;
+    
+    width = pixman_image_get_width (image);
+    height = pixman_image_get_height (image);
+
+
+    format = pixman_image_get_format (image);
+
+    /* Three cases:
+     *
+     *  - image is a8r8g8b8_sRGB: we will display without modification
+     *    under the assumption that the monitor is sRGB
+     *
+     *  - image is a8r8g8b8: we will display without modification
+     *    under the assumption that whoever created the image
+     *    probably did it wrong by using sRGB inputs
+     *
+     *  - other: we will convert to a8r8g8b8 under the assumption that
+     *    whoever created the image probably did it wrong.
+     */
+    switch (format)
+    {
+    case PIXMAN_a8r8g8b8_sRGB:
+    case PIXMAN_a8r8g8b8:
+	copy = pixman_image_ref (image);
+	break;
+
+    default:
+	copy = pixman_image_create_bits (PIXMAN_a8r8g8b8,
+					 width, height, NULL, -1);
+	pixman_image_composite32 (PIXMAN_OP_SRC,
+				  image, NULL, copy,
+				  0, 0, 0, 0, 0, 0,
+				  width, height);
+	break;
+    }
+
+    SkBitmap* bitmap = (SkBitmap*) pixbuf_from_argb32 (pixman_image_get_data (copy),
+				 width, height,
+				 pixman_image_get_stride (copy));
+    canvas->drawBitmap(*bitmap, 0, 0);
+    delete bitmap;
+}
+
+}
+
+bool DrawPixman(SkCanvas* c, int step, bool usePixman) {
+    canvas = c;
+    switch(step) {
+        case 0:
+            checkerboard_main(0, NULL);
+            break;
+        default:
+            alpha_main(0, NULL);
+            break;
+    }
+    return true;
+}
diff --git a/experimental/pixman/pixman.mm b/experimental/pixman/pixman.mm
new file mode 100644
index 0000000..f4db3df
--- /dev/null
+++ b/experimental/pixman/pixman.mm
@@ -0,0 +1,99 @@
+
+#import "SkCanvas.h"
+#import "SkWindow.h"
+#include "SkGraphics.h"
+#include "SkCGUtils.h"
+
+#include <time.h>
+#include <sys/time.h>
+
+bool DrawPixman(SkCanvas* canvas, int step, bool usePixman);
+
+class SkPixmanView : public SkView {
+public:
+    SkPixmanView() {
+        this->setVisibleP(true);
+        this->setClipToBounds(false);
+        usePixman = true;
+        slide = 0;
+        step = -1;
+    };
+protected:
+    virtual void onDraw(SkCanvas* canvas) {
+        static int step = 0; // 12752; // 17908 ; // 17904; // drawLetters first error
+                             // drawStars triggers error at 23275
+                             // error is not easy to debug in its current state
+        static double seconds;
+        if (step == -1) {
+            timeval t;
+            gettimeofday(&t, NULL);
+            seconds = t.tv_sec+t.tv_usec/1000000.0;
+            step = 0;
+        }
+        canvas->drawColor(SK_ColorWHITE);
+        if (DrawPixman(canvas, slide, usePixman)) {
+            if (step == 100) {
+                timeval t;
+                gettimeofday(&t, NULL);
+                double last = seconds;
+                seconds = t.tv_sec+t.tv_usec/1000000.0;
+                SkDebugf("usePixman=%d seconds=%g\n", usePixman, seconds - last);
+                step = 0;
+            }
+            inval(NULL);
+        }
+    }
+    
+    virtual Click* onFindClickHandler(SkScalar , SkScalar ) {
+  //      usePixman ^= true;
+            ++slide;
+        return NULL;
+    }
+
+private:
+    bool usePixman;
+    int slide;
+    int step;
+    typedef SkView INHERITED; 
+};
+
+void application_init();
+void application_term();
+
+void application_init() {
+    SkGraphics::Init();
+    SkEvent::Init();
+}
+
+void application_term() {
+    SkGraphics::Term();
+    SkEvent::Term();
+}
+
+class FillLayout : public SkView::Layout {
+protected:
+    virtual void onLayoutChildren(SkView* parent) {
+        SkView* view = SkView::F2BIter(parent).next();
+        view->setSize(parent->width(), parent->height());
+    }
+};
+
+#import "SimpleApp.h"
+
+@implementation SimpleNSView
+
+- (id)initWithDefaults {
+    if ((self = [super initWithDefaults])) {
+        fWind = new SkOSWindow(self); 
+        fWind->setLayout(new FillLayout, false);
+        fWind->attachChildToFront(new SkPixmanView)->unref();
+    }
+    return self;
+}
+
+- (void)drawRect:(NSRect)dirtyRect {
+    CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+    SkCGDrawBitmap(ctx, fWind->getBitmap(), 0, 0);
+}
+
+@end
diff --git a/forth/Forth.cpp b/forth/Forth.cpp
new file mode 100644
index 0000000..be240e3
--- /dev/null
+++ b/forth/Forth.cpp
@@ -0,0 +1,502 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Forth.h"
+#include "ForthParser.h"
+#include "SkTDArray.h"
+#include "SkString.h"
+#include "SkTDStack.h"
+
+ForthEngine::ForthEngine(ForthOutput* output) : fOutput(output) {
+    size_t size = 32 * sizeof(intptr_t);
+    fStackBase = reinterpret_cast<intptr_t*>(sk_malloc_throw(size));
+    fStackStop = fStackBase + size/sizeof(intptr_t);
+    fStackCurr = fStackStop;
+}
+
+ForthEngine::~ForthEngine() {
+    sk_free(fStackBase);
+}
+
+void ForthEngine::sendOutput(const char text[]) {
+    if (fOutput) {
+        fOutput->show(text);
+    } else {
+        SkDebugf("%s", text);
+    }
+}
+
+void ForthEngine::push(intptr_t value) {
+    if (fStackCurr > fStackBase) {
+        SkASSERT(fStackCurr <= fStackStop && fStackCurr > fStackBase);
+        *--fStackCurr = value;
+    } else {
+        this->signal_error("overflow");
+    }
+}
+
+intptr_t ForthEngine::peek(size_t index) const {
+    SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
+    if (fStackCurr + index < fStackStop) {
+        return fStackCurr[index];
+    } else {
+        this->signal_error("peek out of range");
+        return 0x80000001;
+    }
+}
+
+void ForthEngine::setTop(intptr_t value) {
+    if (fStackCurr < fStackStop) {
+        SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
+        *fStackCurr = value;
+    } else {
+        this->signal_error("underflow");
+    }
+}
+
+intptr_t ForthEngine::pop() {
+    if (fStackCurr < fStackStop) {
+        SkASSERT(fStackCurr < fStackStop && fStackCurr >= fStackBase);
+        return *fStackCurr++;
+    } else {
+        this->signal_error("underflow");
+        return 0x80000001;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ForthWord::call(ForthCallBlock* block) {
+    ForthEngine engine(NULL);
+
+    // setup the initial stack with the callers input data
+    if (block) {
+        // walk the array backwards, so that the top of the stack is data[0]
+        for (size_t i = 0; i < block->in_count; i++) {
+            engine.push(block->in_data[i]);
+        }
+    }
+
+    // now invoke the word
+    this->exec(&engine);
+
+    // now copy back the stack into the caller's output data
+    if (block) {
+        size_t n = engine.depth();
+        block->out_depth = n;
+        if (n > block->out_count) {
+            n = block->out_count;
+        }
+        for (size_t i = 0; i < n; i++) {
+            block->out_data[i] = engine.peek(i);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+    reading an initial 32bit value from the code stream:
+
+    xxxxxxxx xxxxxxxx xxxxxxxx xxxxxx00
+
+    Those last two bits are always 0 for a word, so we set those bits for other
+    opcodes
+
+    00 -- execute this word
+    01 -- push (value & ~3) on the data stack
+    10 -- push value >> 2 on the data stack (sign extended)
+    11 -- switch (value >>> 2) for Code
+ */
+
+class FCode {
+public:
+    enum {
+        kCodeShift  = 2,
+        kCodeMask   = 7,
+        kCodeDataShift  = 5
+    };
+    static unsigned GetCode(intptr_t c) {
+        return ((uint32_t)c >> kCodeShift) & kCodeMask;
+    }
+    static unsigned GetCodeData(intptr_t c) {
+        return (uint32_t)c >> kCodeDataShift;
+    }
+
+    enum Bits {
+        kWord_Bits          = 0,    // must be zero for function address
+        kDataClear2_Bits    = 1,
+        kDataShift2_Bits    = 2,
+        kCodeShift2_Bits    = 3
+    };
+
+    enum Code {
+        kPushInt_Code,  // for data that needs more than 30 bits
+        kIF_Code,
+        kELSE_Code,
+        kDone_Code
+    };
+    static unsigned MakeCode(Code code) {
+        return (code << kCodeShift) | kCodeShift2_Bits;
+    }
+
+    void appendInt(int32_t);
+    void appendWord(ForthWord*);
+    void appendIF();
+    bool appendELSE();
+    bool appendTHEN();
+    void done();
+
+    intptr_t* detach() {
+        this->done();
+        return fData.detach();
+    }
+    intptr_t* begin() {
+        this->done();
+        return fData.begin();
+    }
+
+    static void Exec(const intptr_t*, ForthEngine*);
+
+private:
+    SkTDArray<intptr_t> fData;
+    SkTDStack<size_t>   fIfStack;
+};
+
+void FCode::appendInt(int32_t value) {
+    if ((value & 3) == 0) {
+        *fData.append() = value | kDataClear2_Bits;
+    } else if ((value << 2 >> 2) == value) {
+        *fData.append() = (value << 2) | kDataShift2_Bits;
+    } else {
+        intptr_t* p = fData.append(2);
+        *p++ = (kPushInt_Code << 2) | kCodeShift2_Bits;
+        *p++ = value;
+    }
+}
+
+void FCode::appendWord(ForthWord* word) {
+    SkASSERT((reinterpret_cast<intptr_t>(word) & 3) == 0);
+    *fData.append() = reinterpret_cast<intptr_t>(word);
+}
+
+void FCode::appendIF() {
+    size_t ifIndex = fData.count();
+    fIfStack.push(ifIndex);
+    *fData.append() = MakeCode(kIF_Code);
+}
+
+bool FCode::appendELSE() {
+    if (fIfStack.empty()) {
+        return false;
+    }
+
+    size_t elseIndex = fData.count();
+    *fData.append() = MakeCode(kELSE_Code);
+
+    size_t ifIndex = fIfStack.top();
+    // record the offset in the data part of the if-code
+    fData[ifIndex] |= (elseIndex - ifIndex) << kCodeDataShift;
+
+    // now reuse this IfStack entry to track the else
+    fIfStack.top() = elseIndex;
+    return true;
+}
+
+bool FCode::appendTHEN() {
+    if (fIfStack.empty()) {
+        return false;
+    }
+
+    // this is either an IF or an ELSE
+    size_t index = fIfStack.top();
+    // record the offset in the data part of the code
+    fData[index] |= (fData.count() - index - 1) << kCodeDataShift;
+
+    fIfStack.pop();
+    return true;
+}
+
+void FCode::done() {
+    *fData.append() = (kDone_Code << 2) | kCodeShift2_Bits;
+}
+
+void FCode::Exec(const intptr_t* curr, ForthEngine* engine) {
+    for (;;) {
+        intptr_t c = *curr++;
+        switch (c & 3) {
+            case kWord_Bits:
+                reinterpret_cast<ForthWord*>(c)->exec(engine);
+                break;
+            case kDataClear2_Bits:
+                engine->push(c & ~3);
+                break;
+            case kDataShift2_Bits:
+                engine->push(c >> 2);
+                break;
+            case kCodeShift2_Bits:
+                switch (GetCode(c)) {
+                    case kPushInt_Code:
+                        engine->push(*curr++);
+                        break;
+                    case kIF_Code:
+                        if (!engine->pop()) {
+                            // takes us past the ELSE or THEN
+                            curr += GetCodeData(c);
+                        }
+                        break;
+                    case kELSE_Code:
+                        // takes us past the THEN
+                        curr += GetCodeData(c);
+                        break;
+                    case kDone_Code:
+                        return;
+                }
+                break;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class CustomWord : public ForthWord {
+public:
+    // we assume ownership of code[]
+    CustomWord(intptr_t code[]) : fCode(code) {}
+    virtual ~CustomWord() { sk_free(fCode); }
+
+    virtual void exec(ForthEngine* engine) {
+        FCode::Exec(fCode, engine);
+    }
+
+private:
+    intptr_t* fCode;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+ForthParser::ForthParser() : fDict(4096) {
+    this->addStdWords();
+}
+
+ForthParser::~ForthParser() {
+    SkTDict<ForthWord*>::Iter iter(fDict);
+    ForthWord* word;
+    while (iter.next(&word)) {
+        delete word;
+    }
+}
+
+static const char* parse_error(const char msg[]) {
+    SkDebugf("-- parser error: %s\n", msg);
+    return NULL;
+}
+
+/** returns true if c is whitespace, including null
+ */
+static bool is_ws(int c) {
+    return c <= ' ';
+}
+
+static const char* parse_token(const char** text, size_t* len) {
+    const char* s = *text;
+    while (is_ws(*s)) {
+        if (0 == *s) {
+            return NULL;
+        }
+        s++;
+    }
+    const char* token = s++;
+    while (!is_ws(*s)) {
+        s++;
+    }
+    *text = s;
+    *len = s - token;
+    return token;
+}
+
+static bool is_digit(int c) { return (unsigned)(c - '0') <= 9; }
+static int hex_val(int c) {
+    if (is_digit(c)) {
+        return c - '0';
+    } else {
+        if (c <= 'Z') {
+            return 10 + c - 'A';
+        } else {
+            return 10 + c - 'a';
+        }
+    }
+}
+
+static bool parse_num(const char str[], size_t len, int32_t* numBits) {
+    if (1 == len && !is_digit(*str)) {
+        return false;
+    }
+    const char* start = str;
+    int32_t num = 0;
+    bool neg = false;
+    if (*str == '-') {
+        neg = true;
+        str += 1;
+    } else if (*str == '#') {
+        str++;
+        while (str - start < len) {
+            num = num*16 + hex_val(*str);
+            str += 1;
+        }
+        *numBits = num;
+        return true;
+    }
+
+    while (is_digit(*str)) {
+        num = 10*num + *str - '0';
+        str += 1;
+    }
+    SkASSERT(str - start <= len);
+    if (str - start == len) {
+        if (neg) {
+            num = -num;
+        }
+        *numBits = num;
+        return true;
+    }
+    // if we're not done with the token then the next char must be a decimal
+    if (*str != '.') {
+        return false;
+    }
+    str += 1;
+    float x = num;
+    float denom = 1;
+    while (str - start < len && is_digit(*str)) {
+        x = 10*x + *str - '0';
+        denom *= 10;
+        str += 1;
+    }
+    x /= denom;
+    if (str - start == len) {
+        if (neg) {
+            x = -x;
+        }
+        *numBits = f2i_bits(x);
+        return true;
+    }
+    return false;
+}
+
+static const char* parse_comment(const char text[]) {
+    SkASSERT(*text == '(');
+    while (')' != *++text) {
+        if (0 == *text) {
+            return NULL;
+        }
+    }
+    return text + 1;    // skip past the closing ')'
+}
+
+const char* ForthParser::parse(const char text[], FCode* code) {
+    for (;;) {
+        size_t len;
+        const char* token = parse_token(&text, &len);
+        if (NULL == token) {
+            break;
+        }
+        if (1 == len) {
+            if ('(' == *token) {
+                text = parse_comment(token);
+                if (NULL == text) {
+                    return NULL;
+                }
+                continue;
+            }
+            if (';' == *token) {
+                break;
+            }
+            if (':' == *token) {
+                token = parse_token(&text, &len);
+                if (NULL == token) {
+                    return parse_error("missing name after ':'");
+                }
+                FCode subCode;
+                text = this->parse(text, &subCode);
+                if (NULL == text) {
+                    return NULL;
+                }
+                this->add(token, len, new CustomWord(subCode.detach()));
+                continue;
+            }
+        }
+        int32_t num;
+        if (parse_num(token, len, &num)) {
+            // note that num is just the bit representation of the float
+            code->appendInt(num);
+        } else if (2 == len && memcmp(token, "IF", 2) == 0) {
+            code->appendIF();
+        } else if (4 == len && memcmp(token, "ELSE", 4) == 0) {
+            if (!code->appendELSE()) {
+                return parse_error("ELSE with no matching IF");
+            }
+        } else if (4 == len && memcmp(token, "THEN", 4) == 0) {
+            if (!code->appendTHEN()) {
+                return parse_error("THEN with no matching IF");
+            }
+        } else{
+            ForthWord* word = this->find(token, len);
+            if (NULL == word) {
+                SkString str(token, len);
+                str.prepend("unknown word ");
+                return parse_error(str.c_str());
+            }
+            code->appendWord(word);
+        }
+    }
+    return text;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ForthEnv::Impl {
+public:
+    ForthParser fParser;
+    FCode       fBuilder;
+};
+
+ForthEnv::ForthEnv() {
+    fImpl = new Impl;
+}
+
+ForthEnv::~ForthEnv() {
+    delete fImpl;
+}
+
+void ForthEnv::addWord(const char name[], ForthWord* word) {
+    fImpl->fParser.addWord(name, word);
+}
+
+void ForthEnv::parse(const char text[]) {
+    fImpl->fParser.parse(text, &fImpl->fBuilder);
+}
+
+ForthWord* ForthEnv::findWord(const char name[]) {
+    return fImpl->fParser.find(name, strlen(name));
+}
+
+void ForthEnv::run(ForthOutput* output) {
+    ForthEngine engine(output);
+    FCode::Exec(fImpl->fBuilder.begin(), &engine);
+}
+
+#if 0
+void ForthEnv::run(const char text[], ForthOutput* output) {
+    FCode builder;
+
+    if (fImpl->fParser.parse(text, &builder)) {
+        ForthEngine engine(output);
+        FCode::Exec(builder.begin(), &engine);
+    }
+}
+#endif
+
diff --git a/forth/Forth.h b/forth/Forth.h
new file mode 100644
index 0000000..5e8ca8d
--- /dev/null
+++ b/forth/Forth.h
@@ -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.
+ */
+
+#ifndef Forth_DEFINED
+#define Forth_DEFINED
+
+#include "SkTypes.h"
+
+class ForthOutput {
+public:
+    virtual void show(const char output[]) = 0;
+};
+
+union FloatIntDual {
+    int32_t fInt;
+    float   fFloat;
+};
+
+static inline int32_t f2i_bits(float x) {
+    FloatIntDual d;
+    d.fFloat = x;
+    return d.fInt;
+}
+
+static inline float i2f_bits(int32_t x) {
+    FloatIntDual d;
+    d.fInt = x;
+    return d.fFloat;
+}
+
+class ForthEngine {
+public:
+    ForthEngine(ForthOutput*);
+    ~ForthEngine();
+
+    int         depth() const { return fStackStop - fStackCurr; }
+    void        clearStack() { fStackCurr = fStackStop; }
+
+    void        push(intptr_t value);
+    intptr_t    top() const { return this->peek(0); }
+    intptr_t    peek(size_t index) const;
+    void        setTop(intptr_t value);
+    intptr_t    pop();
+
+    void        fpush(float value) { this->push(f2i_bits(value)); }
+    float       fpeek(size_t i) const { return i2f_bits(this->fpeek(i)); }
+    float       ftop() const { return i2f_bits(this->top()); }
+    void        fsetTop(float value) { this->setTop(f2i_bits(value)); }
+    float       fpop() { return i2f_bits(this->pop()); }
+
+    void sendOutput(const char text[]);
+
+private:
+    ForthOutput* fOutput;
+    intptr_t*   fStackBase;
+    intptr_t*   fStackCurr;
+    intptr_t*   fStackStop;
+
+    void signal_error(const char msg[]) const {
+        SkDebugf("ForthEngine error: %s\n", msg);
+    }
+};
+
+struct ForthCallBlock {
+    const intptr_t* in_data;
+    size_t          in_count;
+    intptr_t*       out_data;
+    size_t          out_count;
+    size_t          out_depth;
+};
+
+class ForthWord {
+public:
+    virtual ~ForthWord() {}
+    virtual void exec(ForthEngine*) = 0;
+
+    // todo: return error state of the engine
+    void call(ForthCallBlock*);
+};
+
+class ForthEnv {
+public:
+    ForthEnv();
+    ~ForthEnv();
+
+
+    void addWord(const char name[], ForthWord*);
+
+    void parse(const char code[]);
+
+    ForthWord* findWord(const char name[]);
+
+    void run(ForthOutput* = NULL);
+
+private:
+    class Impl;
+    Impl* fImpl;
+};
+
+#endif
+
diff --git a/forth/ForthParser.h b/forth/ForthParser.h
new file mode 100644
index 0000000..2138d48
--- /dev/null
+++ b/forth/ForthParser.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 ForthParser_DEFINED
+#define ForthParser_DEFINED
+
+#include "SkTDict.h"
+//#include "SkString.h"
+
+class ForthWord;
+class FCode;
+
+class ForthParser {
+public:
+    ForthParser();
+    ~ForthParser();
+
+    const char* parse(const char text[], FCode*);
+
+    void addWord(const char name[], ForthWord* word) {
+        this->add(name, strlen(name), word);
+    }
+
+    void add(const char name[], size_t len, ForthWord* word) {
+    //    SkString str(name, len);
+    //    SkDebugf("add %s %p\n", str.c_str(), word);
+        SkDEBUGCODE(bool isNewWord = )fDict.set(name, len, word);
+        SkASSERT(isNewWord);
+    }
+
+    ForthWord* find(const char name[], size_t len) const {
+        ForthWord* word;
+        return fDict.find(name, len, &word) ? word : NULL;
+    }
+
+private:
+    void addStdWords();
+
+    SkTDict<ForthWord*> fDict;
+};
+
+#endif
diff --git a/forth/ForthTests.cpp b/forth/ForthTests.cpp
new file mode 100644
index 0000000..08ab7f3
--- /dev/null
+++ b/forth/ForthTests.cpp
@@ -0,0 +1,408 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Forth.h"
+#include "SkString.h"
+
+class Reporter {
+public:
+    int fFailureCount;
+
+    Reporter() : fFailureCount(0) {}
+    void reportFailure(const char expression[], const char file[], int line);
+    void reportFailure(const char msg[]);
+};
+
+typedef void (*ForthWordTestProc)(ForthWord*, ForthEngine*, Reporter*);
+
+#define FORTH_ASSERT(reporter, expression)      \
+    do {                                        \
+        if (!(expression)) {                    \
+            reporter->reportFailure(#expression, __FILE__, __LINE__);   \
+        }                                       \
+    } while (0)
+
+static void drop_test0(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(-17);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 0 == fe->depth());
+}
+
+static void drop_test1(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(-17);
+    fe->push(93);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, -17 == fe->peek(0));
+}
+
+static void dup_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(-17);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 2 == fe->depth());
+    FORTH_ASSERT(reporter, -17 == fe->peek(0));
+    FORTH_ASSERT(reporter, -17 == fe->peek(1));
+}
+
+static void swap_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(-17);
+    fe->push(42);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 2 == fe->depth());
+    FORTH_ASSERT(reporter, -17 == fe->peek(0));
+    FORTH_ASSERT(reporter, 42 == fe->peek(1));
+}
+
+static void over_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(1);
+    fe->push(2);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 3 == fe->depth());
+    FORTH_ASSERT(reporter, 1 == fe->peek(0));
+    FORTH_ASSERT(reporter, 2 == fe->peek(1));
+    FORTH_ASSERT(reporter, 1 == fe->peek(2));
+}
+
+static void rot_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(1);
+    fe->push(2);
+    fe->push(3);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 3 == fe->depth());
+    FORTH_ASSERT(reporter, 2 == fe->peek(2));
+    FORTH_ASSERT(reporter, 3 == fe->peek(1));
+    FORTH_ASSERT(reporter, 1 == fe->peek(0));
+}
+
+static void rrot_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(1);
+    fe->push(2);
+    fe->push(3);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 3 == fe->depth());
+    FORTH_ASSERT(reporter, 2 == fe->peek(0));
+    FORTH_ASSERT(reporter, 1 == fe->peek(1));
+    FORTH_ASSERT(reporter, 3 == fe->peek(2));
+}
+
+static void swap2_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(1);
+    fe->push(2);
+    fe->push(3);
+    fe->push(4);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 4 == fe->depth());
+    FORTH_ASSERT(reporter, 2 == fe->peek(0));
+    FORTH_ASSERT(reporter, 1 == fe->peek(1));
+    FORTH_ASSERT(reporter, 4 == fe->peek(2));
+    FORTH_ASSERT(reporter, 3 == fe->peek(3));
+}
+
+static void dup2_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(1);
+    fe->push(2);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 4 == fe->depth());
+    FORTH_ASSERT(reporter, 1 == fe->peek(3));
+    FORTH_ASSERT(reporter, 2 == fe->peek(2));
+    FORTH_ASSERT(reporter, 1 == fe->peek(1));
+    FORTH_ASSERT(reporter, 2 == fe->peek(0));
+}
+
+static void over2_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(1);
+    fe->push(2);
+    fe->push(3);
+    fe->push(4);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 6 == fe->depth());
+    FORTH_ASSERT(reporter, 1 == fe->peek(5));
+    FORTH_ASSERT(reporter, 2 == fe->peek(4));
+    FORTH_ASSERT(reporter, 3 == fe->peek(3));
+    FORTH_ASSERT(reporter, 4 == fe->peek(2));
+    FORTH_ASSERT(reporter, 1 == fe->peek(1));
+    FORTH_ASSERT(reporter, 2 == fe->peek(0));
+}
+
+static void drop2_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(1);
+    fe->push(2);
+    fe->push(3);
+    fe->push(4);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 2 == fe->depth());
+    FORTH_ASSERT(reporter, 1 == fe->peek(1));
+    FORTH_ASSERT(reporter, 2 == fe->peek(0));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void iadd_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(35);
+    fe->push(99);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 134 == fe->top());
+    fe->push(-135);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, -1 == fe->top());
+}
+
+static void isub_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(35);
+    fe->push(99);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 35-99 == fe->top());
+}
+
+static void imul_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(15);
+    fe->push(-20);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, -300 == fe->top());
+    fe->push(0);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 0 == fe->top());
+}
+
+static void idiv_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(100);
+    fe->push(25);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 4 == fe->top());
+    fe->setTop(10);
+    fe->push(-3);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, -3 == fe->top());
+    fe->setTop(-1);
+    fe->push(-1);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 1 == fe->top());
+}
+
+static void imod_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(10);
+    fe->push(3);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 1 == fe->top());
+    fe->push(5);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 1 == fe->top());
+}
+
+static void idivmod_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(10);
+    fe->push(3);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 2 == fe->depth());
+    FORTH_ASSERT(reporter, 1 == fe->peek(1));
+    FORTH_ASSERT(reporter, 3 == fe->peek(0));
+}
+
+static void idot_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(1);
+    fe->push(2);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 1 == fe->top());
+}
+
+static void iabs_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(10);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 10 == fe->top());
+    fe->setTop(-10);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 10 == fe->top());
+}
+
+static void inegate_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(10);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, -10 == fe->top());
+    fe->setTop(-10);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 10 == fe->top());
+}
+
+static void imin_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(10);
+    fe->push(3);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 3 == fe->top());
+    fe->push(-10);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, -10 == fe->top());
+}
+
+static void imax_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(10);
+    fe->push(3);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 10 == fe->top());
+    fe->push(-10);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 10 == fe->top());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void logical_and_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    const static int data[] = {
+        0, 0, 0,
+        2, 0, 0,
+        0, -1, 0,
+        1, 5, -1
+    };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(data)/3; i++) {
+        fe->push(data[i*3 + 0]);
+        fe->push(data[i*3 + 1]);
+        word->exec(fe);
+        FORTH_ASSERT(reporter, 1 == fe->depth());
+        FORTH_ASSERT(reporter, data[i*3 + 2] == fe->top());
+        fe->pop();
+    }
+}
+
+static void logical_or_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    const static int data[] = {
+        0, 0, 0,
+        2, 0, -1,
+        0, -1, -1,
+        1, 5, -1
+    };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(data)/3; i++) {
+        fe->push(data[i*3 + 0]);
+        fe->push(data[i*3 + 1]);
+        word->exec(fe);
+        FORTH_ASSERT(reporter, 1 == fe->depth());
+        FORTH_ASSERT(reporter, data[i*3 + 2] == fe->top());
+        fe->pop();
+    }
+}
+
+static void logical_not_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    const static int data[] = {
+        0, -1,
+        5, 0,
+        -1, 0
+    };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(data)/2; i++) {
+        fe->push(data[i*2 + 0]);
+        word->exec(fe);
+        FORTH_ASSERT(reporter, 1 == fe->depth());
+        FORTH_ASSERT(reporter, data[i*2 + 1] == fe->top());
+        fe->pop();
+    }
+}
+
+static void if_dup_test(ForthWord* word, ForthEngine* fe, Reporter* reporter) {
+    fe->push(10);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 2 == fe->depth());
+    FORTH_ASSERT(reporter, 10 == fe->peek(1));
+    FORTH_ASSERT(reporter, 10 == fe->peek(0));
+    fe->pop();
+    fe->pop();
+    fe->push(0);
+    word->exec(fe);
+    FORTH_ASSERT(reporter, 1 == fe->depth());
+    FORTH_ASSERT(reporter, 0 == fe->top());
+}
+
+static const struct {
+    const char*         fName;
+    ForthWordTestProc   fProc;
+} gRecs[] = {
+    { "DROP",   drop_test0 },   { "DROP",   drop_test1 },
+    { "DUP",    dup_test },
+    { "SWAP",   swap_test },
+    { "OVER",   over_test },
+    { "ROT",    rot_test },
+    { "-ROT",   rrot_test },
+    { "2SWAP",  swap2_test },
+    { "2DUP",   dup2_test },
+    { "2OVER",  over2_test },
+    { "2DROP",  drop2_test },
+
+    { "+",      iadd_test },
+    { "-",      isub_test },
+    { "*",      imul_test },
+    { "/",      idiv_test },
+    { "MOD",    imod_test },
+    { "/MOD",   idivmod_test },
+
+//    { ".",      idot_test },
+    { "ABS",    iabs_test },
+    { "NEGATE", inegate_test },
+    { "MIN",    imin_test },
+    { "MAX",    imax_test },
+
+    { "AND",    logical_and_test },
+    { "OR",     logical_or_test },
+    { "0=",     logical_not_test },
+    { "?DUP",   if_dup_test },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+void Reporter::reportFailure(const char expression[], const char file[],
+                             int line) {
+    SkDebugf("failed %s:%d: %s\n", file, line, expression);
+    fFailureCount += 1;
+}
+
+void Reporter::reportFailure(const char msg[]) {
+    SkDebugf("%s\n");
+    fFailureCount += 1;
+}
+
+void Forth_test_stdwords(bool verbose);
+void Forth_test_stdwords(bool verbose) {
+    ForthEnv env;
+    Reporter reporter;
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
+        ForthEngine engine(NULL);
+
+        ForthWord* word = env.findWord(gRecs[i].fName);
+        if (NULL == word) {
+            SkString str;
+            str.printf("--- can't find stdword %d", gRecs[i].fName);
+            reporter.reportFailure(str.c_str());
+        } else {
+            if (verbose) {
+                SkDebugf("--- testing %s %p\n", gRecs[i].fName, word);
+            }
+            gRecs[i].fProc(word, &engine, &reporter);
+        }
+    }
+
+    if (0 == reporter.fFailureCount) {
+        SkDebugf("--- success!\n");
+    } else {
+        SkDebugf("--- %d failures\n", reporter.fFailureCount);
+    }
+}
+
diff --git a/forth/StdWords.cpp b/forth/StdWords.cpp
new file mode 100644
index 0000000..8a781f8
--- /dev/null
+++ b/forth/StdWords.cpp
@@ -0,0 +1,462 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Forth.h"
+#include "ForthParser.h"
+#include "SkString.h"
+
+#define BEGIN_WORD(name)   \
+    class name##_ForthWord : public ForthWord { \
+    public:                                     \
+        virtual void exec(ForthEngine* fe)
+
+#define END_WORD };
+
+///////////////////////////////////////////////////////////////////////////////
+
+BEGIN_WORD(drop) {
+    (void)fe->pop();
+} END_WORD
+
+BEGIN_WORD(over) {
+    fe->push(fe->peek(1));
+} END_WORD
+
+BEGIN_WORD(dup) {
+    fe->push(fe->top());
+} END_WORD
+
+BEGIN_WORD(swap) {
+    intptr_t a = fe->pop();
+    intptr_t b = fe->top();
+    fe->setTop(a);
+    fe->push(b);
+} END_WORD
+
+BEGIN_WORD(rot) {
+    intptr_t c = fe->pop();
+    intptr_t b = fe->pop();
+    intptr_t a = fe->pop();
+    fe->push(b);
+    fe->push(c);
+    fe->push(a);
+} END_WORD
+
+BEGIN_WORD(rrot) {
+    intptr_t c = fe->pop();
+    intptr_t b = fe->pop();
+    intptr_t a = fe->pop();
+    fe->push(c);
+    fe->push(a);
+    fe->push(b);
+} END_WORD
+
+BEGIN_WORD(swap2) {
+    intptr_t d = fe->pop();
+    intptr_t c = fe->pop();
+    intptr_t b = fe->pop();
+    intptr_t a = fe->pop();
+    fe->push(c);
+    fe->push(d);
+    fe->push(a);
+    fe->push(b);
+} END_WORD
+
+BEGIN_WORD(dup2) {
+    fe->push(fe->peek(1));
+    fe->push(fe->peek(1));
+} END_WORD
+
+BEGIN_WORD(over2) {
+    fe->push(fe->peek(3));
+    fe->push(fe->peek(3));
+} END_WORD
+
+BEGIN_WORD(drop2) {
+    (void)fe->pop();
+    (void)fe->pop();
+} END_WORD
+
+///////////////// logicals
+
+BEGIN_WORD(logical_and) {
+    intptr_t tmp = fe->pop();
+    fe->setTop(-(tmp && fe->top()));
+} END_WORD
+
+BEGIN_WORD(logical_or) {
+    intptr_t tmp = fe->pop();
+    fe->setTop(-(tmp || fe->top()));
+} END_WORD
+
+BEGIN_WORD(logical_not) {
+    fe->setTop(-(!fe->top()));
+} END_WORD
+
+BEGIN_WORD(if_dup) {
+    intptr_t tmp = fe->top();
+    if (tmp) {
+        fe->push(tmp);
+    }
+} END_WORD
+
+///////////////// ints
+
+class add_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(fe->top() + tmp);
+    }};
+
+class sub_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(fe->top() - tmp);
+    }};
+
+class mul_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(fe->top() * tmp);
+    }};
+
+class div_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(fe->top() / tmp);
+    }};
+
+class mod_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(fe->top() % tmp);
+    }};
+
+class divmod_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t denom = fe->pop();
+        intptr_t numer = fe->pop();
+        fe->push(numer % denom);
+        fe->push(numer / denom);
+    }};
+
+class dot_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        SkString str;
+        str.printf("%d ", fe->pop());
+        fe->sendOutput(str.c_str());
+    }};
+
+class abs_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        int32_t value = fe->top();
+        if (value < 0) {
+            fe->setTop(-value);
+        }
+    }};
+
+class negate_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        fe->setTop(-fe->top());
+    }};
+
+class min_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        int32_t value = fe->pop();
+        if (value < fe->top()) {
+            fe->setTop(value);
+        }
+    }};
+
+class max_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        int32_t value = fe->pop();
+        if (value > fe->top()) {
+            fe->setTop(value);
+        }
+    }
+};
+
+///////////////// floats
+
+class fadd_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->fsetTop(fe->ftop() + tmp);
+    }
+};
+
+class fsub_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->fsetTop(fe->ftop() - tmp);
+    }
+};
+
+class fmul_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->fsetTop(fe->ftop() * tmp);
+    }
+};
+
+class fdiv_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->fsetTop(fe->ftop() / tmp);
+    }
+};
+
+class fdot_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        SkString str;
+        str.printf("%g ", fe->fpop());
+        fe->sendOutput(str.c_str());
+    }
+};
+
+class fabs_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float value = fe->ftop();
+        if (value < 0) {
+            fe->fsetTop(-value);
+        }
+    }
+};
+
+class fmin_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float value = fe->fpop();
+        if (value < fe->ftop()) {
+            fe->fsetTop(value);
+        }
+    }
+};
+
+class fmax_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        float value = fe->fpop();
+        if (value > fe->ftop()) {
+            fe->fsetTop(value);
+        }
+    }
+};
+
+class floor_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->fsetTop(floorf(fe->ftop()));
+    }
+};
+
+class ceil_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->fsetTop(ceilf(fe->ftop()));
+    }
+};
+
+class round_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->fsetTop(floorf(fe->ftop() + 0.5f));
+    }
+};
+
+class f2i_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->setTop((int)fe->ftop());
+    }
+};
+
+class i2f_ForthWord : public ForthWord {
+public:
+    virtual void exec(ForthEngine* fe) {
+        fe->fsetTop((float)fe->top());
+    }
+};
+
+////////////////////////////// int compares
+
+class eq_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        fe->push(-(fe->pop() == fe->pop()));
+    }
+};
+
+class neq_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        fe->push(-(fe->pop() != fe->pop()));
+    }
+};
+
+class lt_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(-(fe->top() < tmp));
+    }
+};
+
+class le_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(-(fe->top() <= tmp));
+    }
+};
+
+class gt_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(-(fe->top() > tmp));
+    }
+};
+
+class ge_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        intptr_t tmp = fe->pop();
+        fe->setTop(-(fe->top() >= tmp));
+    }
+};
+
+BEGIN_WORD(lt0) {
+    fe->setTop(fe->top() >> 31);
+} END_WORD
+
+BEGIN_WORD(ge0) {
+    fe->setTop(~(fe->top() >> 31));
+} END_WORD
+
+BEGIN_WORD(gt0) {
+    fe->setTop(-(fe->top() > 0));
+} END_WORD
+
+BEGIN_WORD(le0) {
+    fe->setTop(-(fe->top() <= 0));
+} END_WORD
+
+/////////////////////////////// float compares
+
+/*  negative zero is our nemesis, otherwise we could use = and <> from ints */
+
+class feq_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        fe->push(-(fe->fpop() == fe->fpop()));
+    }
+};
+
+class fneq_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        fe->push(-(fe->fpop() != fe->fpop()));
+    }
+};
+
+class flt_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->setTop(-(fe->ftop() < tmp));
+    }
+};
+
+class fle_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->setTop(-(fe->ftop() <= tmp));
+    }
+};
+
+class fgt_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->setTop(-(fe->ftop() > tmp));
+    }
+};
+
+class fge_ForthWord : public ForthWord { public:
+    virtual void exec(ForthEngine* fe) {
+        float tmp = fe->fpop();
+        fe->setTop(-(fe->ftop() >= tmp));
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ADD_LITERAL_WORD(sym, name) \
+    this->add(sym, sizeof(sym)-1, new name##_ForthWord)
+
+void ForthParser::addStdWords() {
+    ADD_LITERAL_WORD("DROP", drop);
+    ADD_LITERAL_WORD("DUP", dup);
+    ADD_LITERAL_WORD("SWAP", swap);
+    ADD_LITERAL_WORD("OVER", over);
+    ADD_LITERAL_WORD("ROT", rot);
+    ADD_LITERAL_WORD("-ROT", rrot);
+    ADD_LITERAL_WORD("2SWAP", swap2);
+    ADD_LITERAL_WORD("2DUP", dup2);
+    ADD_LITERAL_WORD("2OVER", over2);
+    ADD_LITERAL_WORD("2DROP", drop2);
+
+    ADD_LITERAL_WORD("+", add);
+    ADD_LITERAL_WORD("-", sub);
+    ADD_LITERAL_WORD("*", mul);
+    ADD_LITERAL_WORD("/", div);
+    ADD_LITERAL_WORD("MOD", mod);
+    ADD_LITERAL_WORD("/MOD", divmod);
+
+    ADD_LITERAL_WORD(".", dot);
+    ADD_LITERAL_WORD("ABS", abs);
+    ADD_LITERAL_WORD("NEGATE", negate);
+    ADD_LITERAL_WORD("MIN", min);
+    ADD_LITERAL_WORD("MAX", max);
+
+    ADD_LITERAL_WORD("AND", logical_and);
+    ADD_LITERAL_WORD("OR", logical_or);
+    ADD_LITERAL_WORD("0=", logical_not);
+    ADD_LITERAL_WORD("?DUP", if_dup);
+
+    this->add("f+", 2, new fadd_ForthWord);
+    this->add("f-", 2, new fsub_ForthWord);
+    this->add("f*", 2, new fmul_ForthWord);
+    this->add("f/", 2, new fdiv_ForthWord);
+    this->add("f.", 2, new fdot_ForthWord);
+    this->add("fabs", 4, new fabs_ForthWord);
+    this->add("fmin", 4, new fmin_ForthWord);
+    this->add("fmax", 4, new fmax_ForthWord);
+    this->add("floor", 5, new floor_ForthWord);
+    this->add("ceil", 4, new ceil_ForthWord);
+    this->add("round", 5, new round_ForthWord);
+    this->add("f>i", 3, new f2i_ForthWord);
+    this->add("i>f", 3, new i2f_ForthWord);
+
+    this->add("=", 1, new eq_ForthWord);
+    this->add("<>", 2, new neq_ForthWord);
+    this->add("<", 1, new lt_ForthWord);
+    this->add("<=", 2, new le_ForthWord);
+    this->add(">", 1, new gt_ForthWord);
+    this->add(">=", 2, new ge_ForthWord);
+    ADD_LITERAL_WORD("0<", lt0);
+    ADD_LITERAL_WORD("0>", gt0);
+    ADD_LITERAL_WORD("0<=", le0);
+    ADD_LITERAL_WORD("0>=", ge0);
+
+    this->add("f=", 2, new feq_ForthWord);
+    this->add("f<>", 3, new fneq_ForthWord);
+    this->add("f<", 2, new flt_ForthWord);
+    this->add("f<=", 3, new fle_ForthWord);
+    this->add("f>", 2, new fgt_ForthWord);
+    this->add("f>=", 3, new fge_ForthWord);
+}
+
diff --git a/gm/aaclip.cpp b/gm/aaclip.cpp
new file mode 100644
index 0000000..f67ac17
--- /dev/null
+++ b/gm/aaclip.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright 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 "SkPath.h"
+
+static SkCanvas* MakeCanvas(const SkIRect& bounds) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height());
+    bm.allocPixels();
+    bm.eraseColor(0);
+
+    SkCanvas* canvas = new SkCanvas(bm);
+    canvas->translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop));
+    return canvas;
+}
+
+static void GetBitmap(const SkCanvas* canvas, SkBitmap* bm) {
+    *bm = canvas->getDevice()->accessBitmap(false);
+}
+
+static void compare_canvas(const SkCanvas* a, const SkCanvas* b) {
+    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]);
+        }
+    }
+}
+
+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.
+*/
+
+static void draw (SkCanvas* canvas, SkRect& target, int x, int y) {
+    SkPaint borderPaint;
+    borderPaint.setColor(SkColorSetRGB(0x0, 0xDD, 0x0));
+    borderPaint.setAntiAlias(true);
+    SkPaint backgroundPaint;
+    backgroundPaint.setColor(SkColorSetRGB(0xDD, 0x0, 0x0));
+    backgroundPaint.setAntiAlias(true);
+    SkPaint foregroundPaint;
+    foregroundPaint.setColor(SkColorSetRGB(0x0, 0x0, 0xDD));
+    foregroundPaint.setAntiAlias(true);
+
+    canvas->save();
+    canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+    target.inset(SkIntToScalar(-2), SkIntToScalar(-2));
+    canvas->drawRect(target, borderPaint);
+    target.inset(SkIntToScalar(2), SkIntToScalar(2));
+    canvas->drawRect(target, backgroundPaint);
+    canvas->clipRect(target, SkRegion::kIntersect_Op, true);
+    target.inset(SkIntToScalar(-4), SkIntToScalar(-4));
+    canvas->drawRect(target, foregroundPaint);
+    canvas->restore();
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+static void draw_rect_tests (SkCanvas* canvas) {
+    draw_square(canvas, 10, 10);
+    draw_column(canvas, 30, 10);
+    draw_bar(canvas, 10, 30);
+}
+
+/**
+   Test a set of clipping problems discovered while writing blitAntiRect,
+   and test all the code paths through the clipping blitters.
+   Each region should show as a blue center surrounded by a 2px green
+   border, with no red.
+*/
+
+class AAClipGM : public GM {
+public:
+    AAClipGM() {
+
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("aaclip");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (false) { // avoid bit rot, suppress warning
+            test_mask();
+        }
+
+        // Initial pixel-boundary-aligned draw
+        draw_rect_tests(canvas);
+
+        // Repeat 4x with .2, .4, .6, .8 px offsets
+        canvas->translate(SK_Scalar1 / 5, SK_Scalar1 / 5);
+        canvas->translate(SkIntToScalar(50), 0);
+        draw_rect_tests(canvas);
+
+        canvas->translate(SK_Scalar1 / 5, SK_Scalar1 / 5);
+        canvas->translate(SkIntToScalar(50), 0);
+        draw_rect_tests(canvas);
+
+        canvas->translate(SK_Scalar1 / 5, SK_Scalar1 / 5);
+        canvas->translate(SkIntToScalar(50), 0);
+        draw_rect_tests(canvas);
+
+        canvas->translate(SK_Scalar1 / 5, SK_Scalar1 / 5);
+        canvas->translate(SkIntToScalar(50), 0);
+        draw_rect_tests(canvas);
+    }
+
+    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new AAClipGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/aarectmodes.cpp b/gm/aarectmodes.cpp
new file mode 100644
index 0000000..07cfa12
--- /dev/null
+++ b/gm/aarectmodes.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 "gm.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+
+static void test4(SkCanvas* canvas) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    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
+    };
+    SkPath path;
+    SkPoint* ptPtr = pts;
+    for (size_t i = 0; i < sizeof(verbs); ++i) {
+        switch ((SkPath::Verb) verbs[i]) {
+            case SkPath::kMove_Verb:
+                path.moveTo(ptPtr->fX, ptPtr->fY);
+                ++ptPtr;
+                break;
+            case SkPath::kLine_Verb:
+                path.lineTo(ptPtr->fX, ptPtr->fY);
+                ++ptPtr;
+                break;
+            case SkPath::kClose_Verb:
+                path.close();
+                break;
+            default:
+                SkASSERT(false);
+                break;
+        }
+    }
+    SkRect clip = {0, 130, 772, 531};
+    canvas->clipRect(clip);
+    canvas->drawPath(path, paint);
+}
+
+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;
+}
+
+namespace skiagm {
+
+    class AARectModesGM : public GM {
+        SkPaint fBGPaint;
+    public:
+        AARectModesGM () {
+            fBGPaint.setShader(make_bg_shader())->unref();
+        }
+
+    protected:
+
+        virtual SkString onShortName() {
+            return SkString("aarectmodes");
+        }
+
+        virtual SkISize onISize() { return make_isize(640, 480); }
+
+        virtual void onDraw(SkCanvas* canvas) {
+            if (false) { // avoid bit rot, suppress warning
+                test4(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);
+            }
+        }
+
+        // disable pdf for now, since it crashes on mac
+        virtual uint32_t onGetFlags() const { return kSkipPDF_Flag; }
+
+    private:
+        typedef GM INHERITED;
+    };
+
+//////////////////////////////////////////////////////////////////////////////
+
+    static GM* MyFactory(void*) { return new AARectModesGM; }
+    static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/arithmode.cpp b/gm/arithmode.cpp
new file mode 100644
index 0000000..6f10dd6
--- /dev/null
+++ b/gm/arithmode.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 "gm.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+
+#include "SkArithmeticMode.h"
+#include "SkGradientShader.h"
+#define WW  100
+#define HH  32
+
+static SkBitmap make_bm() {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, WW, HH);
+    bm.allocPixels();
+    bm.eraseColor(0);
+    return bm;
+}
+
+static SkBitmap make_src() {
+    SkBitmap bm = make_bm();
+    SkCanvas canvas(bm);
+    SkPaint paint;
+    SkPoint pts[] = { {0, 0}, {SkIntToScalar(WW), SkIntToScalar(HH)} };
+    SkColor colors[] = {
+        SK_ColorBLACK, SK_ColorGREEN, SK_ColorCYAN,
+        SK_ColorRED, SK_ColorMAGENTA, SK_ColorWHITE
+    };
+    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors),
+                                                 SkShader::kClamp_TileMode);
+    paint.setShader(s)->unref();
+    canvas.drawPaint(paint);
+    return bm;
+}
+
+static SkBitmap make_dst() {
+    SkBitmap bm = make_bm();
+    SkCanvas canvas(bm);
+    SkPaint paint;
+    SkPoint pts[] = { {0, SkIntToScalar(HH)}, {SkIntToScalar(WW), 0} };
+    SkColor colors[] = {
+        SK_ColorBLUE, SK_ColorYELLOW, SK_ColorBLACK, SK_ColorGREEN, SK_ColorGRAY
+    };
+    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors),
+                                                 SkShader::kClamp_TileMode);
+    paint.setShader(s)->unref();
+    canvas.drawPaint(paint);
+    return bm;
+}
+
+static SkBitmap make_arith(const SkBitmap& src, const SkBitmap& dst,
+                           const SkScalar k[]) {
+    SkBitmap bm = make_bm();
+    SkCanvas canvas(bm);
+    SkPaint paint;
+    canvas.drawBitmap(dst, 0, 0, NULL);
+    SkXfermode* xfer = SkArithmeticMode::Create(k[0], k[1], k[2], k[3]);
+    paint.setXfermode(xfer)->unref();
+    canvas.drawBitmap(src, 0, 0, &paint);
+    return bm;
+}
+
+static void show_k_text(SkCanvas* canvas, SkScalar x, SkScalar y, const SkScalar k[]) {
+    SkPaint paint;
+    paint.setTextSize(SkIntToScalar(24));
+    paint.setAntiAlias(true);
+    for (int i = 0; i < 4; ++i) {
+        SkString str;
+        str.appendScalar(k[i]);
+        SkScalar width = paint.measureText(str.c_str(), str.size());
+        canvas->drawText(str.c_str(), str.size(), x, y + paint.getTextSize(), paint);
+        x += width + SkIntToScalar(10);
+    }
+}
+
+class ArithmodeGM : public skiagm::GM {
+public:
+    ArithmodeGM () {}
+
+protected:
+
+    virtual SkString onShortName() {
+        return SkString("arithmode");
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(640, 480); }
+
+    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,
+            0, 0, 0, one,
+            0, one, 0, 0,
+            0, 0, one, 0,
+            0, one, one, 0,
+            0, one, -one, 0,
+            0, one/2, one/2, 0,
+            0, one/2, one/2, one/4,
+            0, one/2, one/2, -one/4,
+            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 gap = SkIntToScalar(src.width() + 20);
+        while (k < stop) {
+            SkScalar x = 0;
+            SkBitmap res = make_arith(src, dst, k);
+            canvas->drawBitmap(src, x, y, NULL);
+            x += gap;
+            canvas->drawBitmap(dst, x, y, NULL);
+            x += gap;
+            canvas->drawBitmap(res, x, y, NULL);
+            x += gap;
+            show_k_text(canvas, x, y, k);
+            k += 4;
+            y += SkIntToScalar(src.height() + 12);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new ArithmodeGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/bigmatrix.cpp b/gm/bigmatrix.cpp
new file mode 100644
index 0000000..31c0a38
--- /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);
+
+        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
new file mode 100644
index 0000000..6080918
--- /dev/null
+++ b/gm/bitmapcopy.cpp
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "gm.h"
+
+namespace skiagm {
+
+static const char* gConfigNames[] = {
+    "unknown config",
+    "A1",
+    "A8",
+    "Index8",
+    "565",
+    "4444",
+    "8888"
+};
+
+SkBitmap::Config gConfigs[] = {
+  SkBitmap::kRGB_565_Config,
+  SkBitmap::kARGB_4444_Config,
+  SkBitmap::kARGB_8888_Config,
+};
+
+#define NUM_CONFIGS (sizeof(gConfigs) / sizeof(SkBitmap::Config))
+
+static void draw_checks(SkCanvas* canvas, int width, int height) {
+    SkPaint paint;
+    paint.setColor(SK_ColorRED);
+    canvas->drawRectCoords(SkIntToScalar(0), SkIntToScalar(0),
+        SkIntToScalar(width / 2), SkIntToScalar(height / 2), paint);
+    paint.setColor(SK_ColorGREEN);
+    canvas->drawRectCoords(SkIntToScalar(width / 2), SkIntToScalar(0),
+        SkIntToScalar(width), SkIntToScalar(height / 2), paint);
+    paint.setColor(SK_ColorBLUE);
+    canvas->drawRectCoords(SkIntToScalar(0), SkIntToScalar(height / 2),
+        SkIntToScalar(width / 2), SkIntToScalar(height), paint);
+    paint.setColor(SK_ColorYELLOW);
+    canvas->drawRectCoords(SkIntToScalar(width / 2), SkIntToScalar(height / 2),
+        SkIntToScalar(width), SkIntToScalar(height), paint);
+}
+
+class BitmapCopyGM : public GM {
+public:
+    SkBitmap    fDst[NUM_CONFIGS];
+
+    BitmapCopyGM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("bitmapcopy");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(540, 330);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        SkScalar horizMargin(SkIntToScalar(10));
+        SkScalar vertMargin(SkIntToScalar(10));
+
+        draw_checks(canvas, 40, 40);
+        SkBitmap src = canvas->getDevice()->accessBitmap(false);
+
+        for (unsigned i = 0; i < NUM_CONFIGS; ++i) {
+            if (!src.deepCopyTo(&fDst[i], gConfigs[i])) {
+                src.copyTo(&fDst[i], gConfigs[i]);
+            }
+        }
+
+        canvas->clear(0xFFDDDDDD);
+        paint.setAntiAlias(true);
+        SkScalar width = SkIntToScalar(40);
+        SkScalar height = SkIntToScalar(40);
+        if (paint.getFontSpacing() > height) {
+            height = paint.getFontSpacing();
+        }
+        for (unsigned i = 0; i < NUM_CONFIGS; i++) {
+            const char* name = gConfigNames[src.config()];
+            SkScalar textWidth = paint.measureText(name, strlen(name));
+            if (textWidth > width) {
+                width = textWidth;
+            }
+        }
+        SkScalar horizOffset = width + horizMargin;
+        SkScalar vertOffset = height + vertMargin;
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+
+        for (unsigned i = 0; i < NUM_CONFIGS; i++) {
+            canvas->save();
+            // Draw destination config name
+            const char* name = gConfigNames[fDst[i].config()];
+            SkScalar textWidth = paint.measureText(name, strlen(name));
+            SkScalar x = (width - textWidth) / SkScalar(2);
+            SkScalar y = paint.getFontSpacing() / SkScalar(2);
+            canvas->drawText(name, strlen(name), x, y, paint);
+
+            // Draw destination bitmap
+            canvas->translate(0, vertOffset);
+            x = (width - 40) / SkScalar(2);
+            canvas->drawBitmap(fDst[i], x, 0, &paint);
+            canvas->restore();
+
+            canvas->translate(horizOffset, 0);
+        }
+    }
+
+    virtual uint32_t onGetFlags() const { return kSkipPicture_Flag
+                                               | kSkipPipe_Flag; }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#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
new file mode 100644
index 0000000..6419296
--- /dev/null
+++ b/gm/bitmapfilters.cpp
@@ -0,0 +1,136 @@
+
+/*
+ * Copyright 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"
+
+namespace skiagm {
+
+static void make_bm(SkBitmap* bm) {
+    const SkColor colors[4] = {
+        SK_ColorRED, SK_ColorGREEN,
+        SK_ColorBLUE, SK_ColorWHITE
+    };
+    SkPMColor colorsPM[4];
+    for (size_t i = 0; i < SK_ARRAY_COUNT(colors); ++i) {
+        colorsPM[i] = SkPreMultiplyColor(colors[i]);
+    }
+    SkColorTable* ctable = new SkColorTable(colorsPM, 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) {
+    canvas->drawBitmap(bm, x, y, paint);
+    return SkIntToScalar(bm.width()) * 5/4;
+}
+
+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 FilterGM : public GM {
+    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);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("bitmapfilters");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(540, 330);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        this->init();
+
+        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 GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new FilterGM; }
+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..4af6f0b
--- /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
new file mode 100644
index 0000000..eee9668
--- /dev/null
+++ b/gm/bitmapscroll.cpp
@@ -0,0 +1,157 @@
+
+/*
+ * Copyright 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"
+
+namespace skiagm {
+
+/** Create a bitmap image suitable for testing SkBitmap::scrollRect().
+ *
+ *  @param quarterWidth bitmap will be 4x this many pixels wide
+ *  @param quarterHeight bitmap will be 4x this many pixels tall
+ *  @param bitmap the bitmap data is written into this object
+ */
+static void make_bitmap(int quarterWidth, int quarterHeight, SkBitmap *bitmap) {
+    SkPaint pRed, pWhite, pGreen, pBlue, pLine, pAlphaGray;
+    pRed.setColor(0xFFFF9999);
+    pWhite.setColor(0xFFFFFFFF);
+    pGreen.setColor(0xFF99FF99);
+    pBlue.setColor(0xFF9999FF);
+    pLine.setColor(0xFF000000);
+    pLine.setStyle(SkPaint::kStroke_Style);
+    pAlphaGray.setColor(0x66888888);
+
+    // Prepare bitmap, and a canvas that draws into it.
+    bitmap->reset();
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config,
+                      quarterWidth*4, quarterHeight*4);
+    bitmap->allocPixels();
+    SkCanvas canvas(*bitmap);
+
+    SkScalar w = SkIntToScalar(quarterWidth);
+    SkScalar h = SkIntToScalar(quarterHeight);
+    canvas.drawRectCoords(  0,   0, w*2, h*2, pRed);
+    canvas.drawRectCoords(w*2,   0, w*4, h*2, pGreen);
+    canvas.drawRectCoords(  0, h*2, w*2, h*4, pBlue);
+    canvas.drawRectCoords(w*2, h*2, w*4, h*4, pWhite);
+    canvas.drawRectCoords(w, h, w*3, h*3, pAlphaGray);
+    canvas.drawLine(w*2,   0, w*2, h*4, pLine);
+    canvas.drawLine(  0, h*2, w*4, h*2, pLine);
+    canvas.drawRectCoords(w, h, w*3, h*3, pLine);
+}
+
+class BitmapScrollGM : public GM {
+    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);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("bitmapscroll");
+    }
+
+    virtual SkISize onISize() {
+      return make_isize(800, 600);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        this->init();
+        SkIRect scrollCenterRegion = SkIRect::MakeXYWH(
+            quarterWidth, quarterHeight, quarterWidth*2+1, quarterHeight*2+1);
+        int x = quarterWidth;
+        int y = quarterHeight;
+        int xSpacing = quarterWidth * 20;
+        int ySpacing = quarterHeight * 16;
+
+        // Draw left-hand text labels.
+        drawLabel(canvas, "scroll entire bitmap",
+                  x, y, x, y + ySpacing);
+        drawLabel(canvas, "scroll part of bitmap",
+                  x, y + ySpacing, x, y + ySpacing*2);
+        x += 30;
+
+        // Draw various permutations of scrolled bitmaps, scrolling a bit
+        // further each time.
+        draw9(canvas, x, y, NULL, quarterWidth*1/2, quarterHeight*1/2);
+        draw9(canvas, x, y+ySpacing, &scrollCenterRegion,
+              quarterWidth*1/2, quarterHeight*1/2);
+        x += xSpacing;
+        draw9(canvas, x, y, NULL, quarterWidth*3/2, quarterHeight*3/2);
+        draw9(canvas, x, y+ySpacing, &scrollCenterRegion,
+              quarterWidth*3/2, quarterHeight*3/2);
+        x += xSpacing;
+        draw9(canvas, x, y, NULL, quarterWidth*5/2, quarterHeight*5/2);
+        draw9(canvas, x, y+ySpacing, &scrollCenterRegion,
+              quarterWidth*5/2, quarterHeight*5/2);
+        x += xSpacing;
+        draw9(canvas, x, y, NULL, quarterWidth*9/2, quarterHeight*9/2);
+        draw9(canvas, x, y+ySpacing, &scrollCenterRegion,
+              quarterWidth*9/2, quarterHeight*9/2);
+    }
+
+    void drawLabel(SkCanvas* canvas, const char *text, int startX, int startY,
+                 int endX, int endY) {
+        SkPaint paint;
+        paint.setColor(0xFF000000);
+        SkPath path;
+        path.moveTo(SkIntToScalar(startX), SkIntToScalar(startY));
+        path.lineTo(SkIntToScalar(endX), SkIntToScalar(endY));
+        canvas->drawTextOnPath(text, strlen(text), path, NULL, paint);
+    }
+
+    /** Stamp out 9 copies of origBitmap, scrolled in each direction (and
+     *  not scrolled at all).
+     */
+    void draw9(SkCanvas* canvas, int x, int y, SkIRect* subset,
+               int scrollX, int scrollY) {
+        for (int yMult=-1; yMult<=1; yMult++) {
+            for (int xMult=-1; xMult<=1; xMult++) {
+                // Figure out the (x,y) to draw this copy at
+                SkScalar bitmapX = SkIntToScalar(
+                    x + quarterWidth * 5 * (xMult+1));
+                SkScalar bitmapY = SkIntToScalar(
+                    y + quarterHeight * 5 * (yMult+1));
+
+                // 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(
+                    &scrolledBitmap, origBitmap.config());
+                SkASSERT(copyToReturnValue);
+                bool scrollRectReturnValue = scrolledBitmap.scrollRect(
+                    subset, scrollX * xMult, scrollY * yMult);
+                SkASSERT(scrollRectReturnValue);
+                canvas->drawBitmap(scrolledBitmap, bitmapX, bitmapY);
+            }
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+    static const int quarterWidth = 10;
+    static const int quarterHeight = 14;
+    SkBitmap origBitmap;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new BitmapScrollGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/blend.cpp b/gm/blend.cpp
new file mode 100644
index 0000000..3ba92a7
--- /dev/null
+++ b/gm/blend.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 "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);
+    }
+
+    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();
+        canvas->drawSprite(fBitmap, 0, 0, &paint);
+        paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kMultiply_Mode, background)))->unref();
+        canvas->drawSprite(fBitmap, 100, 0, &paint);
+        paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kScreen_Mode, background)))->unref();
+        canvas->drawSprite(fBitmap, 200, 0, &paint);
+        paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kDarken_Mode, background)))->unref();
+        canvas->drawSprite(fBitmap, 300, 0, &paint);
+        paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kLighten_Mode, background)))->unref();
+        canvas->drawSprite(fBitmap, 400, 0, &paint);
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap, fCheckerboard;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ImageBlendGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/blurs.cpp b/gm/blurs.cpp
new file mode 100644
index 0000000..bb688b9
--- /dev/null
+++ b/gm/blurs.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 "gm.h"
+#include "SkBlurMaskFilter.h"
+
+namespace skiagm {
+
+class BlursGM : public GM {
+public:
+    BlursGM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+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");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(700, 500);
+    }
+
+    virtual void onDraw(SkCanvas* 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(SkIntToScalar(25));
+        canvas->translate(SkIntToScalar(-40), SkIntToScalar(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(
+                            SkIntToScalar(20), gRecs[i].fStyle, flags
+                    );
+                    paint.setMaskFilter(mf)->unref();
+                } else {
+                    paint.setMaskFilter(NULL);
+                }
+                canvas->drawCircle(SkIntToScalar(200 + gRecs[i].fCx*100)
+                                   , SkIntToScalar(200 + gRecs[i].fCy*100)
+                                   , SkIntToScalar(50)
+                                   , paint);
+            }
+            // draw text
+            {
+                SkMaskFilter* mf = SkBlurMaskFilter::Create(
+                        SkIntToScalar(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(SkIntToScalar(350), SkIntToScalar(0));
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new BlursGM; }
+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..94cf98f
--- /dev/null
+++ b/gm/cmykjpeg.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 "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() {
+
+        // 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);
+        }
+    }
+
+protected:
+    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() {
+    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..556e88a
--- /dev/null
+++ b/gm/colorfilterimagefilter.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 "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 new SkColorFilterImageFilter(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 new SkColorFilterImageFilter(filter, input);
+}
+
+static SkImageFilter* make_mode_blue(SkImageFilter* input = NULL) {
+    SkAutoTUnref<SkColorFilter> filter(
+        SkColorFilter::CreateModeFilter(SK_ColorBLUE, SkXfermode::kSrcIn_Mode));
+    return new SkColorFilterImageFilter(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
new file mode 100644
index 0000000..af305ee
--- /dev/null
+++ b/gm/colormatrix.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright 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 "SkColorMatrixFilter.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()) {
+            fBitmap = createBitmap(64, 64);
+        }
+    }
+
+public:
+    ColorMatrixGM() {
+        this->setBGColor(0xFF808080);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("colormatrix");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(WIDTH, HEIGHT);
+    }
+
+    SkBitmap createBitmap(int width, int height) {
+        SkBitmap bm;
+        bm.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+        bm.allocPixels();
+        SkCanvas canvas(bm);
+        canvas.clear(0x0);
+        for (int y = 0; y < height; ++y) {
+            for (int x = 0; x < width; ++x) {
+                SkPaint paint;
+                paint.setColor(SkColorSetARGB(255, x * 255 / width, y * 255 / height, 0));
+                canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(x),
+                    SkIntToScalar(y), SK_Scalar1, SK_Scalar1), paint);
+            }
+        }
+        return bm;
+    }
+    virtual void onDraw(SkCanvas* canvas) {
+        this->init();
+
+        SkPaint paint;
+        SkColorMatrix matrix;
+
+        matrix.setIdentity();
+        setColorMatrix(&paint, matrix);
+        canvas->drawBitmap(fBitmap, 0, 0, &paint);
+
+        matrix.setRotate(SkColorMatrix::kR_Axis, 90);
+        setColorMatrix(&paint, matrix);
+        canvas->drawBitmap(fBitmap, 80, 0, &paint);
+
+        matrix.setRotate(SkColorMatrix::kG_Axis, 90);
+        setColorMatrix(&paint, matrix);
+        canvas->drawBitmap(fBitmap, 160, 0, &paint);
+
+        matrix.setRotate(SkColorMatrix::kB_Axis, 90);
+        setColorMatrix(&paint, matrix);
+        canvas->drawBitmap(fBitmap, 240, 0, &paint);
+
+        matrix.setSaturation(SkFloatToScalar(0.0f));
+        setColorMatrix(&paint, matrix);
+        canvas->drawBitmap(fBitmap, 0, 80, &paint);
+
+        matrix.setSaturation(SkFloatToScalar(0.5f));
+        setColorMatrix(&paint, matrix);
+        canvas->drawBitmap(fBitmap, 80, 80, &paint);
+
+        matrix.setSaturation(SkFloatToScalar(1.0f));
+        setColorMatrix(&paint, matrix);
+        canvas->drawBitmap(fBitmap, 160, 80, &paint);
+
+        matrix.setSaturation(SkFloatToScalar(2.0f));
+        setColorMatrix(&paint, matrix);
+        canvas->drawBitmap(fBitmap, 240, 80, &paint);
+
+        matrix.setRGB2YUV();
+        setColorMatrix(&paint, matrix);
+        canvas->drawBitmap(fBitmap, 0, 160, &paint);
+
+        matrix.setYUV2RGB();
+        setColorMatrix(&paint, matrix);
+        canvas->drawBitmap(fBitmap, 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(fBitmap, 160, 160, &paint);
+    }
+
+private:
+    SkBitmap fBitmap;
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ColorMatrixGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/complexclip.cpp b/gm/complexclip.cpp
new file mode 100644
index 0000000..86386b3
--- /dev/null
+++ b/gm/complexclip.cpp
@@ -0,0 +1,197 @@
+
+/*
+ * Copyright 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 "SkParsePath.h"
+#include "SkPath.h"
+//#include "SkRandom.h"
+
+namespace skiagm {
+
+static const SkColor gPathColor = SK_ColorBLACK;
+static const SkColor gClipAColor = SK_ColorBLUE;
+static const SkColor gClipBColor = SK_ColorRED;
+
+class ComplexClipGM : public GM {
+    bool fDoAAClip;
+    bool fDoSaveLayer;
+public:
+    ComplexClipGM(bool aaclip, bool saveLayer)
+    : fDoAAClip(aaclip)
+    , fDoSaveLayer(saveLayer) {
+        this->setBGColor(0xFFDDDDDD);
+//        this->setBGColor(SkColorSetRGB(0xB0,0xDD,0xB0));
+    }
+
+protected:
+
+    SkString onShortName() {
+        SkString str;
+        str.printf("complexclip_%s%s",
+                   fDoAAClip ? "aa" : "bw",
+                   fDoSaveLayer ? "_layer" : "");
+        return str;
+    }
+
+    SkISize onISize() { return make_isize(970, 780); }
+
+    virtual void onDraw(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);
+        SkPaint pathPaint;
+        pathPaint.setAntiAlias(true);
+        pathPaint.setColor(gPathColor);
+
+        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();
+
+        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();
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(20));
+
+        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(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) {
+                this->drawHairlines(canvas, path, clipA, clipB);
+
+                bool doInvA = SkToBool(invBits & 1);
+                bool doInvB = SkToBool(invBits & 2);
+                canvas->save();
+                    // set clip
+                    clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
+                                      SkPath::kEvenOdd_FillType);
+                    clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
+                                      SkPath::kEvenOdd_FillType);
+                    canvas->clipPath(clipA, SkRegion::kIntersect_Op, fDoAAClip);
+                    canvas->clipPath(clipB, gOps[op].fOp, fDoAAClip);
+
+                    // draw path clipped
+                    canvas->drawPath(path, pathPaint);
+                canvas->restore();
+
+
+                SkScalar txtX = SkIntToScalar(45);
+                paint.setColor(gClipAColor);
+                const char* aTxt = doInvA ? "InvA " : "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(gClipBColor);
+                const char* bTxt = doInvB ? "InvB " : "B ";
+                canvas->drawText(bTxt, strlen(bTxt), txtX, SkIntToScalar(220), paint);
+
+                canvas->translate(SkIntToScalar(250),0);
+            }
+            canvas->restore();
+            canvas->translate(0, SkIntToScalar(250));
+        }
+
+        if (fDoSaveLayer) {
+            canvas->restore();
+        }
+    }
+private:
+    void drawHairlines(SkCanvas* canvas, const SkPath& path,
+                       const SkPath& clipA, const SkPath& clipB) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        const SkAlpha fade = 0x33;
+
+        // 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);
+        paint.setColor(gClipBColor); paint.setAlpha(fade);
+        canvas->drawPath(clipB, paint);
+    }
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+// 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
new file mode 100644
index 0000000..ce1f0e0
--- /dev/null
+++ b/gm/complexclip2.cpp
@@ -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.
+ */
+
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+
+namespace skiagm {
+
+class ComplexClip2GM : public GM {
+public:
+    ComplexClip2GM(bool doPaths, bool antiAlias)
+    : fDoPaths(doPaths)
+    , fAntiAlias(antiAlias) {
+        this->setBGColor(SkColorSetRGB(0xDD,0xA0,0xDD));
+
+        // 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;
+        fTotalHeight = kRows * fHeight + SK_Scalar1 * (kRows + 1) * kPadY;
+
+        SkRegion::Op ops[] = {
+            SkRegion::kDifference_Op,
+            SkRegion::kIntersect_Op,
+            SkRegion::kUnion_Op,
+            SkRegion::kXOR_Op,
+            SkRegion::kReverseDifference_Op,
+            SkRegion::kReplace_Op,
+        };
+
+        SkRandom r;
+        for (int i = 0; i < kRows; ++i) {
+            for (int j = 0; j < kCols; ++j) {
+                for (int k = 0; k < 5; ++k) {
+                    fOps[j*kRows+i][k] = ops[r.nextU() % SK_ARRAY_COUNT(ops)];
+                }
+            }
+        }
+    }
+
+protected:
+
+    static const int kRows = 5;
+    static const int kCols = 5;
+    static const int kPadX = 20;
+    static const int kPadY = 20;
+
+    virtual SkString onShortName() {
+        if (!fDoPaths && !fAntiAlias) {
+            return SkString("complexclip2");
+        }
+
+        SkString str;
+        str.printf("complexclip2_%s_%s",
+                    fDoPaths ? "path" : "rect",
+                    fAntiAlias ? "aa" : "bw");
+        return str;
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(SkScalarRoundToInt(fTotalWidth),
+                          SkScalarRoundToInt(fTotalHeight));
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint rectPaint;
+        rectPaint.setStyle(SkPaint::kStroke_Style);
+        rectPaint.setStrokeWidth(-1);
+
+        SkPaint fillPaint;
+        fillPaint.setColor(SkColorSetRGB(0xA0,0xDD,0xA0));
+
+        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);
+
+                // 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]);
+                    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;
+    SkScalar fHeight;
+    SkScalar fTotalWidth;
+    SkScalar fTotalHeight;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+// 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..f74a19e
--- /dev/null
+++ b/gm/composeshader.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 "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
new file mode 100644
index 0000000..5af843f
--- /dev/null
+++ b/gm/convexpaths.cpp
@@ -0,0 +1,266 @@
+
+/*
+ * Copyright 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 "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);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("convexpaths");
+    }
+
+
+    virtual SkISize onISize() {
+        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,
+                             0, 100 * SK_Scalar1);
+        fPaths.back().lineTo(0, 0);
+
+        // CCW
+        fPaths.push_back().moveTo(0, 0);
+        fPaths.back().lineTo(0, 100 * SK_Scalar1);
+        fPaths.back().quadTo(50 * SK_Scalar1, 100 * SK_Scalar1,
+                             0, 0);
+
+        // CW
+        fPaths.push_back().moveTo(0, 50 * SK_Scalar1);
+        fPaths.back().quadTo(50 * SK_Scalar1, 0,
+                             100 * SK_Scalar1, 50 * SK_Scalar1);
+        fPaths.back().quadTo(50 * SK_Scalar1, 100 * SK_Scalar1,
+                             0, 50 * SK_Scalar1);
+
+        // CCW
+        fPaths.push_back().moveTo(0, 50 * SK_Scalar1);
+        fPaths.back().quadTo(50 * SK_Scalar1, 100 * SK_Scalar1,
+                             100 * SK_Scalar1, 50 * SK_Scalar1);
+        fPaths.back().quadTo(50 * SK_Scalar1, 0,
+                             0, 50 * SK_Scalar1);
+
+        fPaths.push_back().addRect(0, 0,
+                                   100 * SK_Scalar1, 100 * SK_Scalar1,
+                                   SkPath::kCW_Direction);
+
+        fPaths.push_back().addRect(0, 0,
+                                   100 * SK_Scalar1, 100 * SK_Scalar1,
+                                   SkPath::kCCW_Direction);
+
+        fPaths.push_back().addCircle(50  * SK_Scalar1, 50  * SK_Scalar1,
+                                     50  * SK_Scalar1, SkPath::kCW_Direction);
+
+        fPaths.push_back().addCircle(50  * SK_Scalar1, 50  * SK_Scalar1,
+                                     40  * SK_Scalar1, SkPath::kCCW_Direction);
+
+        fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
+                                                    50 * SK_Scalar1,
+                                                    100 * SK_Scalar1),
+                                   SkPath::kCW_Direction);
+
+        fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
+                                                    100 * SK_Scalar1,
+                                                    50 * SK_Scalar1),
+                                   SkPath::kCCW_Direction);
+
+        fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
+                                                    100 * SK_Scalar1,
+                                                    5 * SK_Scalar1),
+                                   SkPath::kCCW_Direction);
+
+        fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
+                                                    SK_Scalar1,
+                                                    100 * SK_Scalar1),
+                                   SkPath::kCCW_Direction);
+
+        fPaths.push_back().addRoundRect(SkRect::MakeXYWH(0, 0,
+                                                         SK_Scalar1 * 100,
+                                                         SK_Scalar1 * 100),
+                                        40 * SK_Scalar1, 20 * SK_Scalar1,
+                                        SkPath::kCW_Direction);
+
+        fPaths.push_back().addRoundRect(SkRect::MakeXYWH(0, 0,
+                                                         SK_Scalar1 * 100,
+                                                         SK_Scalar1 * 100),
+                                        20 * SK_Scalar1, 40 * SK_Scalar1,
+                                        SkPath::kCCW_Direction);
+
+        // shallow diagonals
+        fPaths.push_back().lineTo(100 * SK_Scalar1, SK_Scalar1);
+        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);
+
+        // cubics
+        fPaths.push_back().cubicTo( 1 * SK_Scalar1,  1 * SK_Scalar1,
+                                   10 * SK_Scalar1,  90 * SK_Scalar1,
+                                    0 * SK_Scalar1, 100 * SK_Scalar1);
+        fPaths.push_back().cubicTo(100 * SK_Scalar1,  50 * SK_Scalar1,
+                                    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,
+                             SkFloatToScalar(31.25f),        45 * SK_Scalar1);
+        fPaths.back().lineTo(100 * SK_Scalar1,              100 * SK_Scalar1);
+        fPaths.back().lineTo(SkFloatToScalar(8.59375f),      45 * SK_Scalar1);
+
+        // 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);
+        fPaths.push_back().moveTo(50 * SK_Scalar1, 50 * SK_Scalar1);
+        fPaths.back().cubicTo(50 * SK_Scalar1, 50 * SK_Scalar1,
+                              50 * SK_Scalar1, 50 * SK_Scalar1,
+                              50 * SK_Scalar1, 50 * SK_Scalar1);
+
+        // moveTo only paths
+        fPaths.push_back().moveTo(0, 0);
+        fPaths.back().moveTo(0, 0);
+        fPaths.back().moveTo(SK_Scalar1, SK_Scalar1);
+        fPaths.back().moveTo(SK_Scalar1, SK_Scalar1);
+        fPaths.back().moveTo(10 * SK_Scalar1, 10 * SK_Scalar1);
+
+        fPaths.push_back().moveTo(0, 0);
+        fPaths.back().moveTo(0, 0);
+
+        // line degenerate
+        fPaths.push_back().lineTo(100 * SK_Scalar1, 100 * SK_Scalar1);
+        fPaths.push_back().quadTo(100 * SK_Scalar1, 100 * SK_Scalar1, 0, 0);
+        fPaths.push_back().quadTo(100 * SK_Scalar1, 100 * SK_Scalar1,
+                                  50 * SK_Scalar1, 50 * SK_Scalar1);
+        fPaths.push_back().quadTo(50 * SK_Scalar1, 50 * SK_Scalar1,
+                                  100 * SK_Scalar1, 100 * SK_Scalar1);
+        fPaths.push_back().cubicTo(0, 0,
+                                   0, 0,
+                                   100 * SK_Scalar1, 100 * SK_Scalar1);
+
+        // small circle. This is listed last so that it has device coords far
+        // from the origin (small area relative to x,y values).
+        fPaths.push_back().addCircle(0, 0, SkFloatToScalar(0.8f));
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        this->makePaths();
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkRandom rand;
+    canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
+    for (int i = 0; i < fPaths.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;
+        paint.setColor(color);
+        SkASSERT(fPaths[i].isConvex());
+        canvas->drawPath(fPaths[i], paint);
+        canvas->restore();
+    }
+    }
+
+private:
+    typedef GM INHERITED;
+    SkTArray<SkPath> fPaths;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ConvexPathsGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/cubicpaths.cpp b/gm/cubicpaths.cpp
new file mode 100644
index 0000000..1fc13c0
--- /dev/null
+++ b/gm/cubicpaths.cpp
@@ -0,0 +1,309 @@
+/*
+ * Copyright 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 "SkPaint.h"
+#include "SkRandom.h"
+
+namespace skiagm {
+
+class CubicPathGM : public GM {
+public:
+    CubicPathGM() {}
+
+protected:
+    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,
+                  SkScalar strokeWidth) {
+        path.setFillType(fill);
+        SkPaint paint;
+        paint.setStrokeCap(cap);
+        paint.setStrokeWidth(strokeWidth);
+        paint.setStrokeJoin(join);
+        paint.setColor(color);
+        paint.setStyle(style);
+        canvas->save();
+        canvas->clipRect(clip);
+        canvas->drawPath(path, paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(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"},
+        };
+        struct CapAndName {
+            SkPaint::Cap  fCap;
+            SkPaint::Join fJoin;
+            const char*   fName;
+        };
+        static const CapAndName gCaps[] = {
+            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
+            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
+            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
+        };
+        struct PathAndName {
+            SkPath      fPath;
+            const char* fName;
+        };
+        PathAndName path;
+        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
+        path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1,
+                           60*SK_Scalar1, 20*SK_Scalar1,
+                           75*SK_Scalar1, 10*SK_Scalar1);
+        path.fName = "moveTo-cubic";
+
+        SkPaint titlePaint;
+        titlePaint.setColor(SK_ColorBLACK);
+        titlePaint.setAntiAlias(true);
+        titlePaint.setLCDRenderText(true);
+        titlePaint.setTextSize(15 * SK_Scalar1);
+        const char title[] = "Cubic Drawn Into Rectangle Clips With "
+                             "Indicated Style, Fill and Linecaps, with stroke width 10";
+        canvas->drawText(title, strlen(title),
+                            20 * SK_Scalar1,
+                            20 * SK_Scalar1,
+                            titlePaint);
+
+        SkRandom rand;
+        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
+        canvas->save();
+        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
+        canvas->save();
+        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
+            if (0 < cap) {
+                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
+            }
+            canvas->save();
+            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
+                if (0 < fill) {
+                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
+                }
+                canvas->save();
+                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
+                    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);
+                    labelPaint.setLCDRenderText(true);
+                    labelPaint.setTextSize(10 * SK_Scalar1);
+                    canvas->drawText(gStyles[style].fName,
+                                        strlen(gStyles[style].fName),
+                                        0, rect.height() + 12 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gFills[fill].fName,
+                                        strlen(gFills[fill].fName),
+                                        0, rect.height() + 24 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gCaps[cap].fName,
+                                        strlen(gCaps[cap].fName),
+                                        0, rect.height() + 36 * SK_Scalar1,
+                                        labelPaint);
+                }
+                canvas->restore();
+            }
+            canvas->restore();
+        }
+        canvas->restore();
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+class CubicClosePathGM : public GM {
+public:
+    CubicClosePathGM() {}
+
+protected:
+    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,
+                  SkScalar strokeWidth) {
+        path.setFillType(fill);
+        SkPaint paint;
+        paint.setStrokeCap(cap);
+        paint.setStrokeWidth(strokeWidth);
+        paint.setStrokeJoin(join);
+        paint.setColor(color);
+        paint.setStyle(style);
+        canvas->save();
+        canvas->clipRect(clip);
+        canvas->drawPath(path, paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(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"},
+        };
+        struct CapAndName {
+            SkPaint::Cap  fCap;
+            SkPaint::Join fJoin;
+            const char*   fName;
+        };
+        static const CapAndName gCaps[] = {
+            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
+            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
+            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
+        };
+        struct PathAndName {
+            SkPath      fPath;
+            const char* fName;
+        };
+        PathAndName path;
+        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
+        path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1,
+                           60*SK_Scalar1, 20*SK_Scalar1,
+                           75*SK_Scalar1, 10*SK_Scalar1);
+        path.fPath.close();
+        path.fName = "moveTo-cubic-close";
+
+        SkPaint titlePaint;
+        titlePaint.setColor(SK_ColorBLACK);
+        titlePaint.setAntiAlias(true);
+        titlePaint.setLCDRenderText(true);
+        titlePaint.setTextSize(15 * SK_Scalar1);
+        const char title[] = "Cubic Closed Drawn Into Rectangle Clips With "
+                             "Indicated Style, Fill and Linecaps, with stroke width 10";
+        canvas->drawText(title, strlen(title),
+                            20 * SK_Scalar1,
+                            20 * SK_Scalar1,
+                            titlePaint);
+
+        SkRandom rand;
+        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
+        canvas->save();
+        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
+        canvas->save();
+        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
+            if (0 < cap) {
+                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
+            }
+            canvas->save();
+            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
+                if (0 < fill) {
+                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
+                }
+                canvas->save();
+                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
+                    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);
+                    labelPaint.setLCDRenderText(true);
+                    labelPaint.setTextSize(10 * SK_Scalar1);
+                    canvas->drawText(gStyles[style].fName,
+                                        strlen(gStyles[style].fName),
+                                        0, rect.height() + 12 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gFills[fill].fName,
+                                        strlen(gFills[fill].fName),
+                                        0, rect.height() + 24 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gCaps[cap].fName,
+                                        strlen(gCaps[cap].fName),
+                                        0, rect.height() + 36 * SK_Scalar1,
+                                        labelPaint);
+                }
+                canvas->restore();
+            }
+            canvas->restore();
+        }
+        canvas->restore();
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* CubicPathFactory(void*) { return new CubicPathGM; }
+static GMRegistry regCubicPath(CubicPathFactory);
+
+static GM* CubicClosePathFactory(void*) { return new CubicClosePathGM; }
+static GMRegistry regCubicClosePath(CubicClosePathFactory);
+
+}
diff --git a/gm/dashcubics.cpp b/gm/dashcubics.cpp
new file mode 100644
index 0000000..bbce9be
--- /dev/null
+++ b/gm/dashcubics.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 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..2f1f026
--- /dev/null
+++ b/gm/dashing.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright 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);
+            }
+        }
+    }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* F0(void*) { return new DashingGM; }
+static skiagm::GM* F1(void*) { return new Dashing2GM; }
+
+static skiagm::GMRegistry gR0(F0);
+static skiagm::GMRegistry gR1(F1);
+
diff --git a/gm/degeneratesegments.cpp b/gm/degeneratesegments.cpp
new file mode 100644
index 0000000..2e19d05
--- /dev/null
+++ b/gm/degeneratesegments.cpp
@@ -0,0 +1,400 @@
+/*
+ * Copyright 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 "SkPaint.h"
+#include "SkRandom.h"
+
+namespace skiagm {
+
+class DegenerateSegmentsGM : public GM {
+public:
+    DegenerateSegmentsGM() {}
+
+protected:
+    struct PathAndName {
+        SkPath      fPath;
+        const char* fName1;
+        const char* fName2;
+    };
+
+    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.
+    static SkPoint AddMove(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        path.moveTo(moveToPt);
+        return moveToPt;
+    }
+
+    static SkPoint AddMoveClose(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        path.moveTo(moveToPt);
+        path.close();
+        return moveToPt;
+    }
+
+    static SkPoint AddDegenLine(SkPath& path, SkPoint& startPt) {
+        path.lineTo(startPt);
+        return startPt;
+    }
+
+    static SkPoint AddMoveDegenLine(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        path.moveTo(moveToPt);
+        path.lineTo(moveToPt);
+        return moveToPt;
+    }
+
+    static SkPoint AddMoveDegenLineClose(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        path.moveTo(moveToPt);
+        path.lineTo(moveToPt);
+        path.close();
+        return moveToPt;
+    }
+
+    static SkPoint AddDegenQuad(SkPath& path, SkPoint& startPt) {
+        path.quadTo(startPt, startPt);
+        return startPt;
+    }
+
+    static SkPoint AddMoveDegenQuad(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        path.moveTo(moveToPt);
+        path.quadTo(moveToPt, moveToPt);
+        return moveToPt;
+    }
+
+    static SkPoint AddMoveDegenQuadClose(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        path.moveTo(moveToPt);
+        path.quadTo(moveToPt, moveToPt);
+        path.close();
+        return moveToPt;
+    }
+
+    static SkPoint AddDegenCubic(SkPath& path, SkPoint& startPt) {
+        path.cubicTo(startPt, startPt, startPt);
+        return startPt;
+    }
+
+    static SkPoint AddMoveDegenCubic(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        path.moveTo(moveToPt);
+        path.cubicTo(moveToPt, moveToPt, moveToPt);
+        return moveToPt;
+    }
+
+    static SkPoint AddMoveDegenCubicClose(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        path.moveTo(moveToPt);
+        path.cubicTo(moveToPt, moveToPt, moveToPt);
+        path.close();
+        return moveToPt;
+    }
+
+    static SkPoint AddClose(SkPath& path, SkPoint& startPt) {
+        path.close();
+        return startPt;
+    }
+
+    static SkPoint AddLine(SkPath& path, SkPoint& startPt) {
+        SkPoint endPt = startPt + SkPoint::Make(40*SK_Scalar1, 0);
+        path.lineTo(endPt);
+        return endPt;
+    }
+
+    static SkPoint AddMoveLine(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
+        path.moveTo(moveToPt);
+        path.lineTo(endPt);
+        return endPt;
+    }
+
+    static SkPoint AddMoveLineClose(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
+        path.moveTo(moveToPt);
+        path.lineTo(endPt);
+        path.close();
+        return endPt;
+    }
+
+    static SkPoint AddQuad(SkPath& path, SkPoint& startPt) {
+        SkPoint midPt = startPt + SkPoint::Make(20*SK_Scalar1, 5*SK_Scalar1);
+        SkPoint endPt = startPt + SkPoint::Make(40*SK_Scalar1, 0);
+        path.quadTo(midPt, endPt);
+        return endPt;
+    }
+
+    static SkPoint AddMoveQuad(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        SkPoint midPt = moveToPt + SkPoint::Make(20*SK_Scalar1, 5*SK_Scalar1);
+        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
+        path.moveTo(moveToPt);
+        path.quadTo(midPt, endPt);
+        return endPt;
+    }
+
+    static SkPoint AddMoveQuadClose(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        SkPoint midPt = moveToPt + SkPoint::Make(20*SK_Scalar1, 5*SK_Scalar1);
+        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
+        path.moveTo(moveToPt);
+        path.quadTo(midPt, endPt);
+        path.close();
+        return endPt;
+    }
+
+    static SkPoint AddCubic(SkPath& path, SkPoint& startPt) {
+        SkPoint t1Pt = startPt + SkPoint::Make(15*SK_Scalar1, 5*SK_Scalar1);
+        SkPoint t2Pt = startPt + SkPoint::Make(25*SK_Scalar1, 5*SK_Scalar1);
+        SkPoint endPt = startPt + SkPoint::Make(40*SK_Scalar1, 0);
+        path.cubicTo(t1Pt, t2Pt, endPt);
+        return endPt;
+    }
+
+    static SkPoint AddMoveCubic(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        SkPoint t1Pt = moveToPt + SkPoint::Make(15*SK_Scalar1, 5*SK_Scalar1);
+        SkPoint t2Pt = moveToPt + SkPoint::Make(25*SK_Scalar1, 5*SK_Scalar1);
+        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
+        path.moveTo(moveToPt);
+        path.cubicTo(t1Pt, t2Pt, endPt);
+        return endPt;
+    }
+
+    static SkPoint AddMoveCubicClose(SkPath& path, SkPoint& startPt) {
+        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
+        SkPoint t1Pt = moveToPt + SkPoint::Make(15*SK_Scalar1, 5*SK_Scalar1);
+        SkPoint t2Pt = moveToPt + SkPoint::Make(25*SK_Scalar1, 5*SK_Scalar1);
+        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
+        path.moveTo(moveToPt);
+        path.cubicTo(t1Pt, t2Pt, endPt);
+        path.close();
+        return endPt;
+    }
+
+    void drawPath(SkPath& path, SkCanvas* canvas, SkColor color,
+                  const SkRect& clip, SkPaint::Cap cap, SkPaint::Join join,
+                  SkPaint::Style style, SkPath::FillType fill,
+                  SkScalar strokeWidth) {
+        path.setFillType(fill);
+        SkPaint paint;
+        paint.setStrokeCap(cap);
+        paint.setStrokeWidth(strokeWidth);
+        paint.setStrokeJoin(join);
+        paint.setColor(color);
+        paint.setStyle(style);
+        canvas->save();
+        canvas->clipRect(clip);
+        canvas->drawPath(path, paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+    static const AddSegmentFunc gSegmentFunctions[] = {
+        AddMove,
+        AddMoveClose,
+        AddDegenLine,
+        AddMoveDegenLine,
+        AddMoveDegenLineClose,
+        AddDegenQuad,
+        AddMoveDegenQuad,
+        AddMoveDegenQuadClose,
+        AddDegenCubic,
+        AddMoveDegenCubic,
+        AddMoveDegenCubicClose,
+        AddClose,
+        AddLine,
+        AddMoveLine,
+        AddMoveLineClose,
+        AddQuad,
+        AddMoveQuad,
+        AddMoveQuadClose,
+        AddCubic,
+        AddMoveCubic,
+        AddMoveCubicClose
+    };
+    static const char* gSegmentNames[] = {
+        "Move",
+        "MoveClose",
+        "DegenLine",
+        "MoveDegenLine",
+        "MoveDegenLineClose",
+        "DegenQuad",
+        "MoveDegenQuad",
+        "MoveDegenQuadClose",
+        "DegenCubic",
+        "MoveDegenCubic",
+        "MoveDegenCubicClose",
+        "Close",
+        "Line",
+        "MoveLine",
+        "MoveLineClose",
+        "Quad",
+        "MoveQuad",
+        "MoveQuadClose",
+        "Cubic",
+        "MoveCubic",
+        "MoveCubicClose"
+    };
+
+        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 10"},
+            {SkPaint::kStrokeAndFill_Style, "Stroke 10 And Fill"}
+        };
+        struct CapAndName {
+            SkPaint::Cap  fCap;
+            SkPaint::Join fJoin;
+            const char*   fName;
+        };
+        static const CapAndName gCaps[] = {
+            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
+            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
+            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
+        };
+
+        SkPaint titlePaint;
+        titlePaint.setColor(SK_ColorBLACK);
+        titlePaint.setAntiAlias(true);
+        titlePaint.setLCDRenderText(true);
+        titlePaint.setTextSize(15 * SK_Scalar1);
+        const char title[] = "Random Paths Drawn Into Rectangle Clips With "
+                             "Indicated Style, Fill and Linecaps, "
+                             "with Stroke width 6";
+        canvas->drawText(title, strlen(title),
+                            20 * SK_Scalar1,
+                            20 * SK_Scalar1,
+                            titlePaint);
+
+        SkRandom rand;
+        SkRect rect = SkRect::MakeWH(220*SK_Scalar1, 50*SK_Scalar1);
+        canvas->save();
+        canvas->translate(2*SK_Scalar1, 30 * SK_Scalar1); // The title
+        canvas->save();
+        unsigned numSegments = SK_ARRAY_COUNT(gSegmentFunctions);
+        unsigned numCaps = SK_ARRAY_COUNT(gCaps);
+        unsigned numStyles = SK_ARRAY_COUNT(gStyles);
+        unsigned numFills = SK_ARRAY_COUNT(gFills);
+        for (size_t row = 0; row < 6; ++row) {
+            if (0 < row) {
+                canvas->translate(0, rect.height() + 100*SK_Scalar1);
+            }
+            canvas->save();
+            for (size_t column = 0; column < 4; ++column) {
+                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];
+                FillAndName fill = gFills[(rand.nextU() >> 16) % numFills];
+                SkPath path;
+                unsigned s1 = (rand.nextU() >> 16) % numSegments;
+                unsigned s2 = (rand.nextU() >> 16) % numSegments;
+                unsigned s3 = (rand.nextU() >> 16) % numSegments;
+                unsigned s4 = (rand.nextU() >> 16) % numSegments;
+                unsigned s5 = (rand.nextU() >> 16) % numSegments;
+                SkPoint pt = SkPoint::Make(10*SK_Scalar1, 0);
+                pt = gSegmentFunctions[s1](path, pt);
+                pt = gSegmentFunctions[s2](path, pt);
+                pt = gSegmentFunctions[s3](path, pt);
+                pt = gSegmentFunctions[s4](path, pt);
+                pt = gSegmentFunctions[s5](path, pt);
+
+                this->drawPath(path, canvas, color, rect,
+                               cap.fCap, cap.fJoin, style.fStyle,
+                               fill.fFill, SK_Scalar1*6);
+
+                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);
+                labelPaint.setLCDRenderText(true);
+                labelPaint.setTextSize(10 * SK_Scalar1);
+                canvas->drawText(style.fName,
+                                 strlen(style.fName),
+                                 0, rect.height() + 12 * SK_Scalar1,
+                                 labelPaint);
+                canvas->drawText(fill.fName,
+                                 strlen(fill.fName),
+                                 0, rect.height() + 24 * SK_Scalar1,
+                                 labelPaint);
+                canvas->drawText(cap.fName,
+                                 strlen(cap.fName),
+                                 0, rect.height() + 36 * SK_Scalar1,
+                                 labelPaint);
+                canvas->drawText(gSegmentNames[s1],
+                                 strlen(gSegmentNames[s1]),
+                                 0, rect.height() + 48 * SK_Scalar1,
+                                 labelPaint);
+                canvas->drawText(gSegmentNames[s2],
+                                 strlen(gSegmentNames[s2]),
+                                 0, rect.height() + 60 * SK_Scalar1,
+                                 labelPaint);
+                canvas->drawText(gSegmentNames[s3],
+                                 strlen(gSegmentNames[s3]),
+                                 0, rect.height() + 72 * SK_Scalar1,
+                                 labelPaint);
+                canvas->drawText(gSegmentNames[s4],
+                                 strlen(gSegmentNames[s4]),
+                                 0, rect.height() + 84 * SK_Scalar1,
+                                 labelPaint);
+                canvas->drawText(gSegmentNames[s5],
+                                 strlen(gSegmentNames[s5]),
+                                 0, rect.height() + 96 * SK_Scalar1,
+                                 labelPaint);
+            }
+            canvas->restore();
+        }
+        canvas->restore();
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new DegenerateSegmentsGM; }
+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
new file mode 100644
index 0000000..f9b348f
--- /dev/null
+++ b/gm/drawbitmaprect.cpp
@@ -0,0 +1,192 @@
+
+/*
+ * Copyright 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 "SkShader.h"
+#include "SkColorPriv.h"
+#include "SkBlurMaskFilter.h"
+
+// effects
+#include "SkGradientShader.h"
+
+
+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);
+
+    SkCanvas    canvas(*bm);
+
+    SkScalar wScalar = SkIntToScalar(w);
+    SkScalar hScalar = SkIntToScalar(h);
+
+    SkPoint     pt = { wScalar / 2, hScalar / 2 };
+
+    SkScalar    radius = 4 * SkMaxScalar(wScalar, hScalar);
+
+    SkColor     colors[] = { SK_ColorRED, SK_ColorYELLOW,
+                             SK_ColorGREEN, SK_ColorMAGENTA,
+                             SK_ColorBLUE, SK_ColorCYAN,
+                             SK_ColorRED};
+
+    SkScalar    pos[] = {0,
+                         SK_Scalar1 / 6,
+                         2 * SK_Scalar1 / 6,
+                         3 * SK_Scalar1 / 6,
+                         4 * SK_Scalar1 / 6,
+                         5 * SK_Scalar1 / 6,
+                         SK_Scalar1};
+
+    SkPaint     paint;
+    paint.setShader(SkGradientShader::CreateRadial(
+                    pt, radius,
+                    colors, pos,
+                    SK_ARRAY_COUNT(colors),
+                    SkShader::kRepeat_TileMode))->unref();
+    SkRect rect = SkRect::MakeWH(wScalar, hScalar);
+    SkMatrix mat = SkMatrix::I();
+    for (int i = 0; i < 4; ++i) {
+        paint.getShader()->setLocalMatrix(mat);
+        canvas.drawRect(rect, paint);
+        rect.inset(wScalar / 8, hScalar / 8);
+        mat.postScale(SK_Scalar1 / 4, SK_Scalar1 / 4);
+    }
+}
+
+static const int gSize = 1024;
+
+class DrawBitmapRectGM : public GM {
+public:
+    DrawBitmapRectGM() {
+    }
+
+    SkBitmap    fLargeBitmap;
+
+protected:
+    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()) {
+            makebm(&fLargeBitmap,
+                   SkBitmap::kARGB_8888_Config,
+                   kBmpSize, kBmpSize);
+        }
+        SkRect dstRect = { 0, 0, SkIntToScalar(64), SkIntToScalar(64)};
+        static const int kMaxSrcRectSize = 1 << (SkNextLog2(kBmpSize) + 2);
+
+        static const int kPadX = 30;
+        static const int kPadY = 40;
+        SkPaint paint;
+        paint.setAlpha(0x20);
+        canvas->drawBitmapRect(fLargeBitmap, NULL,
+                               SkRect::MakeWH(gSize * SK_Scalar1,
+                                              gSize * SK_Scalar1),
+                               &paint);
+        canvas->translate(SK_Scalar1 * kPadX / 2,
+                          SK_Scalar1 * kPadY / 2);
+        SkPaint blackPaint;
+        SkScalar titleHeight = SK_Scalar1 * 24;
+        blackPaint.setColor(SK_ColorBLACK);
+        blackPaint.setTextSize(titleHeight);
+        blackPaint.setAntiAlias(true);
+        SkString title;
+        title.printf("Bitmap size: %d x %d", kBmpSize, kBmpSize);
+        canvas->drawText(title.c_str(), title.size(), 0,
+                         titleHeight, blackPaint);
+
+        canvas->translate(0, SK_Scalar1 * kPadY / 2  + titleHeight);
+        int rowCount = 0;
+        canvas->save();
+        for (int w = 1; w <= kMaxSrcRectSize; w *= 4) {
+            for (int h = 1; h <= kMaxSrcRectSize; h *= 4) {
+
+                SkIRect srcRect = SkIRect::MakeXYWH((kBmpSize - w) / 2,
+                                                    (kBmpSize - h) / 2,
+                                                    w, h);
+                canvas->drawBitmapRect(fLargeBitmap, &srcRect, dstRect);
+
+                SkString label;
+                label.appendf("%d x %d", w, h);
+                blackPaint.setAntiAlias(true);
+                blackPaint.setStyle(SkPaint::kFill_Style);
+                blackPaint.setTextSize(SK_Scalar1 * 10);
+                SkScalar baseline = dstRect.height() +
+                                    blackPaint.getTextSize() + SK_Scalar1 * 3;
+                canvas->drawText(label.c_str(), label.size(),
+                                    0, baseline,
+                                    blackPaint);
+                blackPaint.setStyle(SkPaint::kStroke_Style);
+                blackPaint.setStrokeWidth(SK_Scalar1);
+                blackPaint.setAntiAlias(false);
+                canvas->drawRect(dstRect, blackPaint);
+
+                canvas->translate(dstRect.width() + SK_Scalar1 * kPadX, 0);
+                ++rowCount;
+                if ((dstRect.width() + kPadX) * rowCount > gSize) {
+                    canvas->restore();
+                    canvas->translate(0, dstRect.height() + SK_Scalar1 * kPadY);
+                    canvas->save();
+                    rowCount = 0;
+                }
+            }
+        }
+
+        {
+            // 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:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#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..fa92393
--- /dev/null
+++ b/gm/drawlooper.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 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
new file mode 100644
index 0000000..df314a3
--- /dev/null
+++ b/gm/emptypath.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 "gm.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+
+namespace skiagm {
+
+class EmptyPathGM : public GM {
+public:
+    EmptyPathGM() {}
+
+protected:
+    SkString onShortName() {
+        return SkString("emptypath");
+    }
+
+    SkISize onISize() { return make_isize(600, 280); }
+
+    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 onDraw(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(15 * SK_Scalar1);
+        const char title[] = "Empty Paths Drawn Into Rectangle Clips With "
+                             "Indicated Style and Fill";
+        canvas->drawText(title, strlen(title),
+                            20 * SK_Scalar1,
+                            20 * SK_Scalar1,
+                            titlePaint);
+
+        SkRandom rand;
+        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
+        int i = 0;
+        canvas->save();
+        canvas->translate(10 * 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() + 40 * SK_Scalar1);
+                    canvas->save();
+                } else {
+                    canvas->translate(rect.width() + 40 * 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);
+
+                SkPaint labelPaint;
+                labelPaint.setColor(color);
+                labelPaint.setAntiAlias(true);
+                labelPaint.setLCDRenderText(true);
+                labelPaint.setTextSize(12 * SK_Scalar1);
+                canvas->drawText(gStyles[style].fName,
+                                 strlen(gStyles[style].fName),
+                                 0, rect.height() + 15 * SK_Scalar1,
+                                 labelPaint);
+                canvas->drawText(gFills[fill].fName,
+                                 strlen(gFills[fill].fName),
+                                 0, rect.height() + 28 * SK_Scalar1,
+                                 labelPaint);
+            }
+        }
+        canvas->restore();
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new EmptyPathGM; }
+static GMRegistry reg(MyFactory);
+
+}
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/filltypes.cpp b/gm/filltypes.cpp
new file mode 100644
index 0000000..86f1fbc
--- /dev/null
+++ b/gm/filltypes.cpp
@@ -0,0 +1,96 @@
+
+/*
+ * Copyright 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"
+
+namespace skiagm {
+
+class FillTypeGM : public GM {
+    SkPath fPath;
+public:
+    FillTypeGM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+    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() {
+        return make_isize(835, 840);
+    }
+
+    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 onDraw(SkCanvas* canvas) {
+        this->makePath();
+
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+
+        SkPaint paint;
+        const SkScalar scale = SkIntToScalar(5)/4;
+
+        paint.setAntiAlias(false);
+
+        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 GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new FillTypeGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/filltypespersp.cpp b/gm/filltypespersp.cpp
new file mode 100644
index 0000000..bdd14a7
--- /dev/null
+++ b/gm/filltypespersp.cpp
@@ -0,0 +1,134 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "gm.h"
+#include "SkGradientShader.h"
+
+namespace skiagm {
+
+class FillTypePerspGM : public GM {
+    SkPath fPath;
+public:
+    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");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(835, 840);
+    }
+
+    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, bool aa) {
+
+        SkPaint paint;
+        SkPoint center = SkPoint::Make(SkIntToScalar(100), SkIntToScalar(100));
+        SkColor colors[] = {SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN};
+        SkScalar pos[] = {0, SK_ScalarHalf, SK_Scalar1};
+        SkShader* s = SkGradientShader::CreateRadial(center,
+                                                     SkIntToScalar(100),
+                                                     colors,
+                                                     pos,
+                                                     SK_ARRAY_COUNT(colors),
+                                                     SkShader::kClamp_TileMode);
+        paint.setShader(s)->unref();
+        paint.setAntiAlias(aa);
+
+        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 onDraw(SkCanvas* canvas) {
+        this->makePath();
+
+        // do perspective drawPaint as the background;
+        SkPaint bkgnrd;
+        SkPoint center = SkPoint::Make(SkIntToScalar(100),
+                                       SkIntToScalar(100));
+        SkColor colors[] = {SK_ColorBLACK, SK_ColorCYAN,
+                            SK_ColorYELLOW, SK_ColorWHITE};
+        SkScalar pos[] = {0, SK_ScalarHalf / 2,
+                          3 * SK_ScalarHalf / 2, SK_Scalar1};
+        SkShader* s = SkGradientShader::CreateRadial(center,
+                                                     SkIntToScalar(1000),
+                                                     colors,
+                                                     pos,
+                                                     SK_ARRAY_COUNT(colors),
+                                                     SkShader::kClamp_TileMode);
+        bkgnrd.setShader(s)->unref();
+        canvas->save();
+            canvas->translate(SkIntToScalar(100), SkIntToScalar(100));
+            SkMatrix mat;
+            mat.reset();
+            mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 1000));
+            canvas->concat(mat);
+            canvas->drawPaint(bkgnrd);
+        canvas->restore();
+
+        // draw the paths in perspective
+        SkMatrix persp;
+        persp.reset();
+        persp.setPerspX(SkScalarToPersp(-SK_Scalar1 / 1800));
+        persp.setPerspY(SkScalarToPersp(SK_Scalar1 / 500));
+        canvas->concat(persp);
+
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+        const SkScalar scale = SkIntToScalar(5)/4;
+
+        showFour(canvas, SK_Scalar1, false);
+        canvas->translate(SkIntToScalar(450), 0);
+        showFour(canvas, scale, false);
+
+        canvas->translate(SkIntToScalar(-450), SkIntToScalar(450));
+        showFour(canvas, SK_Scalar1, true);
+        canvas->translate(SkIntToScalar(450), 0);
+        showFour(canvas, scale, true);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new FillTypePerspGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/fontscaler.cpp b/gm/fontscaler.cpp
new file mode 100644
index 0000000..b331bde
--- /dev/null
+++ b/gm/fontscaler.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright 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 "SkTypeface.h"
+
+namespace skiagm {
+
+class FontScalerGM : public GM {
+public:
+    FontScalerGM() {
+        this->setBGColor(0xFFFFFFFF);
+    }
+
+    virtual ~FontScalerGM() {
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("fontscaler");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(1450, 750);
+    }
+
+    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 onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+
+        paint.setAntiAlias(true);
+        paint.setLCDRenderText(true);
+        //With freetype the default (normal hinting) can be really ugly.
+        //Most distros now set slight (vertical hinting only) in any event.
+        paint.setHinting(SkPaint::kSlight_Hinting);
+        SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromName("Times Roman", SkTypeface::kNormal)));
+
+        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, SkIntToScalar(i * 5), x, y * 10);
+
+                {
+                    SkPaint p;
+                    p.setAntiAlias(true);
+                    SkRect r;
+                    r.set(x - SkIntToScalar(3), SkIntToScalar(15),
+                          x - SkIntToScalar(1), SkIntToScalar(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, SkIntToScalar(360));
+            paint.setSubpixelText(true);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new FontScalerGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/gammatext.cpp b/gm/gammatext.cpp
new file mode 100644
index 0000000..99642f6
--- /dev/null
+++ b/gm/gammatext.cpp
@@ -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.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkGradientShader.h"
+#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,
+                                          SK_ARRAY_COUNT(bw),
+                                          SkShader::kClamp_TileMode);
+}
+
+static bool setFont(SkPaint* paint, const char name[]) {
+    SkTypeface* tf = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
+    if (tf) {
+        paint->setTypeface(tf)->unref();
+        return true;
+    }
+    return false;
+}
+
+#ifdef SK_BUILD_FOR_MAC
+#import <ApplicationServices/ApplicationServices.h>
+#define BITMAP_INFO_RGB     (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
+
+static CGContextRef makeCG(const SkBitmap& bm) {
+    if (SkBitmap::kARGB_8888_Config != bm.config() ||
+        NULL == bm.getPixels()) {
+        return NULL;
+    }
+    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
+    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;
+}
+
+extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
+
+static CGFontRef typefaceToCGFont(const SkTypeface* face) {
+    if (NULL == face) {
+        return 0;
+    }
+
+    CTFontRef ct = SkTypeface_GetCTFontRef(face);
+    return CTFontCopyGraphicsFont(ct, NULL);
+}
+
+static void cgSetPaintForText(CGContextRef cg, const SkPaint& paint) {
+    SkColor c = paint.getColor();
+    CGFloat rgba[] = {
+        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]);
+
+    CGContextSetTextDrawingMode(cg, kCGTextFill);
+    CGContextSetFont(cg, typefaceToCGFont(paint.getTypeface()));
+    CGContextSetFontSize(cg, SkScalarToFloat(paint.getTextSize()));
+
+    CGContextSetAllowsFontSubpixelPositioning(cg, paint.isSubpixelText());
+    CGContextSetShouldSubpixelPositionFonts(cg, paint.isSubpixelText());
+
+    CGContextSetShouldAntialias(cg, paint.isAntiAlias());
+    CGContextSetShouldSmoothFonts(cg, paint.isLCDRenderText());
+}
+
+static void cgDrawText(CGContextRef cg, const void* text, size_t len,
+                       float x, float y, const SkPaint& paint) {
+    if (cg) {
+        cgSetPaintForText(cg, paint);
+
+        uint16_t glyphs[200];
+        int count = paint.textToGlyphs(text, len, glyphs);
+
+        CGContextShowGlyphsAtPoint(cg, x, y, glyphs, count);
+    }
+}
+#endif
+
+namespace skiagm {
+
+/**
+   Test a set of clipping problems discovered while writing blitAntiRect,
+   and test all the code paths through the clipping blitters.
+   Each region should show as a blue center surrounded by a 2px green
+   border, with no red.
+*/
+
+#define HEIGHT 480
+
+class GammaTextGM : public GM {
+public:
+    GammaTextGM() {
+
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("gammatext");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(1024, HEIGHT);
+    }
+
+    static void drawGrad(SkCanvas* canvas) {
+        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, SkIntToScalar(1024), SkIntToScalar(HEIGHT) };
+        canvas->drawRect(r, paint);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+#ifdef SK_BUILD_FOR_MAC
+        CGContextRef cg = makeCG(canvas->getDevice()->accessBitmap(false));
+#endif
+
+        drawGrad(canvas);
+
+        const SkColor fg[] = {
+            0xFFFFFFFF,
+            0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF,
+            0xFFFF0000, 0xFF00FF00, 0xFF0000FF,
+            0xFF000000,
+        };
+
+        const char* text = "Hamburgefons";
+        size_t len = strlen(text);
+
+        SkPaint paint;
+        setFont(&paint, "Times");
+        paint.setTextSize(SkIntToScalar(16));
+        paint.setAntiAlias(true);
+        paint.setLCDRenderText(true);
+
+        SkScalar x = SkIntToScalar(10);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) {
+            paint.setColor(fg[i]);
+
+            SkScalar y = SkIntToScalar(40);
+            SkScalar stopy = SkIntToScalar(HEIGHT);
+            while (y < stopy) {
+                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:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new GammaTextGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/getpostextpath.cpp b/gm/getpostextpath.cpp
new file mode 100644
index 0000000..aa65173
--- /dev/null
+++ b/gm/getpostextpath.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 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..16c01d7
--- /dev/null
+++ b/gm/giantbitmap.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright 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
new file mode 100644
index 0000000..b6f21f8
--- /dev/null
+++ b/gm/gm.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 "gm.h"
+using namespace skiagm;
+
+SkString GM::gResourcePath;
+
+GM::GM() {
+    fBGColor = SK_ColorWHITE;
+}
+GM::~GM() {}
+
+void GM::draw(SkCanvas* canvas) {
+    this->drawBackground(canvas);
+    this->drawContent(canvas);
+}
+
+void GM::drawContent(SkCanvas* canvas) {
+    this->onDraw(canvas);
+}
+
+void GM::drawBackground(SkCanvas* canvas) {
+    this->onDrawBackground(canvas);
+}
+
+const char* GM::shortName() {
+    if (fShortName.size() == 0) {
+        fShortName = this->onShortName();
+    }
+    return fShortName.c_str();
+}
+
+void GM::setBGColor(SkColor color) {
+    fBGColor = color;
+}
+
+void GM::onDrawBackground(SkCanvas* canvas) {
+    canvas->drawColor(fBGColor);
+}
+
+void GM::drawSizeBounds(SkCanvas* canvas, SkColor color) {
+    SkISize size = this->getISize();
+    SkRect r = SkRect::MakeWH(SkIntToScalar(size.width()),
+                              SkIntToScalar(size.height()));
+    SkPaint paint;
+    paint.setColor(color);
+    canvas->drawRect(r, paint);
+}
+
+// need to explicitly declare this, or we get some weird infinite loop llist
+template GMRegistry* SkTRegistry<GM*, void*>::gHead;
diff --git a/gm/gm.h b/gm/gm.h
new file mode 100644
index 0000000..453f104
--- /dev/null
+++ b/gm/gm.h
@@ -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.
+ */
+#ifndef skiagm_DEFINED
+#define skiagm_DEFINED
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkSize.h"
+#include "SkString.h"
+#include "SkTRegistry.h"
+
+namespace skiagm {
+
+        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,
+            kSkipPipe_Flag      = 1 << 2,
+            kSkipTiled_Flag     = 1 << 3,
+            kSkip565_Flag       = 1 << 4,
+        };
+
+        void draw(SkCanvas*);
+        void drawBackground(SkCanvas*);
+        void drawContent(SkCanvas*);
+
+        SkISize getISize() { return this->onISize(); }
+        const char* shortName();
+
+        uint32_t getFlags() const {
+            return this->onGetFlags();
+        }
+
+        // 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);
+
+        // helper: fill a rect in the specified color based on the
+        // GM's getISize bounds.
+        void drawSizeBounds(SkCanvas*, SkColor);
+
+        static void SetResourcePath(const char* resourcePath) {
+            gResourcePath = resourcePath;
+        }
+
+    protected:
+        static SkString gResourcePath;
+
+        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;
+    };
+
+    typedef SkTRegistry<GM*, void*> GMRegistry;
+}
+
+#endif
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
new file mode 100644
index 0000000..3cac1e4
--- /dev/null
+++ b/gm/gmmain.cpp
@@ -0,0 +1,1187 @@
+/*
+ * Copyright 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 "system_preferences.h"
+#include "SkColorPriv.h"
+#include "SkData.h"
+#include "SkDeferredCanvas.h"
+#include "SkDevice.h"
+#include "SkGPipe.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkPicture.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTArray.h"
+#include "SamplePipeControllers.h"
+
+#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;
+
+extern bool gSkSuppressFontCachePurgeSpew;
+
+#ifdef SK_SUPPORT_PDF
+    #include "SkPDFDevice.h"
+    #include "SkPDFDocument.h"
+#endif
+
+// Until we resolve http://code.google.com/p/skia/issues/detail?id=455 ,
+// stop writing out XPS-format image baselines in gm.
+#undef SK_SUPPORT_XPS
+#ifdef SK_SUPPORT_XPS
+    #include "SkXPSDevice.h"
+#endif
+
+#ifdef SK_BUILD_FOR_MAC
+    #include "SkCGUtils.h"
+    #define CAN_IMAGE_PDF   1
+#else
+    #define CAN_IMAGE_PDF   0
+#endif
+
+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_READING_REFERENCE_IMAGE = 0x08;
+const static ErrorBitfield ERROR_WRITING_REFERENCE_IMAGE = 0x10;
+
+// If true, emit a messange when we can't find a reference image to compare
+static bool gNotifyMissingReadReference;
+
+using namespace skiagm;
+
+class Iter {
+public:
+    Iter() {
+        this->reset();
+    }
+
+    void reset() {
+        fReg = GMRegistry::Head();
+    }
+
+    GM* next() {
+        if (fReg) {
+            GMRegistry::Factory fact = fReg->factory();
+            fReg = fReg->next();
+            return fact(0);
+        }
+        return NULL;
+    }
+
+    static int Count() {
+        const GMRegistry* reg = GMRegistry::Head();
+        int count = 0;
+        while (reg) {
+            count += 1;
+            reg = reg->next();
+        }
+        return count;
+    }
+
+private:
+    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,
+};
+
+enum ConfigFlags {
+    kNone_ConfigFlag  = 0x0,
+    /* Write GM images if a write path is provided. */
+    kWrite_ConfigFlag = 0x1,
+    /* Read comparison GM images if a read path is provided. */
+    kRead_ConfigFlag  = 0x2,
+    kRW_ConfigFlag    = (kWrite_ConfigFlag | kRead_ConfigFlag),
+};
+
+struct ConfigData {
+    SkBitmap::Config                fConfig;
+    Backend                         fBackend;
+    GLContextType                   fGLContextType; // GPU backend only
+    int                             fSampleCnt;     // GPU backend only
+    ConfigFlags                     fFlags;
+    const char*                     fName;
+};
+
+/// Returns true if processing should continue, false to skip the
+/// remainder of this config for this GM.
+//@todo thudson 22 April 2011 - could refactor this to take in
+// a factory to generate the context, always call readPixels()
+// (logically a noop for rasters, if wasted time), and thus collapse the
+// GPU special case and also let this be used for SkPicture testing.
+static void setup_bitmap(const ConfigData& gRec, SkISize& size,
+                         SkBitmap* bitmap) {
+    bitmap->setConfig(gRec.fConfig, size.width(), size.height());
+    bitmap->allocPixels();
+    bitmap->eraseColor(0);
+}
+
+#include "SkDrawFilter.h"
+class BWTextDrawFilter : public SkDrawFilter {
+public:
+    virtual void filter(SkPaint*, Type) SK_OVERRIDE;
+};
+void BWTextDrawFilter::filter(SkPaint* p, Type t) {
+    if (kText_Type == t) {
+        p->setAntiAlias(false);
+    }
+}
+
+static void installFilter(SkCanvas* canvas) {
+    if (gForceBWtext) {
+        canvas->setDrawFilter(new BWTextDrawFilter)->unref();
+    }
+}
+
+static void invokeGM(GM* gm, SkCanvas* canvas, bool isPDF = false) {
+    SkAutoCanvasRestore acr(canvas, true);
+
+    if (!isPDF) {
+        canvas->concat(gm->getInitialTransform());
+    }
+    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);
+
+    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);
+        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);
+        // 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
+    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
+    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);
+
+    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) {
+        // write out the generated image
+        SkString genName = make_filename(diffPath, "", name, "png");
+        if (!write_bitmap(genName, bitmap)) {
+            errors |= ERROR_WRITING_REFERENCE_IMAGE;
+        }
+    }
+    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 {
+        if (gNotifyMissingReadReference) {
+            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);
+    ErrorBitfield retval = ERROR_NONE;
+
+    if (readPath && (gRec.fFlags & kRead_ConfigFlag)) {
+        retval |= compare_to_reference_image(readPath, name, bitmap,
+                                             diffPath, renderModeDescriptor);
+    }
+    if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
+        retval |= write_reference_image(gRec, writePath, renderModeDescriptor,
+                                        name, bitmap, pdf);
+    }
+    if (comparisonBitmap) {
+        retval |= compare_to_reference_image(name, bitmap,
+                                             *comparisonBitmap, diffPath,
+                                             renderModeDescriptor);
+    }
+    return retval;
+}
+
+static SkPicture* generate_new_picture(GM* gm) {
+    // Pictures are refcounted so must be on heap
+    SkPicture* pict = new SkPicture;
+    SkISize size = gm->getISize();
+    SkCanvas* cv = pict->beginRecording(size.width(), size.height());
+    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;
+        }
+    } 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 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;
+
+    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 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);
+
+    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 {
+        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);
+
+    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 {
+        return ERROR_NONE;
+    }
+}
+
+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 }
+};
+
+static ErrorBitfield test_pipe_playback(GM* gm,
+                                        const ConfigData& gRec,
+                                        const SkBitmap& comparisonBitmap,
+                                        const char readPath [],
+                                        const char diffPath []) {
+    if (kRaster_Backend != gRec.fBackend) {
+        return ERROR_NONE;
+    }
+    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);
+        writer.endRecording();
+        SkString string("-pipe");
+        string.append(gPipeWritingFlagCombos[i].name);
+        errors |= handle_test_results(gm, gRec, NULL, NULL, diffPath,
+                                   string.c_str(), bitmap, NULL, &comparisonBitmap);
+        if (errors != ERROR_NONE) {
+            break;
+        }
+    }
+    return errors;
+}
+
+static ErrorBitfield test_tiled_pipe_playback(GM* gm,
+                                        const ConfigData& gRec,
+                                        const SkBitmap& comparisonBitmap,
+                                        const char readPath [],
+                                        const char diffPath []) {
+    if (kRaster_Backend != gRec.fBackend) {
+        return ERROR_NONE;
+    }
+    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);
+        writer.endRecording();
+        SkString string("-tiled pipe");
+        string.append(gPipeWritingFlagCombos[i].name);
+        errors |= handle_test_results(gm, gRec, NULL, NULL, diffPath,
+                                      string.c_str(), bitmap, NULL, &comparisonBitmap);
+        if (errors != ERROR_NONE) {
+            break;
+        }
+    }
+    return errors;
+}
+
+static void write_picture_serialization(GM* gm, const ConfigData& rec,
+                                        const char writePicturePath[]) {
+    // only do this once, so we pick raster
+    if (kRaster_Backend == rec.fBackend &&
+        SkBitmap::kARGB_8888_Config == rec.fConfig) {
+
+        SkAutoTUnref<SkPicture> pict(generate_new_picture(gm));
+
+        const char* pictureSuffix = "skp";
+        SkString path = make_filename(writePicturePath, "",
+                                      SkString(gm->shortName()), pictureSuffix);
+
+        SkFILEWStream stream(path.c_str());
+        pict->serialize(&stream);
+    }
+}
+
+#if SK_SUPPORT_GPU
+static const GLContextType kDontCare_GLContextType = GrContextFactory::kNative_GLContextType;
+#else
+static const GLContextType kDontCare_GLContextType = 0;
+#endif
+
+// If the platform does not support writing PNGs of PDFs then there will be no
+// comparison 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, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "8888" },
+    { SkBitmap::kARGB_4444_Config, kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "4444" },
+    { 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
+    /* 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("    [-w writePath] [-r readPath] [-d diffPath] [-i resourcePath]\n");
+    SkDebugf("    [-wp writePicturePath]\n");
+    SkDebugf("    [--config ");
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
+        if (i > 0) {
+            SkDebugf("|");
+        }
+        SkDebugf(gRec[i].fName);
+    }
+    SkDebugf(" ]\n");
+    SkDebugf("    [--noreplay] [--nopipe] [--noserialize] [--forceBWtext] [--nopdf] \n"
+             "    [--tiledPipe] \n"
+             "    [--nodeferred] [--match substring] [--notexturecache]\n"
+             "    [-h|--help]\n"
+             );
+    SkDebugf("    writePath: directory to write rendered images in.\n");
+    SkDebugf("    writePicturePath: directory to write images to in .skp format.\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("    resourcePath: directory that stores image resources.\n");
+    SkDebugf("    --noreplay: do not exercise SkPicture replay.\n");
+    SkDebugf("    --nopipe: Skip SkGPipe replay.\n");
+    SkDebugf("    --tiledPipe: Exercise tiled SkGPipe replay.\n");
+    SkDebugf(
+             "    --noserialize: do not 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");
+    SkDebugf("    --notexturecache: disable the gpu texture cache.\n");
+    SkDebugf("    -h|--help : Show this help message. \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
+        return false;
+    }
+    for (int i = 0; i < array.count(); ++i) {
+        if (strstr(name, array[i])) {
+            // found the name, so don't skip
+            return false;
+        }
+    }
+    return true;
+}
+
+namespace skiagm {
+#if SK_SUPPORT_GPU
+SkAutoTUnref<GrContext> gGrContext;
+/**
+ * Sets the global GrContext, accessible by indivual GMs
+ */
+static void SetGr(GrContext* grContext) {
+    SkSafeRef(grContext);
+    gGrContext.reset(grContext);
+}
+
+/**
+ * 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
+}
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+
+#ifdef SK_ENABLE_INST_COUNT
+    gPrintInstCount = true;
+#endif
+
+    SkGraphics::Init();
+    // we don't need to see this during a run
+    gSkSuppressFontCachePurgeSpew = true;
+
+    setSystemPreferences();
+
+    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
+
+    SkTDArray<const char*> fMatches;
+
+    bool doPDF = true;
+    bool doReplay = true;
+    bool doPipe = true;
+    bool doTiledPipe = false;
+    bool doSerialize = true;
+    bool doDeferred = true;
+    bool disableTextureCache = false;
+    SkTDArray<size_t> configs;
+    bool userConfig = false;
+
+    gNotifyMissingReadReference = true;
+
+    const char* const commandName = argv[0];
+    char* const* stop = argv + argc;
+    for (++argv; argv < stop; ++argv) {
+        if (strcmp(*argv, "-w") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                writePath = *argv;
+            }
+        } else if (strcmp(*argv, "-wp") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                writePicturePath = *argv;
+            }
+        } else if (strcmp(*argv, "-r") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                readPath = *argv;
+            }
+        } else if (strcmp(*argv, "-d") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                diffPath = *argv;
+            }
+        } else if (strcmp(*argv, "-i") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                resourcePath = *argv;
+            }
+        } else if (strcmp(*argv, "--forceBWtext") == 0) {
+            gForceBWtext = true;
+        } else if (strcmp(*argv, "--nopipe") == 0) {
+            doPipe = false;
+        } else if (strcmp(*argv, "--tiledPipe") == 0) {
+            doTiledPipe = 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, "--disable-missing-warning") == 0) {
+            gNotifyMissingReadReference = false;
+        } else if (strcmp(*argv, "--enable-missing-warning") == 0) {
+            gNotifyMissingReadReference = true;
+        } else if (strcmp(*argv, "--serialize") == 0) {
+            // Leaving in this option so that a user need not modify their command line arguments
+            // to still run.
+            doSerialize = true;
+        } else if (strcmp(*argv, "--noserialize") == 0) {
+            doSerialize = 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;
+            }
+        } else if (strcmp(*argv, "--notexturecache") == 0) {
+            disableTextureCache = true;
+        } else if (strcmp(*argv, "--config") == 0) {
+            argv++;
+            if (argv < stop) {
+                int index = findConfig(*argv);
+                if (index >= 0) {
+                    *configs.append() = 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, "--help") == 0 || strcmp(*argv, "-h") == 0) {
+            usage(commandName);
+            return -1;
+        } else {
+            usage(commandName);
+            return -1;
+        }
+    }
+    if (argv != stop) {
+        usage(commandName);
+        return -1;
+    }
+
+    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;
+        }
+    }
+
+    GM::SetResourcePath(resourcePath);
+
+    if (readPath) {
+        fprintf(stderr, "reading from %s\n", readPath);
+    }
+    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);
+    }
+
+    // Accumulate success of all tests.
+    int testsRun = 0;
+    int testsPassed = 0;
+    int testsFailed = 0;
+    int testsMissingReferenceImages = 0;
+
+#if SK_SUPPORT_GPU
+    GrContextFactory* grFactory = new GrContextFactory;
+    if (disableTextureCache) {
+        skiagm::GetGr()->setTextureCacheLimits(0, 0);
+    }
+#endif
+
+    SkTArray<SkString> failedTests;
+
+    Iter iter;
+    GM* gm;
+    while ((gm = iter.next()) != NULL) {
+        const char* shortName = gm->shortName();
+        if (skip_name(fMatches, shortName)) {
+            SkDELETE(gm);
+            continue;
+        }
+
+        SkISize size = gm->getISize();
+        SkDebugf("drawing... %s [%d %d]\n", shortName,
+                 size.width(), size.height());
+        SkBitmap forwardRenderedBitmap;
+
+        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 == 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;
+            GrRenderTarget* renderTarget = NULL;
+#if SK_SUPPORT_GPU
+            SkAutoTUnref<GrRenderTarget> rt;
+            AutoResetGr autogr;
+            if ((ERROR_NONE == testErrors) &&
+                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) {
+                    testErrors |= ERROR_NO_GPU_CONTEXT;
+                }
+            }
+#endif
+
+            if (ERROR_NONE == testErrors) {
+                testErrors |= test_drawing(gm, config,
+                                           writePath, readPath, diffPath,
+                                           GetGr(),
+                                           renderTarget, &forwardRenderedBitmap);
+            }
+
+            if (doDeferred && !testErrors &&
+                (kGPU_Backend == config.fBackend ||
+                 kRaster_Backend == config.fBackend)) {
+                testErrors |= test_deferred_drawing(gm, config,
+                                                    forwardRenderedBitmap,
+                                                    diffPath, GetGr(), renderTarget);
+            }
+
+            if ((ERROR_NONE == testErrors) && doReplay &&
+                !(gmFlags & GM::kSkipPicture_Flag)) {
+                testErrors |= test_picture_playback(gm, config,
+                                                    forwardRenderedBitmap,
+                                                    readPath, diffPath);
+            }
+
+            if ((ERROR_NONE == testErrors) && doPipe &&
+                !(gmFlags & GM::kSkipPipe_Flag)) {
+                testErrors |= test_pipe_playback(gm, config,
+                                                 forwardRenderedBitmap,
+                                                 readPath, diffPath);
+            }
+
+            if ((ERROR_NONE == testErrors) && doTiledPipe &&
+                !SkToBool(gmFlags & (GM::kSkipPipe_Flag | GM::kSkipTiled_Flag))) {
+                testErrors |= test_tiled_pipe_playback(gm, config,
+                                                 forwardRenderedBitmap,
+                                                 readPath, diffPath);
+            }
+
+            if ((ERROR_NONE == testErrors) && doSerialize  &&
+                !(gmFlags & GM::kSkipPicture_Flag)) {
+                testErrors |= test_picture_serialization(gm, config,
+                                                         forwardRenderedBitmap,
+                                                         readPath, diffPath);
+            }
+
+            if (!(gmFlags & GM::kSkipPicture_Flag) && writePicturePath) {
+                write_picture_serialization(gm, config, writePicturePath);
+            }
+
+            // 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++;
+
+                failedTests.push_back(make_name(shortName, config.fName));
+            }
+        }
+        SkDELETE(gm);
+    }
+    SkDebugf("Ran %d tests: %d passed, %d failed, %d missing reference images\n",
+             testsRun, testsPassed, testsFailed, testsMissingReferenceImages);
+    for (int i = 0; i < failedTests.count(); ++i) {
+        SkDebugf("\t\t%s\n", failedTests[i].c_str());
+    }
+
+#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
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
+
diff --git a/gm/gradients.cpp b/gm/gradients.cpp
new file mode 100644
index 0000000..a2d1de2
--- /dev/null
+++ b/gm/gradients.cpp
@@ -0,0 +1,371 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "gm.h"
+#include "SkGradientShader.h"
+
+namespace skiagm {
+
+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* 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, Make2Conical
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GradientsGM : public GM {
+public:
+    GradientsGM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    SkString onShortName() {
+        return SkString("gradients");
+    }
+
+    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);
+                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
+// 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
+ coefficient is 0)
+
+ 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');
+ g.addColorStop(0.99, '#0f0');
+ g.addColorStop(1, '#f00');
+ ctx.fillStyle = g;
+ ctx.fillRect(0, 0, 100, 50);
+ */
+class GradientsDegenrate2PointGM : public GM {
+public:
+    GradientsDegenrate2PointGM() {}
+
+protected:
+    SkString onShortName() {
+        return SkString("gradients_degenerate_2pt");
+    }
+
+    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;
+        c0.iset(-80, 25);
+        SkScalar r0 = SkIntToScalar(70);
+        SkPoint c1;
+        c1.iset(0, 25);
+        SkScalar r1 = SkIntToScalar(150);
+        SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
+                                                             pos, SK_ARRAY_COUNT(pos),
+                                                             SkShader::kClamp_TileMode);
+        SkPaint paint;
+        paint.setShader(s)->unref();
+        canvas->drawPaint(paint);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+/// Tests correctness of *optimized* codepaths in gradients.
+
+class ClampedGradientsGM : public GM {
+public:
+    ClampedGradientsGM() {}
+
+protected:
+    SkString onShortName() { return SkString("clamped_gradients"); }
+
+    virtual SkISize onISize() { return make_isize(640, 510); }
+
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFFDDDDDD);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        this->drawBG(canvas);
+
+        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(300) };
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        SkPoint center;
+        center.iset(0, 300);
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+        SkShader* shader = SkGradientShader::CreateRadial(
+            SkPoint(center),
+            SkIntToScalar(200), gColors, NULL, 5,
+            SkShader::kClamp_TileMode, NULL);
+        paint.setShader(shader);
+        canvas->drawRect(r, paint);
+        shader->unref();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+/// Checks quality of large radial gradients, which may display
+/// some banding.
+
+class RadialGradientGM : public GM {
+public:
+    RadialGradientGM() {}
+
+protected:
+    SkString onShortName() { return SkString("radial_gradient"); }
+    virtual SkISize onISize() { return make_isize(1280, 1280); }
+    void drawBG(SkCanvas* canvas) {
+        canvas->drawColor(0xFF000000);
+    }
+    virtual void onDraw(SkCanvas* canvas) {
+        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.0f),
+                             SkFloatToScalar(0.35f),
+                             SkFloatToScalar(1.0f) };
+        SkShader* shader =
+            SkGradientShader::CreateRadial(center, radius, colors,
+                                           pos, SK_ARRAY_COUNT(pos),
+                                           SkShader::kClamp_TileMode);
+        paint.setShader(shader)->unref();
+        SkRect r = {
+            0, 0, SkIntToScalar(dim.width()), SkIntToScalar(dim.height())
+        };
+        canvas->drawRect(r, paint);
+    }
+private:
+    typedef GM INHERITED;
+};
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new GradientsGM; }
+static GMRegistry reg(MyFactory);
+
+static GM* MyFactory2(void*) { return new GradientsDegenrate2PointGM; }
+static GMRegistry reg2(MyFactory2);
+
+static GM* MyFactory3(void*) { return new ClampedGradientsGM; }
+static GMRegistry reg3(MyFactory3);
+
+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
new file mode 100644
index 0000000..67dd11e
--- /dev/null
+++ b/gm/gradtext.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright 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 "SkTypeface.h"
+
+// test shader w/ transparency
+static SkShader* make_grad(SkScalar width) {
+    SkColor colors[] = { SK_ColorRED, 0x0000FF00, SK_ColorBLUE };
+    SkPoint pts[] = { { 0, 0 }, { width, 0 } };
+    return SkGradientShader::CreateLinear(pts, colors, NULL,
+                                          SK_ARRAY_COUNT(colors),
+                                          SkShader::kMirror_TileMode);
+}
+
+// test opaque shader
+static SkShader* make_grad2(SkScalar width) {
+    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
+    SkPoint pts[] = { { 0, 0 }, { width, 0 } };
+    return SkGradientShader::CreateLinear(pts, colors, NULL,
+                                          SK_ARRAY_COUNT(colors),
+                                          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 () {}
+
+protected:
+
+    virtual SkString onShortName() {
+        return SkString("gradtext");
+    }
+
+    virtual SkISize onISize() { return make_isize(500, 480); }
+
+    static void draw_text(SkCanvas* canvas, const SkPaint& paint) {
+        const char* text = "When in the course of human events";
+        size_t len = strlen(text);
+        canvas->drawText(text, len, 0, 0, paint);
+    }
+
+    static void draw_text3(SkCanvas* canvas, const SkPaint& paint) {
+        SkPaint p(paint);
+
+        p.setAntiAlias(false);
+        draw_text(canvas, p);
+        p.setAntiAlias(true);
+        canvas->translate(0, paint.getTextSize() * 4/3);
+        draw_text(canvas, p);
+        p.setLCDRenderText(true);
+        canvas->translate(0, paint.getTextSize() * 4/3);
+        draw_text(canvas, p);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setTextSize(SkIntToScalar(26));
+
+        const SkISize& size = this->getISize();
+        SkRect r = SkRect::MakeWH(SkIntToScalar(size.width()),
+                                  SkIntToScalar(size.height()) / 2);
+        canvas->drawRect(r, paint);
+
+        canvas->translate(SkIntToScalar(20), paint.getTextSize());
+
+        for (int i = 0; i < 2; ++i) {
+            paint.setShader(make_grad(SkIntToScalar(80)))->unref();
+            draw_text3(canvas, paint);
+
+            canvas->translate(0, paint.getTextSize() * 2);
+
+            paint.setShader(make_grad2(SkIntToScalar(80)))->unref();
+            draw_text3(canvas, paint);
+
+            canvas->translate(0, paint.getTextSize() * 2);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+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
new file mode 100644
index 0000000..67f1fa3
--- /dev/null
+++ b/gm/hairmodes.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright 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"
+
+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;
+}
+
+namespace skiagm {
+
+    class HairModesGM : public GM {
+        SkPaint fBGPaint;
+    public:
+        HairModesGM() {
+            fBGPaint.setShader(make_bg_shader())->unref();
+        }
+
+    protected:
+
+        virtual SkString onShortName() {
+            return SkString("hairmodes");
+        }
+
+        virtual SkISize onISize() { return make_isize(640, 480); }
+
+        virtual void onDraw(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);
+            }
+        }
+
+        // disable pdf for now, since it crashes on mac
+        virtual uint32_t onGetFlags() const { return kSkipPDF_Flag; }
+
+    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..4347c9b
--- /dev/null
+++ b/gm/hittestpath.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 "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..a98fa3f
--- /dev/null
+++ b/gm/image.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 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"
+
+extern GrContext* GetGr();
+
+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");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        drawJpeg(canvas, this->getISize());
+
+        canvas->translate(10, 10);
+        canvas->scale(2, 2);
+
+        // 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, NULL, fBuffer, RB));
+        SkAutoTUnref<SkSurface> surf1(SkSurface::NewRaster(info, NULL));
+        SkAutoTUnref<SkSurface> surf2(SkSurface::NewPicture(info.fWidth, info.fHeight));
+
+        test_surface(canvas, surf0);
+        canvas->translate(80, 0);
+        test_surface(canvas, surf1);
+        canvas->translate(80, 0);
+        test_surface(canvas, surf2);
+    }
+
+    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
new file mode 100644
index 0000000..406f9ef
--- /dev/null
+++ b/gm/imageblur.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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 "SkBlurImageFilter.h"
+
+#define WIDTH 500
+#define HEIGHT 500
+
+namespace skiagm {
+
+class ImageBlurGM : public GM {
+public:
+    ImageBlurGM() {
+        this->setBGColor(0xFF000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("imageblur");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(WIDTH, HEIGHT);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setImageFilter(new SkBlurImageFilter(24.0f, 0.0f))->unref();
+        canvas->saveLayer(NULL, &paint);
+        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;
+            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;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ImageBlurGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/imagefiltersbase.cpp b/gm/imagefiltersbase.cpp
new file mode 100644
index 0000000..bcceb58
--- /dev/null
+++ b/gm/imagefiltersbase.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 "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(0);
+    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(0);
+    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 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[] = {
+#if 1
+            NULL,
+            new IdentityImageFilter,
+            new FailImageFilter,
+            new SkColorFilterImageFilter(cf),
+#endif
+            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..816c88e
--- /dev/null
+++ b/gm/imagefiltersgraph.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 "SkBitmapSource.h"
+#include "SkBlurImageFilter.h"
+#include "SkColorFilter.h"
+#include "SkColorFilterImageFilter.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(500, 500); }
+
+    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(new SkColorFilterImageFilter(cf, erode));
+        SkAutoTUnref<SkImageFilter> merge(new SkMergeImageFilter(blur, color));
+
+        SkPaint paint;
+        paint.setImageFilter(merge);
+        canvas->drawPaint(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
new file mode 100644
index 0000000..706d4fd
--- /dev/null
+++ b/gm/lcdtext.cpp
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+/* Tests text rendering with LCD and subpixel rendering turned on and off.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+
+namespace skiagm {
+
+class LcdTextGM : public GM {
+public:
+    LcdTextGM() {
+        const int pointSize = 36;
+        textHeight = SkIntToScalar(pointSize);
+    }
+
+protected:
+
+    SkString onShortName() {
+        return SkString("lcdtext");
+    }
+
+    SkISize onISize() { return make_isize(640, 480); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        y = textHeight;
+        drawText(canvas, SkString("TEXT: SubpixelTrue LCDRenderTrue"),
+                 true,  true);
+        drawText(canvas, SkString("TEXT: SubpixelTrue LCDRenderFalse"),
+                 true,  false);
+        drawText(canvas, SkString("TEXT: SubpixelFalse LCDRenderTrue"),
+                 false, true);
+        drawText(canvas, SkString("TEXT: SubpixelFalse LCDRenderFalse"),
+                 false, false);
+    }
+
+    void drawText(SkCanvas* canvas, const SkString& string,
+                  bool subpixelTextEnabled, bool lcdRenderTextEnabled) {
+        SkPaint paint;
+        paint.setColor(SK_ColorBLACK);
+        paint.setDither(true);
+        paint.setAntiAlias(true);
+        paint.setSubpixelText(subpixelTextEnabled);
+        paint.setLCDRenderText(lcdRenderTextEnabled);
+        paint.setTextSize(textHeight);
+
+        canvas->drawText(string.c_str(), string.size(), 0, y, paint);
+        y += textHeight;
+    }
+
+private:
+    typedef GM INHERITED;
+    SkScalar y, textHeight;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new LcdTextGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/lighting.cpp b/gm/lighting.cpp
new file mode 100644
index 0000000..46474ab
--- /dev/null
+++ b/gm/lighting.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 "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);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_bitmap();
+            fInitialized = true;
+        }
+        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();
+        canvas->drawSprite(fBitmap, 0, 0, &paint);
+        paint.setImageFilter(SkLightingImageFilter::CreateDistantLitDiffuse(distantDirection, white, surfaceScale, kd))->unref();
+        canvas->drawSprite(fBitmap, 110, 0, &paint);
+        paint.setImageFilter(SkLightingImageFilter::CreateSpotLitDiffuse(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, kd))->unref();
+        canvas->drawSprite(fBitmap, 220, 0, &paint);
+        paint.setImageFilter(SkLightingImageFilter::CreatePointLitSpecular(pointLocation, white, surfaceScale, ks, shininess))->unref();
+        canvas->drawSprite(fBitmap, 0, 110, &paint);
+        paint.setImageFilter(SkLightingImageFilter::CreateDistantLitSpecular(distantDirection, white, surfaceScale, ks, shininess))->unref();
+        canvas->drawSprite(fBitmap, 110, 110, &paint);
+        paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, ks, shininess))->unref();
+        canvas->drawSprite(fBitmap, 220, 110, &paint);
+    }
+
+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
new file mode 100644
index 0000000..fbf3faf
--- /dev/null
+++ b/gm/linepaths.cpp
@@ -0,0 +1,305 @@
+/*
+ * Copyright 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 "SkPaint.h"
+#include "SkRandom.h"
+
+namespace skiagm {
+
+class LinePathGM : public GM {
+public:
+    LinePathGM() {}
+
+protected:
+    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,
+                  SkScalar strokeWidth) {
+        path.setFillType(fill);
+        SkPaint paint;
+        paint.setStrokeCap(cap);
+        paint.setStrokeWidth(strokeWidth);
+        paint.setStrokeJoin(join);
+        paint.setColor(color);
+        paint.setStyle(style);
+        canvas->save();
+        canvas->clipRect(clip);
+        canvas->drawPath(path, paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(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"},
+        };
+        struct CapAndName {
+            SkPaint::Cap  fCap;
+            SkPaint::Join fJoin;
+            const char*   fName;
+        };
+        static const CapAndName gCaps[] = {
+            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
+            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
+            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
+        };
+        struct PathAndName {
+            SkPath      fPath;
+            const char* fName;
+        };
+        PathAndName path;
+        path.fPath.moveTo(25*SK_Scalar1, 15*SK_Scalar1);
+        path.fPath.lineTo(75*SK_Scalar1, 15*SK_Scalar1);
+        path.fName = "moveTo-line";
+
+        SkPaint titlePaint;
+        titlePaint.setColor(SK_ColorBLACK);
+        titlePaint.setAntiAlias(true);
+        titlePaint.setLCDRenderText(true);
+        titlePaint.setTextSize(15 * SK_Scalar1);
+        const char title[] = "Line Drawn Into Rectangle Clips With "
+                             "Indicated Style, Fill and Linecaps, with stroke width 10";
+        canvas->drawText(title, strlen(title),
+                            20 * SK_Scalar1,
+                            20 * SK_Scalar1,
+                            titlePaint);
+
+        SkRandom rand;
+        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
+        canvas->save();
+        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
+        canvas->save();
+        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
+            if (0 < cap) {
+                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
+            }
+            canvas->save();
+            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
+                if (0 < fill) {
+                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
+                }
+                canvas->save();
+                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
+                    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);
+                    labelPaint.setLCDRenderText(true);
+                    labelPaint.setTextSize(10 * SK_Scalar1);
+                    canvas->drawText(gStyles[style].fName,
+                                        strlen(gStyles[style].fName),
+                                        0, rect.height() + 12 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gFills[fill].fName,
+                                        strlen(gFills[fill].fName),
+                                        0, rect.height() + 24 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gCaps[cap].fName,
+                                        strlen(gCaps[cap].fName),
+                                        0, rect.height() + 36 * SK_Scalar1,
+                                        labelPaint);
+                }
+                canvas->restore();
+            }
+            canvas->restore();
+        }
+        canvas->restore();
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+class LineClosePathGM : public GM {
+public:
+    LineClosePathGM() {}
+
+protected:
+    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,
+                  SkScalar strokeWidth) {
+        path.setFillType(fill);
+        SkPaint paint;
+        paint.setStrokeCap(cap);
+        paint.setStrokeWidth(strokeWidth);
+        paint.setStrokeJoin(join);
+        paint.setColor(color);
+        paint.setStyle(style);
+        canvas->save();
+        canvas->clipRect(clip);
+        canvas->drawPath(path, paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(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"},
+        };
+        struct CapAndName {
+            SkPaint::Cap  fCap;
+            SkPaint::Join fJoin;
+            const char*   fName;
+        };
+        static const CapAndName gCaps[] = {
+            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
+            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
+            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
+        };
+        struct PathAndName {
+            SkPath      fPath;
+            const char* fName;
+        };
+        PathAndName path;
+        path.fPath.moveTo(25*SK_Scalar1, 15*SK_Scalar1);
+        path.fPath.lineTo(75*SK_Scalar1, 15*SK_Scalar1);
+        path.fPath.close();
+        path.fName = "moveTo-line-close";
+
+        SkPaint titlePaint;
+        titlePaint.setColor(SK_ColorBLACK);
+        titlePaint.setAntiAlias(true);
+        titlePaint.setLCDRenderText(true);
+        titlePaint.setTextSize(15 * SK_Scalar1);
+        const char title[] = "Line Closed Drawn Into Rectangle Clips With "
+                             "Indicated Style, Fill and Linecaps, with stroke width 10";
+        canvas->drawText(title, strlen(title),
+                            20 * SK_Scalar1,
+                            20 * SK_Scalar1,
+                            titlePaint);
+
+        SkRandom rand;
+        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
+        canvas->save();
+        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
+        canvas->save();
+        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
+            if (0 < cap) {
+                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
+            }
+            canvas->save();
+            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
+                if (0 < fill) {
+                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
+                }
+                canvas->save();
+                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
+                    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);
+                    labelPaint.setLCDRenderText(true);
+                    labelPaint.setTextSize(10 * SK_Scalar1);
+                    canvas->drawText(gStyles[style].fName,
+                                        strlen(gStyles[style].fName),
+                                        0, rect.height() + 12 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gFills[fill].fName,
+                                        strlen(gFills[fill].fName),
+                                        0, rect.height() + 24 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gCaps[cap].fName,
+                                        strlen(gCaps[cap].fName),
+                                        0, rect.height() + 36 * SK_Scalar1,
+                                        labelPaint);
+                }
+                canvas->restore();
+            }
+            canvas->restore();
+        }
+        canvas->restore();
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* LinePathFactory(void*) { return new LinePathGM; }
+static GMRegistry regLinePath(LinePathFactory);
+
+static GM* LineClosePathFactory(void*) { return new LineClosePathGM; }
+static GMRegistry regLineClosePath(LineClosePathFactory);
+
+}
diff --git a/gm/matrixconvolution.cpp b/gm/matrixconvolution.cpp
new file mode 100644
index 0000000..6bb7d99
--- /dev/null
+++ b/gm/matrixconvolution.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 "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->drawSprite(fBitmap, x, y, &paint);
+    }
+
+    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/morphology.cpp b/gm/morphology.cpp
new file mode 100644
index 0000000..83de758
--- /dev/null
+++ b/gm/morphology.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 "SkMorphologyImageFilter.h"
+
+#define WIDTH 640
+#define HEIGHT 480
+
+namespace skiagm {
+
+class MorphologyGM : public GM {
+public:
+    MorphologyGM() {
+        this->setBGColor(0xFF000000);
+        fOnce = false;
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("morphology");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 135, 135);
+        fBitmap.allocPixels();
+        SkDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x0);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        const char* str1 = "ABC";
+        const char* str2 = "XYZ";
+        paint.setColor(0xFFFFFFFF);
+        paint.setTextSize(64);
+        canvas.drawText(str1, strlen(str1), 10, 55, paint);
+        canvas.drawText(str2, strlen(str2), 10, 110, paint);
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(WIDTH, HEIGHT);
+    }
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fOnce) {
+            make_bitmap();
+            fOnce = true;
+        }
+        struct {
+            int fWidth, fHeight;
+            int fRadiusX, fRadiusY;
+        } samples[] = {
+            { 140, 140,   0,   0 },
+            { 140, 140,   0,   2 },
+            { 140, 140,   2,   0 },
+            { 140, 140,   2,   2 },
+            {  24,  24,  25,  25 },
+        };
+        SkPaint paint;
+        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();
+            }
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap;
+    bool fOnce;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new MorphologyGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/ninepatchstretch.cpp b/gm/ninepatchstretch.cpp
new file mode 100644
index 0000000..38b6f7d
--- /dev/null
+++ b/gm/ninepatchstretch.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 "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;
+
+    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
+#endif
+    {
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kSize, kSize);
+        bitmap->allocPixels();
+        dev = new SkDevice(*bitmap);
+    }
+
+    SkCanvas canvas(dev);
+    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);
+}
+
+namespace skiagm {
+
+class NinePatchStretchGM : public GM {
+public:
+    SkBitmap fBM;
+
+    NinePatchStretchGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("ninepatch-stretch");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(400, 400);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        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;
+                SkRect r = SkRect::MakeXYWH(x + ix * fixed, y + iy * fixed,
+                                            size[i].width(), size[i].height());
+                canvas->drawBitmapNine(bm, center, r, &paint);
+            }
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new NinePatchStretchGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
+
+
diff --git a/gm/nocolorbleed.cpp b/gm/nocolorbleed.cpp
new file mode 100755
index 0000000..26b8184
--- /dev/null
+++ b/gm/nocolorbleed.cpp
@@ -0,0 +1,78 @@
+
+/*
+ * Copyright 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"
+
+namespace skiagm {
+
+class NoColorBleedGM : public GM {
+public:
+    NoColorBleedGM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("nocolorbleed");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(200, 200);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkBitmap sprite;
+        sprite.setConfig(SkBitmap::kARGB_8888_Config, 4, 4, 4*sizeof(SkColor));
+        const SkColor spriteData[16] = {
+            SK_ColorBLACK,  SK_ColorCYAN,    SK_ColorMAGENTA, SK_ColorYELLOW,
+            SK_ColorBLACK,  SK_ColorWHITE,   SK_ColorBLACK,   SK_ColorRED,
+            SK_ColorGREEN,  SK_ColorBLACK,   SK_ColorWHITE,   SK_ColorBLUE,
+            SK_ColorYELLOW, SK_ColorMAGENTA, SK_ColorCYAN,    SK_ColorBLACK
+        };
+        sprite.allocPixels();
+        sprite.lockPixels();
+        SkPMColor* addr = sprite.getAddr32(0, 0);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(spriteData); ++i) {
+            addr[i] = SkPreMultiplyColor(spriteData[i]);
+        }
+        sprite.unlockPixels();
+
+        // We draw a magnified subrect of the sprite
+        // sample interpolation may cause color bleeding around edges
+        // the subrect is a pure white area
+        SkIRect srcRect;
+        SkRect dstRect;
+        SkPaint paint;
+        paint.setFilterBitmap(true);
+        //First row : full texture with and without filtering
+        srcRect.setXYWH(0, 0, 4, 4);
+        dstRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0)
+                        , SkIntToScalar(100), SkIntToScalar(100));
+        canvas->drawBitmapRect(sprite, &srcRect, dstRect, &paint);
+        dstRect.setXYWH(SkIntToScalar(100), SkIntToScalar(0)
+                        , SkIntToScalar(100), SkIntToScalar(100));
+        canvas->drawBitmapRect(sprite, &srcRect, dstRect);
+        //Second row : sub rect of texture with and without filtering
+        srcRect.setXYWH(1, 1, 2, 2);
+        dstRect.setXYWH(SkIntToScalar(25), SkIntToScalar(125)
+                        , SkIntToScalar(50), SkIntToScalar(50));
+        canvas->drawBitmapRect(sprite, &srcRect, dstRect, &paint);
+        dstRect.setXYWH(SkIntToScalar(125), SkIntToScalar(125)
+                        , SkIntToScalar(50), SkIntToScalar(50));
+        canvas->drawBitmapRect(sprite, &srcRect, dstRect);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new NoColorBleedGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/patheffects.cpp b/gm/patheffects.cpp
new file mode 100644
index 0000000..a1fb675
--- /dev/null
+++ b/gm/patheffects.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 "gm.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "Sk1DPathEffect.h"
+#include "Sk2DPathEffect.h"
+#include "SkCornerPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkDiscretePathEffect.h"
+
+namespace skiagm {
+
+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.5f);
+
+    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 };
+
+class PathEffectGM : public GM {
+public:
+    PathEffectGM() {}
+
+protected:
+    SkString onShortName() {
+        return SkString("patheffect");
+    }
+
+    SkISize onISize() { return make_isize(800, 600); }
+
+    virtual void onDraw(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);
+        }
+
+        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:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* PathEffectFactory(void*) { return new PathEffectGM; }
+static GMRegistry regPathEffect(PathEffectFactory);
+
+}
diff --git a/gm/pathfill.cpp b/gm/pathfill.cpp
new file mode 100644
index 0000000..bfaabc9
--- /dev/null
+++ b/gm/pathfill.cpp
@@ -0,0 +1,223 @@
+
+/*
+ * Copyright 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"
+
+typedef SkScalar (*MakePathProc)(SkPath*);
+
+static SkScalar make_frame(SkPath* path) {
+    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));
+    paint.getFillPath(*path, path);
+    return SkIntToScalar(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(SkIntToScalar(10), SkIntToScalar(0));
+    return SkIntToScalar(30);
+}
+
+static SkScalar make_rect(SkPath* path) {
+    SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
+                 SkIntToScalar(30), SkIntToScalar(30) };
+    path->addRect(r);
+    path->offset(SkIntToScalar(10), SkIntToScalar(0));
+    return SkIntToScalar(30);
+}
+
+static SkScalar make_oval(SkPath* path) {
+    SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
+                 SkIntToScalar(30), SkIntToScalar(30) };
+    path->addOval(r);
+    path->offset(SkIntToScalar(10), SkIntToScalar(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); }
+
+// 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,
+    make_rect,
+    make_oval,
+    make_sawtooth,
+    make_star_5,
+    make_star_13,
+    make_line,
+};
+
+#define N   SK_ARRAY_COUNT(gProcs)
+
+class PathFillGM : public skiagm::GM {
+    SkPath  fPath[N];
+    SkScalar fDY[N];
+public:
+    PathFillGM() {
+        for (size_t i = 0; i < N; i++) {
+            fDY[i] = gProcs[i](&fPath[i]);
+        }
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("pathfill");
+    }
+
+    virtual SkISize onISize() {
+        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]);
+        }
+    }
+
+private:
+    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 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/pathreverse.cpp b/gm/pathreverse.cpp
new file mode 100644
index 0000000..b605a4c
--- /dev/null
+++ b/gm/pathreverse.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright 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 "SkPath.h"
+#include "SkTypeface.h"
+
+static void test_path(SkCanvas* canvas, const SkPath& path) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    canvas->drawPath(path, paint);
+
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setColor(SK_ColorRED);
+    canvas->drawPath(path, paint);
+}
+
+static void test_rev(SkCanvas* canvas, const SkPath& path) {
+    test_path(canvas, path);
+
+    SkPath rev;
+    rev.reverseAddPath(path);
+    canvas->save();
+    canvas->translate(150, 0);
+    test_path(canvas, rev);
+    canvas->restore();
+}
+
+static void test_rev(SkCanvas* canvas) {
+    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);
+    path.addOval(r);
+    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);
+    SkSafeUnref(paint.setTypeface(hira));
+    path.reset();
+    paint.getTextPath("e", 1, 50, 50, &path);
+    canvas->translate(0, 100);
+    test_rev(canvas, path);
+}
+
+namespace skiagm {
+
+class PathReverseGM : public GM {
+public:
+    PathReverseGM() {
+
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("path-reverse");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(640, 480);
+    }
+
+    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);
+        path.addOval(r);
+        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);
+        SkSafeUnref(paint.setTypeface(hira));
+        path.reset();
+        paint.getTextPath("e", 1, 50, 50, &path);
+        canvas->translate(0, 100);
+        test_rev(canvas, path);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new PathReverseGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/points.cpp b/gm/points.cpp
new file mode 100644
index 0000000..0646d61
--- /dev/null
+++ b/gm/points.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 "gm.h"
+#include "SkRandom.h"
+
+namespace skiagm {
+
+class PointsGM : public GM {
+public:
+    PointsGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("points");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(640, 490);
+    }
+
+    static void fill_pts(SkPoint pts[], size_t n, SkRandom* rand) {
+        for (size_t i = 0; i < n; i++) {
+            // Compute these independently and store in variables, rather
+            // than in the parameter-passing expression, to get consistent
+            // evaluation order across compilers.
+            SkScalar y = rand->nextUScalar1() * 480;
+            SkScalar x = rand->nextUScalar1() * 640;
+            pts[i].set(x, y);
+        }
+    }
+
+    virtual void onDraw(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 GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new PointsGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/poly2poly.cpp b/gm/poly2poly.cpp
new file mode 100644
index 0000000..9ad5bdc
--- /dev/null
+++ b/gm/poly2poly.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 "gm.h"
+
+namespace skiagm {
+
+class Poly2PolyGM : public GM {
+public:
+    Poly2PolyGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("poly2poly");
+    }
+
+    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);
+        paint->setStyle(SkPaint::kFill_Style);
+        SkScalar x = D/2;
+        SkScalar 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 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)
+        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 GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new Poly2PolyGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/quadpaths.cpp b/gm/quadpaths.cpp
new file mode 100644
index 0000000..ac91c62
--- /dev/null
+++ b/gm/quadpaths.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.
+ */
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+
+namespace skiagm {
+
+class QuadPathGM : public GM {
+public:
+    QuadPathGM() {}
+
+protected:
+    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,
+                  SkScalar strokeWidth) {
+        path.setFillType(fill);
+        SkPaint paint;
+        paint.setStrokeCap(cap);
+        paint.setStrokeWidth(strokeWidth);
+        paint.setStrokeJoin(join);
+        paint.setColor(color);
+        paint.setStyle(style);
+        canvas->save();
+        canvas->clipRect(clip);
+        canvas->drawPath(path, paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(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"},
+        };
+        struct CapAndName {
+            SkPaint::Cap  fCap;
+            SkPaint::Join fJoin;
+            const char*   fName;
+        };
+        static const CapAndName gCaps[] = {
+            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
+            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
+            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
+        };
+        struct PathAndName {
+            SkPath      fPath;
+            const char* fName;
+        };
+        PathAndName path;
+        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
+        path.fPath.quadTo(50*SK_Scalar1, 20*SK_Scalar1,
+                          75*SK_Scalar1, 10*SK_Scalar1);
+        path.fName = "moveTo-quad";
+
+        SkPaint titlePaint;
+        titlePaint.setColor(SK_ColorBLACK);
+        titlePaint.setAntiAlias(true);
+        titlePaint.setLCDRenderText(true);
+        titlePaint.setTextSize(15 * SK_Scalar1);
+        const char title[] = "Quad Drawn Into Rectangle Clips With "
+                             "Indicated Style, Fill and Linecaps, with stroke width 10";
+        canvas->drawText(title, strlen(title),
+                            20 * SK_Scalar1,
+                            20 * SK_Scalar1,
+                            titlePaint);
+
+        SkRandom rand;
+        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
+        canvas->save();
+        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
+        canvas->save();
+        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
+            if (0 < cap) {
+                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
+            }
+            canvas->save();
+            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
+                if (0 < fill) {
+                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
+                }
+                canvas->save();
+                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
+                    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);
+                    labelPaint.setLCDRenderText(true);
+                    labelPaint.setTextSize(10 * SK_Scalar1);
+                    canvas->drawText(gStyles[style].fName,
+                                        strlen(gStyles[style].fName),
+                                        0, rect.height() + 12 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gFills[fill].fName,
+                                        strlen(gFills[fill].fName),
+                                        0, rect.height() + 24 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gCaps[cap].fName,
+                                        strlen(gCaps[cap].fName),
+                                        0, rect.height() + 36 * SK_Scalar1,
+                                        labelPaint);
+                }
+                canvas->restore();
+            }
+            canvas->restore();
+        }
+        canvas->restore();
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+class QuadClosePathGM : public GM {
+public:
+    QuadClosePathGM() {}
+
+protected:
+    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,
+                  SkScalar strokeWidth) {
+        path.setFillType(fill);
+        SkPaint paint;
+        paint.setStrokeCap(cap);
+        paint.setStrokeWidth(strokeWidth);
+        paint.setStrokeJoin(join);
+        paint.setColor(color);
+        paint.setStyle(style);
+        canvas->save();
+        canvas->clipRect(clip);
+        canvas->drawPath(path, paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(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"},
+        };
+        struct CapAndName {
+            SkPaint::Cap  fCap;
+            SkPaint::Join fJoin;
+            const char*   fName;
+        };
+        static const CapAndName gCaps[] = {
+            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
+            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
+            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
+        };
+        struct PathAndName {
+            SkPath      fPath;
+            const char* fName;
+        };
+        PathAndName path;
+        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
+        path.fPath.quadTo(50*SK_Scalar1, 20*SK_Scalar1,
+                          75*SK_Scalar1, 10*SK_Scalar1);
+        path.fPath.close();
+        path.fName = "moveTo-quad-close";
+
+        SkPaint titlePaint;
+        titlePaint.setColor(SK_ColorBLACK);
+        titlePaint.setAntiAlias(true);
+        titlePaint.setLCDRenderText(true);
+        titlePaint.setTextSize(15 * SK_Scalar1);
+        const char title[] = "Quad Closed Drawn Into Rectangle Clips With "
+                             "Indicated Style, Fill and Linecaps, with stroke width 10";
+        canvas->drawText(title, strlen(title),
+                            20 * SK_Scalar1,
+                            20 * SK_Scalar1,
+                            titlePaint);
+
+        SkRandom rand;
+        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
+        canvas->save();
+        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
+        canvas->save();
+        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
+            if (0 < cap) {
+                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
+            }
+            canvas->save();
+            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
+                if (0 < fill) {
+                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
+                }
+                canvas->save();
+                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
+                    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);
+                    labelPaint.setLCDRenderText(true);
+                    labelPaint.setTextSize(10 * SK_Scalar1);
+                    canvas->drawText(gStyles[style].fName,
+                                        strlen(gStyles[style].fName),
+                                        0, rect.height() + 12 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gFills[fill].fName,
+                                        strlen(gFills[fill].fName),
+                                        0, rect.height() + 24 * SK_Scalar1,
+                                        labelPaint);
+                    canvas->drawText(gCaps[cap].fName,
+                                        strlen(gCaps[cap].fName),
+                                        0, rect.height() + 36 * SK_Scalar1,
+                                        labelPaint);
+                }
+                canvas->restore();
+            }
+            canvas->restore();
+        }
+        canvas->restore();
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* QuadPathFactory(void*) { return new QuadPathGM; }
+static GMRegistry regQuadPath(QuadPathFactory);
+
+static GM* QuadClosePathFactory(void*) { return new QuadClosePathGM; }
+static GMRegistry regQuadClosePath(QuadClosePathFactory);
+
+}
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/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
new file mode 100644
index 0000000..d8641db
--- /dev/null
+++ b/gm/shadertext.cpp
@@ -0,0 +1,215 @@
+
+/*
+ * Copyright 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(0);
+
+    SkCanvas    canvas(*bm);
+    SkScalar    s = SkIntToScalar(SkMin32(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);
+}
+
+static 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 ShaderTextGM : public GM {
+public:
+    ShaderTextGM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+
+    SkString onShortName() {
+        return SkString("shadertext");
+    }
+
+    SkISize onISize() { return make_isize(1450, 500); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        const char text[] = "Shaded Text";
+        const int textLen = SK_ARRAY_COUNT(text) - 1;
+        const 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();
+
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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..387bd49
--- /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(0);
+
+    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..4e95588
--- /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(0);
+
+    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);
+                int 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
new file mode 100644
index 0000000..2fb1615
--- /dev/null
+++ b/gm/shadows.cpp
@@ -0,0 +1,121 @@
+
+/*
+ * Copyright 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 "SkBlurDrawLooper.h"
+
+namespace skiagm {
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void setup(SkPaint* paint, SkColor c, SkScalar strokeWidth) {
+    paint->setColor(c);
+    if (strokeWidth < 0) {
+        paint->setStyle(SkPaint::kFill_Style);
+    } else {
+        paint->setStyle(SkPaint::kStroke_Style);
+        paint->setStrokeWidth(strokeWidth);
+    }
+}
+
+class ShadowsGM : public GM {
+public:
+    SkPath fCirclePath;
+    SkRect fRect;
+
+    ShadowsGM() {
+        this->setBGColor(0xFFDDDDDD);
+        fCirclePath.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(10) );
+        fRect.set(SkIntToScalar(10), SkIntToScalar(10),
+                  SkIntToScalar(30), SkIntToScalar(30));
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("shadows");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(200, 120);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+    SkBlurDrawLooper* shadowLoopers[5];
+    shadowLoopers[0] =
+        new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5),
+                              SkIntToScalar(10), 0xFF0000FF,
+                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
+                              SkBlurDrawLooper::kOverrideColor_BlurFlag |
+                              SkBlurDrawLooper::kHighQuality_BlurFlag );
+    SkAutoUnref aurL0(shadowLoopers[0]);
+    shadowLoopers[1] =
+        new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5),
+                              SkIntToScalar(10), 0xFF0000FF,
+                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
+                              SkBlurDrawLooper::kOverrideColor_BlurFlag );
+    SkAutoUnref aurL1(shadowLoopers[1]);
+    shadowLoopers[2] =
+        new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(5),
+                              SkIntToScalar(10), 0xFF000000,
+                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
+                              SkBlurDrawLooper::kHighQuality_BlurFlag  );
+    SkAutoUnref aurL2(shadowLoopers[2]);
+    shadowLoopers[3] =
+        new SkBlurDrawLooper (SkIntToScalar(5), SkIntToScalar(-5),
+                              SkIntToScalar(-10), 0x7FFF0000,
+                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
+                              SkBlurDrawLooper::kOverrideColor_BlurFlag |
+                              SkBlurDrawLooper::kHighQuality_BlurFlag  );
+    SkAutoUnref aurL3(shadowLoopers[3]);
+    shadowLoopers[4] =
+        new SkBlurDrawLooper (SkIntToScalar(0), SkIntToScalar(5),
+                              SkIntToScalar(5), 0xFF000000,
+                              SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
+                              SkBlurDrawLooper::kOverrideColor_BlurFlag |
+                              SkBlurDrawLooper::kHighQuality_BlurFlag  );
+    SkAutoUnref aurL4(shadowLoopers[4]);
+
+    static const struct {
+        SkColor fColor;
+        SkScalar fStrokeWidth;
+    } gRec[] = {
+        { SK_ColorRED,      -SK_Scalar1 },
+        { SK_ColorGREEN,    SkIntToScalar(4) },
+        { SK_ColorBLUE,     SkIntToScalar(0)},
+    };
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    for (size_t i = 0; i < SK_ARRAY_COUNT(shadowLoopers); ++i) {
+        SkAutoCanvasRestore acr(canvas, true);
+
+        paint.setLooper(shadowLoopers[i]);
+
+        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);
+    }
+}
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ShadowsGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/simpleaaclip.cpp b/gm/simpleaaclip.cpp
new file mode 100644
index 0000000..c6e8ba9
--- /dev/null
+++ b/gm/simpleaaclip.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright 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
+static GM* MyFactory(void*) { return new SimpleClipGM(
+                                            SimpleClipGM::kRect_GeomType); }
+static GMRegistry reg(MyFactory);
+
+// paths
+static GM* MyFactory2(void*) { return new SimpleClipGM(
+                                            SimpleClipGM::kPath_GeomType); }
+static GMRegistry reg2(MyFactory2);
+
+// aa clip
+static GM* MyFactory3(void*) { return new SimpleClipGM(
+                                            SimpleClipGM::kAAClip_GeomType); }
+static GMRegistry reg3(MyFactory3);
+
+}
diff --git a/gm/strokefill.cpp b/gm/strokefill.cpp
new file mode 100644
index 0000000..445de81
--- /dev/null
+++ b/gm/strokefill.cpp
@@ -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.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkTypeface.h"
+
+namespace skiagm {
+
+class StrokeFillGM : public GM {
+public:
+    StrokeFillGM() {
+
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("stroke-fill");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(640, 480);
+    }
+
+    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);
+        canvas->drawText(text, len, x, y + SkIntToScalar(120), p);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkScalar x = SkIntToScalar(100);
+        SkScalar y = SkIntToScalar(88);
+
+        SkPaint paint;
+        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", 5, x, y, paint);
+
+        face = SkTypeface::CreateFromName("Hiragino Maru Gothic Pro", SkTypeface::kNormal);
+        SkSafeUnref(paint.setTypeface(face));
+        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);
+
+        SkPath path;
+        path.setFillType(SkPath::kWinding_FillType);
+        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);
+        SkASSERT(path2.cheapIsDirection(SkPath::kCW_Direction));
+        canvas->drawPath(path2, paint);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new StrokeFillGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/strokerects.cpp b/gm/strokerects.cpp
new file mode 100644
index 0000000..62a9639
--- /dev/null
+++ b/gm/strokerects.cpp
@@ -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.
+ */
+
+
+
+#include "gm.h"
+#include "SkRandom.h"
+
+namespace skiagm {
+
+#define W   400
+#define H   400
+#define N   100
+
+static const SkScalar SW = SkIntToScalar(W);
+static const SkScalar SH = SkIntToScalar(H);
+
+class StrokeRectGM : public GM {
+public:
+    StrokeRectGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("strokerects");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(W*2, H*2);
+    }
+
+    static void rnd_rect(SkRect* r, SkRandom& rand) {
+        SkScalar x = rand.nextUScalar1() * W;
+        SkScalar y = rand.nextUScalar1() * H;
+        SkScalar w = rand.nextUScalar1() * (W >> 2);
+        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);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+
+        for (int y = 0; y < 2; y++) {
+            paint.setAntiAlias(!!y);
+            for (int x = 0; x < 2; x++) {
+                paint.setStrokeWidth(x * SkIntToScalar(3));
+
+                SkAutoCanvasRestore acr(canvas, true);
+                canvas->translate(SW * x, SH * y);
+                canvas->clipRect(SkRect::MakeLTRB(
+                        SkIntToScalar(2), SkIntToScalar(2)
+                        , SW - SkIntToScalar(2), SH - SkIntToScalar(2)
+                ));
+
+                SkRandom rand;
+                for (int i = 0; i < N; i++) {
+                    SkRect r;
+                    rnd_rect(&r, rand);
+                    canvas->drawRect(r, paint);
+                }
+            }
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new StrokeRectGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/strokes.cpp b/gm/strokes.cpp
new file mode 100644
index 0000000..c479d69
--- /dev/null
+++ b/gm/strokes.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 "gm.h"
+#include "SkRandom.h"
+
+#define W   400
+#define H   400
+#define N   50
+
+static const SkScalar SW = SkIntToScalar(W);
+static const SkScalar SH = SkIntToScalar(H);
+
+static void rnd_rect(SkRect* r, SkPaint* paint, SkRandom& rand) {
+    SkScalar x = rand.nextUScalar1() * W;
+    SkScalar y = rand.nextUScalar1() * H;
+    SkScalar w = rand.nextUScalar1() * (W >> 2);
+    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 skiagm::GM {
+public:
+    StrokesGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("strokes_round");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(W, H*2);
+    }
+
+    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);
+            canvas->translate(0, SH * y);
+            canvas->clipRect(SkRect::MakeLTRB(
+                                              SkIntToScalar(2), SkIntToScalar(2)
+                                              , SW - SkIntToScalar(2), SH - SkIntToScalar(2)
+                                              ));
+
+            SkRandom rand;
+            for (int i = 0; i < N; i++) {
+                SkRect r;
+                rnd_rect(&r, &paint, rand);
+                canvas->drawOval(r, paint);
+                rnd_rect(&r, &paint, rand);
+                canvas->drawRoundRect(r, r.width()/4, r.height()/4, paint);
+                rnd_rect(&r, &paint, rand);
+            }
+        }
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+class Strokes2GM : public skiagm::GM {
+    SkPath fPath;
+public:
+    Strokes2GM() {
+        SkRandom rand;
+        fPath.moveTo(0, 0);
+        for (int i = 0; i < 13; i++) {
+            SkScalar x = rand.nextUScalar1() * (W >> 1);
+            SkScalar y = rand.nextUScalar1() * (H >> 1);
+            fPath.lineTo(x, y);
+        }
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("strokes_poly");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(W, H*2);
+    }
+
+    static void rotate(SkScalar angle, SkScalar px, SkScalar py, SkCanvas* canvas) {
+        SkMatrix matrix;
+        matrix.setRotate(angle, px, py);
+        canvas->concat(matrix);
+    }
+
+    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);
+            canvas->translate(0, SH * y);
+            canvas->clipRect(SkRect::MakeLTRB(SkIntToScalar(2),
+                                              SkIntToScalar(2),
+                                              SW - SkIntToScalar(2),
+                                              SH - SkIntToScalar(2)));
+
+            SkRandom rand;
+            for (int i = 0; i < N/2; i++) {
+                SkRect r;
+                rnd_rect(&r, &paint, rand);
+                rotate(SkIntToScalar(15), SW/2, SH/2, canvas);
+                canvas->drawPath(fPath, paint);
+            }
+        }
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+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.h b/gm/system_preferences.h
new file mode 100644
index 0000000..0fcf489
--- /dev/null
+++ b/gm/system_preferences.h
@@ -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.
+ */
+
+// Set up system preferences with a known state.
+// This is implemented on a per-platform basis.
+// TODO(epoger): move this out of gm/ into a more common directory, so
+// that it can be used more broadly.
+void setSystemPreferences();
diff --git a/gm/system_preferences_default.cpp b/gm/system_preferences_default.cpp
new file mode 100644
index 0000000..1fc4e6c
--- /dev/null
+++ b/gm/system_preferences_default.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.
+ */
+
+// This is a default implementation of setSystemPreferences() that does nothing.
+void setSystemPreferences() {
+}
diff --git a/gm/system_preferences_mac.mm b/gm/system_preferences_mac.mm
new file mode 100644
index 0000000..ee6ab19
--- /dev/null
+++ b/gm/system_preferences_mac.mm
@@ -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.
+ */
+
+#include "system_preferences.h"
+
+#import <Cocoa/Cocoa.h>
+
+void setSystemPreferences() {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    // 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
+    enum {
+        NoFontSmoothing     = 0,
+        LightFontSmoothing  = 1,
+        MediumFontSmoothing = 2,
+        StrongFontSmoothing = 3,
+    };
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    [defaults setInteger:MediumFontSmoothing forKey:@"AppleFontSmoothing"];
+
+    [pool release];
+}
diff --git a/gm/tablecolorfilter.cpp b/gm/tablecolorfilter.cpp
new file mode 100644
index 0000000..c8806f9
--- /dev/null
+++ b/gm/tablecolorfilter.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 "gm.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkTableColorFilter.h"
+
+static void make_bm0(SkBitmap* bm) {
+    int W = 120;
+    int H = 120;
+    bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
+    bm->allocPixels();
+    bm->eraseColor(0);
+
+    SkCanvas canvas(*bm);
+    SkPaint paint;
+    SkPoint pts[] = { {0, 0}, {SkIntToScalar(W), SkIntToScalar(H)} };
+    SkColor colors[] = {
+        SK_ColorBLACK, SK_ColorGREEN, SK_ColorCYAN,
+        SK_ColorRED, 0, SK_ColorBLUE, SK_ColorWHITE
+    };
+    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors),
+                                                 SkShader::kClamp_TileMode);
+    paint.setShader(s)->unref();
+    canvas.drawPaint(paint);
+}
+static void make_bm1(SkBitmap* bm) {
+    int W = 120;
+    int H = 120;
+    bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
+    bm->allocPixels();
+    bm->eraseColor(0);
+
+    SkCanvas canvas(*bm);
+    SkPaint paint;
+    SkScalar cx = SkIntToScalar(W)/2;
+    SkScalar cy = SkIntToScalar(H)/2;
+    SkColor colors[] = {
+        SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE,
+    };
+    SkShader* s = SkGradientShader::CreateRadial(SkPoint::Make(SkIntToScalar(W)/2,
+                                                               SkIntToScalar(H)/2),
+                                                 SkIntToScalar(W)/2, colors, NULL, SK_ARRAY_COUNT(colors),
+                                                 SkShader::kClamp_TileMode);
+    paint.setShader(s)->unref();
+    paint.setAntiAlias(true);
+    canvas.drawCircle(cx, cy, cx, paint);
+}
+
+static void make_table0(uint8_t table[]) {
+    for (int i = 0; i < 256; ++i) {
+        int n = i >> 5;
+        table[i] = (n << 5) | (n << 2) | (n >> 1);
+    }
+}
+static void make_table1(uint8_t table[]) {
+    for (int i = 0; i < 256; ++i) {
+        table[i] = i * i / 255;
+    }
+}
+static void make_table2(uint8_t table[]) {
+    for (int i = 0; i < 256; ++i) {
+        float fi = i / 255.0f;
+        table[i] = static_cast<uint8_t>(sqrtf(fi) * 255);
+    }
+}
+
+static SkColorFilter* make_cf0() {
+    uint8_t table[256]; make_table0(table);
+    return SkTableColorFilter::Create(table);
+}
+static SkColorFilter* make_cf1() {
+    uint8_t table[256]; make_table1(table);
+    return SkTableColorFilter::Create(table);
+}
+static SkColorFilter* make_cf2() {
+    uint8_t table[256]; make_table2(table);
+    return SkTableColorFilter::Create(table);
+}
+static SkColorFilter* make_cf3() {
+    uint8_t table0[256]; make_table0(table0);
+    uint8_t table1[256]; make_table1(table1);
+    uint8_t table2[256]; make_table2(table2);
+    return SkTableColorFilter::CreateARGB(NULL, table0, table1, table2);
+}
+
+class TableColorFilterGM : public skiagm::GM {
+public:
+    TableColorFilterGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("tablecolorfilter");
+    }
+
+    virtual SkISize onISize() {
+        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);
+            paint.setColorFilter(make_cf0())->unref();  x += bm.width() * 9 / 8;
+            canvas->drawBitmap(bm, x, y, &paint);
+            paint.setColorFilter(make_cf1())->unref();  x += bm.width() * 9 / 8;
+            canvas->drawBitmap(bm, x, y, &paint);
+            paint.setColorFilter(make_cf2())->unref();  x += bm.width() * 9 / 8;
+            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;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new TableColorFilterGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/techtalk1.cpp b/gm/techtalk1.cpp
new file mode 100644
index 0000000..00c4956
--- /dev/null
+++ b/gm/techtalk1.cpp
@@ -0,0 +1,399 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..39b974b
--- /dev/null
+++ b/gm/testimagefilters.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 "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+
+#include "SkBlurImageFilter.h"
+#include "SkColorFilterImageFilter.h"
+#include "SkTestImageFilters.h"
+
+#define FILTER_WIDTH    SkIntToScalar(150)
+#define FILTER_HEIGHT   SkIntToScalar(200)
+
+static SkImageFilter* make0() { return new SkDownSampleImageFilter(SK_Scalar1 / 5); }
+static SkImageFilter* make1() { return new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16)); }
+static SkImageFilter* make2() {
+    SkColorFilter* cf = SkColorFilter::CreateModeFilter(SK_ColorBLUE,
+                                                        SkXfermode::kSrcIn_Mode);
+    SkAutoUnref aur(cf);
+    return new SkColorFilterImageFilter(cf);
+}
+static SkImageFilter* make3() {
+    return new SkBlurImageFilter(8, 0);
+}
+
+static SkImageFilter* make4() {
+    SkImageFilter* outer = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
+    SkImageFilter* inner = new SkDownSampleImageFilter(SK_Scalar1 / 5);
+    SkAutoUnref aur0(outer);
+    SkAutoUnref aur1(inner);
+    return new SkComposeImageFilter(outer, inner);
+}
+static SkImageFilter* make5() {
+    SkImageFilter* first = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
+    SkImageFilter* second = new SkDownSampleImageFilter(SK_Scalar1 / 5);
+    SkAutoUnref aur0(first);
+    SkAutoUnref aur1(second);
+    return new SkMergeImageFilter(first, second);
+}
+
+static SkImageFilter* make6() {
+    SkImageFilter* outer = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
+    SkImageFilter* inner = new SkDownSampleImageFilter(SK_Scalar1 / 5);
+    SkAutoUnref aur0(outer);
+    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);
+    SkAutoUnref aur4(blue);
+
+    return new SkMergeImageFilter(compose, blue);
+}
+
+static SkImageFilter* make7() {
+    SkImageFilter* outer = new SkOffsetImageFilter(SkIntToScalar(16), SkIntToScalar(16));
+    SkImageFilter* inner = make3();
+    SkAutoUnref aur0(outer);
+    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);
+    SkAutoUnref aur4(blue);
+
+    return new SkMergeImageFilter(compose, blue);
+}
+
+static void draw0(SkCanvas* canvas) {
+    SkPaint p;
+    p.setAntiAlias(true);
+    SkRect r = SkRect::MakeWH(FILTER_WIDTH, FILTER_HEIGHT);
+    r.inset(SK_Scalar1 * 12, SK_Scalar1 * 12);
+    p.setColor(SK_ColorRED);
+    canvas->drawOval(r, p);
+}
+
+class TestImageFiltersGM : public skiagm::GM {
+public:
+    TestImageFiltersGM () {}
+
+protected:
+
+    virtual SkString onShortName() {
+        return SkString("testimagefilters");
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(700, 460); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+//        this->drawSizeBounds(canvas, 0xFFCCCCCC);
+
+        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;
+
+        canvas->translate(SkIntToScalar(8), SkIntToScalar(8));
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gFilterProc); ++i) {
+            int ix = i % 4;
+            int iy = i / 4;
+
+            SkAutoCanvasRestore acr(canvas, true);
+            canvas->translate(ix * dx, iy * dy);
+
+            SkPaint p;
+            p.setStyle(SkPaint::kStroke_Style);
+            canvas->drawRect(bounds, p);
+
+            SkPaint paint;
+            paint.setImageFilter(gFilterProc[i]())->unref();
+            canvas->saveLayer(&bounds, &paint);
+            draw0(canvas);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new TestImageFiltersGM; }
+static skiagm::GMRegistry reg(MyFactory);
+
+
diff --git a/gm/texdata.cpp b/gm/texdata.cpp
new file mode 100644
index 0000000..09721ac
--- /dev/null
+++ b/gm/texdata.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.
+ */
+
+// This test only works with the GPU backend.
+
+#include "gm.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "effects/GrSingleTextureEffect.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+
+namespace skiagm {
+
+extern GrContext* GetGr();
+
+static const int S = 200;
+
+class TexDataGM : public GM {
+public:
+    TexDataGM() {
+        this->setBGColor(0xff000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("texdata");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(2*S, 2*S);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkDevice* device = canvas->getDevice();
+        GrRenderTarget* target = (GrRenderTarget*) device->accessRenderTarget();
+        GrContext* ctx = GetGr();
+        if (ctx && target) {
+            SkPMColor gTextureData[(2 * S) * (2 * S)];
+            static const int stride = 2 * S;
+            static const SkPMColor gray  = SkPackARGB32(0x40, 0x40, 0x40, 0x40);
+            static const SkPMColor white = SkPackARGB32(0xff, 0xff, 0xff, 0xff);
+            static const SkPMColor red   = SkPackARGB32(0x80, 0x80, 0x00, 0x00);
+            static const SkPMColor blue  = SkPackARGB32(0x80, 0x00, 0x00, 0x80);
+            static const SkPMColor green = SkPackARGB32(0x80, 0x00, 0x80, 0x00);
+            static const SkPMColor black = SkPackARGB32(0x00, 0x00, 0x00, 0x00);
+            for (int i = 0; i < 2; ++i) {
+                int offset = 0;
+                // fill upper-left
+                for (int y = 0; y < S; ++y) {
+                    for (int x = 0; x < S; ++x) {
+                        gTextureData[offset + y * stride + x] = gray;
+                    }
+                }
+                // fill upper-right
+                offset = S;
+                for (int y = 0; y < S; ++y) {
+                    for (int x = 0; x < S; ++x) {
+                        gTextureData[offset + y * stride + x] = white;
+                    }
+                }
+                // fill lower left
+                offset = S * stride;
+                for (int y = 0; y < S; ++y) {
+                    for (int x = 0; x < S; ++x) {
+                        gTextureData[offset + y * stride + x] = black;
+                    }
+                }
+                // fill lower right
+                offset = S * stride + S;
+                for (int y = 0; y < S; ++y) {
+                    for (int x = 0; x < S; ++x) {
+                        gTextureData[offset + y * stride + x] = gray;
+                    }
+                }
+
+                GrTextureDesc desc;
+                // use RT flag bit because in GL it makes the texture be bottom-up
+                desc.fFlags     = i ? kRenderTarget_GrTextureFlagBit :
+                                      kNone_GrTextureFlags;
+                desc.fConfig    = kSkia8888_PM_GrPixelConfig;
+                desc.fWidth     = 2 * S;
+                desc.fHeight    = 2 * S;
+                GrTexture* texture =
+                    ctx->createUncachedTexture(desc, gTextureData, 0);
+
+                if (!texture) {
+                    return;
+                }
+                GrAutoUnref au(texture);
+
+                GrContext::AutoClip acs(ctx, GrRect::MakeWH(2*S, 2*S));
+
+                ctx->setRenderTarget(target);
+
+                GrPaint paint;
+                paint.setBlendFunc(kOne_GrBlendCoeff, kISA_GrBlendCoeff);
+                GrMatrix vm;
+                if (i) {
+                    vm.setRotate(90 * SK_Scalar1,
+                                 S * SK_Scalar1,
+                                 S * SK_Scalar1);
+                } else {
+                    vm.reset();
+                }
+                ctx->setMatrix(vm);
+                GrMatrix tm;
+                tm = vm;
+                tm.postIDiv(2*S, 2*S);
+                paint.colorSampler(0)->setCustomStage(SkNEW_ARGS(GrSingleTextureEffect,
+                                                                 (texture)), tm)->unref();
+
+                ctx->drawRect(paint, GrRect::MakeWH(2*S, 2*S));
+
+                // now update the lower right of the texture in first pass
+                // or upper right in second pass
+                offset = 0;
+                for (int y = 0; y < S; ++y) {
+                    for (int x = 0; x < S; ++x) {
+                        gTextureData[offset + y * stride + x] =
+                            ((x + y) % 2) ? (i ? green : red) : blue;
+                    }
+                }
+                texture->writePixels(S, (i ? 0 : S), S, S,
+                                     texture->config(), gTextureData,
+                                     4 * stride);
+                ctx->drawRect(paint, GrRect::MakeWH(2*S, 2*S));
+            }
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new TexDataGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
+#endif
diff --git a/gm/texteffects.cpp b/gm/texteffects.cpp
new file mode 100644
index 0000000..3436018
--- /dev/null
+++ b/gm/texteffects.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright 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
new file mode 100644
index 0000000..08bcb79
--- /dev/null
+++ b/gm/tilemodes.cpp
@@ -0,0 +1,157 @@
+
+/*
+ * Copyright 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 "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkTypeface.h"
+
+// effects
+#include "SkGradientShader.h"
+#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);
+
+    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 TilingGM : public GM {
+    SkBlurDrawLooper    fLooper;
+public:
+    TilingGM()
+            : fLooper(SkIntToScalar(1), SkIntToScalar(2), SkIntToScalar(2),
+                      0x88000000) {
+        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) {
+
+        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);
+
+        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);
+                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++) {
+            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;
+                    }
+                }
+                {
+                    SkPaint p;
+                    SkString str;
+                    p.setAntiAlias(true);
+                    p.setLooper(&fLooper);
+                    str.printf("%s, %s", gConfigNames[i], gFilterNames[j]);
+                    canvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p);
+                }
+
+                y += r.height() * 4 / 3;
+            }
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new TilingGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gm/tinybitmap.cpp b/gm/tinybitmap.cpp
new file mode 100644
index 0000000..3b1006c
--- /dev/null
+++ b/gm/tinybitmap.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 "gm.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+#include "SkCanvas.h"
+#include "SkUtils.h"
+
+namespace skiagm {
+
+static SkBitmap make_bitmap() {
+    SkBitmap bm;
+
+    SkColorTable* ctable = new SkColorTable(1);
+    SkPMColor* c = ctable->lockColors();
+    c[0] = SkPackARGB32(0x80, 0x80, 0, 0);
+    ctable->unlockColors(true);
+
+    bm.setConfig(SkBitmap::kIndex8_Config, 1, 1);
+    bm.allocPixels(ctable);
+    ctable->unref();
+
+    bm.lockPixels();
+    *bm.getAddr8(0, 0) = 0;
+    bm.unlockPixels();
+    return bm;
+}
+
+class TinyBitmapGM : public GM {
+    SkBitmap    fBM;
+public:
+    TinyBitmapGM() {
+        this->setBGColor(0xFFDDDDDD);
+        fBM = make_bitmap();
+    }
+
+protected:
+    SkString onShortName() {
+        return SkString("tinybitmap");
+    }
+
+    virtual SkISize onISize() { return make_isize(100, 100); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkShader* s =
+            SkShader::CreateBitmapShader(fBM, SkShader::kRepeat_TileMode,
+                                         SkShader::kMirror_TileMode);
+        SkPaint paint;
+        paint.setAlpha(0x80);
+        paint.setShader(s)->unref();
+        canvas->drawPaint(paint);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new TinyBitmapGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/twopointradial.cpp b/gm/twopointradial.cpp
new file mode 100644
index 0000000..40712e8
--- /dev/null
+++ b/gm/twopointradial.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 "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
new file mode 100644
index 0000000..1471cbc
--- /dev/null
+++ b/gm/verttext.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 "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()
+        // : fFace(SkTypeface::CreateFromName("unifont", SkTypeface::kNormal))
+        // : fFace(SkTypeface::CreateFromFile("MotoyaL04Mincho_3.ttf"))
+    {
+    }
+
+    //SkAutoTUnref<SkTypeface> fFace;
+
+protected:
+
+    SkString onShortName() {
+        return SkString("verttext");
+    }
+
+    SkISize onISize() { return make_isize(640, 480); }
+
+    static void drawBaseline(SkCanvas* canvas, const SkPaint& paint,
+                             SkScalar x, SkScalar y) {
+        SkScalar total = paint.measureText(gText, gLen);
+
+        SkPaint p;
+        p.setAntiAlias(true);
+        p.setColor(0x80FF0000);
+        canvas->drawLine(x, y,
+                         paint.isVerticalText() ? x : x + total,
+                         paint.isVerticalText() ? y + total : y,
+                         p);
+
+        p.setColor(0xFF0000FF);
+        SkScalar adv[gLen];
+        paint.getTextWidths(gText, gLen, adv, NULL);
+        for (size_t i = 0; i < gLen; ++i) {
+            canvas->drawCircle(x, y, SK_Scalar1 * 3 / 2, p);
+            if (paint.isVerticalText()) {
+                y += adv[i];
+            } else {
+                x += adv[i];
+            }
+        }
+        canvas->drawCircle(x, y, SK_Scalar1 * 3 / 2, p);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkScalar x = SkIntToScalar(100);
+        SkScalar y = SkIntToScalar(50);
+
+        for (int i = 0; i < 4; ++i) {
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            paint.setTextSize(SkIntToScalar(TEXT_SIZE));
+            //paint.setTypeface(fFace);
+            //paint.setFakeBoldText(true);
+
+            paint.setVerticalText(false);
+            drawBaseline(canvas, paint, x, y);
+            canvas->drawText(gText, gLen, x, y, paint);
+
+            paint.setVerticalText(true);
+            drawBaseline(canvas, paint, x, y);
+            canvas->drawText(gText, gLen, x, y, paint);
+
+            x += SkIntToScalar(40);
+            y += SkIntToScalar(120);
+
+            canvas->rotate(SkIntToScalar(-15));
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new VertTextGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/verttext2.cpp b/gm/verttext2.cpp
new file mode 100644
index 0000000..33380e0
--- /dev/null
+++ b/gm/verttext2.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.
+ */
+
+
+/* Tests text vertical text rendering with different fonts and centering.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkTypeface.h"
+
+namespace skiagm {
+
+class VertText2GM : public GM {
+public:
+    VertText2GM() {
+        const int pointSize = 24;
+        textHeight = SkIntToScalar(pointSize);
+        fProp = SkTypeface::CreateFromName("Helvetica", SkTypeface::kNormal);
+        fMono = SkTypeface::CreateFromName("Courier New", SkTypeface::kNormal);
+    }
+
+    virtual ~VertText2GM() {
+        SkSafeUnref(fProp);
+        SkSafeUnref(fMono);
+    }
+
+protected:
+
+    SkString onShortName() {
+        return SkString("verttext2");
+    }
+
+    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);
+            paint.setAntiAlias(true);
+            y = textHeight;
+            canvas->drawLine(0, SkIntToScalar(10),
+                    SkIntToScalar(110), SkIntToScalar(10), paint);
+            canvas->drawLine(0, SkIntToScalar(240),
+                    SkIntToScalar(110), SkIntToScalar(240), paint);
+            canvas->drawLine(0, SkIntToScalar(470),
+                    SkIntToScalar(110), SkIntToScalar(470), paint);
+            drawText(canvas, SkString("Proportional / Top Aligned"),
+                     fProp,  SkPaint::kLeft_Align);
+            drawText(canvas, SkString("<   Proportional / Centered   >"),
+                     fProp,  SkPaint::kCenter_Align);
+            drawText(canvas, SkString("Monospaced / Top Aligned"),
+                     fMono, SkPaint::kLeft_Align);
+            drawText(canvas, SkString("<    Monospaced / Centered    >"),
+                     fMono, SkPaint::kCenter_Align);
+            canvas->rotate(SkIntToScalar(-15));
+            canvas->translate(textHeight * 4, SkIntToScalar(50));
+            if (i > 0) {
+                canvas->translate(0, SkIntToScalar(50));
+            }
+        }
+    }
+
+    void drawText(SkCanvas* canvas, const SkString& string,
+                  SkTypeface* family, SkPaint::Align alignment) {
+        SkPaint paint;
+        paint.setColor(SK_ColorBLACK);
+        paint.setAntiAlias(true);
+        paint.setVerticalText(true);
+        paint.setTextAlign(alignment);
+        paint.setTypeface(family);
+        paint.setTextSize(textHeight);
+
+        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* fProp;
+    SkTypeface* fMono;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new VertText2GM; }
+static GMRegistry reg(MyFactory);
+
+}
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
new file mode 100644
index 0000000..22b6edd
--- /dev/null
+++ b/gm/xfermodes.cpp
@@ -0,0 +1,188 @@
+
+/*
+ * Copyright 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 "SkShader.h"
+#include "SkXfermode.h"
+
+namespace skiagm {
+
+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);
+
+    SkPaint p;
+    p.setAntiAlias(true);
+
+    SkRect r;
+    SkScalar ww = SkIntToScalar(w);
+    SkScalar hh = SkIntToScalar(h);
+
+    {
+        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);
+
+    {
+        SkCanvas c(*dst);
+        p.setColor(0xFF66AAFF);
+        r.set(ww/3, hh/3, ww*19/20, hh*19/20);
+        c.drawRect(r, p);
+    }
+}
+
+class XfermodesGM : public GM {
+    SkBitmap    fBG;
+    SkBitmap    fSrcB, fDstB;
+    bool        fOnce;
+
+    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);
+    }
+
+    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;
+        }
+    }
+
+public:
+    const static int W = 64;
+    const static int H = 64;
+    XfermodesGM() : fOnce(false) {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("xfermodes");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(790, 640);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        this->init();
+
+        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.setTextAlign(SkPaint::kCenter_Align);
+
+        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);
+
+#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 GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new XfermodesGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gyp/FileReaderApp.gyp b/gyp/FileReaderApp.gyp
new file mode 100644
index 0000000..bcd0f0d
--- /dev/null
+++ b/gyp/FileReaderApp.gyp
@@ -0,0 +1,72 @@
+{
+  'targets': [
+    {
+      'target_name': 'FileReaderApp',
+      'type': 'executable',
+      'mac_bundle' : 1,
+
+      'include_dirs' : [
+        '../include/pipe',
+        '../experimental/FileReaderApp',
+        '../experimental/SimpleCocoaApp',
+      ],
+      'sources': [
+        '../experimental/FileReaderApp/ReaderView.cpp',
+        '../src/pipe/SkGPipeRead.cpp',
+      ],
+      'sources!': [
+        '../src/utils/mac/SkOSWindow_Mac.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'views.gyp:views',
+        'xml.gyp:xml',
+      ],
+      'conditions' : [
+        # Only supports Mac currently
+        ['skia_os == "mac"', {
+          'sources': [
+            '../experimental/SimpleCocoaApp/SkNSWindow.mm',
+            '../experimental/SimpleCocoaApp/SkNSView.mm',
+            '../experimental/FileReaderApp/FileReaderApp-Info.plist',
+            '../experimental/FileReaderApp/FileReaderAppDelegate.mm',
+            '../experimental/FileReaderApp/FileReaderApp_Prefix.pch',
+            '../experimental/FileReaderApp/FileReaderWindow.mm',
+            '../experimental/FileReaderApp/main.m',
+            '../include/utils/mac/SkCGUtils.h',
+            '../src/utils/mac/SkCreateCGImageRef.cpp',
+          ],
+          'link_settings': {
+            'libraries': [
+            '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+            '$(SDKROOT)/System/Library/Frameworks/AppKit.framework',
+            '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+            '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
+            ],
+            'libraries!': [
+            # Currently skia mac apps rely on Carbon and AGL for UI. Future
+            # apps should use Cocoa instead and dependencies on Carbon and AGL
+            # should eventually be removed
+            '$(SDKROOT)/System/Library/Frameworks/Carbon.framework',
+            '$(SDKROOT)/System/Library/Frameworks/AGL.framework',
+            ],
+          },
+          'xcode_settings' : {
+            'INFOPLIST_FILE' : '../experimental/FileReaderApp/FileReaderApp-Info.plist',
+          },
+          'mac_bundle_resources' : [
+            '../experimental/FileReaderApp/English.lproj/InfoPlist.strings',
+            '../experimental/FileReaderApp/English.lproj/MainMenu.xib',
+          ],
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp
new file mode 100644
index 0000000..c33c67f
--- /dev/null
+++ b/gyp/SampleApp.gyp
@@ -0,0 +1,306 @@
+{
+  '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?
+        '../gm',       # needed to pull gm.h
+        '../samplecode', # To pull SampleApp.h and SampleCode.h
+        '../src/pipe/utils', # For TiledPipeController
+      ],
+      'includes': [
+        'gmslides.gypi',
+      ],
+      'sources': [
+        '../gm/gm.cpp',
+        '../gm/gm.h',
+
+        '../samplecode/GMSampleView.h',
+        '../samplecode/ClockFaceView.cpp',
+        '../samplecode/OverView.cpp',
+        '../samplecode/OverView.h',
+        '../samplecode/Sample2PtRadial.cpp',
+        '../samplecode/SampleAAClip.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',
+        '../samplecode/SampleBigBlur.cpp',
+        '../samplecode/SampleBigGradient.cpp',
+        '../samplecode/SampleBitmapRect.cpp',
+        '../samplecode/SampleBlur.cpp',
+        '../samplecode/SampleCamera.cpp',
+        '../samplecode/SampleCircle.cpp',
+        '../samplecode/SampleClip.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/SampleEffects.cpp',
+        '../samplecode/SampleEmboss.cpp',
+        '../samplecode/SampleEmptyPath.cpp',
+        '../samplecode/SampleEncode.cpp',
+        '../samplecode/SampleFillType.cpp',
+        '../samplecode/SampleFilter.cpp',
+        '../samplecode/SampleFilter2.cpp',
+        '../samplecode/SampleFontCache.cpp',
+        '../samplecode/SampleFontScalerTest.cpp',
+        '../samplecode/SampleFuzz.cpp',
+        '../samplecode/SampleGradients.cpp',
+        '../samplecode/SampleHairCurves.cpp',
+        '../samplecode/SampleHairline.cpp',
+        '../samplecode/SampleHairModes.cpp',
+        '../samplecode/SampleLayerMask.cpp',
+        '../samplecode/SampleLayers.cpp',
+        '../samplecode/SampleLCD.cpp',
+        '../samplecode/SampleLines.cpp',
+        '../samplecode/SampleMeasure.cpp',
+        '../samplecode/SampleMipMap.cpp',
+        '../samplecode/SampleMovie.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/SampleShaders.cpp',
+        '../samplecode/SampleShaderText.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/SampleTextOnPath.cpp',
+        '../samplecode/SampleTextureDomain.cpp',
+        '../samplecode/SampleTiling.cpp',
+        '../samplecode/SampleTinyBitmap.cpp',
+        '../samplecode/SampleTriangles.cpp',
+        '../samplecode/SampleTypeface.cpp',
+        '../samplecode/SampleUnitMapper.cpp',
+        '../samplecode/SampleVertices.cpp',
+        '../samplecode/SampleXfermodesBlur.cpp',
+        '../samplecode/TransitionView.cpp',
+        '../samplecode/TransitionView.h',
+
+        # 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',
+
+        # 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
+        '../samplecode/SampleTests.cpp',   #includes unknown file SkShaderExtras.h
+        '../samplecode/SampleWarp.cpp',
+        '../samplecode/SampleFontCache.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+        'views.gyp:views',
+        'animator.gyp:animator',
+        'xml.gyp:xml',
+        'experimental.gyp:experimental',
+        'pdf.gyp:pdf',
+        'views_animated.gyp:views_animated',
+      ],
+      'conditions' : [
+       [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+         'sources!': [
+            '../samplecode/SampleDecode.cpp',
+         ],
+        }],
+        [ 'skia_os == "win"', {
+          'sources!': [
+            # require UNIX functions
+            '../samplecode/SampleEncode.cpp',
+            '../samplecode/SamplePageFlip.cpp',
+          ],
+        }],
+        [ 'skia_os == "mac"', {
+          'sources!': [
+            '../samplecode/SampleDecode.cpp',
+          ],
+          'sources': [
+            # Sample App specific files
+            '../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/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/views/mac/SampleApp-Info.plist',
+          },
+          'mac_bundle_resources' : [
+            '../src/views/mac/SampleApp.xib',
+          ],
+        }],
+        [ 'skia_os == "ios"', {
+          # TODO: This doesn't build properly yet, but it's getting there.
+          'sources!': [
+            '../samplecode/SampleDecode.cpp',
+          ],
+          'sources': [
+            '../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',
+
+            # 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/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',
+          ],
+          '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',
+            '../experimental/iOSSampleApp/iPhone/MainWindow_iPhone.xib',
+          ],
+        }],
+        [ 'skia_os == "android"', {
+          'sources!': [
+            '../samplecode/SampleAnimator.cpp',
+            '../samplecode/SampleUnitMapper.cpp',
+          ],
+          'dependencies!': [
+            'animator.gyp:animator',
+            'experimental.gyp:experimental',
+          ],
+          'dependencies': [
+            'android_system.gyp:SampleAppAndroid',
+          ],
+        }],
+        [ 'skia_gpu == 1', {
+          'include_dirs': [
+            '../src/gpu', # To pull gl/GrGLUtil.h
+          ],
+        }],
+      ],
+      'msvs_settings': {
+        'VCLinkerTool': {
+          'SubSystem': '2',
+        },
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/SimpleCocoaApp.gyp b/gyp/SimpleCocoaApp.gyp
new file mode 100644
index 0000000..3e17ad5
--- /dev/null
+++ b/gyp/SimpleCocoaApp.gyp
@@ -0,0 +1,60 @@
+{
+  'targets': [
+    {
+      'target_name': 'SimpleCocoaApp',
+      'type': 'executable',
+      'mac_bundle' : 1,
+      'include_dirs' : [
+        '../experimental/SimpleCocoaApp/',
+      ],
+      'sources': [
+        '../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': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'views.gyp:views',
+        'xml.gyp:xml',
+      ],
+      'conditions' : [
+        # Only supports Mac currently
+        [ 'skia_os == "mac"', {
+          'sources': [
+            '../include/utils/mac/SkCGUtils.h',
+            '../src/utils/mac/SkCreateCGImageRef.cpp',
+          ],
+          'link_settings': {
+            'libraries': [
+              '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
+            ],
+          },
+          'xcode_settings' : {
+            'INFOPLIST_FILE' : '../experimental/SimpleCocoaApp/SimpleApp-Info.plist',
+          },
+          'mac_bundle_resources' : [
+            '../experimental/SimpleCocoaApp/SimpleApp.xib',
+          ],
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
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
new file mode 100644
index 0000000..37844bb
--- /dev/null
+++ b/gyp/all.gyp
@@ -0,0 +1,41 @@
+# 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
new file mode 100644
index 0000000..a2a9a0a
--- /dev/null
+++ b/gyp/android_system.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_system.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/android.gypi',
+  ],
+}
diff --git a/gyp/angle.gyp b/gyp/angle.gyp
new file mode 100644
index 0000000..70ab5c2
--- /dev/null
+++ b/gyp/angle.gyp
@@ -0,0 +1,40 @@
+# 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',
+      },
+      '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
new file mode 100644
index 0000000..942e849
--- /dev/null
+++ b/gyp/animator.gyp
@@ -0,0 +1,195 @@
+#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.
+
+{
+  'targets': [
+    {
+      'target_name': 'animator',
+      'product_name': 'skia_animator',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../include/effects',
+        '../include/animator',
+        '../include/views',
+        '../include/xml',
+        '../include/utils',
+        '../include/images',
+        '../src/utils',
+      ],
+      'sources': [
+        '../include/animator/SkAnimator.h',
+        '../include/animator/SkAnimatorView.h',
+
+        '../src/animator/SkAnimate.h',
+        '../src/animator/SkAnimateActive.cpp',
+        '../src/animator/SkAnimateActive.h',
+        '../src/animator/SkAnimateBase.cpp',
+        '../src/animator/SkAnimateBase.h',
+        '../src/animator/SkAnimateField.cpp',
+        '../src/animator/SkAnimateMaker.cpp',
+        '../src/animator/SkAnimateMaker.h',
+        '../src/animator/SkAnimateProperties.h',
+        '../src/animator/SkAnimateSet.cpp',
+        '../src/animator/SkAnimateSet.h',
+        '../src/animator/SkAnimator.cpp',
+        '../src/animator/SkAnimatorScript.cpp',
+        '../src/animator/SkAnimatorScript.h',
+        #'../src/animator/SkAnimatorScript2.cpp', fails on windows
+        #'../src/animator/SkAnimatorScript2.h',
+        '../src/animator/SkBoundable.cpp',
+        '../src/animator/SkBoundable.h',
+        '../src/animator/SkBuildCondensedInfo.cpp',
+        #'../src/animator/SkCondensedDebug.cpp', fails on windows
+        #'../src/animator/SkCondensedRelease.cpp',
+        '../src/animator/SkDisplayable.cpp',
+        '../src/animator/SkDisplayable.h',
+        '../src/animator/SkDisplayAdd.cpp',
+        '../src/animator/SkDisplayAdd.h',
+        '../src/animator/SkDisplayApply.cpp',
+        '../src/animator/SkDisplayApply.h',
+        '../src/animator/SkDisplayBounds.cpp',
+        '../src/animator/SkDisplayBounds.h',
+        '../src/animator/SkDisplayEvent.cpp',
+        '../src/animator/SkDisplayEvent.h',
+        '../src/animator/SkDisplayEvents.cpp',
+        '../src/animator/SkDisplayEvents.h',
+        '../src/animator/SkDisplayInclude.cpp',
+        '../src/animator/SkDisplayInclude.h',
+        '../src/animator/SkDisplayInput.cpp',
+        '../src/animator/SkDisplayInput.h',
+        '../src/animator/SkDisplayList.cpp',
+        '../src/animator/SkDisplayList.h',
+        '../src/animator/SkDisplayMath.cpp',
+        '../src/animator/SkDisplayMath.h',
+        '../src/animator/SkDisplayMovie.cpp',
+        '../src/animator/SkDisplayMovie.h',
+        '../src/animator/SkDisplayNumber.cpp',
+        '../src/animator/SkDisplayNumber.h',
+        '../src/animator/SkDisplayPost.cpp',
+        '../src/animator/SkDisplayPost.h',
+        '../src/animator/SkDisplayRandom.cpp',
+        '../src/animator/SkDisplayRandom.h',
+        '../src/animator/SkDisplayScreenplay.cpp',
+        '../src/animator/SkDisplayScreenplay.h',
+        '../src/animator/SkDisplayType.cpp',
+        '../src/animator/SkDisplayType.h',
+        '../src/animator/SkDisplayTypes.cpp',
+        '../src/animator/SkDisplayTypes.h',
+        '../src/animator/SkDisplayXMLParser.cpp',
+        '../src/animator/SkDisplayXMLParser.h',
+        '../src/animator/SkDraw3D.cpp',
+        '../src/animator/SkDraw3D.h',
+        '../src/animator/SkDrawable.cpp',
+        '../src/animator/SkDrawable.h',
+        '../src/animator/SkDrawBitmap.cpp',
+        '../src/animator/SkDrawBitmap.h',
+        '../src/animator/SkDrawBlur.cpp',
+        '../src/animator/SkDrawBlur.h',
+        '../src/animator/SkDrawClip.cpp',
+        '../src/animator/SkDrawClip.h',
+        '../src/animator/SkDrawColor.cpp',
+        '../src/animator/SkDrawColor.h',
+        '../src/animator/SkDrawDash.cpp',
+        '../src/animator/SkDrawDash.h',
+        '../src/animator/SkDrawDiscrete.cpp',
+        '../src/animator/SkDrawDiscrete.h',
+        '../src/animator/SkDrawEmboss.cpp',
+        '../src/animator/SkDrawEmboss.h',
+        '../src/animator/SkDrawExtraPathEffect.cpp',
+        '../src/animator/SkDrawFull.cpp',
+        '../src/animator/SkDrawFull.h',
+        '../src/animator/SkDrawGradient.cpp',
+        '../src/animator/SkDrawGradient.h',
+        '../src/animator/SkDrawGroup.cpp',
+        '../src/animator/SkDrawGroup.h',
+        '../src/animator/SkDrawLine.cpp',
+        '../src/animator/SkDrawLine.h',
+        '../src/animator/SkDrawMatrix.cpp',
+        '../src/animator/SkDrawMatrix.h',
+        '../src/animator/SkDrawOval.cpp',
+        '../src/animator/SkDrawOval.h',
+        '../src/animator/SkDrawPaint.cpp',
+        '../src/animator/SkDrawPaint.h',
+        '../src/animator/SkDrawPath.cpp',
+        '../src/animator/SkDrawPath.h',
+        '../src/animator/SkDrawPoint.cpp',
+        '../src/animator/SkDrawPoint.h',
+        '../src/animator/SkDrawRectangle.cpp',
+        '../src/animator/SkDrawRectangle.h',
+        '../src/animator/SkDrawSaveLayer.cpp',
+        '../src/animator/SkDrawSaveLayer.h',
+        '../src/animator/SkDrawShader.cpp',
+        '../src/animator/SkDrawShader.h',
+        '../src/animator/SkDrawText.cpp',
+        '../src/animator/SkDrawText.h',
+        '../src/animator/SkDrawTextBox.cpp',
+        '../src/animator/SkDrawTextBox.h',
+        '../src/animator/SkDrawTo.cpp',
+        '../src/animator/SkDrawTo.h',
+        '../src/animator/SkDrawTransparentShader.cpp',
+        '../src/animator/SkDrawTransparentShader.h',
+        '../src/animator/SkDump.cpp',
+        '../src/animator/SkDump.h',
+        '../src/animator/SkExtras.h',
+        '../src/animator/SkGetCondensedInfo.cpp',
+        '../src/animator/SkHitClear.cpp',
+        '../src/animator/SkHitClear.h',
+        '../src/animator/SkHitTest.cpp',
+        '../src/animator/SkHitTest.h',
+        '../src/animator/SkIntArray.h',
+        '../src/animator/SkMatrixParts.cpp',
+        '../src/animator/SkMatrixParts.h',
+        '../src/animator/SkMemberInfo.cpp',
+        '../src/animator/SkMemberInfo.h',
+        '../src/animator/SkOpArray.cpp',
+        '../src/animator/SkOpArray.h',
+        '../src/animator/SkOperand.h',
+        '../src/animator/SkOperand2.h',
+        '../src/animator/SkOperandInterpolator.h',
+        '../src/animator/SkOperandIterpolator.cpp',
+        '../src/animator/SkPaintParts.cpp',
+        '../src/animator/SkPaintParts.h',
+        '../src/animator/SkParseSVGPath.cpp',
+        '../src/animator/SkPathParts.cpp',
+        '../src/animator/SkPathParts.h',
+        '../src/animator/SkPostParts.cpp',
+        '../src/animator/SkPostParts.h',
+        '../src/animator/SkScript.cpp',
+        '../src/animator/SkScript.h',
+        '../src/animator/SkScript2.h',
+        '../src/animator/SkScriptCallBack.h',
+        '../src/animator/SkScriptDecompile.cpp',
+        '../src/animator/SkScriptRuntime.cpp',
+        '../src/animator/SkScriptRuntime.h',
+        '../src/animator/SkScriptTokenizer.cpp',
+        '../src/animator/SkSnapshot.cpp',
+        '../src/animator/SkSnapshot.h',
+        '../src/animator/SkTDArray_Experimental.h',
+        '../src/animator/SkTextOnPath.cpp',
+        '../src/animator/SkTextOnPath.h',
+        '../src/animator/SkTextToPath.cpp',
+        '../src/animator/SkTextToPath.h',
+        '../src/animator/SkTime.cpp',
+        '../src/animator/SkTypedArray.cpp',
+        '../src/animator/SkTypedArray.h',
+        '../src/animator/SkXMLAnimatorWriter.cpp',
+        '../src/animator/SkXMLAnimatorWriter.h',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../include/animator',
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/apptype_console.gypi b/gyp/apptype_console.gypi
new file mode 100644
index 0000000..80e925d
--- /dev/null
+++ b/gyp/apptype_console.gypi
@@ -0,0 +1,77 @@
+# target_defaults used for executable targets that generate a console app
+{
+  'target_defaults': {
+    'msvs_settings': {
+      'VCLinkerTool': {
+        #Allows for creation / output to console.
+        #Console (/SUBSYSTEM:CONSOLE)
+        'SubSystem': '1',
+
+        #Console app, use main/wmain
+        'EntryPointSymbol': 'mainCRTStartup',
+      },
+    },
+    'conditions': [
+      [ 'skia_os == "android"', {
+        'dependencies': [
+          'android_deps.gyp:Android_EntryPoint',
+        ],
+      }],
+      ['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',
+        },
+      }],
+    ],
+  },
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/bench.gyp b/gyp/bench.gyp
new file mode 100644
index 0000000..1524b72
--- /dev/null
+++ b/gyp/bench.gyp
@@ -0,0 +1,94 @@
+# GYP file to build performance testbench.
+#
+{
+  'includes': [
+    'apptype_console.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'bench',
+      'type': 'executable',
+      'include_dirs' : [
+        '../src/core',
+      ],
+      'includes': [
+        'bench.gypi'
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+        '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',
+          ],
+        }],
+      ],
+    }
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/bench.gypi b/gyp/bench.gypi
new file mode 100644
index 0000000..6f630d6
--- /dev/null
+++ b/gyp/bench.gypi
@@ -0,0 +1,56 @@
+# sources and conditions used in skia's bench.gyp and chromium's skia.gyp
+#
+{
+  'sources': [
+    '../bench/benchmain.cpp',
+    '../bench/SkBenchmark.h',
+    '../bench/SkBenchmark.cpp',
+
+    '../bench/AAClipBench.cpp',
+    '../bench/BitmapBench.cpp',
+    '../bench/BitmapRectBench.cpp',
+    '../bench/BlurBench.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/MathBench.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/RepeatTileBench.cpp',
+    '../bench/RTreeBench.cpp',
+    '../bench/ScalarBench.cpp',
+    '../bench/ShaderMaskBench.cpp',
+    '../bench/TableBench.cpp',
+    '../bench/TextBench.cpp',
+    '../bench/VertBench.cpp',
+    '../bench/WriterBench.cpp',
+
+    '../bench/SkBenchLogger.h',
+    '../bench/SkBenchLogger.cpp',
+    '../bench/TimerData.h',
+    '../bench/TimerData.cpp',
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/common.gypi b/gyp/common.gypi
new file mode 100644
index 0000000..b620226
--- /dev/null
+++ b/gyp/common.gypi
@@ -0,0 +1,94 @@
+# 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 file is automatically included by gyp_skia when building any target.
+
+{
+  'includes': [
+    'common_variables.gypi',
+  ],
+
+  'target_defaults': {
+    'defines': [
+      'SK_GAMMA_SRGB',
+      'SK_GAMMA_APPLY_TO_A8',
+    ],
+
+    # Validate the 'skia_os' setting against 'OS', because only certain
+    # combinations work.  You should only override 'skia_os' for certain
+    # 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_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)'
+        }],
+      ],
+    },
+    'includes': [
+      'common_conditions.gypi',
+    ],
+    'conditions': [
+      [ 'skia_scalar == "float"',
+        {
+          'defines': [
+            'SK_SCALAR_IS_FLOAT',
+            'SK_CAN_USE_FLOAT',
+          ],
+        }, { # else, skia_scalar != "float"
+          'defines': [
+            'SK_SCALAR_IS_FIXED',
+            'SK_CAN_USE_FLOAT',  # we can still use floats along the way
+          ],
+        }
+      ],
+      [ 'skia_mesa', {
+        'defines': [
+          'SK_MESA',
+        ],
+        'direct_dependent_settings': {
+          'defines': [
+            'SK_MESA',
+          ],
+        },
+      }],
+      [ 'skia_angle', {
+        'defines': [
+          'SK_ANGLE',
+        ],
+        'direct_dependent_settings': {
+          'defines': [
+            'SK_ANGLE',
+          ],
+        },
+      }],
+    ],
+    'configurations': {
+      'Debug': {
+        'defines': [
+          'SK_DEBUG',
+          'GR_DEBUG=1',
+        ],
+      },
+      'Release': {
+        'defines': [
+          'SK_RELEASE',
+          'GR_RELEASE=1',
+        ],
+      },
+    },
+  }, # end 'target_defaults'
+}
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/common_conditions.gypi b/gyp/common_conditions.gypi
new file mode 100644
index 0000000..70b4566
--- /dev/null
+++ b/gyp/common_conditions.gypi
@@ -0,0 +1,337 @@
+# 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' ],
+          },
+          'VCLinkerTool': {
+            'AdditionalDependencies': [
+              'OpenGL32.lib',
+              'usp10.lib',
+            ],
+          },
+        },
+        'configurations': {
+          'Debug': {
+            'msvs_settings': {
+              'VCCLCompilerTool': {
+                'DebugInformationFormat': '4', # editAndContiue (/ZI)
+                'ProgramDataBaseFileName': '$(OutDir)\\$(ProjectName).pdb',
+                'Optimization': '0',           # optimizeDisabled (/Od)
+                'PreprocessorDefinitions': ['_DEBUG'],
+                'RuntimeLibrary': '3',         # rtMultiThreadedDebugDLL (/MDd)
+                'ExceptionHandling': '0',
+                'RuntimeTypeInfo': 'false',      # /GR-
+                'WarningLevel': '3',             # level3 (/W3)
+              },
+              'VCLinkerTool': {
+                'GenerateDebugInformation': 'true', # /DEBUG
+                'LinkIncremental': '2',             # /INCREMENTAL
+              },
+            },
+          },
+          'Release': {
+            'msvs_settings': {
+              'VCCLCompilerTool': {
+                'DebugInformationFormat': '3',      # programDatabase (/Zi)
+                'ProgramDataBaseFileName': '$(OutDir)\\$(ProjectName).pdb',
+                '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)
+                'PreprocessorDefinitions': ['NDEBUG'],
+                'RuntimeLibrary': '2',              # rtMultiThreadedDLL (/MD)
+                'ExceptionHandling': '0',
+                'EnableEnhancedInstructionSet': '2',# /arch:SSE2
+                'RuntimeTypeInfo': 'false',         # /GR-
+                'WarningLevel': '3',                # level3 (/W3)
+              },
+              'VCLinkerTool': {
+                'GenerateDebugInformation': 'true', # /DEBUG
+                'LinkTimeCodeGeneration': '1',      # useLinkTimeCodeGeneration /LTCG
+              },
+              'VCLibrarianTool': {
+                'LinkTimeCodeGeneration': 'true',   # useLinkTimeCodeGeneration /LTCG
+              },
+            },
+          },
+        },
+        'conditions' : [
+          ['skia_arch_width == 64', {
+            'msvs_configuration_platform': 'x64'
+          }],
+          ['skia_arch_width == 32', {
+            'msvs_configuration_platform': 'Win32',
+          }],
+        ],
+      },
+    ],
+
+    ['skia_os in ["linux", "freebsd", "openbsd", "solaris"]',
+      {
+        'defines': [
+          'SK_SAMPLES_FOR_X',
+          'SK_BUILD_FOR_UNIX',
+        ],
+        'configurations': {
+          'Debug': {
+            'cflags': ['-g']
+          },
+          'Release': {
+            'cflags': ['-O3 -g'],
+            'defines': [ 'NDEBUG' ],
+          },
+        },
+        'cflags': [
+          # TODO(tony): Enable -Werror once all the strict-aliasing problems
+          # are fixed.
+          #'-Werror',
+          '-Wall',
+          '-Wextra',
+          '-Wno-unused',
+          # suppressions below here were added for clang
+          '-Wno-unused-parameter',
+          '-Wno-c++11-extensions'
+        ],
+        'conditions' : [
+          ['skia_arch_width == 64', {
+            'cflags': [
+              '-m64',
+            ],
+            'ldflags': [
+              '-m64',
+            ],
+          }],
+          ['skia_arch_width == 32', {
+            'cflags': [
+              '-m32',
+            ],
+            'ldflags': [
+              '-m32',
+            ],
+          }],
+        ],
+        'include_dirs' : [
+          '/usr/include/freetype2',
+        ],
+      },
+    ],
+
+    ['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',
+            },
+          }],
+        ],
+        'configurations': {
+          'Debug': {
+            'xcode_settings': {
+              'GCC_OPTIMIZATION_LEVEL': '0',
+            },
+          },
+          'Release': {
+            'xcode_settings': {
+              'GCC_OPTIMIZATION_LEVEL': '3',
+            },
+            'defines': [ 'NDEBUG' ],
+          },
+        },
+        'xcode_settings': {
+          '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"',
+      {
+        'defines': [
+          'SK_BUILD_FOR_IOS',
+        ],
+        'configurations': {
+          'Debug': {
+            'xcode_settings': {
+              'GCC_OPTIMIZATION_LEVEL': '0',
+            },
+          },
+          'Release': {
+            'xcode_settings': {
+              'GCC_OPTIMIZATION_LEVEL': '3',
+            },
+            'defines': [ 'NDEBUG' ],
+          },
+        },
+        'xcode_settings': {
+          '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"',
+      {
+        'defines': [
+          'SK_BUILD_FOR_ANDROID',
+          'SK_BUILD_FOR_ANDROID_NDK',
+        ],
+        'configurations': {
+          'Debug': {
+            'cflags': ['-g']
+          },
+          'Release': {
+            'cflags': ['-O2'],
+            'defines': [ 'NDEBUG' ],
+          },
+        },
+        'libraries': [
+          '-lstdc++',
+          '-lm',
+          '-llog',
+        ],
+        'cflags': [
+          '-fno-exceptions',
+          '-fno-rtti',
+          '-fuse-ld=gold',
+          '--sysroot=<(android_base)/toolchains/<(android_toolchain)/sysroot',
+        ],
+        'include_dirs' : [
+          '<(android_base)/toolchains/<(android_toolchain)/lib/gcc/arm-linux-androideabi/4.6.x-google/include',
+          '<(android_base)/toolchains/<(android_toolchain)/lib/gcc/arm-linux-androideabi/4.6.x-google/include-fixed',
+          '<(android_base)/toolchains/<(android_toolchain)/arm-linux-androideabi/include/c++/4.6',
+          '<(android_base)/toolchains/<(android_toolchain)/arm-linux-androideabi/include/c++/4.6/arm-linux-androideabi',
+          '<(android_base)/toolchains/<(android_toolchain)/sysroot/usr/include',
+        ],
+        'conditions': [
+          [ 'skia_arch_type == "arm"', {
+            'ldflags': [
+              '-Wl',
+            ],
+          }],
+          [ 'skia_arch_type == "arm" and arm_thumb == 1', {
+            'cflags': [
+              '-mthumb',
+            ],
+          }],
+          [ 'skia_arch_type == "arm" and armv7 == 1', {
+            'variables': {
+              'arm_neon_optional%': 0,
+            },
+            'defines': [
+              '__ARM_ARCH__=7',
+            ],
+            'cflags': [
+              '-march=armv7-a',
+              '-mfloat-abi=softfp',
+            ],
+            'conditions': [
+              [ 'arm_neon == 1', {
+                'defines': [
+                  '__ARM_HAVE_NEON',
+                ],
+                'cflags': [
+                  '-mfpu=neon',
+                ],
+              }],
+              [ 'arm_neon_optional == 1', {
+                'defines': [
+                  '__ARM_HAVE_OPTIONAL_NEON_SUPPORT',
+                ],
+              }],
+            ],
+         }],
+        ],
+      },
+    ],
+
+    # We can POD-style initialization of static mutexes to avoid generating
+    # static initializers if we're using a pthread-compatible thread interface.
+    [ 'skia_os != "win"', {
+      'defines': [
+        '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:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/common_variables.gypi b/gyp/common_variables.gypi
new file mode 100644
index 0000000..3d19e62
--- /dev/null
+++ b/gyp/common_variables.gypi
@@ -0,0 +1,120 @@
+# 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.
+
+{
+  # 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_angle%': 0,
+      'skia_directwrite%': 0,
+      'skia_nacl%': 0,
+      'skia_gpu%': 1,
+      'skia_osx_sdkroot%': 'macosx',
+    },
+
+    # 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_angle%': '<(skia_angle)',
+    'skia_arch_width%': '<(skia_arch_width)',
+    'skia_arch_type%': '<(skia_arch_type)',
+    'skia_directwrite%': '<(skia_directwrite)',
+    'skia_nacl%': '<(skia_nacl)',
+    'skia_gpu%': '<(skia_gpu)',
+    'skia_osx_sdkroot%': '<(skia_osx_sdkroot)',
+    '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
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/core.gyp b/gyp/core.gyp
new file mode 100644
index 0000000..228fd06
--- /dev/null
+++ b/gyp/core.gyp
@@ -0,0 +1,135 @@
+# Core Skia library code.
+{
+  'targets': [
+    {
+      'target_name': 'core',
+      'product_name': 'skia_core',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'msvs_guid': 'B7760B5E-BFA8-486B-ACFD-49E3A6DE8E76',
+
+      'includes': [
+        'core.gypi',
+      ],
+
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../include/pipe',
+        '../include/ports',
+        '../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': [
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+          'cflags': [
+            '-Wno-unused',
+            '-Wno-unused-function',
+          ],
+          'link_settings': {
+            'libraries': [
+              '-lpthread',
+            ],
+          },
+        }],
+        [ 'skia_os == "mac"', {
+          'include_dirs': [
+            '../include/utils/mac',
+            '../third_party/freetype/include/**',
+          ],
+          'sources': [
+            '../include/utils/mac/SkCGUtils.h',
+          ],
+          'link_settings': {
+            'libraries': [
+              '$(SDKROOT)/System/Library/Frameworks/ApplicationServices.framework',
+            ],
+          },
+        }],
+        [ 'skia_os == "ios"', {
+          'include_dirs': [
+            '../include/utils/ios',
+          ],
+          'sources': [
+            '../include/utils/mac/SkCGUtils.h',
+          ],
+          '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',
+            ],
+          },
+        }],
+        [ 'skia_os == "win"', {
+          'include_dirs': [
+            'config/win',
+          ],
+          'sources!': [
+            '../include/core/SkMMapStream.h',
+            '../src/core/SkMMapStream.cpp',
+          ],
+        }],
+        [ 'skia_os == "android"', {
+          'dependencies': [
+             'android_deps.gyp:ft2',
+          ],
+        }],
+        [ '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',
+          ],
+        }],
+      ],
+      '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'
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/core.gypi b/gyp/core.gypi
new file mode 100644
index 0000000..d8f1715
--- /dev/null
+++ b/gyp/core.gypi
@@ -0,0 +1,277 @@
+# 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/SkConcaveToTriangles.cpp',
+        '<(skia_src_path)/core/SkConcaveToTriangles.h',
+        '<(skia_src_path)/core/SkConfig8888.cpp',
+        '<(skia_src_path)/core/SkConfig8888.h',
+        '<(skia_src_path)/core/SkCordic.cpp',
+        '<(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/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/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/SkRTree.h',
+        '<(skia_src_path)/core/SkRTree.cpp',
+        '<(skia_src_path)/core/SkScalar.cpp',
+        '<(skia_src_path)/core/SkScalerContext.cpp',
+        '<(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/SkStroke.h',
+        '<(skia_src_path)/core/SkStroke.cpp',
+        '<(skia_src_path)/core/SkStrokerPriv.cpp',
+        '<(skia_src_path)/core/SkStrokerPriv.h',
+        '<(skia_src_path)/core/SkTextFormatParams.h',
+        '<(skia_src_path)/core/SkTLS.cpp',
+        '<(skia_src_path)/core/SkTSearch.cpp',
+        '<(skia_src_path)/core/SkTSort.h',
+        '<(skia_src_path)/core/SkTemplatesPriv.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/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/SkRandom.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/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/SkTArray.h',
+        '<(skia_include_path)/core/SkTDArray.h',
+        '<(skia_include_path)/core/SkTDStack.h',
+        '<(skia_include_path)/core/SkTDict.h',
+        '<(skia_include_path)/core/SkTDLinkedList.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..3578223
--- /dev/null
+++ b/gyp/debugger.gyp
@@ -0,0 +1,161 @@
+{
+  '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
+        '<@(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',
+
+        # 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_SkGLWidget.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'images.gyp:images',
+        'effects.gyp:effects',
+        '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)/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
new file mode 100644
index 0000000..e110c9f
--- /dev/null
+++ b/gyp/effects.gyp
@@ -0,0 +1,41 @@
+{
+  'targets': [
+    {
+      'target_name': 'effects',
+      'product_name': 'skia_effects',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'includes': [
+        'effects.gypi',
+      ],
+      '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',
+          ],
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/effects.gypi b/gyp/effects.gypi
new file mode 100644
index 0000000..aef7f2c
--- /dev/null
+++ b/gyp/effects.gypi
@@ -0,0 +1,106 @@
+# 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/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/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/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/SkMorphologyImageFilter.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/SkDrawExtraPathEffect.h',
+    '<(skia_include_path)/effects/SkEmbossMaskFilter.h',
+    '<(skia_include_path)/effects/SkGradientShader.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/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/experimental.gyp b/gyp/experimental.gyp
new file mode 100644
index 0000000..09711ac
--- /dev/null
+++ b/gyp/experimental.gyp
@@ -0,0 +1,28 @@
+{
+  'targets': [
+    {
+      'target_name': 'experimental',
+      'type': 'static_library',
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+      ],
+      'sources': [
+        '../experimental/SkSetPoly3To3.cpp',
+        '../experimental/SkSetPoly3To3_A.cpp',
+        '../experimental/SkSetPoly3To3_D.cpp',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../experimental',
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/freetype.gyp b/gyp/freetype.gyp
new file mode 100644
index 0000000..4a885a1
--- /dev/null
+++ b/gyp/freetype.gyp
@@ -0,0 +1,66 @@
+{
+  'targets': [
+    {
+      'target_name': 'skfreetype',
+      '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',
+
+# 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',
+      ],
+      'include_dirs': [
+        '../third_party/freetype/internal',
+        '../third_party/freetype/builds',
+        '../third_party/freetype/include',
+        '../third_party/freetype',
+      ],
+      'cflags': [
+        '-W',
+        '-Wall',
+        '-fPIC',
+        '-DPIC',
+        '-DDARWIN_NO_CARBON',
+        '-DFT2_BUILD_LIBRARY',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../third_party/freetype/include',  # For ft2build.h
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/gm.gyp b/gyp/gm.gyp
new file mode 100644
index 0000000..6f0fc3d
--- /dev/null
+++ b/gyp/gm.gyp
@@ -0,0 +1,64 @@
+# GYP file to build the "gm" (golden master) executable.
+{
+  'includes': [
+    'apptype_console.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'gm',
+      'type': 'executable',
+      'include_dirs' : [
+        '../src/core',
+        '../src/pipe/utils/',
+      ],
+      'includes': [
+        'gmslides.gypi',
+      ],
+      'sources': [
+        '../gm/gm.cpp',
+        '../gm/gmmain.cpp',
+        '../gm/system_preferences_default.cpp',
+        '../src/pipe/utils/SamplePipeControllers.h',
+        '../src/pipe/utils/SamplePipeControllers.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+        'pdf.gyp:pdf',
+      ],
+      'conditions': [
+        ['skia_os == "mac"', {
+          'sources!': [
+            '../gm/system_preferences_default.cpp',
+          ],
+          'sources': [
+            '../gm/system_preferences_mac.mm',
+          ],
+          'link_settings': {
+            'libraries': [
+              '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+              '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+            ],
+          },
+        }],
+        ['skia_os == "win"', {
+          'dependencies': [
+            'xps.gyp:xps',
+          ],
+        }],
+        ['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/gmslides.gypi b/gyp/gmslides.gypi
new file mode 100644
index 0000000..46ec34d
--- /dev/null
+++ b/gyp/gmslides.gypi
@@ -0,0 +1,89 @@
+# include this gypi to include all the golden master slides.
+{
+  'sources': [
+    '../gm/aaclip.cpp',
+    '../gm/aarectmodes.cpp',
+    '../gm/arithmode.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/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/drawbitmaprect.cpp',
+    '../gm/drawlooper.cpp',
+    '../gm/extractbitmap.cpp',
+    '../gm/emptypath.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/morphology.cpp',
+    '../gm/ninepatchstretch.cpp',
+    '../gm/nocolorbleed.cpp',
+    '../gm/patheffects.cpp',
+    '../gm/pathfill.cpp',
+    '../gm/pathreverse.cpp',
+    '../gm/points.cpp',
+    '../gm/poly2poly.cpp',
+    '../gm/quadpaths.cpp',
+    '../gm/samplerstress.cpp',
+    '../gm/shaderbounds.cpp',
+    '../gm/shadertext.cpp',
+    '../gm/shadertext2.cpp',
+    '../gm/shadertext3.cpp',
+    '../gm/shadows.cpp',
+    '../gm/simpleaaclip.cpp',
+    '../gm/strokefill.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',
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
new file mode 100644
index 0000000..0dc84ee
--- /dev/null
+++ b/gyp/gpu.gyp
@@ -0,0 +1,279 @@
+{
+  'target_defaults': {
+    'conditions': [
+      ['skia_os != "win"', {
+        'sources/': [ ['exclude', '_win.(h|cpp)$'],
+        ],
+      }],
+      ['skia_os != "mac"', {
+        'sources/': [ ['exclude', '_mac.(h|cpp|m|mm)$'],
+        ],
+      }],
+      ['skia_os != "linux"', {
+        'sources/': [ ['exclude', '_unix.(h|cpp)$'],
+        ],
+      }],
+      ['skia_os != "ios"', {
+        'sources/': [ ['exclude', '_iOS.(h|cpp|m|mm)$'],
+        ],
+      }],
+      ['skia_os != "android"', {
+        'sources/': [ ['exclude', '_android.(h|cpp)$'],
+        ],
+      }],
+      [ 'skia_os == "android"', {
+        'defines': [
+          'GR_ANDROID_BUILD=1',
+        ],
+      }],
+      [ 'skia_os == "mac"', {
+        'defines': [
+          'GR_MAC_BUILD=1',
+        ],
+      }],
+      [ 'skia_os == "linux"', {
+        'defines': [
+          'GR_LINUX_BUILD=1',
+        ],
+      }],
+      [ 'skia_os == "ios"', {
+        'defines': [
+          'GR_IOS_BUILD=1',
+        ],
+      }],
+      [ 'skia_os == "win"', {
+        'defines': [
+          'GR_WIN32_BUILD=1',
+        ],
+      }],
+      # 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', '.*'],
+          ],
+        },
+      }],
+    ],
+    'direct_dependent_settings': {
+      'conditions': [
+        [ 'skia_os == "android"', {
+          'defines': [
+            'GR_ANDROID_BUILD=1',
+          ],
+        }],
+        [ 'skia_os == "mac"', {
+          'defines': [
+            'GR_MAC_BUILD=1',
+          ],
+        }],
+        [ 'skia_os == "linux"', {
+          'defines': [
+            'GR_LINUX_BUILD=1',
+          ],
+        }],
+        [ 'skia_os == "ios"', {
+          'defines': [
+            'GR_IOS_BUILD=1',
+          ],
+        }],
+        [ 'skia_os == "win"', {
+          'defines': [
+            'GR_WIN32_BUILD=1',
+            'GR_GL_FUNCTION_TYPE=__stdcall',
+          ],
+        }],
+      ],
+      'include_dirs': [
+        '../include/gpu',
+      ],
+    },
+  },
+  'targets': [
+    {
+      'target_name': 'skgr',
+      'product_name': 'skia_skgr',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'includes': [
+        'gpu.gypi',
+      ],
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../src/core',
+        '../include/gpu',
+        '../src/gpu',
+      ],
+      'dependencies': [
+        'angle.gyp:*',
+      ],
+      'export_dependent_settings': [
+        'angle.gyp:*',
+      ],
+      'sources': [
+        '<@(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/gl/mesa/SkMesaGLContext.cpp',
+          ],
+        }],
+        [ 'skia_mesa and skia_os == "mac"', {
+          'include_dirs': [
+             '$(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',
+      'standalone_static_library': 1,
+      'includes': [
+        'gpu.gypi',
+      ],
+     'include_dirs': [
+        '../include/core',
+        '../include/config',
+        '../include/gpu',
+        '../src/core', # SkRasterClip.h
+        '../src/gpu'
+      ],
+      'dependencies': [
+        'angle.gyp:*',
+      ],
+      'export_dependent_settings': [
+        'angle.gyp:*',
+      ],
+      'sources': [
+        '<@(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_os == "linux"', {
+          'sources!': [
+            '../src/gpu/gl/GrGLDefaultInterface_none.cpp',
+            '../src/gpu/gl/GrGLCreateNativeInterface_none.cpp',
+          ],
+          'link_settings': {
+            'libraries': [
+              '-lGL',
+              '-lX11',
+            ],
+          },
+        }],
+        [ 'skia_mesa and skia_os == "linux"', {
+          'link_settings': {
+            'libraries': [
+              '-lOSMesa',
+            ],
+          },
+        }],
+        [ 'skia_os == "mac"', {
+          'link_settings': {
+            'libraries': [
+              '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
+            ],
+          },
+          'sources!': [
+            '../src/gpu/gl/GrGLDefaultInterface_none.cpp',
+            '../src/gpu/gl/GrGLCreateNativeInterface_none.cpp',
+          ],
+        }],
+        [ 'skia_mesa and skia_os == "mac"', {
+          'link_settings': {
+            'libraries': [
+              '$(SDKROOT)/usr/X11/lib/libOSMesa.dylib',
+            ],
+          },
+          'include_dirs': [
+             '$(SDKROOT)/usr/X11/include/',
+          ],
+        }],
+        [ 'not skia_mesa', {
+          'sources!': [
+            '../src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp',
+          ],
+        }],
+        [ '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',
+            '../src/gpu/gl/GrGLCreateNativeInterface_none.cpp',
+          ],
+          'link_settings': {
+            'libraries': [
+              '-lGLESv2',
+              '-lEGL',
+            ],
+          },
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
new file mode 100644
index 0000000..c0a4a7b
--- /dev/null
+++ b/gyp/gpu.gypi
@@ -0,0 +1,266 @@
+# 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/GrCacheID.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/GrCustomStage.h',
+      '<(skia_include_path)/gpu/GrCustomStageUnitTest.h',
+      '<(skia_include_path)/gpu/GrFontScaler.h',
+      '<(skia_include_path)/gpu/GrGlyph.h',
+      '<(skia_include_path)/gpu/GrInstanceCounter.h',
+      '<(skia_include_path)/gpu/GrKey.h',
+      '<(skia_include_path)/gpu/GrMatrix.h',
+      '<(skia_include_path)/gpu/GrNoncopyable.h',
+      '<(skia_include_path)/gpu/GrPaint.h',
+      '<(skia_include_path)/gpu/GrPoint.h',
+      '<(skia_include_path)/gpu/GrProgramStageFactory.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/GrSamplerState.h',
+      '<(skia_include_path)/gpu/GrScalar.h',
+      '<(skia_include_path)/gpu/GrSurface.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/GrCustomStage.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/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/GrMatrix.cpp',
+      '<(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/GrPathRendererChain.h',
+      '<(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/GrRandom.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/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/GrTDArray.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/GrColorTableEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrColorTableEffect.h',
+      '<(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/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/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/GrGLProgramStage.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLProgramStage.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)/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/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/images.gyp b/gyp/images.gyp
new file mode 100644
index 0000000..2989afe
--- /dev/null
+++ b/gyp/images.gyp
@@ -0,0 +1,141 @@
+{
+  '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/SkImageDecoder.h',
+        '../include/images/SkImageEncoder.h',
+        '../include/images/SkImageRef.h',
+        '../include/images/SkImageRef_GlobalPool.h',
+        '../include/images/SkJpegUtility.h',
+        '../include/images/SkMovie.h',
+        '../include/images/SkPageFlipper.h',
+
+        '../src/images/bmpdecoderhelper.cpp',
+        '../src/images/bmpdecoderhelper.h',
+        '../src/images/SkFDStream.cpp',
+        '../src/images/SkFlipPixelRef.cpp',
+        '../src/images/SkImageDecoder.cpp',
+        '../src/images/SkImageDecoder_Factory.cpp',
+        '../src/images/SkImageDecoder_libjpeg.cpp',
+        '../src/images/SkImageDecoder_libbmp.cpp',
+        '../src/images/SkImageDecoder_libgif.cpp',
+        '../src/images/SkImageDecoder_libico.cpp',
+        '../src/images/SkImageDecoder_libpng.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/SkImageRefPool.h',
+        '../src/images/SkImageRef_GlobalPool.cpp',
+        '../src/images/SkImages.cpp',
+        '../src/images/SkJpegUtility.cpp',
+        '../src/images/SkMovie.cpp',
+        '../src/images/SkMovie_gif.cpp',
+        '../src/images/SkPageFlipper.cpp',
+        '../src/images/SkScaledBitmapSampler.cpp',
+        '../src/images/SkScaledBitmapSampler.h',
+
+        '../src/ports/SkImageDecoder_CG.cpp',
+        '../src/ports/SkImageDecoder_WIC.cpp',
+      ],
+      'conditions': [
+        [ 'skia_os == "win"', {
+          'sources!': [
+            '../src/images/SkFDStream.cpp',
+            '../src/images/SkImageDecoder_Factory.cpp',
+            '../src/images/SkImageDecoder_libgif.cpp',
+            '../src/images/SkImageDecoder_libpng.cpp',
+            '../src/images/SkImageEncoder_Factory.cpp',
+            '../src/images/SkMovie_gif.cpp',
+          ],
+          'link_settings': {
+            'libraries': [
+              'windowscodecs.lib',
+            ],
+          },
+        },{ #else if skia_os != win
+          'sources!': [
+            '../src/ports/SkImageDecoder_WIC.cpp',
+          ],
+        }],
+        [ 'skia_os in ["mac", "ios"]', {
+          'sources!': [
+            '../src/images/SkImageDecoder_Factory.cpp',
+            '../src/images/SkImageDecoder_libpng.cpp',
+            '../src/images/SkImageDecoder_libgif.cpp',
+            '../src/images/SkImageEncoder_Factory.cpp',
+            '../src/images/SkMovie_gif.cpp',
+          ],
+        },{ #else if skia_os != mac
+          'sources!': [
+            '../src/ports/SkImageDecoder_CG.cpp',
+          ],
+        }],
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+          'sources!': [
+            '../src/images/SkImageDecoder_libgif.cpp',
+            '../src/images/SkMovie_gif.cpp',
+          ],
+          # libpng stuff:
+          # Any targets that depend on this target should link in libpng and
+          # our code that calls it.
+          # See http://code.google.com/p/gyp/wiki/InputFormatReference#Dependent_Settings
+          'link_settings': {
+            'sources': [
+              '../src/images/SkImageDecoder_libpng.cpp',
+            ],
+            'libraries': [
+              '-lpng',
+            ],
+          },
+          # end libpng stuff
+        }],
+        [ 'skia_os == "android"', {
+          'sources!': [
+          ],
+          'dependencies': [
+             'android_deps.gyp:gif',
+             'android_deps.gyp:png',
+          ],
+          'defines': [
+            'SK_ENABLE_LIBPNG',
+          ],
+        }],
+        [ 'skia_os == "ios"', {
+           'include_dirs': [
+             '../include/utils/mac',
+           ],
+        }],
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../include/images',
+        ],
+      },
+    },
+  ],
+}
+
+# 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/opts.gyp b/gyp/opts.gyp
new file mode 100644
index 0000000..b8d7e22
--- /dev/null
+++ b/gyp/opts.gyp
@@ -0,0 +1,173 @@
+{
+  '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
+    # gcc lameness is that, in order to compile SSE2 intrinsics code, it
+    # must be passed the -msse2 flag.  However, with this flag, it may
+    # emit SSE2 instructions even for scalar code, such as the CPUID
+    # test used to test for the presence of SSE2.  So that, and all other
+    # code must be compiled *without* -msse2.  The gyp lameness is that it
+    # does not allow file-specific CFLAGS, so we must create this extra
+    # target for those files to be compiled with -msse2.
+    #
+    # This is actually only a problem on 32-bit Linux (all Intel Macs have
+    # SSE2, Linux x86_64 has SSE2 by definition, and MSC will happily emit
+    # SSE2 from instrinsics, while generating plain ol' 386 for everything
+    # else).  However, to keep the .gyp file simple and avoid platform-specific
+    # build breakage, we do this on all platforms.
+
+    # For about the same reason, we need to compile the ARM opts files
+    # separately as well.
+    {
+      'target_name': 'opts',
+      'product_name': 'skia_opts',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../src/core',
+        '../src/opts',
+      ],
+      'conditions': [
+        [ 'skia_arch_type == "x86" and skia_os != "ios"', {
+          'conditions': [
+            [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+              'cflags': [
+                '-msse2',
+              ],
+            }],
+          ],
+          '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_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',
+          ],
+          'cflags': [
+            '-fomit-frame-pointer',
+          ],
+          'variables': {
+            'arm_neon_optional%': '<(arm_neon_optional>',
+          },
+          'sources': [
+            '../src/opts/opts_check_arm.cpp',
+            '../src/opts/memset.arm.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_arch_type == "arm" and armv7 == 0) or (skia_os == "ios")', {
+          'sources': [
+            '../src/opts/SkBitmapProcState_opts_none.cpp',
+            '../src/opts/SkBlitRow_opts_none.cpp',
+            '../src/opts/SkUtils_opts_none.cpp',
+          ],
+        }],
+      ],
+    },
+    # For the same lame reasons as what is done for skia_opts, we have to
+    # create another target specifically for SSSE3 code as we would not want
+    # to compile the SSE2 code with -mssse3 which would potentially allow
+    # 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"]', {
+          'cflags': [
+            '-mssse3',
+          ],
+        }],
+        # TODO(epoger): the following will enable SSSE3 on Macs, but it will
+        # break once we set OTHER_CFLAGS anywhere else (the first setting will
+        # be replaced, not added to)
+        [ 'skia_os in ["mac"]', {
+          'xcode_settings': {
+            'OTHER_CFLAGS': ['-mssse3',],
+          },
+        }],
+        [ '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',
+      ],
+      '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',
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/pdf.gyp b/gyp/pdf.gyp
new file mode 100644
index 0000000..7b3b9ea
--- /dev/null
+++ b/gyp/pdf.gyp
@@ -0,0 +1,65 @@
+{
+  '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/SkPDFDevice.h',
+        '../include/pdf/SkPDFDocument.h',
+
+        '../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.
+      'direct_dependent_settings': {
+        'defines': [
+          'SK_SUPPORT_PDF',
+        ],
+        'include_dirs': [
+          '../include/pdf',
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
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
new file mode 100644
index 0000000..f743ad4
--- /dev/null
+++ b/gyp/ports.gyp
@@ -0,0 +1,171 @@
+# Port-specific Skia library code.
+{
+  '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/images',
+        '../include/effects',
+        '../include/ports',
+        '../include/xml',
+        '../src/core',
+        '../src/utils',
+      ],
+      'sources': [
+        '../src/ports/SkDebug_stdio.cpp',
+        '../src/ports/SkDebug_win.cpp',
+        '../src/ports/SkFontDescriptor.h',
+        '../src/ports/SkFontDescriptor.cpp',
+        '../src/ports/SkFontHost_sandbox_none.cpp',
+        '../src/ports/SkFontHost_win.cpp',
+        '../src/ports/SkFontHost_win_dw.cpp',
+        '../src/ports/SkGlobalInitialization_default.cpp',
+        '../src/ports/SkThread_win.cpp',
+
+        '../src/ports/SkFontHost_tables.cpp',
+        '../src/ports/SkMemory_malloc.cpp',
+        '../src/ports/SkOSFile_stdio.cpp',
+        '../src/ports/SkTime_Unix.cpp',
+        '../src/ports/SkTime_win.cpp',
+        '../src/ports/SkXMLParser_empty.cpp',
+      ],
+      'conditions': [
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+          'conditions': [
+            [ 'skia_nacl', {
+              'defines': [
+                'SK_CAN_USE_DLOPEN=0',
+              ],
+              'sources': [
+                '../src/ports/SkFontHost_none.cpp',
+              ],
+            }, {
+              'defines': [
+                #The font host requires at least FreeType 2.3.0 at runtime.
+                'SK_FONTHOST_FREETYPE_RUNTIME_VERSION=0x020300',
+                'SK_CAN_USE_DLOPEN=1',
+              ],
+              'sources': [
+                '../src/ports/SkFontHost_FreeType.cpp',
+                '../src/ports/SkFontHost_FreeType_common.cpp',
+                '../src/ports/SkFontHost_linux.cpp',
+              ],
+              'link_settings': {
+                'libraries': [
+                  '-lfreetype',
+                  '-ldl',
+                ],
+              },
+            }],
+          ],
+          'sources': [
+            '../src/ports/SkThread_pthread.cpp',
+          ],
+        }],
+        [ 'skia_os == "mac"', {
+          'include_dirs': [
+            '../include/utils/mac',
+            '../third_party/freetype/include/**',
+          ],
+          'sources': [
+            '../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/SkThread_pthread.cpp',
+          ],
+          'sources!': [
+            '../src/ports/SkFontHost_tables.cpp',
+          ],
+        }],
+        [ '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',
+            '../src/ports/SkTime_Unix.cpp',
+          ],
+        }, { # else !win
+          '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',
+          ],
+          'sources': [
+            '../src/ports/SkDebug_android.cpp',
+            '../src/ports/SkThread_pthread.cpp',
+            '../src/ports/SkFontHost_android.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_deps.gyp:ft2',
+             'android_deps.gyp:expat',
+          ],
+        }],
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../include/ports',
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/sfnt.gyp b/gyp/sfnt.gyp
new file mode 100644
index 0000000..d4d9627
--- /dev/null
+++ b/gyp/sfnt.gyp
@@ -0,0 +1,49 @@
+{
+  'targets': [
+    {
+      'target_name': 'sfnt',
+      'product_name': 'skia_sfnt',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'dependencies': [
+        'core.gyp:core',
+      ],
+      'include_dirs': [
+        '../src/sfnt',
+      ],
+      'sources': [
+        '../src/sfnt/SkIBMFamilyClass.h',
+        '../src/sfnt/SkOTTableTypes.h',
+        '../src/sfnt/SkOTTable_head.h',
+        '../src/sfnt/SkOTTable_hhea.h',
+        '../src/sfnt/SkOTTable_name.h',
+        '../src/sfnt/SkOTTable_OS_2.h',
+        '../src/sfnt/SkOTTable_OS_2_V0.h',
+        '../src/sfnt/SkOTTable_OS_2_V1.h',
+        '../src/sfnt/SkOTTable_OS_2_V2.h',
+        '../src/sfnt/SkOTTable_OS_2_V3.h',
+        '../src/sfnt/SkOTTable_OS_2_V4.h',
+        '../src/sfnt/SkOTTable_OS_2_VA.h',
+        '../src/sfnt/SkOTTable_post.h',
+        '../src/sfnt/SkPanose.h',
+        '../src/sfnt/SkOTUtils.h',
+        '../src/sfnt/SkPreprocessorSeq.h',
+        '../src/sfnt/SkSFNTHeader.h',
+        '../src/sfnt/SkTypedEnum.h',
+
+        '../src/sfnt/SkOTUtils.cpp',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../src/sfnt',
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/shapeops_demo.gyp b/gyp/shapeops_demo.gyp
new file mode 100644
index 0000000..1279294
--- /dev/null
+++ b/gyp/shapeops_demo.gyp
@@ -0,0 +1,126 @@
+
+{
+  '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/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/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',
+        'animator.gyp:animator',
+        'xml.gyp:xml',
+        'svg.gyp:svg',
+        'experimental.gyp:experimental',
+        'pdf.gyp:pdf',
+      ],
+      '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..b3f63f4
--- /dev/null
+++ b/gyp/shapeops_edge.gyp
@@ -0,0 +1,132 @@
+# 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/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/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',
+        'experimental.gyp:experimental',
+        'images.gyp:images',
+        'pdf.gyp:pdf',
+      ],
+      '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..a611d30
--- /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"', {
+        '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
new file mode 100644
index 0000000..3952fd7
--- /dev/null
+++ b/gyp/svg.gyp
@@ -0,0 +1,90 @@
+{
+  'targets': [
+    {
+      'target_name': 'svg',
+      'product_name': 'skia_svg',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../include/xml',
+        '../include/utils',
+        '../include/svg',
+      ],
+      'sources': [
+        '../include/svg/SkSVGAttribute.h',
+        '../include/svg/SkSVGBase.h',
+        '../include/svg/SkSVGPaintState.h',
+        '../include/svg/SkSVGParser.h',
+        '../include/svg/SkSVGTypes.h',
+
+        '../src/svg/SkSVGCircle.cpp',
+        '../src/svg/SkSVGCircle.h',
+        '../src/svg/SkSVGClipPath.cpp',
+        '../src/svg/SkSVGClipPath.h',
+        '../src/svg/SkSVGDefs.cpp',
+        '../src/svg/SkSVGDefs.h',
+        '../src/svg/SkSVGElements.cpp',
+        '../src/svg/SkSVGElements.h',
+        '../src/svg/SkSVGEllipse.cpp',
+        '../src/svg/SkSVGEllipse.h',
+        '../src/svg/SkSVGFeColorMatrix.cpp',
+        '../src/svg/SkSVGFeColorMatrix.h',
+        '../src/svg/SkSVGFilter.cpp',
+        '../src/svg/SkSVGFilter.h',
+        '../src/svg/SkSVGG.cpp',
+        '../src/svg/SkSVGG.h',
+        '../src/svg/SkSVGGradient.cpp',
+        '../src/svg/SkSVGGradient.h',
+        '../src/svg/SkSVGGroup.cpp',
+        '../src/svg/SkSVGGroup.h',
+        '../src/svg/SkSVGImage.cpp',
+        '../src/svg/SkSVGImage.h',
+        '../src/svg/SkSVGLine.cpp',
+        '../src/svg/SkSVGLine.h',
+        '../src/svg/SkSVGLinearGradient.cpp',
+        '../src/svg/SkSVGLinearGradient.h',
+        '../src/svg/SkSVGMask.cpp',
+        '../src/svg/SkSVGMask.h',
+        '../src/svg/SkSVGMetadata.cpp',
+        '../src/svg/SkSVGMetadata.h',
+        '../src/svg/SkSVGPaintState.cpp',
+        '../src/svg/SkSVGParser.cpp',
+        '../src/svg/SkSVGPath.cpp',
+        '../src/svg/SkSVGPath.h',
+        '../src/svg/SkSVGPolygon.cpp',
+        '../src/svg/SkSVGPolygon.h',
+        '../src/svg/SkSVGPolyline.cpp',
+        '../src/svg/SkSVGPolyline.h',
+        '../src/svg/SkSVGRadialGradient.cpp',
+        '../src/svg/SkSVGRadialGradient.h',
+        '../src/svg/SkSVGRect.cpp',
+        '../src/svg/SkSVGRect.h',
+        '../src/svg/SkSVGStop.cpp',
+        '../src/svg/SkSVGStop.h',
+        '../src/svg/SkSVGSVG.cpp',
+        '../src/svg/SkSVGSVG.h',
+        '../src/svg/SkSVGSymbol.cpp',
+        '../src/svg/SkSVGSymbol.h',
+        '../src/svg/SkSVGText.cpp',
+        '../src/svg/SkSVGText.h',
+        '../src/svg/SkSVGUse.cpp',
+      ],
+      'sources!' : [
+          '../src/svg/SkSVG.cpp', # doesn't compile, maybe this is test code?
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../include/svg',
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/tests.gyp b/gyp/tests.gyp
new file mode 100644
index 0000000..c91388c
--- /dev/null
+++ b/gyp/tests.gyp
@@ -0,0 +1,128 @@
+# GYP file to build unit tests.
+{
+  'includes': [
+    'apptype_console.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'tests',
+      'type': 'executable',
+      'include_dirs' : [
+        '../src/core',
+        '../src/effects',
+        '../src/pdf',
+        '../src/pipe/utils',
+        '../src/utils',
+        '../tools/',
+      ],
+      'sources': [
+        '../tests/AAClipTest.cpp',
+        '../tests/AnnotationTest.cpp',
+        '../tests/AtomicTest.cpp',
+        '../tests/BitmapCopyTest.cpp',
+        '../tests/BitmapGetColorTest.cpp',
+        '../tests/BitSetTest.cpp',
+        '../tests/BlitRowTest.cpp',
+        '../tests/BlurTest.cpp',
+        '../tests/CanvasTest.cpp',
+        '../tests/ClampRangeTest.cpp',
+        '../tests/ClipCacheTest.cpp',
+        '../tests/ClipCubicTest.cpp',
+        '../tests/ClipStackTest.cpp',
+        '../tests/ClipperTest.cpp',
+        '../tests/ColorFilterTest.cpp',
+        '../tests/ColorTest.cpp',
+        '../tests/DataRefTest.cpp',
+        '../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/MathTest.cpp',
+        '../tests/MatrixTest.cpp',
+        '../tests/Matrix44Test.cpp',
+        '../tests/MemsetTest.cpp',
+        '../tests/MetaDataTest.cpp',
+        '../tests/PackBitsTest.cpp',
+        '../tests/PaintTest.cpp',
+        '../tests/ParsePathTest.cpp',
+        '../tests/PathCoverageTest.cpp',
+        '../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/RTreeTest.cpp',
+        '../tests/ScalarTest.cpp',
+        '../tests/ShaderOpacityTest.cpp',
+        '../tests/Sk64Test.cpp',
+        '../tests/skia_test.cpp',
+        '../tests/SortTest.cpp',
+        '../tests/SrcOverTest.cpp',
+        '../tests/StreamTest.cpp',
+        '../tests/StringTest.cpp',
+        '../tests/TDLinkedListTest.cpp',
+        '../tests/Test.cpp',
+        '../tests/Test.h',
+        '../tests/TestSize.cpp',
+        '../tests/TLSTest.cpp',
+        '../tests/ToUnicode.cpp',
+        '../tests/UnicodeTest.cpp',
+        '../tests/UtilsTest.cpp',
+        '../tests/WArrayTest.cpp',
+        '../tests/WritePixelsTest.cpp',
+        '../tests/Writer32Test.cpp',
+        '../tests/XfermodeTest.cpp',
+
+        # Needed for PipeTest.
+        '../src/pipe/utils/SamplePipeControllers.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'experimental.gyp:experimental',
+        'images.gyp:images',
+        'pdf.gyp:pdf',
+        'tools.gyp:picture_utils',
+      ],
+      '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/tools.gyp b/gyp/tools.gyp
new file mode 100644
index 0000000..f6e9a93
--- /dev/null
+++ b/gyp/tools.gyp
@@ -0,0 +1,167 @@
+# GYP file to build various tools.
+#
+# To build on Linux:
+#  ./gyp_skia tools.gyp && make tools
+#
+# Building on other platforms not tested yet.
+#
+{
+  'includes': [
+    'apptype_console.gypi',
+  ],
+  'targets': [
+    {
+      # Build all executable targets defined below.
+      'target_name': 'tools',
+      'type': 'none',
+      'dependencies': [
+        'skdiff',
+        'skhello',
+        'skimage',
+        'render_pictures',
+        'bench_pictures',
+        'pinspect',
+        'filter',
+      ],
+    },
+    {
+      'target_name': 'skdiff',
+      'type': 'executable',
+      'sources': [
+        '../tools/skdiff_main.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+      ],
+    },
+    {
+      'target_name': 'skhello',
+      'type': 'executable',
+      'sources': [
+        '../tools/skhello.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+      ],
+    },
+    {
+      'target_name': 'skimage',
+      'type': 'executable',
+      'sources': [
+        '../tools/skimage_main.cpp',
+      ],
+      'dependencies': [
+        '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.cpp',
+        '../src/pipe/utils/SamplePipeControllers.h',
+        '../src/pipe/utils/SamplePipeControllers.cpp',
+      ],
+      'include_dirs': [
+        '../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': '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',
+      ],
+      'sources': [
+        '../tools/filtermain.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
new file mode 100644
index 0000000..cc25349
--- /dev/null
+++ b/gyp/utils.gyp
@@ -0,0 +1,192 @@
+{
+  'targets': [
+    {
+      'target_name': 'utils',
+      'product_name': 'skia_utils',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../include/effects',
+        '../include/pipe',
+        '../include/utils',
+        '../include/utils/mac',
+        '../include/utils/unix',
+        '../include/utils/win',
+        '../include/xml',
+        '../src/utils',
+      ],
+      'sources': [
+        '../include/utils/SkBoundaryPatch.h',
+        '../include/utils/SkCamera.h',
+        '../include/utils/SkCubicInterval.h',
+        '../include/utils/SkCullPoints.h',
+        '../include/utils/SkDeferredCanvas.h',
+        '../include/utils/SkDumpCanvas.h',
+        '../include/utils/SkInterpolator.h',
+        '../include/utils/SkLayer.h',
+        '../include/utils/SkMatrix44.h',
+        '../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/SkProxyCanvas.h',
+        '../include/utils/SkUnitMappers.h',
+        '../include/utils/SkWGL.h',
+
+        '../src/utils/SkBase64.cpp',
+        '../src/utils/SkBase64.h',
+        '../src/utils/SkBitSet.cpp',
+        '../src/utils/SkBitSet.h',
+        '../src/utils/SkBoundaryPatch.cpp',
+        '../src/utils/SkCamera.cpp',
+        '../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/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/SkProxyCanvas.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',
+
+        #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/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/SkWGL_win.cpp',
+      ],
+      'sources!': [
+          '../src/utils/SDL/SkOSWindow_SDL.cpp',
+      ],
+      'conditions': [
+        [ 'skia_os == "mac"', {
+          'link_settings': {
+            'libraries': [
+              '$(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',
+          ],
+          'sources!': [
+            '../include/utils/mac/SkCGUtils.h',
+            '../src/utils/mac/SkCreateCGImageRef.cpp',
+            '../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/SkThreadUtils_pthread_linux.cpp',
+          ],
+        }],
+        [ 'skia_os == "win"', {
+          'direct_dependent_settings': {
+            'include_dirs': [
+              '../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',
+          ],
+          'sources/': [ ['exclude', '_win.(h|cpp)$'],],
+          'sources!': [
+            '../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/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_nacl == 1', {
+          'sources': [
+            '../src/utils/SkThreadUtils_pthread_other.cpp',
+          ],
+          'sources!': [
+            '../src/utils/SkThreadUtils_pthread_linux.cpp',
+          ],
+        }],
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../include/utils',
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/views.gyp b/gyp/views.gyp
new file mode 100644
index 0000000..80be87e
--- /dev/null
+++ b/gyp/views.gyp
@@ -0,0 +1,133 @@
+# Views is the Skia windowing toolkit.
+# It provides:
+#  * A portable means of creating native windows.
+#  * Events.
+#  * Basic widgets and controls.
+
+{
+  'targets': [
+    {
+      'target_name': 'views',
+      'product_name': 'skia_views',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../include/views',
+        '../include/xml',
+        '../include/utils',
+        '../include/images',
+        '../include/effects',
+        '../include/views/unix',
+      ],
+      'dependencies': [
+        'angle.gyp:*',
+      ],
+      'sources': [
+        '../include/views/SkApplication.h',
+        '../include/views/SkBGViewArtist.h',
+        '../include/views/SkEvent.h',
+        '../include/views/SkEventSink.h',
+        '../include/views/SkKey.h',
+        '../include/views/SkOSMenu.h',
+        '../include/views/SkOSWindow_Mac.h',
+        '../include/views/SkOSWindow_SDL.h',
+        '../include/views/SkOSWindow_Unix.h',
+        '../include/views/SkOSWindow_Win.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/SkWindow.h',
+
+        '../src/views/SkBGViewArtist.cpp',
+        '../src/views/SkEvent.cpp',
+        '../src/views/SkEventSink.cpp',
+        '../src/views/SkOSMenu.cpp',
+        '../src/views/SkParsePaint.cpp',
+        '../src/views/SkProgressView.cpp',
+        '../src/views/SkStackViewLayout.cpp',
+        '../src/views/SkTagList.cpp',
+        '../src/views/SkTagList.h',
+        '../src/views/SkTextBox.cpp',
+        '../src/views/SkTouchGesture.cpp',
+        '../src/views/SkView.cpp',
+        '../src/views/SkViewInflate.cpp',
+        '../src/views/SkViewPriv.cpp',
+        '../src/views/SkViewPriv.h',
+        '../src/views/SkWidgets.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/SDL/SkOSWindow_SDL.cpp',
+      ],
+      'conditions': [
+        [ 'skia_os == "mac"', {
+          'link_settings': {
+            'libraries': [
+              '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+              '$(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!': [
+            '../src/views/unix/SkOSWindow_Unix.cpp',
+            '../src/views/unix/keysym2ucs.c',
+            '../src/views/unix/skia_unix.cpp',
+          ],
+        }],
+        [ 'skia_os == "win"', {
+        },{
+          'sources!': [
+            '../src/views/win/SkOSWindow_win.cpp',
+            '../src/views/win/skia_win.cpp',
+          ],
+        }],
+        [ 'skia_gpu == 1', {
+          'include_dirs': [
+            '../include/gpu',
+          ],
+        }],
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../include/views',
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
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
new file mode 100644
index 0000000..b7fdb83
--- /dev/null
+++ b/gyp/xml.gyp
@@ -0,0 +1,57 @@
+{
+  'targets': [
+    {
+      'target_name': 'xml',
+      'product_name': 'skia_xml',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../include/xml',
+        '../include/utils',
+      ],
+      'sources': [
+        '../include/xml/SkBML_WXMLParser.h',
+        '../include/xml/SkBML_XMLParser.h',
+        '../include/xml/SkDOM.h',
+        '../include/xml/SkJS.h',
+        '../include/xml/SkXMLParser.h',
+        '../include/xml/SkXMLWriter.h',
+
+        '../src/xml/SkBML_Verbs.h',
+        '../src/xml/SkBML_XMLParser.cpp',
+        '../src/xml/SkDOM.cpp',
+        '../src/xml/SkJS.cpp',
+        '../src/xml/SkJSDisplayable.cpp',
+        '../src/xml/SkXMLParser.cpp',
+        '../src/xml/SkXMLPullParser.cpp',
+        '../src/xml/SkXMLWriter.cpp',
+      ],
+      'sources!': [
+          '../src/xml/SkXMLPullParser.cpp', #if 0 around class decl in header
+      ],
+      'conditions': [
+        [ 'skia_os in ["win", "mac", "linux", "freebsd", "openbsd", "solaris", "android", "ios"]', {
+          'sources!': [
+            # no jsapi.h by default on system
+            '../include/xml/SkJS.h',
+            '../src/xml/SkJS.cpp',
+            '../src/xml/SkJSDisplayable.cpp',
+          ],
+        }],
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../include/xml',
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/xps.gyp b/gyp/xps.gyp
new file mode 100644
index 0000000..e5b7c0d
--- /dev/null
+++ b/gyp/xps.gyp
@@ -0,0 +1,66 @@
+{
+  'targets': [
+    {
+      'target_name': 'xps',
+      'product_name': 'skia_xps',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'images.gyp:images',
+      ],
+      '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',
+        '../include/device/xps/SkXPSDevice.h',
+
+        '../src/device/xps/SkXPSDevice.cpp',
+      ],
+      'conditions': [
+        [ 'skia_os == "win"', {
+          'link_settings': {
+            'libraries': [
+              'T2Embed.lib',
+              'FontSub.lib',
+            ],
+          },
+        },{ #else if 'skia_os != "win"'
+          'include_dirs!': [
+            '../include/utils/win',
+          ],
+          'sources!': [
+            '../include/device/xps/SkXPSDevice.h',
+
+            '../src/device/xps/SkXPSDevice.cpp',
+          ],
+        }],
+      ],
+      # This section makes all targets that depend on this target
+      # #define SK_SUPPORT_XPS and have access to the xps header files.
+      'direct_dependent_settings': {
+        'conditions': [
+          [ 'skia_os == "win"', {
+            'defines': [
+              'SK_SUPPORT_XPS',
+            ],
+          }],
+        ],
+        'include_dirs': [
+          '../include/device/xps',
+          '../src/utils', # needed to get SkBitSet.h
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/zlib.gyp b/gyp/zlib.gyp
new file mode 100644
index 0000000..864a059
--- /dev/null
+++ b/gyp/zlib.gyp
@@ -0,0 +1,45 @@
+{
+  'targets': [
+    {
+      'target_name': 'zlib',
+      'type': 'static_library',
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+      ],
+      'sources': [
+        '../include/core/SkFlate.h',
+
+        '../src/core/SkFlate.cpp',
+      ],
+      'conditions': [
+        [ 'skia_os == "mac"', {
+          'link_settings': {
+            'libraries': [
+              '$(SDKROOT)/usr/lib/libz.dylib',
+            ],
+          },
+          'defines': [ 'SK_ZLIB_INCLUDE=<zlib.h>', ],
+        }],
+        [ '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"]', {
+          'link_settings': { 'libraries': [ '-lz', ], },
+          'defines': [ 'SK_ZLIB_INCLUDE=<zlib.h>', ],
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp_skia b/gyp_skia
new file mode 100755
index 0000000..d6b5504
--- /dev/null
+++ b/gyp_skia
@@ -0,0 +1,102 @@
+#!/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 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')
+
+# Directory within which we want all generated files (including Makefiles)
+# to be written.
+output_dir = os.path.join(os.path.abspath(script_dir), 'out')
+
+# 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
+
+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(output_dir)])
+
+  # Tell make to write its output into the same dir
+  args.extend(['-Goutput_dir=.'])
+
+  # 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
new file mode 100644
index 0000000..924c595
--- /dev/null
+++ b/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/include/animator/SkAnimatorView.h b/include/animator/SkAnimatorView.h
new file mode 100644
index 0000000..940dd26
--- /dev/null
+++ b/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/include/config/SkUserConfig.h b/include/config/SkUserConfig.h
new file mode 100644
index 0000000..93dda70
--- /dev/null
+++ b/include/config/SkUserConfig.h
@@ -0,0 +1,194 @@
+
+/*
+ * Copyright 2006 The Android 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.
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  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
+//#define SK_SCALAR_IS_FIXED
+
+
+/*  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
+
+/*  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
+
+/*  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.
+ */
+//#define SK_CPU_BENDIAN
+//#define SK_CPU_LENDIAN
+
+/*  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.
+ */
+//#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   (1024 * 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. 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.
+ */
+//#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
+
+/**
+ *  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
+    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
+
+
+/* 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/config/sk_stdint.h b/include/config/sk_stdint.h
new file mode 100644
index 0000000..360755e
--- /dev/null
+++ b/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/include/core/Sk64.h b/include/core/Sk64.h
new file mode 100644
index 0000000..d232d82
--- /dev/null
+++ b/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/include/core/SkAdvancedTypefaceMetrics.h b/include/core/SkAdvancedTypefaceMetrics.h
new file mode 100755
index 0000000..d326379
--- /dev/null
+++ b/include/core/SkAdvancedTypefaceMetrics.h
@@ -0,0 +1,159 @@
+
+/*
+ * 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:
+    SK_DECLARE_INST_COUNT(SkAdvancedTypefaceMetrics)
+
+    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;
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+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/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
new file mode 100644
index 0000000..9c81e83
--- /dev/null
+++ b/include/core/SkBitmap.h
@@ -0,0 +1,765 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkColorTable.h"
+#include "SkPoint.h"
+#include "SkRefCnt.h"
+
+struct SkIRect;
+struct SkRect;
+class SkPaint;
+class SkPixelRef;
+class SkRegion;
+
+// 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();
+
+    /**
+     *  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);
+
+    ~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 pixelref. Note: this can return true even if the
+        dimensions of the bitmap are > 0 (see empty()).
+    */
+    bool isNull() const { return 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);
+
+    /**
+     *  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().
+    */
+    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. 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;
+
+    /** 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). 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
+                      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);
+
+    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;
+
+    /** 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&);
+
+    SkDEBUGCODE(void validate() const;)
+
+    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
+            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;
+    private:
+        typedef SkRefCnt INHERITED;
+    };
+
+    /** 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
+
+    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
+
+    /* 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 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/include/core/SkBlitRow.h b/include/core/SkBlitRow.h
new file mode 100644
index 0000000..febc405
--- /dev/null
+++ b/include/core/SkBlitRow.h
@@ -0,0 +1,105 @@
+/*
+ * 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);
+
+    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);
+
+   /** Function pointer that blends a single color with a row of 32-bit colors
+       onto a 32-bit destination
+   */
+   typedef void (*ColorProc)(SkPMColor* dst, const SkPMColor* src, int count,
+                             SkPMColor color);
+
+    /** Blend a single color onto a row of S32 pixels, writing the result
+        into a row of D32 pixels. src and dst may be the same memory, but
+        if they are not, they may not overlap.
+     */
+    static void Color32(SkPMColor dst[], const SkPMColor src[],
+                        int count, SkPMColor color);
+
+    //! Public entry-point to return a blit function ptr
+    static ColorProc ColorProcFactory();
+
+    /** 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
+        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/SkBounder.h b/include/core/SkBounder.h
new file mode 100644
index 0000000..1ede548
--- /dev/null
+++ b/include/core/SkBounder.h
@@ -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.
+ */
+
+
+#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:
+    SK_DECLARE_INST_COUNT(SkBounder)
+
+    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;
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
+
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
new file mode 100644
index 0000000..0d7b899
--- /dev/null
+++ b/include/core/SkCanvas.h
@@ -0,0 +1,1082 @@
+
+/*
+ * Copyright 2006 The Android 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 SkMetaData;
+class SkPicture;
+class SkSurface_Base;
+
+/** \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:
+    SK_DECLARE_INST_COUNT(SkCanvas)
+
+    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();
+
+    SkMetaData& getMetaData();
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     *  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.
+     *
+     * @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(bool updateMatrixClip = false) const;
+
+    /**
+     *  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);
+    }
+
+    /** 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
+        @return true if the rect (transformed by the canvas' matrix) does not
+                     intersect with the canvas' clip
+    */
+    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
+        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
+        @return true if the path (transformed by the canvas' matrix) does not
+                     intersect with the canvas' clip
+    */
+    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
+        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) const {
+        SkASSERT(SkScalarToCompareType(top) <= SkScalarToCompareType(bottom));
+        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
+        // 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) 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 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);
+
+    /**
+     *  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).
+     *
+     *  DEPRECATED -- call getClipDeviceBounds() instead.
+     */
+    const SkRegion& getTotalClip() const;
+
+    /** 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)
+     */
+    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;
+    };
+
+    /**
+     *  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.
+     */
+    void replayClips(ClipVisitor*) const;
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** 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
+    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);
+
+    // 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();
+
+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;
+    int         fSaveLayerCount;    // number of successful saveLayer calls
+
+    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);
+
+    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 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*);
+
+    // 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;
+
+    const SkRectCompareType& getLocalClipBoundsCompareType() const {
+        if (fLocalBoundsCompareTypeDirty) {
+            this->computeLocalClipBoundsCompareType();
+            fLocalBoundsCompareTypeDirty = false;
+        }
+        return fLocalBoundsCompareType;
+    }
+    void computeLocalClipBoundsCompareType() const;
+
+    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
+
+    typedef SkRefCnt INHERITED;
+};
+
+/** 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/include/core/SkChecksum.h b/include/core/SkChecksum.h
new file mode 100644
index 0000000..e5cc3d1
--- /dev/null
+++ b/include/core/SkChecksum.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 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
+     *
+     *  @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
new file mode 100644
index 0000000..1b52a35
--- /dev/null
+++ b/include/core/SkChunkAlloc.h
@@ -0,0 +1,66 @@
+
+/*
+ * Copyright 2006 The Android 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();
+
+    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; }
+    int blockCount() const { return fBlockCount; }
+
+    /**
+     *  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;
+    size_t  fChunkSize;
+    size_t  fTotalCapacity;
+    int     fBlockCount;
+
+    Block* newBlock(size_t bytes, AllocFailType ftype);
+};
+
+#endif
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
new file mode 100644
index 0000000..a67e0a5
--- /dev/null
+++ b/include/core/SkClipStack.h
@@ -0,0 +1,267 @@
+
+/*
+ * 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"
+#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:
+    SkClipStack();
+    SkClipStack(const SkClipStack& b);
+    explicit SkClipStack(const SkRect& r);
+    explicit SkClipStack(const SkIRect& r);
+    ~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();
+
+    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
+    };
+
+    /**
+     * 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;
+
+    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();
+
+    /**
+     * 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 ignoreable) 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;
+
+private:
+    struct Rec;
+
+public:
+    class Iter {
+    public:
+        enum IterStart {
+            kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
+            kTop_IterStart = SkDeque::Iter::kBack_IterStart
+        };
+
+        /**
+         * Creates an uninitialized iterator. Must be reset()
+         */
+        Iter();
+
+        Iter(const SkClipStack& stack, IterStart startLoc);
+
+        struct Clip {
+            Clip() : fRect(NULL), fPath(NULL), fOp(SkRegion::kIntersect_Op),
+                     fDoAA(false) {}
+            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;
+            int32_t         fGenID;
+        };
+
+        /**
+         *  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();
+        const Clip* prev();
+
+        /**
+         * Moves the iterator to the topmost clip with the specified RegionOp
+         * and returns that clip. If no clip with that op is found,
+         * returns NULL.
+         */
+        const Clip* skipToTopmost(SkRegion::Op op);
+
+        /**
+         * Restarts the iterator on a clip stack.
+         */
+        void reset(const SkClipStack& stack, IterStart startLoc);
+
+    private:
+        const SkClipStack* fStack;
+        Clip               fClip;
+        SkDeque::Iter      fIter;
+
+        /**
+         * updateClip updates fClip to the current state of fIter. It unifies
+         * functionality needed by both next() and prev().
+         */
+        const Clip* updateClip(const SkClipStack::Rec* rec);
+    };
+
+    /**
+     * 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::Clip;
+        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 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 rec's generation ID.
+     */
+    void purgeClip(Rec* rec);
+
+    /**
+     * Return the next unique generation ID.
+     */
+    static int32_t GetNextGenID();
+};
+
+#endif
+
diff --git a/include/core/SkColor.h b/include/core/SkColor.h
new file mode 100644
index 0000000..e6d4352
--- /dev/null
+++ b/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/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
new file mode 100644
index 0000000..935b691
--- /dev/null
+++ b/include/core/SkColorFilter.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 SkColorFilter_DEFINED
+#define SkColorFilter_DEFINED
+
+#include "SkColor.h"
+#include "SkFlattenable.h"
+#include "SkXfermode.h"
+
+class SkBitmap;
+
+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);
+
+    /**
+     *  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 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_GROUP()
+protected:
+    SkColorFilter() {}
+    SkColorFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {}
+
+private:
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
diff --git a/include/core/SkColorPriv.h b/include/core/SkColorPriv.h
new file mode 100644
index 0000000..21743bd
--- /dev/null
+++ b/include/core/SkColorPriv.h
@@ -0,0 +1,864 @@
+
+/*
+ * Copyright 2006 The Android 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"
+
+///@{
+/** 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.
+
+    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
+ *   srcWeight is [0..256], unlike SkFourByteInterp which takes [0..255]
+ */
+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);
+    unsigned b = SkAlphaBlend(SkGetPackedB32(src), SkGetPackedB32(dst), scale);
+
+    return SkPackARGB32(a, r, g, b);
+}
+
+/**
+ * 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:
+ *   (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/include/core/SkColorShader.h b/include/core/SkColorShader.h
new file mode 100644
index 0000000..0823b70
--- /dev/null
+++ b/include/core/SkColorShader.h
@@ -0,0 +1,68 @@
+
+/*
+ * 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]) const SK_OVERRIDE;
+
+    virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorShader)
+
+protected:
+    SkColorShader(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+
+    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/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
new file mode 100644
index 0000000..a8a8e0b
--- /dev/null
+++ b/include/core/SkComposeShader.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 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();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeShader)
+
+protected:
+    SkComposeShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+
+    SkShader*   fShaderA;
+    SkShader*   fShaderB;
+    SkXfermode* fMode;
+
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/include/core/SkData.h b/include/core/SkData.h
new file mode 100644
index 0000000..4db17a0
--- /dev/null
+++ b/include/core/SkData.h
@@ -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.
+ */
+
+
+
+#ifndef SkData_DEFINED
+#define SkData_DEFINED
+
+#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 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.
+     */
+    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;
+
+    /**
+     *  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.
+     */
+    static SkData* NewWithProc(const void* data, size_t length,
+                               ReleaseProc proc, void* context);
+
+    /**
+     *  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);
+
+    /**
+     *  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();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkData)
+
+protected:
+    SkData(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    ReleaseProc fReleaseProc;
+    void*       fReleaseProcContext;
+
+    const void* fPtr;
+    size_t      fSize;
+
+    SkData(const void* ptr, size_t size, ReleaseProc, void* context);
+    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.
+ */
+class SkAutoDataUnref : SkNoncopyable {
+public:
+    SkAutoDataUnref(SkData* data) : fRef(data) {}
+    ~SkAutoDataUnref() {
+        SkSafeUnref(fRef);
+    }
+
+    SkData* get() const { return fRef; }
+
+    void release() {
+        if (fRef) {
+            fRef->unref();
+            fRef = NULL;
+        }
+    }
+
+    SkData *operator->() const { return fRef; }
+    operator SkData*() { return fRef; }
+
+private:
+    SkData*     fRef;
+};
+
+#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
new file mode 100644
index 0000000..eef335d
--- /dev/null
+++ b/include/core/SkDeque.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 SkDeque_DEFINED
+#define SkDeque_DEFINED
+
+#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:
+    /**
+     * 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 { return fFront; }
+    const void* back() const  { return fBack; }
+
+    void* front() {
+        return (void*)((const SkDeque*)this)->front();
+    }
+
+    void* back() {
+        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();
+
+    void pop_front();
+    void pop_back();
+
+private:
+    struct Block;
+
+public:
+    class Iter {
+    public:
+        enum IterStart {
+            kFront_IterStart,
+            kBack_IterStart
+        };
+
+        /**
+         * Creates an uninitialized iterator. Must be reset()
+         */
+        Iter();
+
+        Iter(const SkDeque& d, IterStart startLoc);
+        void* next();
+        void* prev();
+
+        void reset(const SkDeque& d, IterStart startLoc);
+
+    private:
+        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:
+    // allow unit test to call numBlocksAllocated
+    friend class DequeUnitTestHelper;
+
+    void*   fFront;
+    void*   fBack;
+
+    Block*  fFrontBlock;
+    Block*  fBackBlock;
+    size_t  fElemSize;
+    void*   fInitialStorage;
+    int     fCount;             // number of elements in the deque
+    int     fAllocCount;        // number of elements to allocate per block
+
+    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
new file mode 100644
index 0000000..33be2f6
--- /dev/null
+++ b/include/core/SkDevice.h
@@ -0,0 +1,427 @@
+
+/*
+ * 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:
+    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,
+     *  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; }
+
+    /**
+     * 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
+    };
+
+    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*);
+
+    /**
+     *
+     *  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.
+     *
+     *  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&);
+
+    /** 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);
+
+    /**
+     *  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.
+     */
+    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 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 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
+    // 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 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); }
+    // 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;
+
+#ifdef SK_DEBUG
+    bool        fAttachedToCanvas;
+#endif
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/core/SkDither.h b/include/core/SkDither.h
new file mode 100644
index 0000000..d82b416
--- /dev/null
+++ b/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/include/core/SkDraw.h b/include/core/SkDraw.h
new file mode 100644
index 0000000..5db4bf0
--- /dev/null
+++ b/include/core/SkDraw.h
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright 2006 The Android 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,
+                           SkPaint::Style style);
+
+    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
+
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+};
+
+#endif
+
+
diff --git a/include/core/SkDrawFilter.h b/include/core/SkDrawFilter.h
new file mode 100644
index 0000000..7471f9b
--- /dev/null
+++ b/include/core/SkDrawFilter.h
@@ -0,0 +1,48 @@
+
+/*
+ * 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 SK_API SkDrawFilter : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkDrawFilter)
+
+    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;
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/core/SkDrawLooper.h b/include/core/SkDrawLooper.h
new file mode 100644
index 0000000..8a218c3
--- /dev/null
+++ b/include/core/SkDrawLooper.h
@@ -0,0 +1,73 @@
+
+/*
+ * 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;
+struct SkRect;
+
+/** \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:
+    SK_DECLARE_INST_COUNT(SkDrawLooper)
+
+    /**
+     *  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/SkEmptyShader.h b/include/core/SkEmptyShader.h
new file mode 100644
index 0000000..13da457
--- /dev/null
+++ b/include/core/SkEmptyShader.h
@@ -0,0 +1,42 @@
+
+/*
+ * 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;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmptyShader)
+
+protected:
+    SkEmptyShader(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/include/core/SkEndian.h b/include/core/SkEndian.h
new file mode 100644
index 0000000..910cf1e
--- /dev/null
+++ b/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/SkFixed.h b/include/core/SkFixed.h
new file mode 100644
index 0000000..0f8f758
--- /dev/null
+++ b/include/core/SkFixed.h
@@ -0,0 +1,290 @@
+
+/*
+ * Copyright 2006 The Android 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)
+
+#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
+
+#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;
+#define SK_Fract1           (1 << 30)
+#define Sk_FracHalf         (1 << 29)
+#define SK_FractPIOver180   (0x11DF46A)
+
+#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
+*/
+#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(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
+    */
+    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
+
+///////////////////////////////////////////////////////////////////////////////
+
+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
new file mode 100644
index 0000000..e4c1417
--- /dev/null
+++ b/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/include/core/SkFlattenable.h b/include/core/SkFlattenable.h
new file mode 100644
index 0000000..0b21abc
--- /dev/null
+++ b/include/core/SkFlattenable.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 SkFlattenable_DEFINED
+#define SkFlattenable_DEFINED
+
+#include "SkRefCnt.h"
+
+class SkFlattenableReadBuffer;
+class SkFlattenableWriteBuffer;
+
+#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 \
+    }
+
+#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;
+
+    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:
+    static void InitializeFlattenables();
+
+    friend class SkGraphics;
+    friend class SkFlattenableWriteBuffer;
+
+    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
new file mode 100644
index 0000000..3c725fe
--- /dev/null
+++ b/include/core/SkFloatBits.h
@@ -0,0 +1,139 @@
+
+/*
+ * 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);
+
+
+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));
+}
+
+//  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/include/core/SkFloatingPoint.h b/include/core/SkFloatingPoint.h
new file mode 100644
index 0000000..b3e490d
--- /dev/null
+++ b/include/core/SkFloatingPoint.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 SkFloatingPoint_DEFINED
+#define SkFloatingPoint_DEFINED
+
+#include "SkTypes.h"
+
+#include <math.h>
+#include <float.h>
+#include "SkFloatBits.h"
+
+// 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 powf(base, 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
diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h
new file mode 100644
index 0000000..737b2eb
--- /dev/null
+++ b/include/core/SkFontHost.h
@@ -0,0 +1,288 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkTypeface.h"
+
+class SkDescriptor;
+class SkScalerContext;
+struct SkScalerContextRec;
+class SkStream;
+class SkWStream;
+
+/** \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
+    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
+    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.
+        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[],
+                                      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(). 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) 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*);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** 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);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** 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.
+
+        The provided typeface corresponds to the fFontID field.
+     */
+    static void FilterRec(SkScalerContextRec* rec, SkTypeface* typeface);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** 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);
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    /** 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/include/core/SkGeometry.h b/include/core/SkGeometry.h
new file mode 100644
index 0000000..26f27ba
--- /dev/null
+++ b/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/include/core/SkGraphics.h b/include/core/SkGraphics.h
new file mode 100644
index 0000000..c9a0f53
--- /dev/null
+++ b/include/core/SkGraphics.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 SkGraphics_DEFINED
+#define SkGraphics_DEFINED
+
+#include "SkTypes.h"
+
+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
+     *  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);
+
+    /**
+     *  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:
+     *  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);
+
+    /**
+     *  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
+        with the C++ runtime to call SkGraphics::FreeCaches()
+    */
+    static void InstallNewHandler();
+};
+
+class SkAutoGraphics {
+public:
+    SkAutoGraphics() {
+        SkGraphics::Init();
+    }
+    ~SkAutoGraphics() {
+        SkGraphics::Term();
+    }
+};
+
+#endif
+
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
new file mode 100644
index 0000000..05350bf
--- /dev/null
+++ b/include/core/SkImage.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 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;
+struct GrPlatformTextureDesc;
+
+// need for TileMode
+#include "SkShader.h"
+
+////// EXPERIMENTAL
+
+class SkColorSpace;
+
+/**
+ *  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&, SkColorSpace*, const void* pixels, size_t rowBytes);
+    static SkImage* NewRasterData(const Info&, SkColorSpace*, SkData* pixels, size_t rowBytes);
+    static SkImage* NewEncodedData(SkData*);
+    static SkImage* NewTexture(GrContext*, const GrPlatformTextureDesc&);
+
+    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
new file mode 100644
index 0000000..596da24
--- /dev/null
+++ b/include/core/SkImageFilter.h
@@ -0,0 +1,164 @@
+/*
+ * 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 SkColorFilter;
+class SkDevice;
+class SkMatrix;
+struct SkIPoint;
+struct SkIRect;
+struct SkRect;
+class GrCustomStage;
+class GrTexture;
+
+/**
+ *  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:
+    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;
+    };
+
+    /**
+     *  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);
+
+    /**
+     *  Returns true if the filter can be expressed a single-pass
+     *  GrCustomStage, used to process this filter on the GPU, or false if
+     *  not.
+     *
+     *  If stage is non-NULL, a new GrCustomStage instance is stored
+     *  in it.  The caller assumes ownership of the stage, and it is up to the
+     *  caller to unref it.
+     */
+    virtual bool asNewCustomStage(GrCustomStage** stage, GrTexture*) const;
+
+    /**
+     *  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 asNewCustomStage().
+     *  The default implementation returns false.
+     */
+    virtual bool canFilterImageGPU() const;
+
+    /**
+     *  Process this image filter on the GPU.  texture is the source texture
+     *  for processing, and rect is the effect region to process.  The
+     *  function must allocate a new texture of at least rect width/height
+     *  size, and return it to the caller.  The default implementation returns
+     *  NULL.
+     */
+    virtual GrTexture* onFilterImageGPU(Proxy*, GrTexture* texture, const SkRect& rect);
+
+    /**
+     *  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(int inputCount, SkImageFilter** inputs);
+
+    // The ... represents inputCount SkImageFilter pointers, upon which this
+    // constructor will call SkSafeRef().  This is the same behaviour as
+    // the SkImageFilter(int, SkImageFilter**) constructor above.
+    explicit SkImageFilter(int inputCount, ...);
+
+    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&,
+                               SkBitmap* result, SkIPoint* offset);
+    // 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..02b63db
--- /dev/null
+++ b/include/core/SkInstCnt.h
@@ -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.
+ */
+
+
+#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
+ */
+//#if defined SK_DEBUG && !defined SK_ENABLE_INST_COUNT
+//#define SK_ENABLE_INST_COUNT
+//#endif
+
+#ifdef SK_ENABLE_INST_COUNT
+#include <stdlib.h>
+#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
new file mode 100644
index 0000000..5827dbe
--- /dev/null
+++ b/include/core/SkLineClipper.h
@@ -0,0 +1,48 @@
+
+/*
+ * 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,
+        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]
+            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/include/core/SkMMapStream.h b/include/core/SkMMapStream.h
new file mode 100644
index 0000000..a3b35f2
--- /dev/null
+++ b/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/include/core/SkMallocPixelRef.h b/include/core/SkMallocPixelRef.h
new file mode 100644
index 0000000..2241a51
--- /dev/null
+++ b/include/core/SkMallocPixelRef.h
@@ -0,0 +1,51 @@
+
+/*
+ * 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, bool ownPixels = true);
+    virtual ~SkMallocPixelRef();
+
+    //! Return the allocation size for the pixels
+    size_t getSize() const { return fSize; }
+    void* getAddr() const { return fStorage; }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMallocPixelRef)
+
+protected:
+    // overrides from SkPixelRef
+    virtual void* onLockPixels(SkColorTable**);
+    virtual void onUnlockPixels();
+
+    SkMallocPixelRef(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    void*           fStorage;
+    size_t          fSize;
+    SkColorTable*   fCTable;
+    bool            fOwnPixels;
+
+    typedef SkPixelRef INHERITED;
+};
+
+
+#endif
diff --git a/include/core/SkMask.h b/include/core/SkMask.h
new file mode 100644
index 0000000..5dadb63
--- /dev/null
+++ b/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/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h
new file mode 100644
index 0000000..47cf516
--- /dev/null
+++ b/include/core/SkMaskFilter.h
@@ -0,0 +1,116 @@
+
+/*
+ * Copyright 2006 The Android 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"
+#include "SkPaint.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 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;
+
+    /** 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);
+
+    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& buffer) : INHERITED(buffer) {}
+
+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,
+                    SkPaint::Style style);
+
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
+
diff --git a/include/core/SkMath.h b/include/core/SkMath.h
new file mode 100644
index 0000000..35f7eda
--- /dev/null
+++ b/include/core/SkMath.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 SkMath_DEFINED
+#define SkMath_DEFINED
+
+#include "SkTypes.h"
+
+/**
+ *  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)
+
+///////////////////////////////////////////////////////////////////////////////
+
+//! 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)
+ */
+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;
+}
+
+/**
+ *  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.
+ */
+#ifdef SK_ARM_HAS_EDSP
+    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/((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;
+}
+
+/**
+ *  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;
+}
+
+#endif
+
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
new file mode 100644
index 0000000..f2c6512
--- /dev/null
+++ b/include/core/SkMatrix.h
@@ -0,0 +1,650 @@
+
+/*
+ * Copyright 2006 The Android 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 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();
+        }
+        // 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 SK_WARN_UNUSED_RESULT 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;
+
+    /** 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));
+    }
+
+#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);
+#endif
+    friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
+        return !(a == b);
+    }
+
+    enum {
+        // 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 writeToMemory(void* buffer) const;
+    // return the number of bytes read
+    uint32_t readFromMemory(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 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 uint32_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) & mask)
+                 == (kUnknown_Mask | kOnlyPerspectiveValid_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/include/core/SkMetaData.h b/include/core/SkMetaData.h
new file mode 100644
index 0000000..86f186f
--- /dev/null
+++ b/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/include/core/SkOSFile.h b/include/core/SkOSFile.h
new file mode 100644
index 0000000..b5477cd
--- /dev/null
+++ b/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) || defined(SK_BUILD_FOR_IOS)
+    #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) || defined(SK_BUILD_FOR_IOS)
+        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/include/core/SkPackBits.h b/include/core/SkPackBits.h
new file mode 100644
index 0000000..f0614a0
--- /dev/null
+++ b/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/include/core/SkPaint.h b/include/core/SkPaint.h
new file mode 100644
index 0000000..16b1b84
--- /dev/null
+++ b/include/core/SkPaint.h
@@ -0,0 +1,991 @@
+
+
+/*
+ * Copyright 2006 The Android 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 "SkColor.h"
+#include "SkDrawLooper.h"
+#include "SkXfermode.h"
+
+class SkAnnotation;
+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;
+struct SkPoint;
+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;
+
+    /** 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.
+     *  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
+        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*);
+
+    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.
+     */
+    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);
+
+    /** 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);
+
+#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
+    };
+
+    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 advance 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;
+
+    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&);
+
+    uint32_t getGenerationID() const;
+
+    /** Returns the base glyph count for the strike associated with this paint
+    */
+    unsigned getBaseGlyphCount(SkUnichar text) 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;
+    SkXfermode*     fXfermode;
+    SkMaskFilter*   fMaskFilter;
+    SkColorFilter*  fColorFilter;
+    SkRasterizer*   fRasterizer;
+    SkDrawLooper*   fLooper;
+    SkImageFilter*  fImageFilter;
+    SkAnnotation*   fAnnotation;
+
+    SkColor         fColor;
+    SkScalar        fWidth;
+    SkScalar        fMiterLimit;
+    // 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,
+    };
+
+    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;
+
+    static void Term();
+
+    enum {
+        kCanonicalTextSizeForPaths = 64
+    };
+    friend class SkAutoGlyphCache;
+    friend class SkCanvas;
+    friend class SkDraw;
+    friend class SkGraphics; // So Term() can be called.
+    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
+};
+
+#endif
+
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
new file mode 100644
index 0000000..be5f612
--- /dev/null
+++ b/include/core/SkPath.h
@@ -0,0 +1,908 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkInstCnt.h"
+#include "SkMatrix.h"
+#include "SkTDArray.h"
+#include "SkRefCnt.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 SkPathRef;
+
+#ifndef SK_DEBUG_PATH_REF
+    #define SK_DEBUG_PATH_REF 0
+#endif
+
+/** \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:
+    SK_DECLARE_INST_COUNT_ROOT(SkPath);
+
+    SkPath();
+    SkPath(const SkPath&);
+    ~SkPath();
+
+    SkPath& operator=(const SkPath&);
+
+    friend  SK_API 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);
+    }
+
+    /** 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.
+        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;
+
+    /**
+     *  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);
+    }
+
+    /** 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) &&
+               p2.equalsWithinTolerance(p3);
+    }
+
+    /** 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) &&
+               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;
+
+    /** Return the number of points in the path
+     */
+    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
+         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;
+
+    /** 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);
+
+    /** 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 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
+        @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
+            @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], 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
+            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;
+
+        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.
+    */
+    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
+                        This must not be NULL.
+            @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;
+    };
+
+    /**
+     *  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;
+
+    /**
+     *  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;
+    const SkPath* getSourcePath() const;
+    void setSourcePath(const SkPath* path);
+#endif
+
+    SkDEBUGCODE(void validate() const;)
+
+private:
+    enum SerializationOffsets {
+        kIsFinite_SerializationShift = 25,
+        kIsOval_SerializationShift = 24,
+        kConvexity_SerializationShift = 16,
+        kFillType_SerializationShift = 8,
+        kSegmentMask_SerializationShift = 0
+    };
+
+#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 SkBool8     fIsFinite;    // only meaningful if bounds are valid
+    mutable SkBool8     fIsOval;
+#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();
+
+    inline bool hasOnlyMoveTos() const;
+
+    friend class SkAutoPathBoundsUpdate;
+    friend class SkAutoDisableOvalCheck;
+    friend class SkBench_AddPathTest; // perf test pathTo/reversePathTo
+};
+
+#endif
diff --git a/include/core/SkPathEffect.h b/include/core/SkPathEffect.h
new file mode 100644
index 0000000..5d9b68a
--- /dev/null
+++ b/include/core/SkPathEffect.h
@@ -0,0 +1,226 @@
+
+/*
+ * Copyright 2006 The Android 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"
+#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;
+};
+
+/** \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:
+    SK_DECLARE_INST_COUNT(SkPathEffect)
+
+    SkPathEffect() {}
+
+    /**
+     *  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*) = 0;
+
+    /**
+     *  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);
+
+protected:
+    SkPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    // illegal
+    SkPathEffect(const SkPathEffect&);
+    SkPathEffect& operator=(const SkPathEffect&);
+
+    typedef SkFlattenable INHERITED;
+};
+
+/** \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&) const 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) {}
+
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*) SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposePathEffect)
+
+protected:
+    SkComposePathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    // 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) {}
+
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*) SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSumPathEffect)
+
+protected:
+    SkSumPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    // illegal
+    SkSumPathEffect(const SkSumPathEffect&);
+    SkSumPathEffect& operator=(const SkSumPathEffect&);
+
+    typedef SkPairPathEffect INHERITED;
+};
+
+#endif
+
diff --git a/include/core/SkPathMeasure.h b/include/core/SkPathMeasure.h
new file mode 100644
index 0000000..6866cce
--- /dev/null
+++ b/include/core/SkPathMeasure.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 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 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 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
+        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/SkPicture.h b/include/core/SkPicture.h
new file mode 100644
index 0000000..203efde
--- /dev/null
+++ b/include/core/SkPicture.h
@@ -0,0 +1,189 @@
+
+/*
+ * 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"
+#include "SkSerializationHelpers.h"
+
+class SkBitmap;
+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:
+    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.
+    */
+    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);
+    /**
+     *  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
+            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,
+        /*  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.
+        @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; }
+
+    /**
+     *  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;
+
+    /** 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;
+
+    /** Used by the R-Tree when kOptimizeForClippedPlayback_RecordingFlag is
+        set, these were empirically determined to produce reasonable performance
+        in most cases.
+    */
+    static const int kRTreeMinChildren = 6;
+    static const int kRTreeMaxChildren = 11;
+
+    friend class SkFlatPicture;
+    friend class SkPicturePlayback;
+
+    typedef SkRefCnt INHERITED;
+};
+
+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/include/core/SkPixelRef.h b/include/core/SkPixelRef.h
new file mode 100644
index 0000000..9222b29
--- /dev/null
+++ b/include/core/SkPixelRef.h
@@ -0,0 +1,212 @@
+
+/*
+ * 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"
+#include "SkFlattenable.h"
+
+class SkColorTable;
+struct SkIRect;
+class SkMutex;
+
+// this is an opaque class, not interpreted by skia
+class SkGpuTexture;
+
+/** \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 SkFlattenable {
+public:
+    SK_DECLARE_INST_COUNT(SkPixelRef)
+
+    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; }
+
+#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
+
+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; }
+
+    // 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);
+
+    /**
+     *  If a subclass passed a particular mutex to the base constructor, it can
+     *  override that to go back to the default mutex by calling this. However,
+     *  this should only be called from within the subclass' constructor.
+     */
+    void useDefaultMutex() { this->setMutex(NULL); }
+
+private:
+
+    SkBaseMutex*    fMutex; // must remain in scope for the life of this object
+    void*           fPixels;
+    SkColorTable*   fColorTable;    // we do not track ownership, subclass does
+    int             fLockCount;
+
+    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
+    bool    fIsImmutable;
+    // only ever set in constructor, const after that
+    bool    fPreLocked;
+
+    void setMutex(SkBaseMutex* mutex);
+
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
diff --git a/include/core/SkPoint.h b/include/core/SkPoint.h
new file mode 100644
index 0000000..1ba710d
--- /dev/null
+++ b/include/core/SkPoint.h
@@ -0,0 +1,510 @@
+
+/*
+ * Copyright 2006 The Android 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; }
+
+    /**
+     *  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; }
+
+    /** 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)
+#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);
+    }
+
+    /** 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 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;
+    }
+
+    friend bool operator!=(const SkPoint& a, const SkPoint& b) {
+        return a.fX != b.fX || a.fY != b.fY;
+    }
+
+    /** 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& 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
+        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/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
new file mode 100644
index 0000000..cbfe9b1
--- /dev/null
+++ b/include/core/SkPostConfig.h
@@ -0,0 +1,328 @@
+
+/*
+ * Copyright 2006 The Android 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)
+    #define SK_SCALAR_IS_FLOAT
+#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
+
+#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
+ * 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));
+            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 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
+#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__) && !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
+#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
+
+//////////////////////////////////////////////////////////////////////
+
+#ifndef SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+#define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 1
+#endif
diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h
new file mode 100644
index 0000000..756aaea
--- /dev/null
+++ b/include/core/SkPreConfig.h
@@ -0,0 +1,254 @@
+
+/*
+ * Copyright 2006 The Android 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_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__) || \
+          defined(__GLIBC__) || defined(__GNU__)
+        #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
+    #if !defined(SK_WARN_UNUSED_RESULT)
+        #define SK_WARN_UNUSED_RESULT
+    #endif
+    #include "sk_stdint.h"
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(SK_RESTRICT)
+    #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
+#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
+
+//////////////////////////////////////////////////////////////////////
+
+/**
+ *  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
+
+//////////////////////////////////////////////////////////////////////
+
+#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
+
+//////////////////////////////////////////////////////////////////////
+
+/**
+ * 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/SkRandom.h b/include/core/SkRandom.h
new file mode 100644
index 0000000..0f9b9aa
--- /dev/null
+++ b/include/core/SkRandom.h
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkRandom_DEFINED
+#define SkRandom_DEFINED
+
+#include "Sk64.h"
+#include "SkScalar.h"
+
+/** \class SkRandom
+
+    Utility class that implements pseudo random 32bit numbers using a fast
+    linear equation. Unlike rand(), this class holds its own seed (initially
+    set to 0), so that multiple instances can be used with no side-effects.
+*/
+class SkRandom {
+public:
+    SkRandom() : fSeed(0) {}
+    SkRandom(uint32_t seed) : fSeed(seed) {}
+
+    /** Return the next pseudo random number as an unsigned 32bit value.
+    */
+    uint32_t nextU() { uint32_t r = fSeed * kMul + kAdd; fSeed = r; return r; }
+
+    /** Return the next pseudo random number as a signed 32bit value.
+    */
+    int32_t nextS() { return (int32_t)this->nextU(); }
+
+    /** Return the next pseudo random number as an unsigned 16bit value.
+    */
+    U16CPU nextU16() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number as a signed 16bit value.
+    */
+    S16CPU nextS16() { return this->nextS() >> 16; }
+
+    /** Return the next pseudo random number, as an unsigned value of
+        at most bitCount bits.
+        @param bitCount The maximum number of bits to be returned
+    */
+    uint32_t nextBits(unsigned bitCount) {
+        SkASSERT(bitCount > 0 && bitCount <= 32);
+        return this->nextU() >> (32 - bitCount);
+    }
+
+    /** Return the next pseudo random unsigned number, mapped to lie within
+        [min, max] inclusive.
+    */
+    uint32_t nextRangeU(uint32_t min, uint32_t max) {
+        SkASSERT(min <= max);
+        return min + this->nextU() % (max - min + 1);
+    }
+
+    /** Return the next pseudo random 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->nextSScalar1(), (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; }
+
+    /** Return the next pseudo random number as a signed 64bit value.
+    */
+    void next64(Sk64* a) {
+        SkASSERT(a);
+        a->set(this->nextS(), this->nextU());
+    }
+
+    /**
+     *  Return the current seed. This allows the caller to later reset to the
+     *  same seed (using setSeed) so it can generate the same sequence.
+     */
+    int32_t getSeed() const { return fSeed; }
+
+    /** Set the seed of the random object. The seed is initialized to 0 when the
+        object is first created, and is updated each time the next pseudo random
+        number is requested.
+    */
+    void setSeed(int32_t seed) { fSeed = (uint32_t)seed; }
+
+private:
+    //  See "Numerical Recipes in C", 1992 page 284 for these constants
+    enum {
+        kMul = 1664525,
+        kAdd = 1013904223
+    };
+    uint32_t fSeed;
+};
+
+#endif
+
diff --git a/include/core/SkRasterizer.h b/include/core/SkRasterizer.h
new file mode 100644
index 0000000..5f71d3f
--- /dev/null
+++ b/include/core/SkRasterizer.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 SkRasterizer_DEFINED
+#define SkRasterizer_DEFINED
+
+#include "SkFlattenable.h"
+#include "SkMask.h"
+
+class SkMaskFilter;
+class SkMatrix;
+class SkPath;
+struct SkIRect;
+
+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);
+
+protected:
+    SkRasterizer(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+    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/include/core/SkReader32.h b/include/core/SkReader32.h
new file mode 100644
index 0000000..4f6809f
--- /dev/null
+++ b/include/core/SkReader32.h
@@ -0,0 +1,152 @@
+
+/*
+ * 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 "SkMatrix.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#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 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 SkToU32(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;
+    }
+
+    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;
+        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(); }
+
+    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);
+    }
+
+    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
+     *  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/include/core/SkRect.h b/include/core/SkRect.h
new file mode 100644
index 0000000..21a3736
--- /dev/null
+++ b/include/core/SkRect.h
@@ -0,0 +1,695 @@
+
+/*
+ * Copyright 2006 The Android 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 SK_WARN_UNUSED_RESULT MakeEmpty() {
+        SkIRect r;
+        r.setEmpty();
+        return r;
+    }
+
+    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 SK_WARN_UNUSED_RESULT MakeSize(const SkISize& size) {
+        SkIRect r;
+        r.set(0, 0, size.width(), size.height());
+        return r;
+    }
+
+    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 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;
+    }
+
+    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 holds true for dy and the top and bottom.
+    */
+    void inset(int32_t dx, int32_t 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 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
+        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;
+    }
+
+    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.
+    */
+    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;
+    }
+
+    /**
+     *  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.
+    */
+    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& SK_WARN_UNUSED_RESULT EmptyIRect() {
+        static const SkIRect gEmpty = { 0, 0, 0, 0 };
+        return gEmpty;
+    }
+};
+
+/** \struct SkRect
+*/
+struct SK_API SkRect {
+    SkScalar    fLeft, fTop, fRight, fBottom;
+
+    static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
+        SkRect r;
+        r.setEmpty();
+        return r;
+    }
+
+    static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
+        SkRect r;
+        r.set(0, 0, w, h);
+        return r;
+    }
+
+    static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
+        SkRect r;
+        r.set(0, 0, size.width(), size.height());
+        return r;
+    }
+
+    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 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;
+    }
+
+    /**
+     *  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
+        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
+        // 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 == fLeft)  | (SK_FixedNaN == fTop) |
+                    (SK_FixedNaN == fRight) | (SK_FixedNaN == fBottom);
+        return !isNaN;
+#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; }
+    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) {
+        // 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) {
+        (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) {
+        fLeft = x;
+        fTop = y;
+        fRight = x + width;
+        fBottom = y + height;
+    }
+
+    void setWH(SkScalar width, SkScalar height) {
+        fLeft = 0;
+        fTop = 0;
+        fRight = width;
+        fBottom = 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 holds
+       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/include/core/SkRefCnt.h b/include/core/SkRefCnt.h
new file mode 100644
index 0000000..3a0f8be
--- /dev/null
+++ b/include/core/SkRefCnt.h
@@ -0,0 +1,255 @@
+
+/*
+ * Copyright 2006 The Android 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"
+#include "SkInstCnt.h"
+#include "SkTemplates.h"
+
+/** \class SkRefCnt
+
+    SkRefCnt is the base class for objects that may be shared by multiple
+    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.
+    */
+    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);  // No barrier required.
+    }
+
+    /** Decrement the reference count. If the reference count is 1 before the
+        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) {
+            // 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();
+        }
+    }
+
+    void validate() const {
+        SkASSERT(fRefCnt > 0);
+    }
+
+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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** 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)
+
+
+/** Call obj->ref() and return obj. The obj must not be NULL.
+ */
+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()
+ */
+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;
+    }
+
+    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
+     *  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;
+    }
+
+    /**
+     * 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;
+};
+
+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/SkRegion.h b/include/core/SkRegion.h
new file mode 100644
index 0000000..ab8f220
--- /dev/null
+++ b/include/core/SkRegion.h
@@ -0,0 +1,436 @@
+
+/*
+ * 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 writeToMemory(void* buffer) const;
+
+    /**
+     *  Initialized the region from the buffer, returning the number
+     *  of bytes actually read.
+     */
+    uint32_t readFromMemory(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 {
+        // T
+        // [B N L R S]
+        // S
+        kRectRegionRuns = 7
+    };
+
+    friend class android::Region;    // needed for marshalling efficiently
+
+    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();
+
+    /**
+     *  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]);
+
+    // 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;
+    friend class Spanerator;
+    friend class SkRgnBuilder;
+    friend class SkFlatRegion;
+};
+
+#endif
diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h
new file mode 100644
index 0000000..f357e6a
--- /dev/null
+++ b/include/core/SkScalar.h
@@ -0,0 +1,358 @@
+
+/*
+ * Copyright 2006 The Android 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           (*SkTCast<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      (*SkTCast<const float*>(&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) {
+        // 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
+    /** 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 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)
+    /** 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)
+    #define SkScalarToFloat(n)  SkFixedToFloat(n)
+    #define SkFloatToScalar(n)  SkFloatToFixed(n)
+
+    #define SkScalarToDouble(n) SkFixedToDouble(n)
+    #define SkDoubleToScalar(n) SkDoubleToFixed(n)
+    #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))
+
+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);
+}
+
+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
+    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/include/core/SkScalarCompare.h b/include/core/SkScalarCompare.h
new file mode 100644
index 0000000..0842fdd
--- /dev/null
+++ b/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/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
new file mode 100644
index 0000000..a0a1b05
--- /dev/null
+++ b/include/core/SkShader.h
@@ -0,0 +1,366 @@
+
+/*
+ * Copyright 2006 The Android 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 GrContext;
+class GrCustomStage;
+class GrSamplerState;
+
+/** \class SkShader
+ *
+ *  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:
+    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.
+     */
+    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 {
+        /** 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
+    };
+
+    // 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;
+
+    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
+     */
+    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.
+        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 = 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
+        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]) 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,
+        kConical_GradientType,
+        kLast_GradientType = kConical_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;
+
+    /**
+     *  If the shader subclass has a GrCustomStage implementation, this installs
+     *  a custom stage on the sampler. A GrContext pointer is required since custom
+     *  stages may need to create textures. The sampler parameter is necessary to set a
+     *  texture matrix. It will eventually be removed and this function will operate as a
+     *  GrCustomStage factory.
+     */
+    virtual bool asNewCustomStage(GrContext* context, GrSamplerState* sampler) const;
+
+    //////////////////////////////////////////////////////////////////////////
+    //  Factory methods for stock shaders
+
+    /** Call this to create a new shader that will draw with the specified bitmap.
+     *
+     *  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);
+
+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& );
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+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/SkSize.h b/include/core/SkSize.h
new file mode 100644
index 0000000..808583f
--- /dev/null
+++ b/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/include/core/SkStream.h b/include/core/SkStream.h
new file mode 100644
index 0000000..08d0878
--- /dev/null
+++ b/include/core/SkStream.h
@@ -0,0 +1,375 @@
+
+/*
+ * Copyright 2006 The Android 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:
+    SK_DECLARE_INST_COUNT(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();
+
+    /**
+     *  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
+        @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:
+    SK_DECLARE_INST_COUNT(SkFILEStream)
+
+    /** 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;
+
+    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()
+    */
+    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;
+
+    typedef SkStream INHERITED;
+};
+
+/** \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:
+    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.
+        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);
+
+    typedef SkStream INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+
+class SK_API SkFILEWStream : public SkWStream {
+public:
+    SK_DECLARE_INST_COUNT(SkFILEWStream)
+
+    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;
+
+    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; }
+
+private:
+    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();
+
+    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();
+
+    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
+typedef SkFILEStream SkURLStream;
+
+#endif
diff --git a/include/core/SkString.h b/include/core/SkString.h
new file mode 100644
index 0000000..5896ef6
--- /dev/null
+++ b/include/core/SkString.h
@@ -0,0 +1,206 @@
+
+/*
+ * Copyright 2006 The Android 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
+*/
+
+static 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[]);
+int SkStrStartsWithOneOf(const char string[], const char prefixes[]);
+static bool SkStrContains(const char string[], const char substring[]) {
+    SkASSERT(string);
+    SkASSERT(substring);
+    return (NULL != strstr(string, substring));
+}
+
+#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
+
+char* SkStrAppendFloat(char buffer[], float);
+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);
+    }
+    bool contains(const char substring[]) const {
+        return SkStrContains(fRec->data(), substring);
+    }
+
+    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[], ...) 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);
+
+    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/SkSurface.h b/include/core/SkSurface.h
new file mode 100644
index 0000000..18ae5cc
--- /dev/null
+++ b/include/core/SkSurface.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 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&, SkColorSpace*,
+                                      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&, SkColorSpace*);
+
+    /**
+     *  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&,
+                                      SkColorSpace*, 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&, SkColorSpace*);
+
+    /**
+     *  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
new file mode 100644
index 0000000..3ca6679
--- /dev/null
+++ b/include/core/SkTArray.h
@@ -0,0 +1,413 @@
+/*
+ * 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 || 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;
+            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/include/core/SkTDArray.h b/include/core/SkTDArray.h
new file mode 100644
index 0000000..997a070
--- /dev/null
+++ b/include/core/SkTDArray.h
@@ -0,0 +1,342 @@
+
+/*
+ * Copyright 2006 The Android 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 (int)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) {
+        size_t 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);
+        size_t 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);
+        size_t 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;
+    }
+
+    /**
+     * 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; }
+    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) {
+            SkDELETE (*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/include/core/SkTDLinkedList.h b/include/core/SkTDLinkedList.h
new file mode 100644
index 0000000..9247812
--- /dev/null
+++ b/include/core/SkTDLinkedList.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkTDLinkedList_DEFINED
+#define SkTDLinkedList_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 SkTDLinkedList class. It should be placed in the private section
+ * of any class that will be stored in a double linked list.
+ */
+#define SK_DEFINE_DLINKEDLIST_INTERFACE(ClassName)              \
+    friend class SkTDLinkedList<ClassName>;                     \
+    /* back pointer to the owning list - for debugging */       \
+    SkDEBUGCODE(SkPtrWrapper<SkTDLinkedList<ClassName> > fList;)\
+    SkPtrWrapper<ClassName> fPrev;                              \
+    SkPtrWrapper<ClassName> fNext;
+
+/**
+ * This class implements a templated internal doubly linked list data structure.
+ */
+template <class T> class SkTDLinkedList : public SkNoncopyable {
+public:
+    SkTDLinkedList()
+        : 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
+    }
+
+    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() : fCur(NULL) {}
+
+        T* init(SkTDLinkedList& list, IterStart startLoc) {
+            if (kHead_IterStart == startLoc) {
+                fCur = list.fHead;
+            } else {
+                SkASSERT(kTail_IterStart == startLoc);
+                fCur = list.fTail;
+            }
+
+            return fCur;
+        }
+
+        /**
+         * Return the next/previous element in the list or NULL if at the end.
+         */
+        T* next() {
+            if (NULL == fCur) {
+                return NULL;
+            }
+
+            fCur = fCur->fNext;
+            return fCur;
+        }
+
+        T* prev() {
+            if (NULL == fCur) {
+                return NULL;
+            }
+
+            fCur = fCur->fPrev;
+            return fCur;
+        }
+
+    private:
+        T* fCur;
+    };
+
+#ifdef SK_DEBUG
+    void validate() const {
+        GrAssert(!fHead == !fTail);
+    }
+
+    /**
+     * 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 // SkTDLinkedList_DEFINED
diff --git a/include/core/SkTDStack.h b/include/core/SkTDStack.h
new file mode 100644
index 0000000..be34e01
--- /dev/null
+++ b/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/include/core/SkTDict.h b/include/core/SkTDict.h
new file mode 100644
index 0000000..3620899
--- /dev/null
+++ b/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/include/core/SkTLazy.h b/include/core/SkTLazy.h
new file mode 100644
index 0000000..cd1e8f2
--- /dev/null
+++ b/include/core/SkTLazy.h
@@ -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.
+ */
+
+
+
+#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)];
+};
+
+/**
+ * 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) {}
+
+    /**
+     * Returns a writable T*. The first time this is called the initial object is cloned.
+     */
+    T* writable() {
+        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/SkTRegistry.h b/include/core/SkTRegistry.h
new file mode 100644
index 0000000..34fcffd
--- /dev/null
+++ b/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/include/core/SkTScopedPtr.h b/include/core/SkTScopedPtr.h
new file mode 100644
index 0000000..580d72f
--- /dev/null
+++ b/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/include/core/SkTSearch.h b/include/core/SkTSearch.h
new file mode 100644
index 0000000..2541634
--- /dev/null
+++ b/include/core/SkTSearch.h
@@ -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.
+ */
+
+
+#ifndef SkTSearch_DEFINED
+#define SkTSearch_DEFINED
+
+#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)
+{
+    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 (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;
+}
+
+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;
+}
+
+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[],
+                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];
+};
+
+// 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
new file mode 100644
index 0000000..b7831ea
--- /dev/null
+++ b/include/core/SkTemplates.h
@@ -0,0 +1,352 @@
+
+/*
+ * Copyright 2006 The Android 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.
+*/
+
+/**
+ *  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
+    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) : fObj(obj) {}
+    ~SkAutoTDelete() { delete fObj; }
+
+    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;
+};
+
+template <typename T> class SkAutoTDeleteArray : SkNoncopyable {
+public:
+    SkAutoTDeleteArray(T array[]) : fArray(array) {}
+    ~SkAutoTDeleteArray() { SkDELETE_ARRAY(fArray); }
+
+    T*      get() const { return fArray; }
+    void    free() { SkDELETE_ARRAY(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:
+    SkAutoTArray() {
+        fArray = NULL;
+        SkDEBUGCODE(fCount = 0;)
+    }
+    /** Allocate count number of T elements
+     */
+    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];
+        }
+        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 < (unsigned)fCount);
+        return fArray[index];
+    }
+
+private:
+    T*  fArray;
+    SkDEBUGCODE(int 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/include/core/SkThread.h b/include/core/SkThread.h
new file mode 100644
index 0000000..4a2499a
--- /dev/null
+++ b/include/core/SkThread.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 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_add(int32_t*, int32_t);
+int32_t sk_atomic_dec(int32_t*);
+int32_t sk_atomic_conditional_inc(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();
+    }
+
+    SkAutoMutexAcquire(SkBaseMutex* mutex) : fMutex(mutex) {
+        if (mutex) {
+            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/include/core/SkThread_platform.h b/include/core/SkThread_platform.h
new file mode 100644
index 0000000..ce7a871
--- /dev/null
+++ b/include/core/SkThread_platform.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 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 inline __attribute__((always_inline)) int32_t sk_atomic_inc(int32_t *addr) {
+    return __sync_fetch_and_add(addr, 1);
+}
+
+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
+
+/* 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_add(addr, inc)    android_atomic_add(inc, addr)
+#define sk_atomic_dec(addr)         android_atomic_dec(addr)
+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);
+}
+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;
+        }
+    }
+}
+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 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 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
+
+#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/include/core/SkTime.h b/include/core/SkTime.h
new file mode 100644
index 0000000..ede1fd9
--- /dev/null
+++ b/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/include/core/SkTrace.h b/include/core/SkTrace.h
new file mode 100644
index 0000000..2d48799
--- /dev/null
+++ b/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/include/core/SkTypeface.h b/include/core/SkTypeface.h
new file mode 100644
index 0000000..7fb6b81
--- /dev/null
+++ b/include/core/SkTypeface.h
@@ -0,0 +1,203 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkWeakRefCnt.h"
+
+class SkStream;
+class SkAdvancedTypefaceMetrics;
+class SkWStream;
+
+typedef uint32_t SkFontID;
+typedef uint32_t SkFontTableTag;
+
+/** \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 SkWeakRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkTypeface)
+
+    /** 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 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;
+
+    // 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 SkWeakRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/core/SkTypes.h b/include/core/SkTypes.h
new file mode 100644
index 0000000..d44c560
--- /dev/null
+++ b/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   -SK_MaxS32
+#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)    (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]))
+
+#define SkAlign2(x)     (((x) + 1) >> 1 << 1)
+#define SkIsAlign2(x)   (0 == ((x) & 1))
+
+#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))
+
+/** 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/include/core/SkUnPreMultiply.h b/include/core/SkUnPreMultiply.h
new file mode 100644
index 0000000..4fa5d57
--- /dev/null
+++ b/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/include/core/SkUnitMapper.h b/include/core/SkUnitMapper.h
new file mode 100644
index 0000000..45bb987
--- /dev/null
+++ b/include/core/SkUnitMapper.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 SkUnitMapper_DEFINED
+#define SkUnitMapper_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+
+#include "SkFlattenable.h"
+
+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/SkUtils.h b/include/core/SkUtils.h
new file mode 100644
index 0000000..b700b96
--- /dev/null
+++ b/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/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
new file mode 100644
index 0000000..9354d6d
--- /dev/null
+++ b/include/core/SkWriter32.h
@@ -0,0 +1,229 @@
+
+/*
+ * 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 "SkPath.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkMatrix.h"
+#include "SkRegion.h"
+
+class SkStream;
+class SkWStream;
+
+class SkWriter32 : SkNoncopyable {
+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),
+          fHeadIsExternalStorage(false) {}
+
+    ~SkWriter32();
+
+    /**
+     *  Returns the single block backing the writer, or NULL if the memory is
+     *  to be dynamically allocated.
+     */
+    void* getSingleBlock() const { return fSingleBlock; }
+
+    // return the current offset (will always be a multiple of 4)
+    uint32_t bytesWritten() const { return fSize; }
+    // DEPRECATED: use byetsWritten instead
+    uint32_t  size() const { return this->bytesWritten(); }
+
+    void      reset();
+    uint32_t* reserve(size_t size); // size MUST be multiple of 4
+
+    /**
+     *  Specify the single block to back the writer, rathern than dynamically
+     *  allocating the memory. If block == NULL, then the writer reverts to
+     *  dynamic allocation (and resets).
+     */
+    void reset(void* block, size_t size);
+
+    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 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);
+    }
+
+    /**
+     *  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);
+    }
+
+    /**
+     *  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);
+
+    /**
+     *  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 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:
+    size_t      fMinSize;
+    uint32_t    fSize;
+
+    char*       fSingleBlock;
+    uint32_t    fSingleBlockSize;
+
+    struct Block;
+    Block*  fHead;
+    Block*  fTail;
+
+    bool fHeadIsExternalStorage;
+
+    Block* newBlock(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
new file mode 100644
index 0000000..99d5a9c
--- /dev/null
+++ b/include/core/SkXfermode.h
@@ -0,0 +1,236 @@
+
+/*
+ * Copyright 2006 The Android 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:
+    SK_DECLARE_INST_COUNT(SkXfermode)
+
+    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_GROUP()
+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;
+
+    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) {
+        fProc = proc;
+    }
+
+private:
+    SkXfermodeProc  fProc;
+
+    typedef SkXfermode INHERITED;
+};
+
+#endif
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
new file mode 100644
index 0000000..6624b4b
--- /dev/null
+++ b/include/effects/Sk1DPathEffect.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 Sk1DPathEffect_DEFINED
+#define Sk1DPathEffect_DEFINED
+
+#include "SkPathEffect.h"
+#include "SkPath.h"
+
+class SkPathMeasure;
+
+// This class is not exported to java.
+class SK_API Sk1DPathEffect : public SkPathEffect {
+public:
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*) SK_OVERRIDE;
+
+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 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
+        @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);
+
+    virtual bool filterPath(SkPath*, const SkPath&, SkStrokeRec*) SK_OVERRIDE;
+
+    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;
+
+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/include/effects/Sk2DPathEffect.h b/include/effects/Sk2DPathEffect.h
new file mode 100644
index 0000000..feb0da6
--- /dev/null
+++ b/include/effects/Sk2DPathEffect.h
@@ -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.
+ */
+
+#ifndef Sk2DPathEffect_DEFINED
+#define Sk2DPathEffect_DEFINED
+
+#include "SkPath.h"
+#include "SkPathEffect.h"
+#include "SkMatrix.h"
+
+class SK_API Sk2DPathEffect : public SkPathEffect {
+public:
+    Sk2DPathEffect(const SkMatrix& mat);
+
+    // overrides
+    virtual bool filterPath(SkPath*, const SkPath&, SkStrokeRec*) SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sk2DPathEffect)
+
+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&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    SkMatrix    fMatrix, fInverse;
+    bool        fMatrixIsInvertible;
+
+    // illegal
+    Sk2DPathEffect(const Sk2DPathEffect&);
+    Sk2DPathEffect& operator=(const Sk2DPathEffect&);
+
+    friend class Sk2DPathEffectBlitter;
+    typedef SkPathEffect INHERITED;
+};
+
+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* rec) SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLine2DPathEffect)
+
+protected:
+    virtual void nextSpan(int u, int v, int ucount, SkPath* dst) 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&);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPath2DPathEffect)
+
+protected:
+    SkPath2DPathEffect(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const 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/include/effects/SkArithmeticMode.h b/include/effects/SkArithmeticMode.h
new file mode 100644
index 0000000..87da333
--- /dev/null
+++ b/include/effects/SkArithmeticMode.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 SkArithmeticMode_DEFINED
+#define SkArithmeticMode_DEFINED
+
+#include "SkXfermode.h"
+
+class SK_API 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/include/effects/SkAvoidXfermode.h b/include/effects/SkAvoidXfermode.h
new file mode 100644
index 0000000..1be2679
--- /dev/null
+++ b/include/effects/SkAvoidXfermode.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2006 The Android 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 SK_API 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;
+
+    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;
+
+    typedef SkXfermode 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..c16186f
--- /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 GrTexture* onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) 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
new file mode 100644
index 0000000..fa730e3
--- /dev/null
+++ b/include/effects/SkBlurDrawLooper.h
@@ -0,0 +1,69 @@
+/*
+ * 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);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurDrawLooper)
+
+protected:
+    SkBlurDrawLooper(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+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/include/effects/SkBlurImageFilter.h b/include/effects/SkBlurImageFilter.h
new file mode 100644
index 0000000..757fee5
--- /dev/null
+++ b/include/effects/SkBlurImageFilter.h
@@ -0,0 +1,35 @@
+/*
+ * 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 "SkSingleInputImageFilter.h"
+#include "SkSize.h"
+
+class SK_API SkBlurImageFilter : public SkSingleInputImageFilter {
+public:
+    SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input = NULL);
+
+    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;
+
+    bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    virtual GrTexture* onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) SK_OVERRIDE;
+
+private:
+    SkSize   fSigma;
+    typedef SkSingleInputImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkBlurMaskFilter.h b/include/effects/SkBlurMaskFilter.h
new file mode 100644
index 0000000..2ab321a
--- /dev/null
+++ b/include/effects/SkBlurMaskFilter.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 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_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..e9124f9
--- /dev/null
+++ b/include/effects/SkColorFilterImageFilter.h
@@ -0,0 +1,37 @@
+/*
+ * 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:
+    SkColorFilterImageFilter(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:
+    SkColorFilter*  fColorFilter;
+
+    typedef SkSingleInputImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkColorMatrix.h b/include/effects/SkColorMatrix.h
new file mode 100644
index 0000000..ff02d9d
--- /dev/null
+++ b/include/effects/SkColorMatrix.h
@@ -0,0 +1,44 @@
+/*
+ * 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 SK_API 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/include/effects/SkColorMatrixFilter.h b/include/effects/SkColorMatrixFilter.h
new file mode 100644
index 0000000..005781f
--- /dev/null
+++ b/include/effects/SkColorMatrixFilter.h
@@ -0,0 +1,52 @@
+/*
+ * 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:
+    explicit SkColorMatrixFilter(const SkColorMatrix&);
+    SkColorMatrixFilter(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;
+
+    struct State {
+        int32_t fArray[20];
+        int     fShift;
+        int32_t fResult[4];
+    };
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorMatrixFilter)
+
+protected:
+    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);
+
+    Proc        fProc;
+    State       fState;
+    uint32_t    fFlags;
+
+    void initState(const SkScalar array[20]);
+
+    typedef SkColorFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkCornerPathEffect.h b/include/effects/SkCornerPathEffect.h
new file mode 100644
index 0000000..88afea3
--- /dev/null
+++ b/include/effects/SkCornerPathEffect.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 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, SkStrokeRec*) SK_OVERRIDE;
+
+    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
new file mode 100644
index 0000000..0397466
--- /dev/null
+++ b/include/effects/SkDashPathEffect.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 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:
+    /** 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);
+    virtual ~SkDashPathEffect();
+
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*) SK_OVERRIDE;
+
+    // overrides for SkFlattenable
+    //  This method is not exported to java.
+    virtual Factory getFactory();
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+
+protected:
+    SkDashPathEffect(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+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/include/effects/SkDiscretePathEffect.h b/include/effects/SkDiscretePathEffect.h
new file mode 100644
index 0000000..6705748
--- /dev/null
+++ b/include/effects/SkDiscretePathEffect.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 SkDiscretePathEffect_DEFINED
+#define SkDiscretePathEffect_DEFINED
+
+#include "SkPathEffect.h"
+
+/** \class SkDiscretePathEffect
+
+    This path effect chops a path into discrete segments, and randomly displaces them.
+*/
+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.
+        Note: works on filled or framed paths
+    */
+    SkDiscretePathEffect(SkScalar segLength, SkScalar deviation);
+
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*) SK_OVERRIDE;
+
+    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/SkDrawExtraPathEffect.h b/include/effects/SkDrawExtraPathEffect.h
new file mode 100644
index 0000000..392a46b
--- /dev/null
+++ b/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/SkEmbossMaskFilter.h b/include/effects/SkEmbossMaskFilter.h
new file mode 100644
index 0000000..96c25b2
--- /dev/null
+++ b/include/effects/SkEmbossMaskFilter.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 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 SK_API 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);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmbossMaskFilter)
+
+protected:
+    SkEmbossMaskFilter(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    Light       fLight;
+    SkScalar    fBlurRadius;
+
+    typedef SkMaskFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkGradientShader.h b/include/effects/SkGradientShader.h
new file mode 100644
index 0000000..443be93
--- /dev/null
+++ b/include/effects/SkGradientShader.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 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 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.
+        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_GROUP()
+};
+
+#endif
+
diff --git a/include/effects/SkKernel33MaskFilter.h b/include/effects/SkKernel33MaskFilter.h
new file mode 100644
index 0000000..41e73c9
--- /dev/null
+++ b/include/effects/SkKernel33MaskFilter.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 SkKernel33MaskFilter_DEFINED
+#define SkKernel33MaskFilter_DEFINED
+
+#include "SkMaskFilter.h"
+
+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*);
+
+protected:
+    SkKernel33ProcMaskFilter(SkFlattenableReadBuffer& rb);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    int fPercent256;
+
+    typedef SkMaskFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkKernel33MaskFilter)
+
+private:
+    int fKernel[3][3];
+    int fShift;
+
+    SkKernel33MaskFilter(SkFlattenableReadBuffer& rb);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    typedef SkKernel33ProcMaskFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkLayerDrawLooper.h b/include/effects/SkLayerDrawLooper.h
new file mode 100644
index 0000000..5196af0
--- /dev/null
+++ b/include/effects/SkLayerDrawLooper.h
@@ -0,0 +1,136 @@
+/*
+ * 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:
+    SK_DECLARE_INST_COUNT(SkLayerDrawLooper)
+
+            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);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLayerDrawLooper)
+
+protected:
+    SkLayerDrawLooper(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+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/include/effects/SkLayerRasterizer.h b/include/effects/SkLayerRasterizer.h
new file mode 100644
index 0000000..7a1ef74
--- /dev/null
+++ b/include/effects/SkLayerRasterizer.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 SkLayerRasterizer_DEFINED
+#define SkLayerRasterizer_DEFINED
+
+#include "SkRasterizer.h"
+#include "SkDeque.h"
+#include "SkScalar.h"
+
+class SkPaint;
+
+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.
+    */
+    void addLayer(const SkPaint& paint, SkScalar dx, SkScalar dy);
+
+    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);
+
+private:
+    SkDeque fLayers;
+
+    typedef SkRasterizer INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkLightingImageFilter.h b/include/effects/SkLightingImageFilter.h
new file mode 100644
index 0000000..95498bc
--- /dev/null
+++ b/include/effects/SkLightingImageFilter.h
@@ -0,0 +1,90 @@
+/*
+ * 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..144fdb4
--- /dev/null
+++ b/include/effects/SkMagnifierImageFilter.h
@@ -0,0 +1,37 @@
+/*
+ * 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 asNewCustomStage(GrCustomStage** stage,
+                                  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..f7440cd
--- /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 asNewCustomStage(GrCustomStage** stage, 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/SkMorphologyImageFilter.h b/include/effects/SkMorphologyImageFilter.h
new file mode 100644
index 0000000..edee221
--- /dev/null
+++ b/include/effects/SkMorphologyImageFilter.h
@@ -0,0 +1,76 @@
+/*
+ * 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 "SkSingleInputImageFilter.h"
+#include "SkSize.h"
+
+class SK_API SkMorphologyImageFilter : public SkSingleInputImageFilter {
+public:
+    SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input);
+
+protected:
+    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 SkSingleInputImageFilter INHERITED;
+};
+
+class SK_API SkDilateImageFilter : public SkMorphologyImageFilter {
+public:
+    SkDilateImageFilter(int radiusX, int radiusY, SkImageFilter* input = NULL)
+    : INHERITED(radiusX, radiusY, input) {}
+
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+    virtual GrTexture* onFilterImageGPU(Proxy* proxy, GrTexture* src,
+                                        const SkRect& rect) SK_OVERRIDE;
+#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, SkImageFilter* input = NULL)
+    : INHERITED(radiusX, radiusY, input) {}
+
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+    virtual GrTexture* onFilterImageGPU(Proxy* proxy, GrTexture* src,
+                                        const SkRect& rect) SK_OVERRIDE;
+#endif
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkErodeImageFilter)
+
+protected:
+    SkErodeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    typedef SkMorphologyImageFilter INHERITED;
+};
+
+#endif
+
diff --git a/include/effects/SkPaintFlagsDrawFilter.h b/include/effects/SkPaintFlagsDrawFilter.h
new file mode 100644
index 0000000..fbf1807
--- /dev/null
+++ b/include/effects/SkPaintFlagsDrawFilter.h
@@ -0,0 +1,25 @@
+/*
+ * 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 SK_API SkPaintFlagsDrawFilter : public SkDrawFilter {
+public:
+    SkPaintFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags);
+
+    // overrides
+    virtual void filter(SkPaint*, Type);
+
+private:
+    uint16_t    fClearFlags;    // user specified
+    uint16_t    fSetFlags;      // user specified
+};
+
+#endif
diff --git a/include/effects/SkPixelXorXfermode.h b/include/effects/SkPixelXorXfermode.h
new file mode 100644
index 0000000..53f1210
--- /dev/null
+++ b/include/effects/SkPixelXorXfermode.h
@@ -0,0 +1,37 @@
+/*
+ * 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 SK_API SkPixelXorXfermode : public SkXfermode {
+public:
+    SkPixelXorXfermode(SkColor opColor) : fOpColor(opColor) {}
+
+    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);
+
+private:
+    SkColor fOpColor;
+
+    typedef SkXfermode INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkPorterDuff.h b/include/effects/SkPorterDuff.h
new file mode 100644
index 0000000..1bba171
--- /dev/null
+++ b/include/effects/SkPorterDuff.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 SkPorterDuff_DEFINED
+#define SkPorterDuff_DEFINED
+
+#include "SkColor.h"
+#include "SkXfermode.h"
+
+class SkXfermode;
+
+/** DEPRECATED - use SkXfermode::Mode instead
+ */
+class SK_API 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/SkSingleInputImageFilter.h b/include/effects/SkSingleInputImageFilter.h
new file mode 100644
index 0000000..e7819c4
--- /dev/null
+++ b/include/effects/SkSingleInputImageFilter.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 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);
+
+#if SK_SUPPORT_GPU
+    // Recurses on input (if non-NULL), and returns the processed result as
+    // a texture, otherwise returns src.
+    GrTexture* getInputResultAsTexture(Proxy* proxy, GrTexture* src, const SkRect& rect);
+#endif
+
+    SkImageFilter* input() const { return getInput(0); }
+private:
+    typedef SkImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkStippleMaskFilter.h b/include/effects/SkStippleMaskFilter.h
new file mode 100644
index 0000000..b5d162e
--- /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) SK_OVERRIDE;
+
+    // getFormat is from SkMaskFilter
+    virtual SkMask::Format getFormat() 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
new file mode 100644
index 0000000..5714d07
--- /dev/null
+++ b/include/effects/SkTableColorFilter.h
@@ -0,0 +1,36 @@
+
+#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]);
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
+};
+
+#endif
diff --git a/include/effects/SkTableMaskFilter.h b/include/effects/SkTableMaskFilter.h
new file mode 100644
index 0000000..c407b40
--- /dev/null
+++ b/include/effects/SkTableMaskFilter.h
@@ -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.
+ */
+
+#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 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);
+
+    /** 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*);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTableMaskFilter)
+
+protected:
+    SkTableMaskFilter(SkFlattenableReadBuffer& rb);
+    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
new file mode 100755
index 0000000..5174856
--- /dev/null
+++ b/include/effects/SkTestImageFilters.h
@@ -0,0 +1,103 @@
+#ifndef _SkTestImageFilters_h
+#define _SkTestImageFilters_h
+
+#include "SkImageFilter.h"
+#include "SkPoint.h"
+
+class SK_API SkOffsetImageFilter : public SkImageFilter {
+public:
+    SkOffsetImageFilter(SkScalar dx, SkScalar dy) : INHERITED(0) {
+        fOffset.set(dx, dy);
+    }
+
+    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 SkImageFilter INHERITED;
+};
+
+class SK_API SkComposeImageFilter : public SkImageFilter {
+public:
+    SkComposeImageFilter(SkImageFilter* outer, SkImageFilter* inner) : INHERITED(2, outer, inner) {}
+    virtual ~SkComposeImageFilter();
+
+    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;
+
+private:
+    typedef SkImageFilter INHERITED;
+};
+
+#include "SkXfermode.h"
+
+class SK_API 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();
+
+    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
+    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 initAllocModes();
+    void initModes(const SkXfermode::Mode []);
+
+    typedef SkImageFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Fun mode that scales down (only) and then scales back up to look pixelated
+class SK_API SkDownSampleImageFilter : public SkImageFilter {
+public:
+    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;
+
+private:
+    SkScalar fScale;
+
+    typedef SkImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkTransparentShader.h b/include/effects/SkTransparentShader.h
new file mode 100644
index 0000000..74187f7
--- /dev/null
+++ b/include/effects/SkTransparentShader.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 SkTransparentShader_DEFINED
+#define SkTransparentShader_DEFINED
+
+#include "SkShader.h"
+
+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 void    shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
+    virtual void    shadeSpan16(int x, int y, uint16_t span[], int count) SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTransparentShader)
+
+private:
+    // these are a cache from the call to setContext()
+    const SkBitmap* fDevice;
+    uint8_t         fAlpha;
+
+    SkTransparentShader(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/include/gpu/GrAARectRenderer.h b/include/gpu/GrAARectRenderer.h
new file mode 100644
index 0000000..e8b9f15
--- /dev/null
+++ b/include/gpu/GrAARectRenderer.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 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;
+
+    static const uint16_t       gFillAARectIdx[];
+    static const uint16_t       gStrokeAARectIdx[];
+
+    static int aaFillRectIndexCount();
+    GrIndexBuffer* aaFillRectIndexBuffer(GrGpu* gpu);
+
+    static int aaStrokeRectIndexCount();
+    GrIndexBuffer* aaStrokeRectIndexBuffer(GrGpu* gpu);
+
+    typedef GrRefCnt INHERITED;
+};
+
+#endif // GrAARectRenderer_DEFINED
diff --git a/include/gpu/GrCacheID.h b/include/gpu/GrCacheID.h
new file mode 100644
index 0000000..e593d7e
--- /dev/null
+++ b/include/gpu/GrCacheID.h
@@ -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.
+ */
+
+#ifndef GrCacheID_DEFINED
+#define GrCacheID_DEFINED
+
+#include "GrTypes.h"
+
+///////////////////////////////////////////////////////////////////////////////
+#define GR_DECLARE_RESOURCE_CACHE_TYPE()                         \
+    static int8_t GetResourceType();
+
+#define GR_DEFINE_RESOURCE_CACHE_TYPE(ClassName)                 \
+    int8_t ClassName::GetResourceType() {                        \
+        static int8_t kResourceTypeID = 0;                       \
+        if (0 == kResourceTypeID) {                              \
+            kResourceTypeID = GrCacheID::GetNextResourceType();  \
+        }                                                        \
+        return kResourceTypeID;                                  \
+    }
+
+
+///////////////////////////////////////////////////////////////////////////////
+#define GR_DECLARE_RESOURCE_CACHE_DOMAIN(AccessorName)           \
+    static int8_t AccessorName();
+
+#define GR_DEFINE_RESOURCE_CACHE_DOMAIN(ClassName, AccessorName) \
+    int8_t ClassName::AccessorName() {                           \
+        static int8_t kDomainID = 0;                             \
+        if (0 == kDomainID) {                                    \
+            kDomainID = GrCacheID::GetNextDomain();              \
+        }                                                        \
+        return kDomainID;                                        \
+    }
+
+/**
+ * The cache ID adds structure to the IDs used for caching GPU resources. It
+ * is broken into three portions:
+ *      the public portion - which is filled in by Skia clients
+ *      the private portion - which is used by the cache (domain & type)
+ *      the resource-specific portion - which is filled in by each GrResource-
+ *              derived class.
+ *
+ * For the public portion each client of the cache makes up its own
+ * unique-per-resource identifier (e.g., bitmap genID). A public ID of
+ * 'kScratch_CacheID' indicates that the resource is a "scratch" resource.
+ * When used to acquire a resource it indicates the cache user is
+ * looking for a resource that matches a resource-subclass-specific set of
+ * “dimensions” such as width, height, buffer size, or pixel config, but not
+ * for particular resource contents (e.g., texel or vertex values). The public
+ * IDs are unique within a private ID value but not necessarily across
+ * private IDs.
+ *
+ * The domain portion identifies the cache client while the type field
+ * indicates the resource type. When the public portion indicates that the
+ * resource is a scratch resource, the domain field should be kUnrestricted
+ * so that scratch resources can be recycled across domains.
+ */
+class GrCacheID {
+public:
+    uint64_t     fPublicID;
+
+    uint32_t     fResourceSpecific32;
+
+    uint8_t      fDomain;
+private:
+    uint8_t      fResourceType;
+
+public:
+    uint16_t     fResourceSpecific16;
+
+    GrCacheID(uint8_t resourceType)
+        : fPublicID(kDefaultPublicCacheID)
+        , fDomain(GrCacheData::kScratch_ResourceDomain)
+        , fResourceType(resourceType) {
+    }
+
+    void toRaw(uint32_t v[4]);
+
+    uint8_t getResourceType() const { return fResourceType; }
+
+    /*
+     * Default value for public portion of GrCacheID
+     */
+    static const uint64_t kDefaultPublicCacheID = 0;
+
+    static const uint8_t kInvalid_ResourceType = 0;
+
+    static uint8_t    GetNextDomain();
+    static uint8_t    GetNextResourceType();
+
+
+};
+
+#endif // GrCacheID_DEFINED
diff --git a/include/gpu/GrClipData.h b/include/gpu/GrClipData.h
new file mode 100644
index 0000000..0cf7022
--- /dev/null
+++ b/include/gpu/GrClipData.h
@@ -0,0 +1,58 @@
+
+/*
+ * 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/GrColor.h b/include/gpu/GrColor.h
new file mode 100644
index 0000000..4318f14
--- /dev/null
+++ b/include/gpu/GrColor.h
@@ -0,0 +1,71 @@
+
+/*
+ * 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 GrColor_DEFINED
+#define GrColor_DEFINED
+
+#include "GrTypes.h"
+
+/**
+ *  GrColor is 4 bytes for R, G, B, A, in a compile-time specific order. The
+ *  components are stored premultiplied.
+ */
+typedef uint32_t GrColor;
+
+
+// shift amount to assign a component to a GrColor int
+// These shift values are chosen for compatibility with GL attrib arrays
+// ES doesn't allow BGRA vertex attrib order so if they were not in this order
+// we'd have to swizzle in shaders. Note the assumption that the cpu is little
+// endian.
+#define GrColor_SHIFT_R     0
+#define GrColor_SHIFT_G     8
+#define GrColor_SHIFT_B     16
+#define GrColor_SHIFT_A     24
+
+/**
+ *  Pack 4 components (RGBA) into a GrColor int
+ */
+static inline GrColor GrColorPackRGBA(unsigned r, unsigned g,
+                                      unsigned b, unsigned a) {
+    GrAssert((uint8_t)r == r);
+    GrAssert((uint8_t)g == g);
+    GrAssert((uint8_t)b == b);
+    GrAssert((uint8_t)a == a);
+    return  (r << GrColor_SHIFT_R) |
+            (g << GrColor_SHIFT_G) |
+            (b << GrColor_SHIFT_B) |
+            (a << GrColor_SHIFT_A);
+}
+
+// extract a component (byte) from a GrColor int
+
+#define GrColorUnpackR(color)   (((color) >> GrColor_SHIFT_R) & 0xFF)
+#define GrColorUnpackG(color)   (((color) >> GrColor_SHIFT_G) & 0xFF)
+#define GrColorUnpackB(color)   (((color) >> GrColor_SHIFT_B) & 0xFF)
+#define GrColorUnpackA(color)   (((color) >> GrColor_SHIFT_A) & 0xFF)
+
+/**
+ *  Since premultiplied means that alpha >= color, we construct a color with
+ *  each component==255 and alpha == 0 to be "illegal"
+ */
+#define GrColor_ILLEGAL     (~(0xFF << GrColor_SHIFT_A))
+
+/** 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
new file mode 100644
index 0000000..c61c34e
--- /dev/null
+++ b/include/gpu/GrConfig.h
@@ -0,0 +1,427 @@
+
+/*
+ * 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 GrConfig_DEFINED
+#define GrConfig_DEFINED
+
+#include "SkTypes.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// preconfig section:
+//
+// All the work before including GrUserConfig.h should center around guessing
+// what platform we're on, and defining low-level symbols based on that.
+//
+// A build environment may have already defined symbols, so we first check
+// for that
+//
+
+// hack to ensure we know what sort of Apple platform we're on
+#if defined(__APPLE_CPP__) || defined(__APPLE_CC__)
+    #include <TargetConditionals.h>
+#endif
+
+/**
+ *  Gr defines are set to 0 or 1, rather than being undefined or defined
+ */
+
+#if !defined(GR_ANDROID_BUILD)
+    #define GR_ANDROID_BUILD    0
+#endif
+#if !defined(GR_IOS_BUILD)
+    #define GR_IOS_BUILD        0
+#endif
+#if !defined(GR_LINUX_BUILD)
+    #define GR_LINUX_BUILD      0
+#endif
+#if !defined(GR_MAC_BUILD)
+    #define GR_MAC_BUILD        0
+#endif
+#if !defined(GR_WIN32_BUILD)
+    #define GR_WIN32_BUILD      0
+#endif
+#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.
+ */
+#if !GR_ANDROID_BUILD && !GR_IOS_BUILD && !GR_LINUX_BUILD && !GR_MAC_BUILD && !GR_WIN32_BUILD && !GR_QNX_BUILD
+    #if defined(_WIN32)
+        #undef GR_WIN32_BUILD
+        #define GR_WIN32_BUILD      1
+//      #error "WIN"
+    #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
+        #undef GR_IOS_BUILD
+        #define GR_IOS_BUILD        1
+//      #error "IOS"
+    #elif defined(SK_BUILD_FOR_ANDROID)
+        #undef GR_ANDROID_BUILD
+        #define GR_ANDROID_BUILD    1
+//      #error "ANDROID"
+    #elif TARGET_OS_MAC
+        #undef GR_MAC_BUILD
+        #define GR_MAC_BUILD        1
+//      #error "MAC"
+    #elif TARGET_OS_QNX || defined(__QNXNTO__)
+        #undef GR_QNX_BUILD
+        #define GR_QNX_BUILD        1
+//      #error "QNX"
+    #else
+        #undef GR_LINUX_BUILD
+        #define GR_LINUX_BUILD      1
+//      #error "LINUX"
+    #endif
+#endif
+
+// we need both GR_DEBUG and GR_RELEASE to be defined as 0 or 1
+//
+#ifndef GR_DEBUG
+    #ifdef GR_RELEASE
+        #define GR_DEBUG !GR_RELEASE
+    #else
+        #ifdef NDEBUG
+            #define GR_DEBUG    0
+        #else
+            #define GR_DEBUG    1
+        #endif
+    #endif
+#endif
+
+#ifndef GR_RELEASE
+    #define GR_RELEASE  !GR_DEBUG
+#endif
+
+#if GR_DEBUG == GR_RELEASE
+    #error "GR_DEBUG and GR_RELEASE must not be the same"
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_WIN32_BUILD
+// VC8 doesn't support stdint.h, so we define those types here.
+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 __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#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
+ *  these.
+ */
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+#include <stdint.h>
+#endif
+
+/*
+ *  The "user config" file can be empty, and everything should work. It is
+ *  meant to store a given platform/client's overrides of our guess-work.
+ *
+ *  A alternate user config file can be specified by defining
+ *  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)
+ */
+#if !defined(GR_USER_CONFIG_FILE)
+    #include "GrUserConfig.h"
+#else
+    #include GR_USER_CONFIG_FILE
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// postconfig section:
+//
+
+// GR_IMPLEMENTATION should be define to 1 when building Gr and 0 when including
+// it in another dependent build. The Gr makefile/ide-project should define this
+// to 1.
+#if !defined(GR_IMPLEMENTATION)
+    #define GR_IMPLEMENTATION 0
+#endif
+
+// If Gr is built as a shared library then GR_DLL should be defined to 1 (both
+// when building Gr and when including its headers in dependent builds). Only
+// currently supported minimally for Chrome's Win32 Multi-DLL build (TODO:
+// correctly exort all of the public API correctly and support shared lib on
+// other platforms).
+#if !defined(GR_DLL)
+    #define GR_DLL 0
+#endif
+
+#if GR_DLL
+    #if GR_WIN32_BUILD
+        #if GR_IMPLEMENTATION
+            #define GR_API __declspec(dllexport)
+        #else
+            #define GR_API __declspec(dllimport)
+        #endif
+    #else
+        #define GR_API __attribute__((visibility("default")))
+    #endif
+#else
+    #define GR_API
+#endif
+
+// By now we must have a GR_..._BUILD symbol set to 1, and a decision about
+// debug -vs- release
+//
+
+#define GrPrintf SkDebugf
+
+/**
+ *  GR_STRING makes a string of X where X is expanded before conversion to a string
+ *  if X itself contains macros.
+ */
+#define GR_STRING(X) GR_STRING_IMPL(X)
+#define GR_STRING_IMPL(X) #X
+
+/**
+ *  GR_CONCAT concatenates X and Y  where each is expanded before
+ *  contanenation if either contains macros.
+ */
+#define GR_CONCAT(X,Y) GR_CONCAT_IMPL(X,Y)
+#define GR_CONCAT_IMPL(X,Y) X##Y
+
+/**
+ *  Creates a string of the form "<filename>(<linenumber>) : "
+ */
+#define GR_FILE_AND_LINE_STR __FILE__ "(" GR_STRING(__LINE__) ") : "
+
+/**
+ *  Compilers have different ways of issuing warnings. This macro
+ *  attempts to abstract them, but may need to be specialized for your
+ *  particular compiler.
+ *  To insert compiler warnings use "#pragma message GR_WARN(<string>)"
+ */
+#if defined(_MSC_VER) && _MSC_VER
+    #define GR_WARN(MSG) (GR_FILE_AND_LINE_STR "WARNING: " MSG)
+#else//__GNUC__ - may need other defines for different compilers
+    #define GR_WARN(MSG) ("WARNING: " MSG)
+#endif
+
+/**
+ *  GR_ALWAYSBREAK is an unconditional break in all builds.
+ */
+#if !defined(GR_ALWAYSBREAK)
+    #if     GR_WIN32_BUILD
+        #define GR_ALWAYSBREAK SkNO_RETURN_HINT(); __debugbreak()
+    #else
+        // TODO: do other platforms really not have continuable breakpoints?
+        // sign extend for 64bit architectures to be sure this is
+        // in the high address range
+        #define GR_ALWAYSBREAK SkNO_RETURN_HINT(); *((int*)(int64_t)(int32_t)0xbeefcafe) = 0;
+    #endif
+#endif
+
+/**
+ *  GR_DEBUGBREAK is an unconditional break in debug builds.
+ */
+#if !defined(GR_DEBUGBREAK)
+    #if GR_DEBUG
+        #define GR_DEBUGBREAK GR_ALWAYSBREAK
+    #else
+        #define GR_DEBUGBREAK
+    #endif
+#endif
+
+/**
+ *  GR_ALWAYSASSERT is an assertion in all builds.
+ */
+#if !defined(GR_ALWAYSASSERT)
+    #define GR_ALWAYSASSERT(COND)                                        \
+        do {                                                             \
+            if (!(COND)) {                                               \
+                GrPrintf("%s %s failed\n", GR_FILE_AND_LINE_STR, #COND); \
+                GR_ALWAYSBREAK;                                          \
+            }                                                            \
+        } while (false)
+#endif
+
+/**
+ *  GR_DEBUGASSERT is an assertion in debug builds only.
+ */
+#if !defined(GR_DEBUGASSERT)
+    #if GR_DEBUG
+        #define GR_DEBUGASSERT(COND) GR_ALWAYSASSERT(COND)
+    #else
+        #define GR_DEBUGASSERT(COND)
+    #endif
+#endif
+
+/**
+ *  Prettier forms of the above macros.
+ */
+#define GrAssert(COND) GR_DEBUGASSERT(COND)
+#define GrAlwaysAssert(COND) GR_ALWAYSASSERT(COND)
+
+/**
+ * Crash from unrecoverable condition, optionally with a message.
+ */
+inline void GrCrash() { GrAlwaysAssert(false); }
+inline void GrCrash(const char* msg) { GrPrintf(msg); GrAlwaysAssert(false); }
+
+/**
+ *  GR_DEBUGCODE compiles the code X in debug builds only
+ */
+#if !defined(GR_DEBUGCODE)
+    #if GR_DEBUG
+        #define GR_DEBUGCODE(X) X
+    #else
+        #define GR_DEBUGCODE(X)
+    #endif
+#endif
+
+/**
+ *  GR_STATIC_ASSERT is a compile time assertion. Depending on the platform
+ *  it may print the message in the compiler log. Obviously, the condition must
+ *  be evaluatable at compile time.
+ */
+// VS 2010 and GCC compiled with c++0x or gnu++0x support the new
+// static_assert.
+#if !defined(GR_STATIC_ASSERT)
+    #if (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__)
+        #define GR_STATIC_ASSERT(CONDITION) static_assert(CONDITION, "bug")
+    #else
+        template <bool> class GR_STATIC_ASSERT_FAILURE;
+        template <> class GR_STATIC_ASSERT_FAILURE<true> {};
+        #define GR_STATIC_ASSERT(CONDITION) \
+            enum {GR_CONCAT(X,__LINE__) = \
+            sizeof(GR_STATIC_ASSERT_FAILURE<CONDITION>)}
+    #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
+#if !defined(GR_TEXT_SCALAR_TYPE_IS_FLOAT)
+    #define GR_TEXT_SCALAR_TYPE_IS_FLOAT   0
+#endif
+#if !defined(GR_TEXT_SCALAR_TYPE_IS_FIXED)
+    #define GR_TEXT_SCALAR_TYPE_IS_FIXED   0
+#endif
+
+#ifndef GR_DUMP_TEXTURE_UPLOAD
+    #define GR_DUMP_TEXTURE_UPLOAD  0
+#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.
+ */
+#if !defined(GR_STATIC_RECT_VB)
+    #define GR_STATIC_RECT_VB 0
+#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
+ *  program.
+ */
+#if !defined(GR_AGGRESSIVE_SHADER_OPTS)
+    #define GR_AGGRESSIVE_SHADER_OPTS 1
+#endif
+
+/**
+ * GR_GEOM_BUFFER_LOCK_THRESHOLD gives a threshold (in bytes) for when Gr should
+ * lock a GrGeometryBuffer to update its contents. It will use lock() if the
+ * size of the updated region is greater than the threshold. Otherwise it will
+ * use updateData().
+ */
+#if !defined(GR_GEOM_BUFFER_LOCK_THRESHOLD)
+    #define GR_GEOM_BUFFER_LOCK_THRESHOLD (1 << 15)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// tail section:
+//
+// Now we just assert if we are missing some required define, or if we detect
+// and inconsistent combination of defines
+//
+
+
+/**
+ *  Only one build target macro should be 1 and the rest should be 0.
+ */
+#define GR_BUILD_SUM    (GR_WIN32_BUILD + GR_MAC_BUILD + GR_IOS_BUILD + GR_ANDROID_BUILD + GR_LINUX_BUILD + GR_QNX_BUILD)
+#if 0 == GR_BUILD_SUM
+    #error "Missing a GR_BUILD define"
+#elif 1 != GR_BUILD_SUM
+    #error "More than one GR_BUILD defined"
+#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
+    #undef  GR_TEXT_SCALAR_IS_FLOAT
+    #define GR_TEXT_SCALAR_IS_FLOAT         1
+    #pragma message GR_WARN("Text scalar type not defined, defaulting to float")
+#endif
+
+#if 0
+#if GR_WIN32_BUILD
+//    #pragma message GR_WARN("GR_WIN32_BUILD")
+#endif
+#if GR_MAC_BUILD
+//    #pragma message GR_WARN("GR_MAC_BUILD")
+#endif
+#if GR_IOS_BUILD
+//    #pragma message GR_WARN("GR_IOS_BUILD")
+#endif
+#if GR_ANDROID_BUILD
+//    #pragma message GR_WARN("GR_ANDROID_BUILD")
+#endif
+#if GR_LINUX_BUILD
+//    #pragma message GR_WARN("GR_LINUX_BUILD")
+#endif
+#if GR_QNX_BUILD
+//    #pragma message GR_WARN("GR_QNX_BUILD")
+#endif
+#endif
+
+#endif
+
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
new file mode 100644
index 0000000..be0f573
--- /dev/null
+++ b/include/gpu/GrContext.h
@@ -0,0 +1,1006 @@
+
+/*
+ * 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 GrContext_DEFINED
+#define GrContext_DEFINED
+
+#include "GrConfig.h"
+#include "GrPaint.h"
+#include "GrAARectRenderer.h"
+#include "GrClipData.h"
+// not strictly needed but requires WK change in LayerTextureUpdaterCanvas to
+// remove.
+#include "GrRenderTarget.h"
+#include "SkClipStack.h"
+
+class GrAutoScratchTexture;
+class GrCacheKey;
+class GrDrawState;
+class GrDrawTarget;
+class GrFontCache;
+class GrGpu;
+class GrIndexBuffer;
+class GrIndexBufferAllocPool;
+class GrInOrderDrawBuffer;
+class GrPathRenderer;
+class GrPathRendererChain;
+class GrResourceEntry;
+class GrResourceCache;
+class GrStencilBuffer;
+class GrVertexBuffer;
+class GrVertexBufferAllocPool;
+class GrSoftwarePathRenderer;
+
+class GR_API GrContext : public GrRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrContext)
+
+    /**
+     * Creates a GrContext from within a 3D context.
+     */
+    static GrContext* Create(GrEngine engine,
+                             GrPlatform3DContext context3D);
+
+    /**
+     * Returns the number of GrContext instances for the current thread.
+     */
+    static int GetThreadInstanceCount();
+
+    virtual ~GrContext();
+
+    /**
+     * The GrContext normally assumes that no outsider is setting state
+     * within the underlying 3D API's context/device/whatever. This call informs
+     * the context that the state was modified and it should resend. Shouldn't
+     * be called frequently for good performance.
+     */
+    void resetContext();
+
+    /**
+     * 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:
+     *  1) ~GrContext will not try to free the objects in the 3D API.
+     *  2) If you've created GrResources that outlive the GrContext they will
+     *     be marked as invalid (GrResource::isValid()) and won't attempt to
+     *     free their underlying resource in the 3D API.
+     * Content drawn since the last GrContext::flush() may be lost.
+     */
+    void contextLost();
+
+    /**
+     * Similar to contextLost, but makes no attempt to reset state.
+     * Use this method when GrContext destruction is pending, but
+     * the graphics context is destroyed first.
+     */
+    void contextDestroyed();
+
+    /**
+     * Frees GPU created by the context. Can be called to reduce GPU memory
+     * pressure.
+     */
+    void freeGpuResources();
+
+    /**
+     * Returns the number of bytes of GPU memory hosted by the texture cache.
+     */
+    size_t getGpuTextureCacheBytes() const;
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Textures
+
+    /**
+     *  Create a new entry, based on the specified key and texture, and return
+     *  a "locked" texture. Must call be balanced with an unlockTexture() call.
+     *
+     * @param params    The texture params used to draw a texture may help determine
+     *                  the cache entry used. (e.g. different versions may exist
+     *                  for different wrap modes on GPUs with limited NPOT
+     *                  texture support). NULL implies clamp wrap modes.
+     * @param desc      Description of the texture properties.
+     * @param cacheData Cache-specific properties (e.g., texture gen ID)
+     * @param srcData   Pointer to the pixel values.
+     * @param rowBytes  The number of bytes between rows of the texture. Zero
+     *                  implies tightly packed rows.
+     */
+    GrTexture* createTexture(const GrTextureParams* params,
+                             const GrTextureDesc& desc,
+                             const GrCacheData& cacheData,
+                             void* srcData, size_t rowBytes);
+
+    /**
+     * Look for a texture that matches 'key' in the cache. If not found,
+     * return NULL.
+     */
+    GrTexture* findTexture(const GrCacheKey& key);
+
+    /**
+     *  Search for an entry based on key and dimensions. If found,
+     *  return it. The return value will be NULL if not found.
+     *
+     *  @param desc     Description of the texture properties.
+     *  @param cacheData Cache-specific properties (e.g., texture gen ID)
+     *  @param params   The texture params used to draw a texture may help determine
+     *                  the cache entry used. (e.g. different versions may exist
+     *                  for different wrap modes on GPUs with limited NPOT
+     *                  texture support). NULL implies clamp wrap modes.
+     */
+    GrTexture* findTexture(const GrTextureDesc& desc,
+                           const GrCacheData& cacheData,
+                           const GrTextureParams* params);
+    /**
+     * Determines whether a texture is in the cache. If the texture is found it
+     * will not be locked or returned. This call does not affect the priority of
+     * the texture for deletion.
+     */
+    bool isTextureInCache(const GrTextureDesc& desc,
+                          const GrCacheData& cacheData,
+                          const GrTextureParams* params) const;
+
+    /**
+     * Enum that determines how closely a returned scratch texture must match
+     * a provided GrTextureDesc.
+     */
+    enum ScratchTexMatch {
+        /**
+         * Finds a texture that exactly matches the descriptor.
+         */
+        kExact_ScratchTexMatch,
+        /**
+         * Finds a texture that approximately matches the descriptor. Will be
+         * at least as large in width and height as desc specifies. If desc
+         * specifies that texture is a render target then result will be a
+         * render target. If desc specifies a render target and doesn't set the
+         * no stencil flag then result will have a stencil. Format and aa level
+         * will always match.
+         */
+        kApprox_ScratchTexMatch
+    };
+
+    /**
+     * Returns a texture matching the desc. It's contents are unknown. Subsequent
+     * requests with the same descriptor are not guaranteed to return the same
+     * texture. The same texture is guaranteed not be returned again until it is
+     * unlocked. Call must be balanced with an unlockTexture() call.
+     *
+     * 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
+     * such an API will create gaps in the tiling pattern. This includes clamp
+     * mode. (This may be addressed in a future update.)
+     */
+    GrTexture* lockScratchTexture(const GrTextureDesc& desc,
+                                  ScratchTexMatch match);
+
+    /**
+     *  When done with an entry, call unlockTexture(entry) on it, which returns
+     *  it to the cache, where it may be purged.
+     */
+    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& desc,
+                                     void* srcData,
+                                     size_t rowBytes);
+
+    /**
+     * 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 GrTextureParams*,
+                                   int width,
+                                   int height) const;
+
+    /**
+     *  Return the current texture cache limits.
+     *
+     *  @param maxTextures If non-null, returns maximum number of textures that
+     *                     can be held in the cache.
+     *  @param maxTextureBytes If non-null, returns maximum number of bytes of
+     *                         texture memory that can be held in the cache.
+     */
+    void getTextureCacheLimits(int* maxTextures, size_t* maxTextureBytes) const;
+
+    /**
+     *  Specify the texture cache limits. If the current cache exceeds either
+     *  of these, it will be purged (LRU) to keep the cache within these limits.
+     *
+     *  @param maxTextures The maximum number of textures that can be held in
+     *                     the cache.
+     *  @param maxTextureBytes The maximum number of bytes of texture memory
+     *                         that can be held in the cache.
+     */
+    void setTextureCacheLimits(int maxTextures, size_t maxTextureBytes);
+
+    /**
+     *  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.
+     */
+    int getMaxRenderTargetSize() const;
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Render targets
+
+    /**
+     * Sets the render target.
+     * @param target    the render target to set. (should not be NULL.)
+     */
+    void setRenderTarget(GrRenderTarget* target);
+
+    /**
+     * Gets the current render target.
+     * @return the currently bound render target. Should never be NULL.
+     */
+    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
+
+    /**
+     * Wraps an existing texture with a GrTexture object.
+     *
+     * OpenGL: if the object is a texture Gr may change its GL texture params
+     *         when it is drawn.
+     *
+     * @param  desc     description of the object to create.
+     *
+     * @return GrTexture object or NULL on failure.
+     */
+    GrTexture* createPlatformTexture(const GrPlatformTextureDesc& desc);
+
+    /**
+     * Wraps an existing render target with a GrRenderTarget object. It is
+     * similar to createPlatformTexture but can be used to draw into surfaces
+     * that are not also textures (e.g. FBO 0 in OpenGL, or an MSAA buffer that
+     * the client will resolve to a texture).
+     *
+     * @param  desc     description of the object to create.
+     *
+     * @return GrTexture object or NULL on failure.
+     */
+     GrRenderTarget* createPlatformRenderTarget(
+                                    const GrPlatformRenderTargetDesc& desc);
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Matrix state
+
+    /**
+     * Gets the current transformation matrix.
+     * @return the current matrix.
+     */
+    const GrMatrix& getMatrix() const;
+
+    /**
+     * Sets the transformation matrix.
+     * @param m the matrix to set.
+     */
+    void setMatrix(const GrMatrix& 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;
+
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Clip state
+    /**
+     * Gets the current clip.
+     * @return the current clip.
+     */
+    const GrClipData* getClip() const;
+
+    /**
+     * Sets the clip.
+     * @param clipData  the clip to set.
+     */
+    void setClip(const GrClipData* clipData);
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Draws
+
+    /**
+     * 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,
+               GrRenderTarget* target = NULL);
+
+    /**
+     *  Draw everywhere (respecting the clip) with the paint.
+     */
+    void drawPaint(const GrPaint& paint);
+
+    /**
+     *  Draw the rect using a paint.
+     *  @param paint        describes how to color pixels.
+     *  @param strokeWidth  If strokeWidth < 0, then the rect is filled, else
+     *                      the rect is mitered stroked based on strokeWidth. If
+     *                      strokeWidth == 0, then the stroke is always a single
+     *                      pixel thick.
+     *  @param matrix       Optional matrix applied to the rect. Applied before
+     *                      context's matrix or the paint's matrix.
+     *  The rects coords are used to access the paint (through texture matrix)
+     */
+    void drawRect(const GrPaint& paint,
+                  const GrRect&,
+                  GrScalar strokeWidth = -1,
+                  const GrMatrix* matrix = NULL);
+
+    /**
+     * Maps a rect of paint coordinates onto the a rect of destination
+     * coordinates. Each rect can optionally be transformed. The srcRect
+     * is stretched over the dstRect. The dstRect is transformed by the
+     * context's matrix and the srcRect is transformed by the paint's matrix.
+     * Additional optional matrices can be provided by parameters.
+     *
+     * @param paint     describes how to color pixels.
+     * @param dstRect   the destination rect to draw.
+     * @param srcRect   rect of paint coordinates to be mapped onto dstRect
+     * @param dstMatrix Optional matrix to transform dstRect. Applied before
+     *                  context's matrix.
+     * @param srcMatrix Optional matrix to transform srcRect Applied before
+     *                  paint's matrix.
+     */
+    void drawRectToRect(const GrPaint& paint,
+                        const GrRect& dstRect,
+                        const GrRect& srcRect,
+                        const GrMatrix* dstMatrix = NULL,
+                        const GrMatrix* 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.
+     */
+    void drawPath(const GrPaint& paint, const SkPath& path, GrPathFill fill);
+
+    /**
+     * Draws vertices with a paint.
+     *
+     * @param   paint           describes how to color pixels.
+     * @param   primitiveType   primitives type to draw.
+     * @param   vertexCount     number of vertices.
+     * @param   positions       array of vertex positions, required.
+     * @param   texCoords       optional array of texture coordinates used
+     *                          to access the paint.
+     * @param   colors          optional array of per-vertex colors, supercedes
+     *                          the paint's color field.
+     * @param   indices         optional array of indices. If NULL vertices
+     *                          are drawn non-indexed.
+     * @param   indexCount      if indices is non-null then this is the
+     *                          number of indices.
+     */
+    void drawVertices(const GrPaint& paint,
+                      GrPrimitiveType primitiveType,
+                      int vertexCount,
+                      const GrPoint positions[],
+                      const GrPoint texs[],
+                      const GrColor colors[],
+                      const uint16_t indices[],
+                      int indexCount);
+
+    /**
+     * Draws an oval.
+     *
+     * @param paint         describes how to color pixels.
+     * @param rect          the bounding rect of the oval.
+     * @param strokeWidth   if strokeWidth < 0, then the oval is filled, else
+     *                      the rect is stroked based on strokeWidth. If
+     *                      strokeWidth == 0, then the stroke is always a single
+     *                      pixel thick.
+     */
+    void drawOval(const GrPaint& paint,
+                  const GrRect& rect,
+                  SkScalar strokeWidth);
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Misc.
+
+    /**
+     * Flags that affect flush() behavior.
+     */
+    enum FlushBits {
+        /**
+         * A client may want Gr to bind a GrRenderTarget in the 3D API so that
+         * it can be rendered to directly. However, Gr lazily sets state. Simply
+         * calling setRenderTarget() followed by flush() without flags may not
+         * bind the render target. This flag forces the context to bind the last
+         * set render target in the 3D API.
+         */
+        kForceCurrentRenderTarget_FlushBit   = 0x1,
+        /**
+         * A client may reach a point where it has partially rendered a frame
+         * through a GrContext that it knows the user will never see. This flag
+         * causes the flush to skip submission of deferred content to the 3D API
+         * during the flush.
+         */
+        kDiscard_FlushBit                    = 0x2,
+    };
+
+    /**
+     * Call to ensure all drawing to the context has been issued to the
+     * underlying 3D API.
+     * @param flagsBitfield     flags that control the flushing behavior. See
+     *                          FlushBits.
+     */
+    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 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 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 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 = 0,
+                                uint32_t pixelOpsFlags = 0);
+
+    /**
+     * 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 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 = 0,
+                                 uint32_t pixelOpsFlags = 0);
+
+    /**
+     * Reads a rectangle of pixels from a texture.
+     * @param texture       the texture to read from.
+     * @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 PixelOpsFlags enum above.
+     *
+     * @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 = 0,
+                           uint32_t pixelOpsFlags = 0);
+
+    /**
+     * Writes a rectangle of pixels to a texture.
+     * @param texture       the render target to read from.
+     * @param left          left edge of the rectangle to write (inclusive)
+     * @param top           top edge of the rectangle to write (inclusive)
+     * @param width         width of rectangle to write in pixels.
+     * @param height        height of rectangle to write in pixels.
+     * @param config        the pixel config of the source buffer
+     * @param buffer        memory to read pixels from
+     * @param rowBytes      number of bytes between consecutive rows. Zero
+     *                      means rows are tightly packed.
+     * @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,
+                            uint32_t pixelOpsFlags = 0);
+
+
+    /**
+     * Copies all texels from one texture to another.
+     * @param src           the texture to copy from.
+     * @param dst           the render target to copy to.
+     */
+    void copyTexture(GrTexture* src, GrRenderTarget* dst);
+
+    /**
+     * Resolves a render target that has MSAA. The intermediate MSAA buffer is
+     * 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
+     * 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 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.
+     */
+     GrTexture* gaussianBlur(GrTexture* srcTexture,
+                             bool canClobberSrc,
+                             const SkRect& rect,
+                             float sigmaX, float sigmaY);
+
+    /**
+     * Zooms a subset of the texture to a larger size with a nice edge.
+     * The inner rectangle is a simple scaling of the texture by a factor of
+     * |zoom|.  The outer |inset| pixels transition from the background texture
+     * to the zoomed coordinate system at a rate of
+     * (distance_to_edge / inset) ^2, producing a rounded lens effect.
+     * @param srcTexture      The source texture to be zoomed.
+     * @param dstRect         The destination rectangle.
+     * @param srcRect         The source rectangle.  Must be smaller than
+     *                        dstRect
+     * @param inset           Number of pixels to blend along the edges.
+     * @return the zoomed texture, which is dstTexture.
+     */
+     GrTexture* zoom(GrTexture* srcTexture,
+                     const SkRect& dstRect, const SkRect& srcRect, float inset);
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Helpers
+
+    class AutoRenderTarget : public ::GrNoncopyable {
+    public:
+        AutoRenderTarget(GrContext* context, GrRenderTarget* target) {
+            fPrevTarget = context->getRenderTarget();
+            GrSafeRef(fPrevTarget);
+            context->setRenderTarget(target);
+            fContext = context;
+        }
+        AutoRenderTarget(GrContext* context) {
+            fPrevTarget = context->getRenderTarget();
+            GrSafeRef(fPrevTarget);
+            fContext = context;
+        }
+        ~AutoRenderTarget() {
+            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 custom stages. 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 custom stages 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 GrMatrix& 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->preConcatSamplerMatricesWithInverse(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 GrMatrix& 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 GrMatrix& preConcat, GrPaint* paint = NULL) {
+            if (NULL != paint) {
+                paint->preConcatSamplerMatrices(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;
+        GrMatrix    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.
+    GrGpu* getGpu() { return fGpu; }
+    const GrGpu* getGpu() const { return fGpu; }
+    GrFontCache* getFontCache() { return fFontCache; }
+    GrDrawTarget* getTextTarget(const GrPaint& paint);
+    const GrIndexBuffer* getQuadIndexBuffer() const;
+
+    /**
+     * 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.
+     */
+    void addStencilBuffer(GrStencilBuffer* sb);
+    GrStencilBuffer* findStencilBuffer(int width, int height, int sampleCnt);
+
+    GrPathRenderer* getPathRenderer(const SkPath& path,
+                                    GrPathFill fill,
+                                    const GrDrawTarget* target,
+                                    bool antiAlias,
+                                    bool allowSW);
+
+#if GR_CACHE_STATS
+    void printCacheStats() const;
+#endif
+
+private:
+    // Used to indicate whether a draw should be performed immediately or queued in fDrawBuffer.
+    enum BufferedDraw {
+        kYes_BufferedDraw,
+        kNo_BufferedDraw,
+    };
+    BufferedDraw fLastDrawWasBuffered;
+
+    GrGpu*              fGpu;
+    GrDrawState*        fDrawState;
+
+    GrResourceCache*    fTextureCache;
+    GrFontCache*        fFontCache;
+
+    GrPathRendererChain*        fPathRendererChain;
+    GrSoftwarePathRenderer*     fSoftwarePathRenderer;
+
+    GrVertexBufferAllocPool*    fDrawBufferVBAllocPool;
+    GrIndexBufferAllocPool*     fDrawBufferIBAllocPool;
+    GrInOrderDrawBuffer*        fDrawBuffer;
+
+    GrAARectRenderer*           fAARectRenderer;
+
+    bool                        fDidTestPMConversions;
+    int                         fPMToUPMConversion;
+    int                         fUPMToPMConversion;
+
+    struct CleanUpData {
+        PFCleanUpFunc fFunc;
+        void*         fInfo;
+    };
+
+    SkTDArray<CleanUpData>      fCleanUpData;
+
+    GrContext(GrGpu* gpu);
+
+    void setupDrawBuffer();
+
+    void flushDrawBuffer();
+
+    /// 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);
+
+    void internalDrawPath(const GrPaint& paint, const SkPath& path, GrPathFill fill);
+
+    GrTexture* createResizedTexture(const GrTextureDesc& desc,
+                                    const GrCacheData& cacheData,
+                                    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);
+
+    GrCustomStage* createPMToUPMEffect(GrTexture* texture, bool swapRAndB);
+    GrCustomStage* createUPMToPMEffect(GrTexture* texture, bool swapRAndB);
+
+    typedef GrRefCnt INHERITED;
+};
+
+/**
+ * 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)
+        , fTexture(NULL) {
+    }
+
+    GrAutoScratchTexture(GrContext* context,
+                         const GrTextureDesc& desc,
+                         GrContext::ScratchTexMatch match =
+                            GrContext::kApprox_ScratchTexMatch)
+      : fContext(NULL)
+      , fTexture(NULL) {
+      this->set(context, desc, match);
+    }
+
+    ~GrAutoScratchTexture() {
+        this->reset();
+    }
+
+    void reset() {
+        if (NULL != fContext && NULL != fTexture) {
+            fContext->unlockScratchTexture(fTexture);
+            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 unlockTexture we simply don't re-add it. It will be reattached
+     * in GrTexture::internal_dispose.
+     *
+     * Note that the caller is assumed to accept and manage the ref to the
+     * returned texture.
+     */
+    GrTexture* detach() {
+        GrTexture* temp = fTexture;
+
+        // Conceptually the texture's cache entry loses its ref to the
+        // texture while the caller of this method gets a ref.
+        GrAssert(NULL != temp->getCacheEntry());
+
+        fTexture = NULL;
+
+        temp->setFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit);
+        return temp;
+    }
+
+    GrTexture* set(GrContext* context,
+                   const GrTextureDesc& desc,
+                   GrContext::ScratchTexMatch match =
+                        GrContext::kApprox_ScratchTexMatch) {
+        this->reset();
+
+        fContext = context;
+        if (NULL != fContext) {
+            fTexture = fContext->lockScratchTexture(desc, match);
+            if (NULL == fTexture) {
+                fContext = NULL;
+            }
+            return fTexture;
+        } else {
+            return NULL;
+        }
+    }
+
+    GrTexture* texture() { return fTexture; }
+
+private:
+    GrContext*                    fContext;
+    GrTexture*                    fTexture;
+};
+
+#endif
diff --git a/include/gpu/GrContextFactory.h b/include/gpu/GrContextFactory.h
new file mode 100644
index 0000000..600beda
--- /dev/null
+++ b/include/gpu/GrContextFactory.h
@@ -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.
+ */
+
+#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;
+        }
+        GrPlatform3DContext p3dctx =
+            reinterpret_cast<GrPlatform3DContext>(glCtx.get()->gl());
+        grCtx.reset(GrContext::Create(kOpenGL_Shaders_GrEngine, 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/GrCustomStage.h b/include/gpu/GrCustomStage.h
new file mode 100644
index 0000000..d75675c
--- /dev/null
+++ b/include/gpu/GrCustomStage.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrCustomStage_DEFINED
+#define GrCustomStage_DEFINED
+
+#include "GrRefCnt.h"
+#include "GrNoncopyable.h"
+#include "GrProgramStageFactory.h"
+#include "GrCustomStageUnitTest.h"
+#include "GrTextureAccess.h"
+
+class GrContext;
+class GrTexture;
+class SkString;
+
+/** 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();
+    GrCustomStage objects *must* be immutable: after being constructed,
+    their fields may not change.  (Immutability isn't actually required
+    until they've been used in a draw call, but supporting that would require
+    setters and getters that could fail, copy-on-write, or deep copying of these
+    objects when they're stored by a GrGLProgramStage.)
+  */
+class GrCustomStage : public GrRefCnt {
+
+public:
+    SK_DECLARE_INST_COUNT(GrCustomStage)
+
+    typedef GrProgramStageFactory::StageKey StageKey;
+
+    explicit GrCustomStage(int numTextures);
+    virtual ~GrCustomStage();
+
+    /** If given an input texture that is/is not opaque, is this
+        stage guaranteed to produce an opaque output? */
+    virtual bool isOpaque(bool inputTextureIsOpaque) const;
+
+    /** 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, GrTProgramStageFactory. It is templated
+        on the subclass of GrCustomStage. The subclass must have a nested type
+        (or typedef) named GLProgramStage which will be the subclass of
+        GrGLProgramStage created by the factory.
+
+        Example:
+        class MyCustomStage : public GrCustomStage {
+        ...
+            virtual const GrProgramStageFactory& getFactory() const
+                                                            SK_OVERRIDE {
+                return GrTProgramStageFactory<MyCustomStage>::getInstance();
+            }
+        ...
+        };
+     */
+    virtual const GrProgramStageFactory& getFactory() const = 0;
+
+    /** Returns true if the other custom stage will generate identical output.
+        Must only be called if the two are already known to be of the
+        same type (i.e.  they return the same value from getFactory()).
+
+        Equality is not the same thing as equivalence.
+        To test for equivalence (that they will generate the same
+        shader code, but may have different uniforms), check equality
+        of the stageKey produced by the GrProgramStageFactory:
+        a.getFactory().glStageKey(a) == b.getFactory().glStageKey(b).
+
+        The default implementation of this function returns true iff
+        the two stages have the same return value for numTextures() and
+        for texture() over all valid indicse.
+     */
+    virtual bool isEqual(const GrCustomStage&) const;
+
+    /** Human-meaningful string to identify this effect; may be embedded
+        in generated shader code. */
+    const char* name() const { return this->getFactory().name(); }
+
+    int numTextures() const { return fNumTextures; }
+
+    /** Returns the access pattern for the texture at index. index must be valid according to
+        numTextures(). */
+    virtual const GrTextureAccess& textureAccess(int index) const;
+
+    /** Shortcut for textureAccess(index).texture(); */
+    GrTexture* texture(int index) const { return this->textureAccess(index).getTexture(); }
+
+    void* operator new(size_t size);
+    void operator delete(void* target);
+
+private:
+    int fNumTextures;
+    typedef GrRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/gpu/GrCustomStageUnitTest.h b/include/gpu/GrCustomStageUnitTest.h
new file mode 100644
index 0000000..e0f179d
--- /dev/null
+++ b/include/gpu/GrCustomStageUnitTest.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 GrCustomStageUnitTest_DEFINED
+#define GrCustomStageUnitTest_DEFINED
+
+#include "SkRandom.h"
+#include "GrNoncopyable.h"
+#include "SkTArray.h"
+
+namespace GrCustomStageUnitTest {
+// Used to access the dummy textures in TestCreate procs.
+enum {
+    kSkiaPMTextureIdx = 0,
+    kAlphaTextureIdx = 1,
+};
+}
+
+#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+
+class GrCustomStage;
+class GrContext;
+class GrTexture;
+
+class GrCustomStageTestFactory : GrNoncopyable {
+public:
+
+    typedef GrCustomStage* (*CreateProc)(SkRandom*, GrContext*, GrTexture* dummyTextures[]);
+
+    GrCustomStageTestFactory(CreateProc createProc) {
+        fCreateProc = createProc;
+        GetFactories()->push_back(this);
+    }
+
+    static GrCustomStage* CreateStage(SkRandom* random,
+                                      GrContext* context,
+                                      GrTexture* dummyTextures[]) {
+        uint32_t idx = random->nextRangeU(0, GetFactories()->count() - 1);
+        GrCustomStageTestFactory* factory = (*GetFactories())[idx];
+        return factory->fCreateProc(random, context, dummyTextures);
+    }
+
+private:
+    CreateProc fCreateProc;
+    static SkTArray<GrCustomStageTestFactory*, true>* GetFactories();
+};
+
+/** GrCustomStage subclasses should insert this macro in their declaration to be included in the
+ *  program generation unit test.
+ */
+#define GR_DECLARE_CUSTOM_STAGE_TEST                                           \
+    static GrCustomStageTestFactory gTestFactory;                              \
+    static GrCustomStage* TestCreate(SkRandom*, GrContext*, GrTexture* dummyTextures[2])
+
+/** GrCustomStage subclasses should insert this macro in their implemenation file. They must then
+ *  also implement this static function:
+ *      GrCustomStage* CreateStage(SkRandom*, GrContext*, GrTexture* dummyTextures[2]);
+ *  dummyTextures[] are valied textures that they can optionally use for their texture accesses. 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_CUSTOM_STAGE_TEST(CustomStage)                               \
+    GrCustomStageTestFactory CustomStage :: gTestFactory(CustomStage :: 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_CUSTOM_STAGE_TEST \
+    static GrCustomStage* TestCreate(SkRandom*, GrContext*, GrTexture* dummyTextures[2])
+#define GR_DEFINE_CUSTOM_STAGE_TEST(X)
+
+#endif // !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+#endif
diff --git a/include/gpu/GrFontScaler.h b/include/gpu/GrFontScaler.h
new file mode 100644
index 0000000..a69e912
--- /dev/null
+++ b/include/gpu/GrFontScaler.h
@@ -0,0 +1,42 @@
+
+/*
+ * 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 GrFontScaler_DEFINED
+#define GrFontScaler_DEFINED
+
+#include "GrGlyph.h"
+#include "GrKey.h"
+
+class SkPath;
+
+/**
+ *  This is a virtual base class which Gr's interface to the host platform's
+ *  font scaler.
+ *
+ *  The client is responsible for subclassing, and instantiating this. The
+ *  instance is create for a specific font+size+matrix.
+ */
+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
new file mode 100644
index 0000000..956404e
--- /dev/null
+++ b/include/gpu/GrGlyph.h
@@ -0,0 +1,82 @@
+
+/*
+ * 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 GrGlyph_DEFINED
+#define GrGlyph_DEFINED
+
+#include "GrRect.h"
+#include "SkPath.h"
+
+class GrAtlas;
+
+/*  Need this to be quad-state:
+    - complete w/ image
+    - just metrics
+    - failed to get image, but has metrics
+    - failed to get metrics
+ */
+struct GrGlyph {
+    typedef uint32_t PackedID;
+
+    GrAtlas*    fAtlas;
+    SkPath*     fPath;
+    PackedID    fPackedID;
+    GrIRect16   fBounds;
+    GrIPoint16  fAtlasLocation;
+
+    void init(GrGlyph::PackedID packed, const GrIRect& bounds) {
+        fAtlas = NULL;
+        fPath = NULL;
+        fPackedID = packed;
+        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;
+    }
+};
+
+
+#endif
+
diff --git a/include/gpu/GrInstanceCounter.h b/include/gpu/GrInstanceCounter.h
new file mode 100644
index 0000000..b3e21d2
--- /dev/null
+++ b/include/gpu/GrInstanceCounter.h
@@ -0,0 +1,40 @@
+
+/*
+ * 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
new file mode 100644
index 0000000..17d00de
--- /dev/null
+++ b/include/gpu/GrKey.h
@@ -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.
+ */
+
+
+
+#ifndef GrKey_DEFINED
+#define GrKey_DEFINED
+
+#include "GrRefCnt.h"
+
+class GrKey : public GrRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrKey)
+
+    typedef intptr_t Hash;
+
+    explicit GrKey(Hash hash) : fHash(hash) {}
+
+    intptr_t getHash() const { return fHash; }
+
+    bool operator<(const GrKey& rh) const {
+        return fHash < rh.fHash || (fHash == rh.fHash && this->lt(rh));
+    }
+    bool operator==(const GrKey& rh) const {
+        return fHash == rh.fHash && this->eq(rh);
+    }
+
+protected:
+    virtual bool lt(const GrKey& rh) const = 0;
+    virtual bool eq(const GrKey& rh) const = 0;
+
+private:
+    const Hash fHash;
+
+    typedef GrRefCnt INHERITED;
+};
+
+#endif
+
diff --git a/include/gpu/GrMatrix.h b/include/gpu/GrMatrix.h
new file mode 100644
index 0000000..055680a
--- /dev/null
+++ b/include/gpu/GrMatrix.h
@@ -0,0 +1,19 @@
+
+/*
+ * 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
new file mode 100644
index 0000000..cad722f
--- /dev/null
+++ b/include/gpu/GrNoncopyable.h
@@ -0,0 +1,31 @@
+
+/*
+ * 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 GrNoncopyable_DEFINED
+#define GrNoncopyable_DEFINED
+
+#include "GrTypes.h"
+
+/**
+ *  Base for classes that want to disallow copying themselves. It makes its
+ *  copy-constructor and assignment operators private (and unimplemented).
+ */
+class GR_API GrNoncopyable {
+public:
+    GrNoncopyable() {}
+
+private:
+    // illegal
+    GrNoncopyable(const GrNoncopyable&);
+    GrNoncopyable& operator=(const GrNoncopyable&);
+};
+
+#endif
+
diff --git a/include/gpu/GrPaint.h b/include/gpu/GrPaint.h
new file mode 100644
index 0000000..3c29662
--- /dev/null
+++ b/include/gpu/GrPaint.h
@@ -0,0 +1,337 @@
+
+/*
+ * 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 GrPaint_DEFINED
+#define GrPaint_DEFINED
+
+#include "GrTexture.h"
+#include "GrColor.h"
+#include "GrSamplerState.h"
+
+#include "SkXfermode.h"
+
+/**
+ * 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 GrCustomStage.
+ *
+ * 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 it turn feeds into the color matrix. The output of the color matrix
+ * is 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 and color matrix in stages and remove from GrPaint.
+ */
+class GrPaint {
+public:
+    enum {
+        kMaxColorStages     = 2,
+        kMaxCoverageStages  = 1,
+    };
+
+    GrPaint() { this->reset(); }
+
+    GrPaint(const GrPaint& paint) { *this = paint; }
+
+    ~GrPaint() {}
+
+    /**
+     * 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; }
+
+    /**
+     * Turns off application of a color matrix. By default the color matrix is disabled.
+     */
+    void disableColorMatrix() { fColorMatrixEnabled = false; }
+
+    /**
+     * Specifies and enables a 4 x 5 color matrix.
+     */
+    void setColorMatrix(const float matrix[20]) {
+        fColorMatrixEnabled = true;
+        memcpy(fColorMatrix, matrix, sizeof(fColorMatrix));
+    }
+
+    bool isColorMatrixEnabled() const { return fColorMatrixEnabled; }
+    const float* getColorMatrix() const { return fColorMatrix; }
+
+    /**
+     * Disables both the matrix and SkXfermode::Mode color filters.
+     */
+    void resetColorFilter() {
+        fColorFilterXfermode = SkXfermode::kDst_Mode;
+        fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
+        fColorMatrixEnabled = false;
+    }
+
+    /**
+     * 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 colorSampler(0).
+     */
+    GrSamplerState* colorSampler(int i) {
+        GrAssert((unsigned)i < kMaxColorStages);
+        return fColorSamplers + i;
+    }
+
+    const GrSamplerState& getColorSampler(int i) const {
+        GrAssert((unsigned)i < kMaxColorStages);
+        return fColorSamplers[i];
+    }
+
+    bool isColorStageEnabled(int i) const {
+        GrAssert((unsigned)i < kMaxColorStages);
+        return (NULL != fColorSamplers[i].getCustomStage());
+    }
+
+    /**
+     * Specifies a stage of the coverage pipeline. Coverage stages' texture matrices are always
+     * applied to the primitive's position, never to explicit texture coords.
+     */
+    GrSamplerState* coverageSampler(int i) {
+        GrAssert((unsigned)i < kMaxCoverageStages);
+        return fCoverageSamplers + i;
+    }
+
+    const GrSamplerState& getCoverageSampler(int i) const {
+        GrAssert((unsigned)i < kMaxCoverageStages);
+        return fCoverageSamplers[i];
+    }
+
+    bool isCoverageStageEnabled(int i) const {
+        GrAssert((unsigned)i < kMaxCoverageStages);
+        return (NULL != fCoverageSamplers[i].getCustomStage());
+    }
+
+    bool hasCoverageStage() const {
+        for (int i = 0; i < kMaxCoverageStages; ++i) {
+            if (this->isCoverageStageEnabled(i)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool hasColorStage() const {
+        for (int i = 0; i < kMaxColorStages; ++i) {
+            if (this->isColorStageEnabled(i)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    bool hasStage() const { return this->hasColorStage() || this->hasCoverageStage(); }
+
+    /**
+     * Preconcats the matrix of all enabled stages with the inverse of a matrix. If the matrix
+     * inverse cannot be computed (and there is at least one enabled stage) then false is returned.
+     */
+    bool preConcatSamplerMatricesWithInverse(const GrMatrix& matrix) {
+        GrMatrix inv;
+        bool computed = false;
+        for (int i = 0; i < kMaxColorStages; ++i) {
+            if (this->isColorStageEnabled(i)) {
+                if (!computed && !matrix.invert(&inv)) {
+                    return false;
+                } else {
+                    computed = true;
+                }
+                fColorSamplers[i].preConcatMatrix(inv);
+            }
+        }
+        for (int i = 0; i < kMaxCoverageStages; ++i) {
+            if (this->isCoverageStageEnabled(i)) {
+                if (!computed && !matrix.invert(&inv)) {
+                    return false;
+                } else {
+                    computed = true;
+                }
+                fCoverageSamplers[i].preConcatMatrix(inv);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Preconcats the matrix of all stages with a matrix.
+     */
+    void preConcatSamplerMatrices(const GrMatrix& matrix) {
+        for (int i = 0; i < kMaxColorStages; ++i) {
+            if (this->isColorStageEnabled(i)) {
+                fColorSamplers[i].preConcatMatrix(matrix);
+            }
+        }
+        for (int i = 0; i < kMaxCoverageStages; ++i) {
+            if (this->isCoverageStageEnabled(i)) {
+                fCoverageSamplers[i].preConcatMatrix(matrix);
+            }
+        }
+    }
+
+    GrPaint& operator=(const GrPaint& paint) {
+        fSrcBlendCoeff = paint.fSrcBlendCoeff;
+        fDstBlendCoeff = paint.fDstBlendCoeff;
+        fAntiAlias = paint.fAntiAlias;
+        fDither = paint.fDither;
+
+        fColor = paint.fColor;
+        fCoverage = paint.fCoverage;
+
+        fColorFilterColor = paint.fColorFilterColor;
+        fColorFilterXfermode = paint.fColorFilterXfermode;
+        fColorMatrixEnabled = paint.fColorMatrixEnabled;
+        if (fColorMatrixEnabled) {
+            memcpy(fColorMatrix, paint.fColorMatrix, sizeof(fColorMatrix));
+        }
+
+        for (int i = 0; i < kMaxColorStages; ++i) {
+            if (paint.isColorStageEnabled(i)) {
+                fColorSamplers[i] = paint.fColorSamplers[i];
+            }
+        }
+        for (int i = 0; i < kMaxCoverageStages; ++i) {
+            if (paint.isCoverageStageEnabled(i)) {
+                fCoverageSamplers[i] = paint.fCoverageSamplers[i];
+            }
+        }
+        return *this;
+    }
+
+    /**
+     * Resets the paint to the defaults.
+     */
+    void reset() {
+        this->resetBlend();
+        this->resetOptions();
+        this->resetColor();
+        this->resetCoverage();
+        this->resetTextures();
+        this->resetColorFilter();
+        this->resetMasks();
+    }
+
+    // internal use
+    // GrPaint's textures and masks map to the first N stages
+    // of GrDrawTarget in that order (textures followed by masks)
+    enum {
+        kFirstColorStage = 0,
+        kFirstCoverageStage = kMaxColorStages,
+        kTotalStages = kFirstColorStage + kMaxColorStages + kMaxCoverageStages,
+    };
+
+private:
+
+    GrSamplerState              fColorSamplers[kMaxColorStages];
+    GrSamplerState              fCoverageSamplers[kMaxCoverageStages];
+
+    GrBlendCoeff                fSrcBlendCoeff;
+    GrBlendCoeff                fDstBlendCoeff;
+    bool                        fAntiAlias;
+    bool                        fDither;
+    bool                        fColorMatrixEnabled;
+
+    GrColor                     fColor;
+    uint8_t                     fCoverage;
+
+    GrColor                     fColorFilterColor;
+    SkXfermode::Mode            fColorFilterXfermode;
+    float                       fColorMatrix[20];
+
+    void resetBlend() {
+        fSrcBlendCoeff = kOne_GrBlendCoeff;
+        fDstBlendCoeff = kZero_GrBlendCoeff;
+    }
+
+    void resetOptions() {
+        fAntiAlias = false;
+        fDither = false;
+    }
+
+    void resetColor() {
+        fColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
+    }
+
+    void resetCoverage() {
+        fCoverage = 0xff;
+    }
+
+    void resetTextures() {
+        for (int i = 0; i < kMaxColorStages; ++i) {
+            fColorSamplers[i].reset();
+        }
+    }
+
+    void resetMasks() {
+        for (int i = 0; i < kMaxCoverageStages; ++i) {
+            fCoverageSamplers[i].reset();
+        }
+    }
+};
+
+#endif
diff --git a/include/gpu/GrPoint.h b/include/gpu/GrPoint.h
new file mode 100644
index 0000000..fd2c5a7
--- /dev/null
+++ b/include/gpu/GrPoint.h
@@ -0,0 +1,31 @@
+
+/*
+ * 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 GrPoint_DEFINED
+#define GrPoint_DEFINED
+
+#include "GrTypes.h"
+#include "GrScalar.h"
+#include "SkPoint.h"
+
+#define GrPoint     SkPoint
+#define GrVec       SkVector
+
+struct GrIPoint16 {
+    int16_t fX, fY;
+
+    void set(intptr_t x, intptr_t y) {
+        fX = GrToS16(x);
+        fY = GrToS16(y);
+    }
+};
+
+#endif
+
diff --git a/include/gpu/GrProgramStageFactory.h b/include/gpu/GrProgramStageFactory.h
new file mode 100644
index 0000000..90f6b32
--- /dev/null
+++ b/include/gpu/GrProgramStageFactory.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrProgramStageFactory_DEFINED
+#define GrProgramStageFactory_DEFINED
+
+#include "GrTypes.h"
+#include "SkTemplates.h"
+#include "GrNoncopyable.h"
+
+/** Given a GrCustomStage of a particular type, creates the corresponding
+    graphics-backend-specific GrProgramStage. Also tracks equivalence
+    of shaders generated via a key.
+ */
+
+class GrCustomStage;
+class GrGLProgramStage;
+class GrGLCaps;
+
+class GrProgramStageFactory : public GrNoncopyable {
+public:
+    typedef uint32_t StageKey;
+    enum {
+        kProgramStageKeyBits = 10,
+        kTexturingStageKeyBits = 6
+    };
+
+    virtual StageKey glStageKey(const GrCustomStage& stage,
+                                const GrGLCaps& caps ) const = 0;
+    virtual GrGLProgramStage* createGLInstance(
+        const GrCustomStage& stage) const = 0;
+
+    bool operator ==(const GrProgramStageFactory& b) const {
+        return fStageClassID == b.fStageClassID;
+    }
+    bool operator !=(const GrProgramStageFactory& b) const {
+        return !(*this == b);
+    }
+
+    virtual const char* name() const = 0;
+
+protected:
+    enum {
+        kIllegalStageClassID = 0,
+    };
+
+    GrProgramStageFactory() {
+        fStageClassID = kIllegalStageClassID;
+    }
+
+    static StageKey GenID() {
+        // fCurrStageClassID has been initialized to kIllegalStageClassID. 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(&fCurrStageClassID) + 1;
+        GrAssert(id < (1 << (8 * sizeof(StageKey) - kProgramStageKeyBits)));
+        return id;
+    }
+
+    StageKey fStageClassID;
+
+private:
+    static int32_t fCurrStageClassID;
+};
+
+template <typename StageClass>
+class GrTProgramStageFactory : public GrProgramStageFactory {
+
+public:
+    typedef typename StageClass::GLProgramStage GLProgramStage;
+
+    /** Returns a human-readable name that is accessible via GrCustomStage or
+        GrGLProgramStage and is consistent between the two of them.
+     */
+    virtual const char* name() const SK_OVERRIDE { return StageClass::Name(); }
+
+    /** Returns a value that idenitifes the GLSL shader code generated by
+        a GrCustomStage. This enables caching of generated shaders. Part of the
+        id identifies the GrCustomShader subclass. The remainder is based
+        on the aspects of the GrCustomStage object's configuration that affect
+        GLSL code generation. */
+    virtual StageKey glStageKey(const GrCustomStage& stage,
+                                const GrGLCaps& caps) const SK_OVERRIDE {
+        GrAssert(kIllegalStageClassID != fStageClassID);
+        StageKey stageID = GLProgramStage::GenKey(stage, caps);
+        StageKey textureKey = GLProgramStage::GenTextureKey(stage, caps);
+#if GR_DEBUG
+        static const StageKey kIllegalIDMask =
+            (uint16_t) (~((1U << kProgramStageKeyBits) - 1));
+        GrAssert(!(kIllegalIDMask & stageID));
+
+        static const StageKey kIllegalTextureKeyMask =
+            (uint16_t) (~((1U << kTexturingStageKeyBits) - 1));
+        GrAssert(!(kIllegalTextureKeyMask & textureKey));
+#endif
+        return fStageClassID | (textureKey << kProgramStageKeyBits) | stageID;
+    }
+
+    /** Returns a new instance of the appropriate *GL* implementation class
+        for the given GrCustomStage; caller is responsible for deleting
+        the object. */
+    virtual GLProgramStage* createGLInstance(
+                        const GrCustomStage& stage) const SK_OVERRIDE {
+        return SkNEW_ARGS(GLProgramStage, (*this, stage));
+    }
+
+    /** This class is a singleton. This function returns the single instance.
+     */
+    static const GrProgramStageFactory& getInstance() {
+        static SkAlignedSTStorage<1, GrTProgramStageFactory> gInstanceMem;
+        static const GrTProgramStageFactory* gInstance;
+        if (!gInstance) {
+            gInstance = SkNEW_PLACEMENT(gInstanceMem.get(),
+                                        GrTProgramStageFactory);
+        }
+        return *gInstance;
+    }
+
+protected:
+    GrTProgramStageFactory() {
+        fStageClassID = GenID() << (kProgramStageKeyBits + kTexturingStageKeyBits) ;
+    }
+};
+
+#endif
diff --git a/include/gpu/GrRect.h b/include/gpu/GrRect.h
new file mode 100644
index 0000000..42ddd8e
--- /dev/null
+++ b/include/gpu/GrRect.h
@@ -0,0 +1,37 @@
+
+/*
+ * 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 GrRect_DEFINED
+#define GrRect_DEFINED
+
+#include "GrPoint.h"
+#include "SkRect.h"
+
+typedef SkIRect GrIRect;
+typedef SkRect  GrRect;
+
+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);
+        fRight  = SkToS16(r.fRight);
+        fBottom = SkToS16(r.fBottom);
+    }
+};
+
+#endif
+
diff --git a/include/gpu/GrRefCnt.h b/include/gpu/GrRefCnt.h
new file mode 100644
index 0000000..50c32c6
--- /dev/null
+++ b/include/gpu/GrRefCnt.h
@@ -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.
+ */
+
+
+
+#ifndef GrRefCnt_DEFINED
+#define GrRefCnt_DEFINED
+
+#include "GrTypes.h"
+#include "SkRefCnt.h"
+
+typedef SkRefCnt GrRefCnt;
+typedef SkAutoRef GrAutoRef;
+typedef SkAutoUnref GrAutoUnref;
+
+#define GrSafeRef SkSafeRef
+#define GrSafeUnref SkSafeUnref
+#define GrSafeAssign(a, b)  SkRefCnt_SafeAssign(a, b)
+
+template<typename T>
+static inline void GrSafeSetNull(T*& obj) {
+    if (NULL != obj) {
+        obj->unref();
+        obj = NULL;
+    }
+}
+
+#endif
+
diff --git a/include/gpu/GrRenderTarget.h b/include/gpu/GrRenderTarget.h
new file mode 100644
index 0000000..f3add50
--- /dev/null
+++ b/include/gpu/GrRenderTarget.h
@@ -0,0 +1,184 @@
+/*
+    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.
+ */
+
+
+#ifndef GrRenderTarget_DEFINED
+#define GrRenderTarget_DEFINED
+
+#include "GrRect.h"
+#include "GrSurface.h"
+
+class GrStencilBuffer;
+class GrTexture;
+
+/**
+ * GrRenderTarget represents a 2D buffer of pixels that can be rendered to.
+ * A context's render target is set by setRenderTarget(). Render targets are
+ * created by a createTexture with the kRenderTarget_TextureFlag flag.
+ * Additionally, GrContext provides methods for creating GrRenderTargets
+ * that wrap externally created render targets.
+ */
+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 rendertarget, may be NULL.
+     */
+    virtual GrTexture* asTexture() SK_OVERRIDE { return fTexture; }
+    virtual const GrTexture* asTexture() const SK_OVERRIDE { return fTexture; }
+
+    /**
+     * @return this render target.
+     */
+    virtual GrRenderTarget* asRenderTarget() SK_OVERRIDE { return this; }
+    virtual const GrRenderTarget* asRenderTarget() const  SK_OVERRIDE {
+        return this;
+    }
+
+    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;
+
+    // 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;
+
+    /**
+     * 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)
+     * @return the 3D API's handle to this object (e.g. FBO ID in OpenGL)
+     */
+    virtual intptr_t getRenderTargetResolvedHandle() const = 0;
+
+    /**
+     * @return true if the surface is multisampled, false otherwise
+     */
+    bool isMultisampled() const { return 0 != fDesc.fSampleCnt; }
+
+    /**
+     * @return the number of samples-per-pixel or zero if non-MSAA.
+     */
+    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.
+     * @param rect  a rect bounding the area needing resolve. NULL indicates
+     *              the whole RT needs resolving.
+     */
+    void flagAsNeedingResolve(const GrIRect* rect = NULL);
+
+    /**
+     * Call to override the region that needs to be resolved.
+     */
+    void overrideResolveRect(const GrIRect rect);
+
+    /**
+     * Call to indicate that GrRenderTarget was externally resolved. This may
+     * allow Gr to skip a redundant resolve step.
+     */
+    void flagAsResolved() { fResolveRect.setLargestInverted(); }
+
+    /**
+     * @return true if the GrRenderTarget requires MSAA resolving
+     */
+    bool needsResolve() const { return !fResolveRect.isEmpty(); }
+
+    /**
+     * Returns a rect bounding the region needing resolving.
+     */
+    const GrIRect& getResolveRect() const { return fResolveRect; }
+
+    /**
+     * If the render target is multisampled this will perform a multisample
+     * resolve. Any pending draws to the target are first flushed. This only
+     * applies to render targets that are associated with GrTextures. After the
+     * function returns the GrTexture will contain the resolved pixels.
+     */
+    void resolve();
+
+    // 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
+    // resolve destination.
+    enum ResolveType {
+        kCanResolve_ResolveType,
+        kAutoResolves_ResolveType,
+        kCantResolve_ResolveType,
+    };
+    virtual ResolveType getResolveType() const = 0;
+
+    /**
+     * GrStencilBuffer is not part of the public API.
+     */
+    GrStencilBuffer* getStencilBuffer() const { return fStencilBuffer; }
+    void setStencilBuffer(GrStencilBuffer* stencilBuffer);
+
+protected:
+    GrRenderTarget(GrGpu* gpu,
+                   GrTexture* texture,
+                   const GrTextureDesc& desc)
+        : INHERITED(gpu, desc)
+        , fStencilBuffer(NULL)
+        , fTexture(texture) {
+        fResolveRect.setLargestInverted();
+    }
+
+    friend class GrTexture;
+    // When a texture unrefs an owned rendertarget this func
+    // removes the back pointer. This could be called from
+    // texture's destructor but would have to be done in derived
+    // 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
+
+    GrIRect           fResolveRect;
+
+    typedef GrSurface INHERITED;
+};
+
+#endif
diff --git a/include/gpu/GrResource.h b/include/gpu/GrResource.h
new file mode 100644
index 0000000..aeab180
--- /dev/null
+++ b/include/gpu/GrResource.h
@@ -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.
+ */
+
+
+#ifndef GrResource_DEFINED
+#define GrResource_DEFINED
+
+#include "GrRefCnt.h"
+
+#include "SkTDLinkedList.h"
+
+class GrGpu;
+class GrContext;
+class GrResourceEntry;
+
+/**
+ * Base class for the GPU resources created by a GrContext.
+ */
+class GrResource : public GrRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrResource)
+
+    /**
+     * Frees the resource in the underlying 3D API. It must be safe to call this
+     * when the resource has been previously abandoned.
+     */
+    void release();
+
+    /**
+     * Removes references to objects in the underlying 3D API without freeing
+     * them. Used when the API context has been torn down before the GrContext.
+     */
+    void abandon();
+
+    /**
+     * Tests whether a resource has been abandoned or released. All resources
+     * will be in this state after their creating GrContext is destroyed or has
+     * contextLost called. It's up to the client to test isValid() before
+     * attempting to use a resource if it holds refs on resources across
+     * ~GrContext, freeResources with the force flag, or contextLost.
+     *
+     * @return true if the resource has been released or abandoned,
+     *         false otherwise.
+     */
+    bool isValid() const { return NULL != fGpu; }
+
+    /**
+     * Retrieves the size of the object in GPU memory. This is approximate since
+     * we aren't aware of additional padding or copies made by the driver.
+     *
+     * @return the size of the buffer in bytes
+     */
+    virtual size_t sizeInBytes() const = 0;
+
+     /**
+      * Retrieves the context that owns the resource. Note that it is possible
+      * for this to return NULL. When resources have been release()ed or
+      * abandon()ed they no longer have an owning context. Destroying a
+      * GrContext automatically releases all its resources.
+      */
+    const GrContext* getContext() const;
+    GrContext* getContext();
+
+    void setCacheEntry(GrResourceEntry* cacheEntry) { fCacheEntry = cacheEntry; }
+    GrResourceEntry* getCacheEntry() { return fCacheEntry; }
+
+protected:
+    explicit GrResource(GrGpu* gpu);
+    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; }
+
+private:
+
+#if GR_DEBUG
+    friend class GrGpu; // for assert in GrGpu to access getGpu
+#endif
+
+    GrGpu*      fGpu;       // not reffed. The GrGpu can be deleted while there
+                            // are still live GrResources. It will call
+                            // release() on all such resources in its
+                            // destructor.
+
+    // we're a dlinklist
+    SK_DEFINE_DLINKEDLIST_INTERFACE(GrResource);
+
+    GrResourceEntry* fCacheEntry;  // NULL if not in cache
+
+    typedef GrRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/gpu/GrSamplerState.h b/include/gpu/GrSamplerState.h
new file mode 100644
index 0000000..da52e95
--- /dev/null
+++ b/include/gpu/GrSamplerState.h
@@ -0,0 +1,101 @@
+
+/*
+ * 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 "GrCustomStage.h"
+#include "GrMatrix.h"
+#include "GrTypes.h"
+
+#include "SkShader.h"
+
+class GrSamplerState {
+public:
+
+    GrSamplerState()
+    : fCustomStage (NULL) {
+        memset(this, 0, sizeof(GrSamplerState));
+        this->reset();
+    }
+
+    ~GrSamplerState() {
+        GrSafeUnref(fCustomStage);
+    }
+
+    bool operator ==(const GrSamplerState& s) const {
+        /* We must be bit-identical as far as the CustomStage;
+           there may be multiple CustomStages that will produce
+           the same shader code and so are equivalent.
+           Can't take the address of fWrapX because it's :8 */
+        int bitwiseRegion = (intptr_t) &fCustomStage - (intptr_t) this;
+        GrAssert(sizeof(GrSamplerState) ==
+                 bitwiseRegion + sizeof(fCustomStage));
+        return !memcmp(this, &s, bitwiseRegion) &&
+               ((fCustomStage == s.fCustomStage) ||
+                (fCustomStage && s.fCustomStage &&
+                 (fCustomStage->getFactory() ==
+                     s.fCustomStage->getFactory()) &&
+                 fCustomStage->isEqual(*s.fCustomStage)));
+    }
+    bool operator !=(const GrSamplerState& s) const { return !(*this == s); }
+
+    GrSamplerState& operator =(const GrSamplerState& s) {
+        fMatrix = s.fMatrix;
+        GrSafeAssign(fCustomStage, s.fCustomStage);
+        return *this;
+    }
+
+    const GrMatrix& getMatrix() const { return fMatrix; }
+
+    /**
+     *  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); }
+
+    /**
+     * Do not call this function. It will be removed soon.
+     */
+    void setMatrixDeprecated(const GrMatrix& matrix) { fMatrix = matrix; }
+
+    void reset() {
+        fMatrix.reset();
+        GrSafeSetNull(fCustomStage);
+    }
+
+    GrCustomStage* setCustomStage(GrCustomStage* stage) {
+        GrSafeAssign(fCustomStage, stage);
+        fMatrix.reset();
+        return stage;
+    }
+
+    GrCustomStage* setCustomStage(GrCustomStage* stage, const GrMatrix& matrix) {
+        GrSafeAssign(fCustomStage, stage);
+        fMatrix = matrix;
+        return stage;
+    }
+
+    const GrCustomStage* getCustomStage() const { return fCustomStage; }
+
+private:
+    GrMatrix            fMatrix;
+
+    GrCustomStage*      fCustomStage;
+};
+
+#endif
+
diff --git a/include/gpu/GrScalar.h b/include/gpu/GrScalar.h
new file mode 100644
index 0000000..5d2f13b
--- /dev/null
+++ b/include/gpu/GrScalar.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 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)  SkFloatToScalar(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..aa237ae
--- /dev/null
+++ b/include/gpu/GrSurface.h
@@ -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.
+ */
+
+
+#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; }
+
+    /**
+     * 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 bewtween 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 bewtween 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:
+    GrTextureDesc fDesc;
+
+    GrSurface(GrGpu* gpu, const GrTextureDesc& desc)
+    : INHERITED(gpu)
+    , fDesc(desc) {
+    }
+
+private:
+    typedef GrResource INHERITED;
+};
+
+#endif // GrSurface_DEFINED
diff --git a/include/gpu/GrTextContext.h b/include/gpu/GrTextContext.h
new file mode 100644
index 0000000..6b28b79
--- /dev/null
+++ b/include/gpu/GrTextContext.h
@@ -0,0 +1,64 @@
+
+/*
+ * 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 GrTextContext_DEFINED
+#define GrTextContext_DEFINED
+
+#include "GrContext.h"
+#include "GrGlyph.h"
+#include "GrPaint.h"
+
+struct GrGpuTextVertex;
+class GrContext;
+class GrTextStrike;
+class GrFontScaler;
+class GrDrawTarget;
+
+class GrTextContext {
+public:
+    GrTextContext(GrContext*, const GrPaint&);
+    ~GrTextContext();
+
+    void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top,
+                         GrFontScaler*);
+
+    void flush();   // optional; automatically called by destructor
+
+private:
+    GrPaint         fPaint;
+    GrVertexLayout  fVertexLayout;
+    GrContext*      fContext;
+    GrDrawTarget*   fDrawTarget;
+
+    GrFontScaler*   fScaler;
+    GrTextStrike*   fStrike;
+
+    inline void flushGlyphs();
+    void setupDrawTarget();
+
+    enum {
+        kMinRequestedGlyphs      = 1,
+        kDefaultRequestedGlyphs  = 64,
+        kMinRequestedVerts       = kMinRequestedGlyphs * 4,
+        kDefaultRequestedVerts   = kDefaultRequestedGlyphs * 4,
+    };
+
+    GrGpuTextVertex*        fVertices;
+
+    int32_t                 fMaxVertices;
+    GrTexture*              fCurrTexture;
+    int                     fCurrVertex;
+
+    GrIRect                 fClipRect;
+    GrContext::AutoMatrix   fAutoMatrix;
+};
+
+#endif
+
diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h
new file mode 100644
index 0000000..5f6d535
--- /dev/null
+++ b/include/gpu/GrTexture.h
@@ -0,0 +1,176 @@
+
+/*
+ * 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 GrTexture_DEFINED
+#define GrTexture_DEFINED
+
+#include "GrSurface.h"
+#include "GrCacheID.h"
+
+class GrRenderTarget;
+class GrResourceKey;
+class GrTextureParams;
+
+class GrTexture : public GrSurface {
+
+public:
+    SK_DECLARE_INST_COUNT(GrTexture)
+    GR_DECLARE_RESOURCE_CACHE_TYPE()
+
+    // from GrResource
+    /**
+     * Informational texture flags
+     */
+    enum FlagBits {
+        kFirstBit = (kLastPublic_GrTextureFlagBit << 1),
+
+        /**
+         * This texture should be returned to the texture cache when
+         * it is no longer reffed
+         */
+        kReturnToCache_FlagBit        = kFirstBit,
+    };
+
+    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 SK_OVERRIDE {
+        return (size_t) fDesc.fWidth *
+                        fDesc.fHeight *
+                        GrBytesPerPixel(fDesc.fConfig);
+    }
+
+    // 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;
+
+    /**
+     * @return this texture
+     */
+    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
+     * GrGpu::setRenderTarget().
+     *
+     * @return    handle to render target or NULL if the texture is not a
+     *            render target
+     */
+    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
+     * texture. Afterwards asRenderTarget() will return NULL. The
+     * GrRenderTarget survives the release if another ref is held on it.
+     */
+    void releaseRenderTarget();
+
+    /**
+     *  Return the native ID or handle to the texture, depending on the
+     *  platform. e.g. on opengl, return the texture ID.
+     */
+    virtual intptr_t 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* sampler,
+                                    const GrTextureDesc& desc,
+                                    const GrCacheData& cacheData,
+                                    bool scratch);
+
+    static bool NeedsResizing(const GrResourceKey& key);
+    static bool IsScratchTexture(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, const GrTextureDesc& desc)
+    : INHERITED(gpu, desc)
+    , fRenderTarget(NULL) {
+
+        // only make sense if alloc size is pow2
+        fShiftFixedX = 31 - Gr_clz(fDesc.fWidth);
+        fShiftFixedY = 31 - Gr_clz(fDesc.fHeight);
+    }
+
+    // GrResource overrides
+    virtual void onRelease() SK_OVERRIDE;
+    virtual void onAbandon() SK_OVERRIDE;
+
+    void validateDesc() const;
+
+private:
+    // 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;
+
+    virtual void internal_dispose() const SK_OVERRIDE;
+
+    typedef GrSurface INHERITED;
+};
+
+#endif
+
diff --git a/include/gpu/GrTextureAccess.h b/include/gpu/GrTextureAccess.h
new file mode 100644
index 0000000..0d0860f
--- /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 GrCustomStage 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 GrCustomStage subclass's
+     * constructor if it will be accessible via GrCustomStage::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
new file mode 100644
index 0000000..f084427
--- /dev/null
+++ b/include/gpu/GrTypes.h
@@ -0,0 +1,694 @@
+
+/*
+ * 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 GrTypes_DEFINED
+#define GrTypes_DEFINED
+
+#include "SkTypes.h"
+#include "GrConfig.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Defines overloaded bitwise operators to make it easier to use an enum as a
+ * bitfield.
+ */
+#define GR_MAKE_BITFIELD_OPS(X) \
+    inline X operator | (X a, X b) { \
+        return (X) (+a | +b); \
+    } \
+    \
+    inline X operator & (X a, X b) { \
+        return (X) (+a & +b); \
+    } \
+    template <typename T> \
+    inline X operator & (T a, X b) { \
+        return (X) (+a & +b); \
+    } \
+    template <typename T> \
+    inline X operator & (X a, T b) { \
+        return (X) (+a & +b); \
+    } \
+
+#define GR_DECL_BITFIELD_OPS_FRIENDS(X) \
+    friend X operator | (X a, X b); \
+    \
+    friend X operator & (X a, X b); \
+    \
+    template <typename T> \
+    friend X operator & (T a, X b); \
+    \
+    template <typename T> \
+    friend X operator & (X a, T b); \
+////////////////////////////////////////////////////////////////////////////////
+
+
+/**
+ *  Macro to round n up to the next multiple of 4, or return it unchanged if
+ *  n is already a multiple of 4
+ */
+#define GrALIGN4(n)     SkAlign4(n)
+#define GrIsALIGN4(n)   SkIsAlign4(n)
+
+template <typename T> const T& GrMin(const T& a, const T& b) {
+    return (a < b) ? a : b;
+}
+
+template <typename T> const T& GrMax(const T& a, const T& b) {
+    return (b < a) ? a : b;
+}
+
+// compile time versions of min/max
+#define GR_CT_MAX(a, b) (((b) < (a)) ? (a) : (b))
+#define GR_CT_MIN(a, b) (((b) < (a)) ? (b) : (a))
+
+/**
+ *  divide, rounding up
+ */
+static inline int32_t GrIDivRoundUp(int x, int y) {
+    GrAssert(y > 0);
+    return (x + (y-1)) / y;
+}
+static inline uint32_t GrUIDivRoundUp(uint32_t x, uint32_t y) {
+    return (x + (y-1)) / 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 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, size_t alignment) {
+    return (alignment - x % alignment) % alignment;
+}
+
+/**
+ *  align down
+ */
+static inline uint32_t GrUIAlignDown(uint32_t x, uint32_t alignment) {
+    return (x / alignment) * alignment;
+}
+static inline size_t GrSizeAlignDown(size_t x, uint32_t alignment) {
+    return (x / alignment) * alignment;
+}
+
+/**
+ *  Count elements in an array
+ */
+#define GR_ARRAY_COUNT(array)  SK_ARRAY_COUNT(array)
+
+//!< allocate a block of memory, will never return NULL
+extern void* GrMalloc(size_t bytes);
+
+//!< free block allocated by GrMalloc. ptr may be NULL
+extern void GrFree(void* ptr);
+
+static inline void Gr_bzero(void* dst, size_t size) {
+    memset(dst, 0, size);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  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) {
+    return n && 0 == (n & (n - 1));
+}
+
+/**
+ *  Return the next power of 2 >= n.
+ */
+static inline uint32_t GrNextPow2(uint32_t n) {
+    return n ? (1 << (32 - Gr_clz(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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  16.16 fixed point type
+ */
+typedef int32_t GrFixed;
+
+#if GR_DEBUG
+
+static inline int16_t GrToS16(intptr_t x) {
+    GrAssert((int16_t)x == x);
+    return (int16_t)x;
+}
+
+#else
+
+#define GrToS16(x)  x
+
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Possible 3D APIs that may be used by Ganesh.
+ */
+enum GrEngine {
+    kOpenGL_Shaders_GrEngine,
+    kOpenGL_Fixed_GrEngine,
+};
+
+/**
+ * Engine-specific 3D context handle
+ *      GrGLInterface* for OpenGL. If NULL will use the default GL interface.
+ */
+typedef intptr_t GrPlatform3DContext;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Type used to describe format of vertices in arrays
+ * Values are defined in GrDrawTarget
+ */
+typedef int GrVertexLayout;
+
+/**
+* Geometric primitives used for drawing.
+*/
+enum GrPrimitiveType {
+    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_GrPrimitiveType == type || kLineStrip_GrPrimitiveType == type;
+}
+
+static inline bool GrIsPrimTypeTris(GrPrimitiveType type) {
+    return kTriangles_GrPrimitiveType == type     ||
+           kTriangleStrip_GrPrimitiveType == type ||
+           kTriangleFan_GrPrimitiveType == type;
+}
+
+/**
+ * Coeffecients for alpha-blending.
+ */
+enum GrBlendCoeff {
+    kInvalid_GrBlendCoeff = -1,
+
+    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
+};
+
+/**
+ *  Formats for masks, used by the font cache.
+ *  Important that these are 0-based.
+ */
+enum GrMaskFormat {
+    kA8_GrMaskFormat,    //!< 1-byte per pixel
+    kA565_GrMaskFormat,  //!< 2-bytes per pixel
+    kA888_GrMaskFormat,  //!< 4-bytes per pixel
+
+    kCount_GrMaskFormats //!< used to allocate arrays sized for mask formats
+};
+
+/**
+ *  Return the number of bytes-per-pixel for the specified mask format.
+ */
+static inline int GrMaskFormatBytesPerPixel(GrMaskFormat format) {
+    GrAssert((unsigned)format <= 2);
+    // kA8   (0) -> 1
+    // kA565 (1) -> 2
+    // kA888 (2) -> 4
+    return 1 << (int)format;
+}
+
+/**
+ * Pixel configurations.
+ */
+enum GrPixelConfig {
+    kUnknown_GrPixelConfig,
+    kAlpha_8_GrPixelConfig,
+    kIndex_8_GrPixelConfig,
+    kRGB_565_GrPixelConfig,
+    /**
+     * Premultiplied
+     */
+    kRGBA_4444_GrPixelConfig,
+    /**
+     * Premultiplied. Byte order is r,g,b,a.
+     */
+    kRGBA_8888_GrPixelConfig,
+    /**
+     * Premultiplied. Byte order is b,g,r,a.
+     */
+    kBGRA_8888_GrPixelConfig,
+
+    kGrPixelConfigCount
+};
+
+// 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_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_GrPixelConfig = kRGBA_8888_GrPixelConfig;
+#else
+    #error "SK_*32_SHIFT values must correspond to GL_BGRA or GL_RGBA format."
+#endif
+
+// 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_GrPixelConfig:
+            return true;
+        default:
+            return false;
+    }
+}
+
+// Returns true if the pixel config has 8bit b,g,r,a components in that byte
+// order
+static inline bool GrPixelConfigIsBGRA8888(GrPixelConfig config) {
+    switch (config) {
+        case kBGRA_8888_GrPixelConfig:
+            return true;
+        default:
+            return false;
+    }
+}
+
+// Returns true if the pixel config is 32 bits per pixel
+static inline bool GrPixelConfigIs32Bit(GrPixelConfig config) {
+    switch (config) {
+        case kRGBA_8888_GrPixelConfig:
+        case kBGRA_8888_GrPixelConfig:
+            return true;
+        default:
+            return false;
+    }
+}
+
+// Takes a config and returns the equivalent config with the R and B order
+// swapped if such a config exists. Otherwise, kUnknown_GrPixelConfig
+static inline GrPixelConfig GrPixelConfigSwapRAndB(GrPixelConfig config) {
+    switch (config) {
+        case kBGRA_8888_GrPixelConfig:
+            return kRGBA_8888_GrPixelConfig;
+        case kRGBA_8888_GrPixelConfig:
+            return kBGRA_8888_GrPixelConfig;
+        default:
+            return kUnknown_GrPixelConfig;
+    }
+}
+
+static inline size_t GrBytesPerPixel(GrPixelConfig config) {
+    switch (config) {
+        case kAlpha_8_GrPixelConfig:
+        case kIndex_8_GrPixelConfig:
+            return 1;
+        case kRGB_565_GrPixelConfig:
+        case kRGBA_4444_GrPixelConfig:
+            return 2;
+        case kRGBA_8888_GrPixelConfig:
+        case kBGRA_8888_GrPixelConfig:
+            return 4;
+        default:
+            return 0;
+    }
+}
+
+static inline bool GrPixelConfigIsOpaque(GrPixelConfig config) {
+    switch (config) {
+        case kRGB_565_GrPixelConfig:
+            return true;
+        default:
+            return false;
+    }
+}
+
+static inline bool GrPixelConfigIsAlphaOnly(GrPixelConfig config) {
+    switch (config) {
+        case kAlpha_8_GrPixelConfig:
+            return true;
+        default:
+            return false;
+    }
+}
+
+/**
+ * Optional bitfield flags that can be passed to createTexture.
+ */
+enum GrTextureFlags {
+    kNone_GrTextureFlags            = 0x0,
+    /**
+     * Creates a texture that can be rendered to as a GrRenderTarget. Use
+     * GrTexture::asRenderTarget() to access.
+     */
+    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
+     * creation.
+     * MAKE THIS PRIVATE?
+     */
+    kNoStencil_GrTextureFlagBit     = 0x2,
+    /**
+     * 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)
+
+enum {
+   /**
+    *  For Index8 pixel config, the colortable must be 256 entries
+    */
+    kGrColorTableSize = 256 * 4 //sizeof(GrColor)
+};
+
+
+/**
+ * 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 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.
+     */
+    int                    fSampleCnt;
+};
+
+/**
+ * GrCacheData holds user-provided cache-specific data. It is used in
+ * combination with the GrTextureDesc to construct a cache key for texture
+ * resources.
+ */
+struct GrCacheData {
+    /*
+     * Scratch textures should all have this value as their fClientCacheID
+     */
+    static const uint64_t kScratch_CacheID = 0xBBBBBBBB;
+
+    /**
+      * Resources in the "scratch" domain can be used by any domain. All
+      * scratch textures will have this as their domain.
+      */
+    static const uint8_t kScratch_ResourceDomain = 0;
+
+
+    // No default constructor is provided since, if you are creating one
+    // of these, you should definitely have a key (or be using the scratch
+    // key).
+    GrCacheData(uint64_t key)
+    : fClientCacheID(key)
+    , fResourceDomain(kScratch_ResourceDomain) {
+    }
+
+    /**
+     * A user-provided texture ID. It should be unique to the texture data and
+     * does not need to take into account the width or height. Two textures
+     * with the same ID but different dimensions will not collide. This field
+     * is only relevant for textures that will be cached.
+     */
+    uint64_t               fClientCacheID;
+
+    /**
+     * Allows cache clients to cluster their textures inside domains (e.g.,
+     * alpha clip masks). Only relevant for cached textures.
+     */
+    uint8_t                fResourceDomain;
+};
+
+/**
+ * Clips are composed from these objects.
+ */
+enum GrClipType {
+    kRect_ClipType,
+    kPath_ClipType
+};
+
+/**
+ * Commands used to describe a path. Each command
+ * is accompanied by some number of points.
+ */
+enum GrPathCmd {
+    kMove_PathCmd,      //!< Starts a new subpath at
+                        //   at the returned point
+                        // 1 point
+    kLine_PathCmd,      //!< Adds a line segment
+                        // 2 points
+    kQuadratic_PathCmd, //!< Adds a quadratic segment
+                        // 3 points
+    kCubic_PathCmd,     //!< Adds a cubic segment
+                        // 4 points
+    kClose_PathCmd,     //!< Closes the current subpath
+                        //   by connecting a line to the
+                        //   starting point.
+                        // 0 points
+    kEnd_PathCmd        //!< Indicates the end of the last subpath
+                        //   when iterating
+                        // 0 points.
+};
+
+/**
+ * Gets the number of points associated with a path command.
+ */
+static int inline NumPathCmdPoints(GrPathCmd cmd) {
+    static const int gNumPoints[] = {
+        1, 2, 3, 4, 0, 0
+    };
+    return gNumPoints[cmd];
+}
+
+/**
+ * Path filling rules
+ */
+enum GrPathFill {
+    kWinding_GrPathFill,
+    kEvenOdd_GrPathFill,
+    kInverseWinding_GrPathFill,
+    kInverseEvenOdd_GrPathFill,
+    kHairLine_GrPathFill,
+
+    kGrPathFillCount
+};
+
+static inline GrPathFill GrNonInvertedFill(GrPathFill fill) {
+    static const GrPathFill gNonInvertedFills[] = {
+        kWinding_GrPathFill, // kWinding_GrPathFill
+        kEvenOdd_GrPathFill, // kEvenOdd_GrPathFill
+        kWinding_GrPathFill, // kInverseWinding_GrPathFill
+        kEvenOdd_GrPathFill, // kInverseEvenOdd_GrPathFill
+        kHairLine_GrPathFill,// kHairLine_GrPathFill
+    };
+    GR_STATIC_ASSERT(0 == kWinding_GrPathFill);
+    GR_STATIC_ASSERT(1 == kEvenOdd_GrPathFill);
+    GR_STATIC_ASSERT(2 == kInverseWinding_GrPathFill);
+    GR_STATIC_ASSERT(3 == kInverseEvenOdd_GrPathFill);
+    GR_STATIC_ASSERT(4 == kHairLine_GrPathFill);
+    GR_STATIC_ASSERT(5 == kGrPathFillCount);
+    return gNonInvertedFills[fill];
+}
+
+static inline bool GrIsFillInverted(GrPathFill fill) {
+    static const bool gIsFillInverted[] = {
+        false, // kWinding_GrPathFill
+        false, // kEvenOdd_GrPathFill
+        true,  // kInverseWinding_GrPathFill
+        true,  // kInverseEvenOdd_GrPathFill
+        false, // kHairLine_GrPathFill
+    };
+    GR_STATIC_ASSERT(0 == kWinding_GrPathFill);
+    GR_STATIC_ASSERT(1 == kEvenOdd_GrPathFill);
+    GR_STATIC_ASSERT(2 == kInverseWinding_GrPathFill);
+    GR_STATIC_ASSERT(3 == kInverseEvenOdd_GrPathFill);
+    GR_STATIC_ASSERT(4 == kHairLine_GrPathFill);
+    GR_STATIC_ASSERT(5 == kGrPathFillCount);
+    return gIsFillInverted[fill];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// opaque type for 3D API object handles
+typedef intptr_t GrPlatform3DObject;
+
+/**
+ * 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,
+ * 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).
+ *
+ * Textures that are also render targets are supported as well. Gr will manage
+ * any ancillary 3D API (stencil buffer, FBO id, etc) objects necessary for
+ * Gr to draw into the render target. To access the render target object
+ * call GrTexture::asRenderTarget().
+ *
+ * If in addition to the render target flag, the caller also specifies a sample
+ * 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 {
+    /**
+     * No flags enabled
+     */
+    kNone_GrPlatformTextureFlag              = kNone_GrTextureFlags,
+    /**
+     * Indicates that the texture is also a render target, and thus should have
+     * a GrRenderTarget object.
+     *
+     * D3D (future): client must have created the texture with flags that allow
+     * it to be used as a render target.
+     */
+    kRenderTarget_GrPlatformTextureFlag      = kRenderTarget_GrTextureFlagBit,
+};
+GR_MAKE_BITFIELD_OPS(GrPlatformTextureFlags)
+
+struct GrPlatformTextureDesc {
+    GrPlatformTextureDesc() { memset(this, 0, sizeof(*this)); }
+    GrPlatformTextureFlags          fFlags;
+    int                             fWidth;         //<! width in pixels
+    int                             fHeight;        //<! height in pixels
+    GrPixelConfig                   fConfig;        //<! color format
+    /**
+     * If the render target flag is set and sample count is greater than 0
+     * then Gr will create an MSAA buffer that resolves to the texture.
+     */
+    int                             fSampleCnt;
+    /**
+     * Handle to the 3D API object.
+     * OpenGL: Texture ID.
+     */
+    GrPlatform3DObject              fTextureHandle;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * 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
+ * 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)); }
+    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.
+     */
+    int                             fSampleCnt;
+    /**
+     * Number of bits of stencil per-pixel.
+     */
+    int                             fStencilBits;
+    /**
+     * Handle to the 3D API object.
+     * OpenGL: FBO ID
+     */
+    GrPlatform3DObject              fRenderTargetHandle;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+// 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
new file mode 100644
index 0000000..d514486
--- /dev/null
+++ b/include/gpu/GrUserConfig.h
@@ -0,0 +1,69 @@
+
+/*
+ * 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 GrUserConfig_DEFINED
+#define GrUserConfig_DEFINED
+
+#if defined(GR_USER_CONFIG_FILE)
+    #error "default user config pulled in but GR_USER_CONFIG_FILE is defined."
+#endif
+
+#if 0
+    #undef GR_RELEASE
+    #undef GR_DEBUG
+    #define GR_RELEASE  0
+    #define GR_DEBUG    1
+#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.
+ */
+//#define GR_DUMP_TEXTURE_UPLOAD    1
+
+/*
+ * When drawing rects this causes Ganesh to use a vertex buffer containing
+ * a unit square that is positioned by a matrix. Enable on systems where
+ * emitting per-rect-draw verts is more expensive than constant/matrix
+ * updates. Defaults to 0.
+ */
+//#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.
+ */
+//#define GR_AGGRESSIVE_SHADER_OPTS 1
+
+/*
+ * This gives a threshold in bytes of when to lock a GrGeometryBuffer vs using
+ * updateData. (Note the depending on the underlying 3D API the update functions
+ * may always be implemented using a lock)
+ */
+//#define GR_GEOM_BUFFER_LOCK_THRESHOLD (1<<15)
+
+///////////////////////////////////////////////////////////////////////////////
+// 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/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
new file mode 100644
index 0000000..a1d00ba
--- /dev/null
+++ b/include/gpu/SkGpuDevice.h
@@ -0,0 +1,187 @@
+
+/*
+ * 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 SkGpuDevice_DEFINED
+#define SkGpuDevice_DEFINED
+
+#include "SkGr.h"
+#include "SkBitmap.h"
+#include "SkDevice.h"
+#include "SkRegion.h"
+#include "GrContext.h"
+
+struct SkDrawProcs;
+struct GrSkDrawProcs;
+class GrTextContext;
+
+/**
+ *  Subclass of SkDevice, which directs all drawing to the GrGpu owned by the
+ *  canvas.
+ */
+class SK_API SkGpuDevice : public SkDevice {
+public:
+    /**
+     *  New device that will create an offscreen renderTarget based on the
+     *  config, width, height. 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);
+
+    /**
+     *  New device that will render to the specified renderTarget.
+     */
+    SkGpuDevice(GrContext*, GrRenderTarget*);
+
+    /**
+     *  New device that will render to the texture (as a rendertarget).
+     *  The GrTexture's asRenderTarget() must be non-NULL or device will not
+     *  function.
+     */
+    SkGpuDevice(GrContext*, GrTexture*);
+
+    virtual ~SkGpuDevice();
+
+    GrContext* context() const { return fContext; }
+
+    virtual SkGpuRenderTarget* accessRenderTarget() SK_OVERRIDE;
+
+    // overrides from SkDevice
+
+    virtual void clear(SkColor color) SK_OVERRIDE;
+    virtual void writePixels(const SkBitmap& bitmap, int x, int y,
+                             SkCanvas::Config8888 config8888) 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& 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,
+                          SkScalar x, SkScalar y, const SkPaint&) SK_OVERRIDE;
+    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+                             const SkScalar pos[], SkScalar constY,
+                             int scalarsPerPos, const SkPaint&) SK_OVERRIDE;
+    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint&) 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&) SK_OVERRIDE;
+    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+                            const SkPaint&) SK_OVERRIDE;
+    virtual bool filterTextFlags(const SkPaint&, TextFlags*) SK_OVERRIDE;
+
+    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 canHandleImageFilter(SkImageFilter*) SK_OVERRIDE;
+    virtual bool filterImage(SkImageFilter*, const SkBitmap&, const SkMatrix&,
+                             SkBitmap*, SkIPoint*) SK_OVERRIDE;
+
+    class SkAutoCachedTexture; // used internally
+
+protected:
+    bool isBitmapInTextureCache(const SkBitmap& bitmap,
+                                const GrTextureParams& params) const;
+
+    // overrides from SkDevice
+    virtual bool onReadPixels(const SkBitmap& bitmap,
+                              int x, int y,
+                              SkCanvas::Config8888 config8888) SK_OVERRIDE;
+
+private:
+    GrContext*      fContext;
+
+    GrSkDrawProcs*  fDrawProcs;
+
+    GrClipData      fClipData;
+
+    // state for our render-target
+    GrRenderTarget*     fRenderTarget;
+    bool                fNeedClear;
+
+    // called from rt and tex cons
+    void initFromRenderTarget(GrContext*, GrRenderTarget*, bool cached);
+
+    // 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) SK_OVERRIDE;
+
+    SkDrawProcs* initDrawForText(GrTextContext*);
+    bool bindDeviceAsTexture(GrPaint* paint);
+
+    // 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 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
new file mode 100644
index 0000000..88ca08a
--- /dev/null
+++ b/include/gpu/SkGr.h
@@ -0,0 +1,110 @@
+
+/*
+ * 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 SkGr_DEFINED
+#define SkGr_DEFINED
+
+#include <stddef.h>
+
+// Gr headers
+#include "GrTypes.h"
+#include "GrContext.h"
+#include "GrFontScaler.h"
+
+// skia headers
+#include "SkBitmap.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkRegion.h"
+#include "SkClipStack.h"
+
+#if (GR_DEBUG && defined(SK_RELEASE)) || (GR_RELEASE && defined(SK_DEBUG))
+//    #error "inconsistent GR_DEBUG and SK_DEBUG"
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Sk to Gr Type conversions
+
+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))
+
+GR_STATIC_ASSERT((int)SkPath::kMove_Verb  == (int)kMove_PathCmd);
+GR_STATIC_ASSERT((int)SkPath::kLine_Verb  == (int)kLine_PathCmd);
+GR_STATIC_ASSERT((int)SkPath::kQuad_Verb  == (int)kQuadratic_PathCmd);
+GR_STATIC_ASSERT((int)SkPath::kCubic_Verb == (int)kCubic_PathCmd);
+GR_STATIC_ASSERT((int)SkPath::kClose_Verb == (int)kClose_PathCmd);
+GR_STATIC_ASSERT((int)SkPath::kDone_Verb  == (int)kEnd_PathCmd);
+
+#define sk_path_verb_to_gr_path_command(X) ((GrPathCmd)(X))
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorPriv.h"
+
+/**
+ *  Convert the SkBitmap::Config to the corresponding PixelConfig, or
+ *  kUnknown_PixelConfig if the conversion cannot be done.
+ */
+GrPixelConfig SkBitmapConfig2GrPixelConfig(SkBitmap::Config);
+
+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);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrTexture* GrLockCachedBitmapTexture(GrContext*,
+                                     const SkBitmap&,
+                                     const GrTextureParams*);
+
+void GrUnlockCachedBitmapTexture(GrTexture*);
+
+////////////////////////////////////////////////////////////////////////////////
+// Classes
+
+class SkGlyphCache;
+
+class SkGrFontScaler : public GrFontScaler {
+public:
+    explicit SkGrFontScaler(SkGlyphCache* strike);
+    virtual ~SkGrFontScaler();
+
+    // overrides
+    virtual const GrKey* getKey();
+    virtual GrMaskFormat getMaskFormat();
+    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, SkPath*);
+
+private:
+    SkGlyphCache* fStrike;
+    GrKey*  fKey;
+//    DECLARE_INSTANCE_COUNTER(SkGrFontScaler);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/include/gpu/SkGrPixelRef.h b/include/gpu/SkGrPixelRef.h
new file mode 100644
index 0000000..4476a84
--- /dev/null
+++ b/include/gpu/SkGrPixelRef.h
@@ -0,0 +1,71 @@
+
+/*
+ * 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) 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
new file mode 100644
index 0000000..ffa4bef
--- /dev/null
+++ b/include/gpu/SkGrTexturePixelRef.h
@@ -0,0 +1,20 @@
+
+/*
+ * 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 SkGrTexturePixelRef_DEFINED
+#define SkGrTexturePixelRef_DEFINED
+
+#include "SkGrPixelRef.h"
+
+typedef SkGrPixelRef SkGrTexturePixelRef;
+typedef SkGrPixelRef SkGrRenderTargetPixelRef;
+
+#endif
+
diff --git a/include/gpu/gl/GrGLConfig.h b/include/gpu/gl/GrGLConfig.h
new file mode 100644
index 0000000..20d9031
--- /dev/null
+++ b/include/gpu/gl/GrGLConfig.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.
+ */
+
+
+
+#ifndef GrGLConfig_DEFINED
+#define GrGLConfig_DEFINED
+
+#include "GrTypes.h"
+
+/**
+ * Optional GL config file.
+ */
+#ifdef GR_GL_CUSTOM_SETUP_HEADER
+    #include GR_GL_CUSTOM_SETUP_HEADER
+#endif
+
+#if !defined(GR_GL_FUNCTION_TYPE)
+    #define GR_GL_FUNCTION_TYPE
+#endif
+
+/**
+ * The following are optional defines that can be enabled at the compiler
+ * command line, in a IDE project, in a GrUserConfig.h file, or in a GL custom
+ * file (if one is in use). If a GR_GL_CUSTOM_SETUP_HEADER is used they can
+ * also be placed there.
+ *
+ * GR_GL_LOG_CALLS: if 1 Gr can print every GL call using GrPrintf. Defaults to
+ * 0. Logging can be enabled and disabled at runtime using a debugger via to
+ * global gLogCallsGL. The initial value of gLogCallsGL is controlled by
+ * GR_GL_LOG_CALLS_START.
+ *
+ * GR_GL_LOG_CALLS_START: controls the initial value of gLogCallsGL when
+ * GR_GL_LOG_CALLS is 1. Defaults to 0.
+ *
+ * GR_GL_CHECK_ERROR: if enabled Gr can do a glGetError() after every GL call.
+ * Defaults to 1 if GR_DEBUG is set, otherwise 0. When GR_GL_CHECK_ERROR is 1
+ * this can be toggled in a debugger using the gCheckErrorGL global. The initial
+ * value of gCheckErrorGL is controlled by by GR_GL_CHECK_ERROR_START.
+ *
+ * GR_GL_CHECK_ERROR_START: controls the initial value of gCheckErrorGL
+ * when GR_GL_CHECK_ERROR is 1.  Defaults to 1.
+ *
+ * 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
+ * the GL stream is converted to DX as in ANGLE on Chrome). 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:
+ *  glBufferData(GL_..._BUFFER, size, NULL, usage);       //<--hint, NULL means
+ *  glBufferSubData(GL_..._BUFFER, 0, lessThanSize, data) //   old data can't be
+ *                                                        //   used again.
+ * However, this can be an unoptimization on some platforms, esp. Chrome.
+ * Chrome's cmd buffer will create a new allocation and memset the whole thing
+ * to zero (for security reasons). Defaults to 1 (enabled).
+ *
+ * GR_GL_PER_GL_FUNC_CALLBACK: When set to 1 the GrGLInterface object provides
+ * a function pointer that is called just before every gl function. The ptr must
+ * be valid (i.e. there is no NULL check). However, by default the callback will
+ * be set to a function that does nothing. The signature of the function is:
+ *    void function(const GrGLInterface*)
+ * It is not extern "C".
+ * The GrGLInterface field fCallback specifies the function ptr and there is an
+ * additional field fCallbackData of type intptr_t for client data.
+ *
+ * GR_GL_RGBA_8888_PIXEL_OPS_SLOW: Set this to 1 if it is known that performing
+ * glReadPixels / glTex(Sub)Image with format=GL_RGBA, type=GL_UNISIGNED_BYTE is
+ * significantly slower than format=GL_BGRA, type=GL_UNISIGNED_BYTE.
+ *
+ * GR_GL_FULL_READPIXELS_FASTER_THAN_PARTIAL: Set this to 1 if calling
+ * glReadPixels to read the entire framebuffer is faster than calling it with
+ * the same sized rectangle but with a framebuffer bound that is larger than
+ * the rectangle read.
+ *
+ * GR_GL_CHECK_ALLOC_WITH_GET_ERROR: If set to 1 this will then glTexImage,
+ * glBufferData, glRenderbufferStorage, etc will be checked for errors. This
+ * amounts to ensuring the error is GL_NO_ERROR, calling the allocating
+ * function, and then checking that the error is still GL_NO_ERROR. When the
+ * value is 0 we will assume no error was generated without checking.
+ *
+ * GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT: We will normally check the FBO status
+ * every time we bind a texture or renderbuffer to an FBO. However, in some
+ * environments CheckFrameBufferStatus is very expensive. If this is set we will
+ * 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)
+    #define GR_GL_LOG_CALLS                             GR_DEBUG
+#endif
+
+#if !defined(GR_GL_LOG_CALLS_START)
+    #define GR_GL_LOG_CALLS_START                       0
+#endif
+
+#if !defined(GR_GL_CHECK_ERROR)
+    #define GR_GL_CHECK_ERROR                           GR_DEBUG
+#endif
+
+#if !defined(GR_GL_CHECK_ERROR_START)
+    #define GR_GL_CHECK_ERROR_START                     1
+#endif
+
+#if !defined(GR_GL_NO_CONSTANT_ATTRIBUTES)
+    #define GR_GL_NO_CONSTANT_ATTRIBUTES                0
+#endif
+
+#if !defined(GR_GL_USE_BUFFER_DATA_NULL_HINT)
+    #define GR_GL_USE_BUFFER_DATA_NULL_HINT             1
+#endif
+
+#if !defined(GR_GL_PER_GL_FUNC_CALLBACK)
+    #define GR_GL_PER_GL_FUNC_CALLBACK                  0
+#endif
+
+#if !defined(GR_GL_RGBA_8888_PIXEL_OPS_SLOW)
+    #define GR_GL_RGBA_8888_PIXEL_OPS_SLOW              0
+#endif
+
+#if !defined(GR_GL_FULL_READPIXELS_FASTER_THAN_PARTIAL)
+    #define GR_GL_FULL_READPIXELS_FASTER_THAN_PARTIAL   0
+#endif
+
+#if !defined(GR_GL_CHECK_ALLOC_WITH_GET_ERROR)
+    #define GR_GL_CHECK_ALLOC_WITH_GET_ERROR            1
+#endif
+
+#if !defined(GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT)
+    #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
+ * respecified using glBufferData performance can fall off of a cliff. The
+ * driver winds up performing many DMA mapping / unmappings and chews up ~50% of
+ * the core. However, it has been observed that occaisonally respecifiying the
+ * buffer using glBufferData and then writing data using glBufferSubData
+ * prevents the bad behavior.
+ *
+ * There is a lot of uncertainty around this issue. In Chrome backgrounding
+ * the tab somehow initiates this behavior and we don't know what the connection
+ * is. Another observation is that Chrome's cmd buffer server will actually
+ * create a buffer full of zeros when it sees a NULL data param (for security
+ * reasons). If this is disabled and NULL is actually passed all the way to the
+ * driver then the workaround doesn't help.
+ *
+ * The issue is tracked at:
+ * http://code.google.com/p/chromium/issues/detail?id=114865
+ *
+ * When the workaround is enabled we will use the glBufferData / glBufferSubData
+ * trick every 128 array buffer uploads.
+ *
+ * Hopefully we will understand this better and have a cleaner fix or get a
+ * OS/driver level fix.
+ */
+#define GR_GL_MAC_BUFFER_OBJECT_PERFOMANCE_WORKAROUND   \
+    (GR_MAC_BUILD &&                                    \
+     !GR_GL_USE_BUFFER_DATA_NULL_HINT)
+
+#endif
diff --git a/include/gpu/gl/GrGLConfig_chrome.h b/include/gpu/gl/GrGLConfig_chrome.h
new file mode 100644
index 0000000..50ea34c
--- /dev/null
+++ b/include/gpu/gl/GrGLConfig_chrome.h
@@ -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.
+ */
+#ifndef GrGLConfig_chrome_DEFINED
+#define GrGLConfig_chrome_DEFINED
+
+// glGetError() forces a sync with gpu process on chrome
+#define GR_GL_CHECK_ERROR_START                     0
+
+// ANGLE creates a temp VB for vertex attributes not specified per-vertex.
+#define GR_GL_NO_CONSTANT_ATTRIBUTES                GR_WIN32_BUILD
+
+// For RGBA teximage/readpixels ANGLE will sw-convert to/from BGRA.
+#define GR_GL_RGBA_8888_PIXEL_OPS_SLOW              GR_WIN32_BUILD
+
+// ANGLE can go faster if the entire fbo is read rather than a subrect
+#define GR_GL_FULL_READPIXELS_FASTER_THAN_PARTIAL   GR_WIN32_BUILD
+
+// cmd buffer allocates memory and memsets it to zero when it sees glBufferData
+// with NULL.
+#define GR_GL_USE_BUFFER_DATA_NULL_HINT             0
+
+// chrome uses this to set the context on each GL call.
+#define GR_GL_PER_GL_FUNC_CALLBACK                  1
+
+// Check error is even more expensive in chrome (cmd buffer flush). The
+// compositor also doesn't check its allocations.
+#define GR_GL_CHECK_ALLOC_WITH_GET_ERROR            0
+
+// CheckFramebufferStatus in chrome synchronizes the gpu and renderer processes.
+#define GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT      1
+
+#endif
diff --git a/include/gpu/gl/GrGLFunctions.h b/include/gpu/gl/GrGLFunctions.h
new file mode 100644
index 0000000..b0b3b16
--- /dev/null
+++ b/include/gpu/gl/GrGLFunctions.h
@@ -0,0 +1,219 @@
+
+/*
+ * Copyright 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);
+    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* 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
new file mode 100644
index 0000000..e1ca40e
--- /dev/null
+++ b/include/gpu/gl/GrGLInterface.h
@@ -0,0 +1,321 @@
+
+/*
+ * 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 GrGLInterface_DEFINED
+#define GrGLInterface_DEFINED
+
+#include "GrGLFunctions.h"
+#include "GrRefCnt.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Classifies GL contexts (currently as Desktop vs. ES2). This is a bitfield.
+ * A GrGLInterface (defined below) may support multiple bindings.
+ */
+enum GrGLBinding {
+    kNone_GrGLBinding = 0x0,
+
+    kDesktop_GrGLBinding = 0x01,
+    kES2_GrGLBinding = 0x02,
+
+    // for iteration of GrGLBindings
+    kFirstGrGLBinding = kDesktop_GrGLBinding,
+    kLastGrGLBinding = kES2_GrGLBinding
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * 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
+ * passed to Create then the "default" GL interface is used. If the default is
+ * also NULL GrContext creation will fail.
+ *
+ * The default interface is returned by GrGLDefaultInterface. This function's
+ * 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
+ * GrGLDefaultInterface, each should bump the ref count.
+ *
+ * By defining GR_GL_PER_GL_CALL_IFACE_CALLBACK to 1 the client can specify a
+ * callback function that will be called prior to each GL function call. See
+ * comments in GrGLConfig.h
+ */
+
+struct GrGLInterface;
+
+const GrGLInterface* GrGLDefaultInterface();
+
+/**
+ * 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
+ * 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
+ * CPU overhead.
+ */
+const GrGLInterface* GrGLCreateNullInterface();
+
+/**
+ * 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
+
+/*
+ * 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 interface is
+    // validated for the current OpenGL context.
+    bool validate(GrGLBinding binding) const;
+
+    // Indicator variable specifying the type of GL implementation
+    // exported:  GLES2 and/or Desktop.
+    GrGLBinding fBindingsExported;
+
+    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;
+
+    // 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
+    GrGLInterfaceCallbackProc fCallback;
+    GrGLInterfaceCallbackData fCallbackData;
+#endif
+
+};
+
+#endif
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..c51f54c
--- /dev/null
+++ b/include/gpu/gl/SkDebugGLContext.h
@@ -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.
+ */
+#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
new file mode 100644
index 0000000..3f99eae
--- /dev/null
+++ b/include/gpu/gl/SkGLContext.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 SkGLContext_DEFINED
+#define SkGLContext_DEFINED
+
+#include "GrGLInterface.h"
+#include "SkString.h"
+
+/**
+ * Create an offscreen opengl context with an RGBA8 / 8bit stencil FBO.
+ * Provides a GrGLInterface struct of function pointers for the context.
+ */
+
+class SkGLContext : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkGLContext)
+
+    SkGLContext();
+    virtual ~SkGLContext();
+
+    /**
+     * Initializes the context and makes it current.
+     */
+    bool init(const int width, const int height);
+
+    int getFBOID() const { return fFBO; }
+
+    const GrGLInterface* gl() const { return fGL; }
+
+    virtual void makeCurrent() const = 0;
+
+    bool hasExtension(const char* extensionName) const;
+
+protected:
+    /**
+     * 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.
+     */
+    virtual const GrGLInterface* createGLContext() = 0;
+
+    /**
+     * Subclass should destroy the underlying GL context.
+     */
+    virtual void destroyGLContext() = 0;
+
+private:
+    SkString fExtensionString;
+    GrGLuint fFBO;
+    GrGLuint fColorBufferID;
+    GrGLuint fDepthStencilBufferID;
+    const GrGLInterface* fGL;
+
+    typedef SkRefCnt INHERITED;
+};
+
+/**
+ * Helper macro for using the GL context through the GrGLInterface. Example:
+ * SK_GL(glCtx, GenTextures(1, &texID));
+ */
+#define SK_GL(ctx, X) (ctx).gl()->f ## X
+
+#endif
diff --git a/include/gpu/gl/SkMesaGLContext.h b/include/gpu/gl/SkMesaGLContext.h
new file mode 100644
index 0000000..14a3ca7
--- /dev/null
+++ b/include/gpu/gl/SkMesaGLContext.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 SkMesaGLContext_DEFINED
+#define SkMesaGLContext_DEFINED
+
+#include "SkGLContext.h"
+
+#if SK_MESA
+
+class SkMesaGLContext : public SkGLContext {
+private:
+    typedef intptr_t Context;
+
+public:
+    SkMesaGLContext();
+
+    virtual ~SkMesaGLContext();
+
+    virtual void makeCurrent() const SK_OVERRIDE;
+
+    class AutoContextRestore {
+    public:
+        AutoContextRestore();
+        ~AutoContextRestore();
+
+    private:
+        Context fOldContext;
+        GrGLint fOldWidth;
+        GrGLint fOldHeight;
+        GrGLint fOldFormat;
+        void*   fOldImage;
+    };
+
+protected:
+    virtual const GrGLInterface* createGLContext() SK_OVERRIDE;
+    virtual void destroyGLContext() SK_OVERRIDE;
+
+private:
+    Context fContext;
+    GrGLubyte *fImage;
+};
+
+#endif
+
+#endif
diff --git a/include/gpu/gl/SkNativeGLContext.h b/include/gpu/gl/SkNativeGLContext.h
new file mode 100644
index 0000000..52118da
--- /dev/null
+++ b/include/gpu/gl/SkNativeGLContext.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 SkNativeGLContext_DEFINED
+#define SkNativeGLContext_DEFINED
+
+#include "SkGLContext.h"
+
+#if defined(SK_BUILD_FOR_MAC)
+    #include <AGL/agl.h>
+#elif defined(SK_BUILD_FOR_ANDROID)
+    #include <GLES2/gl2.h>
+    #include <EGL/egl.h>
+#elif defined(SK_BUILD_FOR_UNIX)
+    #include <X11/Xlib.h>
+    #include <GL/glx.h>
+#elif defined(SK_BUILD_FOR_WIN32)
+    #include <Windows.h>
+    #include <GL/GL.h>
+#endif
+
+class SkNativeGLContext : public SkGLContext {
+public:
+    SkNativeGLContext();
+
+    virtual ~SkNativeGLContext();
+
+    virtual void makeCurrent() const SK_OVERRIDE;
+
+    class AutoContextRestore {
+    public:
+        AutoContextRestore();
+        ~AutoContextRestore();
+
+    private:
+    #if defined(SK_BUILD_FOR_MAC)
+        AGLContext fOldAGLContext;
+    #elif defined(SK_BUILD_FOR_UNIX)
+        GLXContext fOldGLXContext;
+        Display* fOldDisplay;
+        GLXDrawable fOldDrawable;
+    #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
+    };
+
+protected:
+    virtual const GrGLInterface* createGLContext() SK_OVERRIDE;
+    virtual void destroyGLContext() SK_OVERRIDE;
+
+private:
+#if defined(SK_BUILD_FOR_MAC)
+    AGLContext fContext;
+#elif defined(SK_BUILD_FOR_UNIX)
+    GLXContext fContext;
+    Display* fDisplay;
+    Pixmap fPixmap;
+    GLXPixmap fGlxPixmap;
+#elif defined(SK_BUILD_FOR_WIN32)
+    HWND fWindow;
+    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
+};
+
+#endif
diff --git a/include/gpu/gl/SkNullGLContext.h b/include/gpu/gl/SkNullGLContext.h
new file mode 100644
index 0000000..9e16cee
--- /dev/null
+++ b/include/gpu/gl/SkNullGLContext.h
@@ -0,0 +1,27 @@
+
+/*
+ * 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 SkNullGLContext_DEFINED
+#define SkNullGLContext_DEFINED
+
+#include "SkGLContext.h"
+
+class SkNullGLContext : public SkGLContext {
+
+public:
+    SkNullGLContext() {};
+
+    virtual void makeCurrent() const SK_OVERRIDE {};
+
+protected:
+    virtual const GrGLInterface* createGLContext() SK_OVERRIDE;
+
+    virtual void destroyGLContext() SK_OVERRIDE {};
+};
+
+#endif
+
diff --git a/include/images/SkFlipPixelRef.h b/include/images/SkFlipPixelRef.h
new file mode 100644
index 0000000..ac43780
--- /dev/null
+++ b/include/images/SkFlipPixelRef.h
@@ -0,0 +1,102 @@
+
+/*
+ * 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();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkFlipPixelRef)
+
+protected:
+    virtual void* onLockPixels(SkColorTable**);
+    virtual void onUnlockPixels();
+
+    SkFlipPixelRef(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+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);
+
+    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/include/images/SkImageDecoder.h b/include/images/SkImageDecoder.h
new file mode 100644
index 0000000..a8ec1c6
--- /dev/null
+++ b/include/images/SkImageDecoder.h
@@ -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.
+ */
+
+
+#ifndef SkImageDecoder_DEFINED
+#define SkImageDecoder_DEFINED
+
+#include "SkBitmap.h"
+#include "SkRefCnt.h"
+
+class SkStream;
+
+/** \class SkImageDecoder
+
+    Base class for decoding compressed images into a SkBitmap
+*/
+class SkImageDecoder {
+public:
+    virtual ~SkImageDecoder();
+
+    enum Format {
+        kUnknown_Format,
+        kBMP_Format,
+        kGIF_Format,
+        kICO_Format,
+        kJPEG_Format,
+        kPNG_Format,
+        kWBMP_Format,
+
+        kLastKnownFormat = kWBMP_Format
+    };
+
+    /** Return the compressed data's format (see Format enum)
+    */
+    virtual Format getFormat() const;
+
+    /** 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; }
+
+    /** \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:
+        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; }
+    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:
+        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; }
+    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*);
+
+    // 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 decode(SkStream* stream, SkBitmap* bitmap, Mode mode) {
+        return this->decode(stream, bitmap, SkBitmap::kNo_Config, mode);
+    }
+
+    /** 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;
+
+    /** 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;
+
+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;
+
+    // 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:
+    SK_DECLARE_INST_COUNT(SkImageDecoderFactory)
+
+    virtual SkImageDecoder* newDecoder(SkStream*) = 0;
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+class SkDefaultImageDecoderFactory : SkImageDecoderFactory {
+public:
+    // calls SkImageDecoder::Factory(stream)
+    virtual SkImageDecoder* newDecoder(SkStream* stream) {
+        return SkImageDecoder::Factory(stream);
+    }
+};
+
+// 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);
+
+#endif
diff --git a/include/images/SkImageEncoder.h b/include/images/SkImageEncoder.h
new file mode 100644
index 0000000..5e350d4
--- /dev/null
+++ b/include/images/SkImageEncoder.h
@@ -0,0 +1,60 @@
+
+/*
+ * 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
+    };
+    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;
+};
+
+// 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);
+
+#endif
diff --git a/include/images/SkImageRef.h b/include/images/SkImageRef.h
new file mode 100644
index 0000000..dcc4c0e
--- /dev/null
+++ b/include/images/SkImageRef.h
@@ -0,0 +1,104 @@
+
+/*
+ * 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*);
+
+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&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    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/include/images/SkImageRef_GlobalPool.h b/include/images/SkImageRef_GlobalPool.h
new file mode 100644
index 0000000..914b0eb
--- /dev/null
+++ b/include/images/SkImageRef_GlobalPool.h
@@ -0,0 +1,61 @@
+
+/*
+ * 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();
+
+    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 <=
+     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/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/SkJpegUtility.h b/include/images/SkJpegUtility.h
new file mode 100644
index 0000000..74f1a21
--- /dev/null
+++ b/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/include/images/SkMovie.h b/include/images/SkMovie.h
new file mode 100644
index 0000000..f32d609
--- /dev/null
+++ b/include/images/SkMovie.h
@@ -0,0 +1,80 @@
+
+/*
+ * 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:
+    SK_DECLARE_INST_COUNT(SkMovie)
+
+    /** 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();
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/images/SkPageFlipper.h b/include/images/SkPageFlipper.h
new file mode 100644
index 0000000..91a1e9d
--- /dev/null
+++ b/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/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
new file mode 100644
index 0000000..6d86226
--- /dev/null
+++ b/include/pdf/SkPDFDevice.h
@@ -0,0 +1,279 @@
+
+/*
+ * 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 SkPDFDevice_DEFINED
+#define SkPDFDevice_DEFINED
+
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTScopedPtr.h"
+
+class SkPDFArray;
+class SkPDFDevice;
+class SkPDFDict;
+class SkPDFFont;
+class SkPDFFormXObject;
+class SkPDFGlyphSetMap;
+class SkPDFGraphicState;
+class SkPDFObject;
+class SkPDFShader;
+class SkPDFStream;
+
+// Private classes.
+struct ContentEntry;
+struct GraphicStateEntry;
+
+/** \class SkPDFDevice
+
+    The drawing context for the PDF backend.
+*/
+class SkPDFDevice : public SkDevice {
+public:
+    /** Create a PDF drawing context with the given width and height.
+     *  72 points/in means letter paper is 612x792.
+     *  @param pageSize Page size in points.
+     *  @param contentSize The content size of the page in points. This will be
+     *         combined with the initial transform to determine the drawing area
+     *         (as reported by the width and height methods). Anything outside
+     *         of the drawing area will be clipped.
+     *  @param initialTransform The initial transform to apply to the page.
+     *         This may be useful to, for example, move the origin in and
+     *         over a bit to account for a margin, scale the canvas,
+     *         or apply a rotation.  Note1: the SkPDFDevice also applies
+     *         a scale+translate transform to move the origin from the
+     *         bottom left (PDF default) to the top left.  Note2: drawDevice
+     *         (used by layer restore) draws the device after this initial
+     *         transform is applied, so the PDF device does an
+     *         inverse scale+translate to accommodate the one that SkPDFDevice
+     *         always does.
+     */
+    // TODO(vandebo): The sizes should be SkSize and not SkISize.
+    SK_API SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
+                       const SkMatrix& initialTransform);
+    SK_API virtual ~SkPDFDevice();
+
+    virtual uint32_t getDeviceCapabilities() SK_OVERRIDE;
+
+    virtual void clear(SkColor color) SK_OVERRIDE;
+
+    /** These are called inside the per-device-layer loop for each draw call.
+     When these are called, we have already applied any saveLayer operations,
+     and are handling any looping from the paint, and any effects from the
+     DrawFilter.
+     */
+    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);
+    virtual void drawPath(const SkDraw&, const SkPath& origpath,
+                          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&) 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&) SK_OVERRIDE;
+    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+                             const SkScalar pos[], SkScalar constY,
+                             int scalarsPerPos, const SkPaint&) 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*, 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.
+    };
+
+    /** Sets the drawing area for the device. Subsequent draw calls are directed
+     *  to the specific drawing area (margin or content). The default drawing
+     *  area is the content drawing area.
+     *
+     *  Currently if margin content is drawn and then a complex (for PDF) xfer
+     *  mode is used, like SrcIn, Clear, etc, the margin content will get
+     *  clipped. A simple way to avoid the bug is to always draw the margin
+     *  content last.
+     */
+    SK_API void setDrawingArea(DrawingArea drawingArea);
+
+    // PDF specific methods.
+
+    /** Returns the resource dictionary for this device.
+     */
+    SK_API SkPDFDict* getResourceDict();
+
+    /** 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,
+                             bool recursive) const;
+
+    /** Get the fonts used on this device.
+     */
+    SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
+
+    /** Returns a copy of the media box for this device. The caller is required
+     *  to unref() this when it is finished.
+     */
+    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.
+        DEPRECATED: use copyContentToData()
+     */
+    SK_API SkStream* content() const;
+
+    /** Returns a SkStream with the page contents.  The caller is responsible
+     *  for calling data->unref() when it is finished.
+     */
+    SK_API SkData* copyContentToData() const;
+
+    SK_API const SkMatrix& initialTransform() const {
+        return fInitialTransform;
+    }
+
+    /** Returns a SkPDFGlyphSetMap which represents glyph usage of every font
+     *  that shows on this device.
+     */
+    const SkPDFGlyphSetMap& getFontGlyphUsage() const {
+        return *(fFontGlyphUsage.get());
+    }
+
+protected:
+    virtual bool onReadPixels(const SkBitmap& bitmap, int x, int y,
+                              SkCanvas::Config8888) SK_OVERRIDE;
+
+    virtual bool allowImageFilter(SkImageFilter*) SK_OVERRIDE;
+
+private:
+    // TODO(vandebo): push most of SkPDFDevice's state into a core object in
+    // order to get the right access levels without using friend.
+    friend class ScopedContentEntry;
+
+    SkISize fPageSize;
+    SkISize fContentSize;
+    SkMatrix fInitialTransform;
+    SkClipStack fExistingClipStack;
+    SkRegion fExistingClipRegion;
+    SkPDFArray* fAnnotations;
+    SkPDFDict* fResourceDict;
+
+    SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
+    SkTDArray<SkPDFObject*> fXObjectResources;
+    SkTDArray<SkPDFFont*> fFontResources;
+    SkTDArray<SkPDFObject*> fShaderResources;
+
+    SkTScopedPtr<ContentEntry> fContentEntries;
+    ContentEntry* fLastContentEntry;
+    SkTScopedPtr<ContentEntry> fMarginContentEntries;
+    ContentEntry* fLastMarginContentEntry;
+    DrawingArea fDrawingArea;
+
+    const SkClipStack* fClipStack;
+
+    // Accessor and setter functions based on the current DrawingArea.
+    SkTScopedPtr<ContentEntry>* getContentEntries();
+    ContentEntry* getLastContentEntry();
+    void setLastContentEntry(ContentEntry* contentEntry);
+
+    // Glyph ids used for each font on this device.
+    SkTScopedPtr<SkPDFGlyphSetMap> fFontGlyphUsage;
+
+    SkPDFDevice(const SkISize& layerSize, const SkClipStack& existingClipStack,
+                const SkRegion& existingClipRegion);
+
+    // override from SkDevice
+    virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config,
+                                               int width, int height,
+                                               bool isOpaque,
+                                               Usage usage) SK_OVERRIDE;
+
+    void init();
+    void cleanUp(bool clearFontUsage);
+    SkPDFFormXObject* createFormXObjectFromDevice();
+
+    // Clear the passed clip from all existing content entries.
+    void clearClipFromContent(const SkClipStack* clipStack,
+                              const SkRegion& clipRegion);
+    void drawFormXObjectWithClip(SkPDFFormXObject* form,
+                                 const SkClipStack* clipStack,
+                                 const SkRegion& clipRegion,
+                                 bool invertClip);
+
+    // If the paint or clip is such that we shouldn't draw anything, this
+    // returns NULL and does not create a content entry.
+    // setUpContentEntry and finishContentEntry can be used directly, but
+    // the preferred method is to use the ScopedContentEntry helper class.
+    ContentEntry* setUpContentEntry(const SkClipStack* clipStack,
+                                    const SkRegion& clipRegion,
+                                    const SkMatrix& matrix,
+                                    const SkPaint& paint,
+                                    bool hasText,
+                                    SkPDFFormXObject** dst);
+    void finishContentEntry(SkXfermode::Mode xfermode,
+                            SkPDFFormXObject* dst);
+    bool isContentEmpty();
+
+    void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
+                                            const SkClipStack& clipStack,
+                                            const SkRegion& clipRegion,
+                                            const SkPaint& paint,
+                                            bool hasText,
+                                            GraphicStateEntry* entry);
+    int addGraphicStateResource(SkPDFGraphicState* gs);
+
+    void updateFont(const SkPaint& paint, uint16_t glyphID,
+                    ContentEntry* contentEntry);
+    int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
+
+    void internalDrawPaint(const SkPaint& paint, ContentEntry* contentEntry);
+    void internalDrawBitmap(const SkMatrix& matrix,
+                            const SkClipStack* clipStack,
+                            const SkRegion& clipRegion,
+                            const SkBitmap& bitmap,
+                            const SkIRect* srcRect,
+                            const SkPaint& paint);
+
+    /** Helper method for copyContentToData. It is responsible for copying the
+     *  list of content entries |entry| to |data|.
+     */
+    void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const;
+
+    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
new file mode 100644
index 0000000..167634e
--- /dev/null
+++ b/include/pdf/SkPDFDocument.h
@@ -0,0 +1,101 @@
+
+/*
+ * Copyright 2010 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkPDFDocument_DEFINED
+#define SkPDFDocument_DEFINED
+
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+#include "SkTScopedPtr.h"
+
+class SkPDFCatalog;
+class SkPDFDevice;
+class SkPDFDict;
+class SkPDFPage;
+class SkPDFObject;
+class SkWStream;
+
+/** \class SkPDFDocument
+
+    A SkPDFDocument assembles pages together and generates the final PDF file.
+*/
+class SkPDFDocument {
+public:
+    enum Flags {
+        kNoCompression_Flags = 0x01,  //!< mask disable stream compression.
+        kNoLinks_Flags       = 0x02,  //!< do not honor link annotations.
+
+        kDraftMode_Flags     = 0x01,
+    };
+    /** Create a PDF document.
+     */
+    explicit SK_API SkPDFDocument(Flags flags = (Flags)0);
+    SK_API ~SkPDFDocument();
+
+    /** Output the PDF to the passed stream.  It is an error to call this (it
+     *  will return false and not modify stream) if no pages have been added
+     *  or there are pages missing (i.e. page 1 and 3 have been added, but not
+     *  page 2).
+     *
+     *  @param stream    The writable output stream to send the PDF to.
+     */
+    SK_API bool emitPDF(SkWStream* stream);
+
+    /** Sets the specific page to the passed PDF device. If the specified
+     *  page is already set, this overrides it. Returns true if successful.
+     *  Will fail if the document has already been emitted.
+     *
+     *  @param pageNumber The position to add the passed device (1 based).
+     *  @param pdfDevice  The page to add to this document.
+     */
+    SK_API bool setPage(int pageNumber, SkPDFDevice* pdfDevice);
+
+    /** Append the passed pdf device to the document as a new page.  Returns
+     *  true if successful.  Will fail if the document has already been emitted.
+     *
+     *  @param pdfDevice The page to add to this document.
+     */
+    SK_API bool appendPage(SkPDFDevice* pdfDevice);
+
+    /** Get the count of unique font types used in the document.
+     */
+    SK_API void getCountOfFontTypes(
+        int counts[SkAdvancedTypefaceMetrics::kNotEmbeddable_Font + 1]) const;
+
+private:
+    SkTScopedPtr<SkPDFCatalog> fCatalog;
+    int64_t fXRefFileOffset;
+
+    SkTDArray<SkPDFPage*> fPages;
+    SkTDArray<SkPDFDict*> fPageTree;
+    SkPDFDict* fDocCatalog;
+    SkTDArray<SkPDFObject*> fPageResources;
+    SkTDArray<SkPDFObject*> fSubstitutes;
+    int fSecondPageFirstResourceIndex;
+
+    SkPDFDict* fTrailerDict;
+
+    /** Output the PDF header to the passed stream.
+     *  @param stream    The writable output stream to send the header to.
+     */
+    void emitHeader(SkWStream* stream);
+
+    /** Get the size of the header.
+     */
+    size_t headerSize();
+
+    /** Output the PDF footer to the passed stream.
+     *  @param stream    The writable output stream to send the footer to.
+     *  @param objCount  The number of objects in the PDF.
+     */
+    void emitFooter(SkWStream* stream, int64_t objCount);
+};
+
+#endif
diff --git a/include/pipe/SkGPipe.h b/include/pipe/SkGPipe.h
new file mode 100644
index 0000000..27af16d
--- /dev/null
+++ b/include/pipe/SkGPipe.h
@@ -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.
+ */
+
+
+
+#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();
+    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
+    };
+
+    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, uint32_t playbackFlags = 0,
+                    size_t* bytesRead = NULL);
+private:
+    SkCanvas*           fCanvas;
+    class SkGPipeState* fState;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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
+     *  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;
+    virtual int numberOfReaders() const { return 1; }
+
+private:
+    friend class SkGPipeWriter;
+    void setCanvas(SkGPipeCanvas*);
+
+    SkGPipeCanvas* fCanvas;
+};
+
+class SkGPipeWriter {
+public:
+    SkGPipeWriter();
+    ~SkGPipeWriter();
+
+    bool isRecording() const { return NULL != fCanvas; }
+
+    enum Flags {
+        /**
+         *  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,
+        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:
+    enum {
+        kDefaultRecordingCanvasSize = 32767,
+    };
+
+    SkGPipeCanvas* fCanvas;
+    SkWriter32     fWriter;
+};
+
+#endif
diff --git a/include/ports/SkHarfBuzzFont.h b/include/ports/SkHarfBuzzFont.h
new file mode 100644
index 0000000..22749af
--- /dev/null
+++ b/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/SkTypeface_android.h b/include/ports/SkTypeface_android.h
new file mode 100644
index 0000000..1ee17e6
--- /dev/null
+++ b/include/ports/SkTypeface_android.h
@@ -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.
+ */
+
+
+#ifndef SkTypeface_android_DEFINED
+#define SkTypeface_android_DEFINED
+
+#include "SkTypeface.h"
+
+enum FallbackScripts {
+    kArabic_FallbackScript,
+    kArmenian_FallbackScript,
+    kBengali_FallbackScript,
+    kDevanagari_FallbackScript,
+    kEthiopic_FallbackScript,
+    kGeorgian_FallbackScript,
+    kHebrewRegular_FallbackScript,
+    kHebrewBold_FallbackScript,
+    kKannada_FallbackScript,
+    kMalayalam_FallbackScript,
+    kTamilRegular_FallbackScript,
+    kTamilBold_FallbackScript,
+    kThai_FallbackScript,
+    kTelugu_FallbackScript,
+    kFallbackScriptNumber
+};
+
+// This particular mapping will be removed after WebKit is updated to use the
+// new mappings. No new caller should use the kTamil_FallbackScript but rather
+// the more specific Tamil scripts in the standard enum.
+#define kTamil_FallbackScript kTamilRegular_FallbackScript
+
+#define SkTypeface_ValidScript(s) (s >= 0 && s < kFallbackScriptNumber)
+
+/**
+ *  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 script id.
+ *  @return reference to the matching typeface. Caller must call
+ *          unref() when they are done.
+ */
+SK_API SkTypeface* SkCreateTypefaceForScript(FallbackScripts script);
+
+/**
+ *  Return the string representation for the fallback script on Android.
+ *  If the script is not valid, returns null.
+ */
+SK_API const char* SkGetFallbackScriptID(FallbackScripts script);
+
+/**
+ *  Return the fallback script enum for the ID on Android.
+ *  If the ID is not valid, or can not map to a fallback
+ *  script, returns kFallbackScriptNumber.
+ */
+SK_API FallbackScripts SkGetFallbackScriptFromID(const char* id);
+
+/**
+ *  Return a new typeface of the font in the fallback font list containing
+ *  the specified chararacter. If no typeface is found, returns null.
+ */
+SK_API SkTypeface* SkCreateFallbackTypefaceForChar(SkUnichar uni,
+                                                   SkTypeface::Style style);
+
+/**
+ *  Get the family name of the font in the fallback font list containing
+ *  the specified chararacter. if no font is found, returns false.
+ */
+SK_API bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name);
+
+/**
+ *  For test only.
+ *  Load font config from given xml files, instead of those from Android system.
+ */
+SK_API void SkUseTestFontConfigFile(const char* mainconf, const char* fallbackconf,
+                                    const char* fontsdir);
+
+#endif
diff --git a/include/ports/SkTypeface_mac.h b/include/ports/SkTypeface_mac.h
new file mode 100644
index 0000000..eb9d25f
--- /dev/null
+++ b/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/include/ports/SkTypeface_win.h b/include/ports/SkTypeface_win.h
new file mode 100644
index 0000000..6da843f
--- /dev/null
+++ b/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/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..a512eab
--- /dev/null
+++ b/include/svg/SkSVGBase.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 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
new file mode 100644
index 0000000..7d0ffe7
--- /dev/null
+++ b/include/text/SkTextLayout.h
@@ -0,0 +1,61 @@
+
+/*
+ * 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:
+    SK_DECLARE_INST_COUNT(SkTextStyle)
+
+    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;
+
+    typedef SkRefCnt INHERITED;
+};
+
+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/include/utils/SkBoundaryPatch.h b/include/utils/SkBoundaryPatch.h
new file mode 100644
index 0000000..89060a6
--- /dev/null
+++ b/include/utils/SkBoundaryPatch.h
@@ -0,0 +1,67 @@
+
+/*
+ * 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:
+    SK_DECLARE_INST_COUNT(SkBoundary)
+
+    // 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;
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+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/include/utils/SkCamera.h b/include/utils/SkCamera.h
new file mode 100644
index 0000000..6527c32
--- /dev/null
+++ b/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);
+    }
+
+    // deprecated, 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/include/utils/SkCubicInterval.h b/include/utils/SkCubicInterval.h
new file mode 100644
index 0000000..bd1f9b9
--- /dev/null
+++ b/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/include/utils/SkCullPoints.h b/include/utils/SkCullPoints.h
new file mode 100644
index 0000000..fafa0fc
--- /dev/null
+++ b/include/utils/SkCullPoints.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 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;
+};
+
+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
new file mode 100644
index 0000000..97848f1
--- /dev/null
+++ b/include/utils/SkDeferredCanvas.h
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2012 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 "SkPixelRef.h"
+
+class DeferredDevice;
+
+/** \class SkDeferredCanvas
+    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 NotificationClient;
+
+    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);
+
+    virtual ~SkDeferredCanvas();
+
+    /**
+     *  Specify a device to be used by this canvas. Calling setDevice will
+     *  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.
+     */
+    virtual SkDevice* setDevice(SkDevice* device);
+
+    /**
+     *  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.
+     *
+     *  @param notificationClient interface for dispatching notifications
+     *  @return The notificationClient argument, for convenience.
+     */
+    NotificationClient* setNotificationClient(NotificationClient* notificationClient);
+
+    /**
+     *  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);
+
+    /**
+     *  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);
+
+    /**
+     * 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,
+                          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 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;
+    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;
+
+public:
+    class NotificationClient {
+    public:
+        /**
+         *  Called before executing one or several draw commands, which means
+         *  once per flush when deferred rendering is enabled.
+         */
+        virtual void prepareForDraw() {}
+
+        /**
+         *  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.
+         */
+        virtual void storageAllocatedForRecordingChanged(
+            size_t newAllocatedStorage) {}
+
+        /**
+         *  Called after pending draw commands have been flushed
+         */
+        virtual void flushedDrawCommands() {}
+
+        /**
+         *  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.
+         */
+        virtual void skippedPendingDrawCommands() {}
+
+    private:
+        typedef SkRefCnt INHERITED;
+    };
+
+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;
+};
+
+
+#endif
diff --git a/include/utils/SkDumpCanvas.h b/include/utils/SkDumpCanvas.h
new file mode 100644
index 0000000..4eb1f25
--- /dev/null
+++ b/include/utils/SkDumpCanvas.h
@@ -0,0 +1,152 @@
+
+/*
+ * 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:
+        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; }
+    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 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;
+    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/include/utils/SkInterpolator.h b/include/utils/SkInterpolator.h
new file mode 100644
index 0000000..289e886
--- /dev/null
+++ b/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/include/utils/SkJSON.h b/include/utils/SkJSON.h
new file mode 100644
index 0000000..c601fa8
--- /dev/null
+++ b/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/include/utils/SkLayer.h b/include/utils/SkLayer.h
new file mode 100644
index 0000000..70fa01b
--- /dev/null
+++ b/include/utils/SkLayer.h
@@ -0,0 +1,130 @@
+
+/*
+ * 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:
+    SK_DECLARE_INST_COUNT(SkLayer)
+
+    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/include/utils/SkMatrix44.h b/include/utils/SkMatrix44.h
new file mode 100644
index 0000000..93140b0
--- /dev/null
+++ b/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/include/utils/SkMeshUtils.h b/include/utils/SkMeshUtils.h
new file mode 100644
index 0000000..564df67
--- /dev/null
+++ b/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/include/utils/SkNWayCanvas.h b/include/utils/SkNWayCanvas.h
new file mode 100644
index 0000000..065adf0
--- /dev/null
+++ b/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 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;
+    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/include/utils/SkNinePatch.h b/include/utils/SkNinePatch.h
new file mode 100644
index 0000000..4d8788b
--- /dev/null
+++ b/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/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
new file mode 100644
index 0000000..7491cd6
--- /dev/null
+++ b/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/include/utils/SkParsePaint.h b/include/utils/SkParsePaint.h
new file mode 100644
index 0000000..b35fcf1
--- /dev/null
+++ b/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/include/utils/SkParsePath.h b/include/utils/SkParsePath.h
new file mode 100644
index 0000000..b48c2c5
--- /dev/null
+++ b/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/include/utils/SkProxyCanvas.h b/include/utils/SkProxyCanvas.h
new file mode 100644
index 0000000..aa47085
--- /dev/null
+++ b/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 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;
+    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/SkUnitMappers.h b/include/utils/SkUnitMappers.h
new file mode 100644
index 0000000..901650d
--- /dev/null
+++ b/include/utils/SkUnitMappers.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 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);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiscreteMapper)
+
+protected:
+    SkDiscreteMapper(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    int     fSegments;
+    SkFract fScale;    // computed from fSegments
+
+    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);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkCosineMapper)
+
+protected:
+    SkCosineMapper(SkFlattenableReadBuffer&);
+
+private:
+
+    typedef SkUnitMapper INHERITED;
+};
+
+#endif
+
diff --git a/include/utils/SkWGL.h b/include/utils/SkWGL.h
new file mode 100644
index 0000000..619b8e0
--- /dev/null
+++ b/include/utils/SkWGL.h
@@ -0,0 +1,109 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#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_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:
+    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;
+
+    /**
+     * 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 *);
+    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/ios/SkStream_NSData.h b/include/utils/ios/SkStream_NSData.h
new file mode 100755
index 0000000..8e6f064
--- /dev/null
+++ b/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/include/utils/mac/SkCGUtils.h b/include/utils/mac/SkCGUtils.h
new file mode 100644
index 0000000..46f8996
--- /dev/null
+++ b/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/win/SkAutoCoInitialize.h b/include/utils/win/SkAutoCoInitialize.h
new file mode 100644
index 0000000..709fa6b
--- /dev/null
+++ b/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/include/utils/win/SkHRESULT.h b/include/utils/win/SkHRESULT.h
new file mode 100644
index 0000000..9738f74
--- /dev/null
+++ b/include/utils/win/SkHRESULT.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 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 HRN variants will return NULL 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 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, )
+//@}
+#endif
diff --git a/include/utils/win/SkIStream.h b/include/utils/win/SkIStream.h
new file mode 100644
index 0000000..e4e045c
--- /dev/null
+++ b/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/include/utils/win/SkTScopedComPtr.h b/include/utils/win/SkTScopedComPtr.h
new file mode 100644
index 0000000..85c314a
--- /dev/null
+++ b/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 { SkASSERT(fPtr != NULL); 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/include/views/SkApplication.h b/include/views/SkApplication.h
new file mode 100644
index 0000000..8369f68
--- /dev/null
+++ b/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/include/views/SkBGViewArtist.h b/include/views/SkBGViewArtist.h
new file mode 100644
index 0000000..869beab
--- /dev/null
+++ b/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/SkEvent.h b/include/views/SkEvent.h
new file mode 100644
index 0000000..f4df448
--- /dev/null
+++ b/include/views/SkEvent.h
@@ -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.
+ */
+
+#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);
+
+#if defined(SK_BUILD_FOR_WIN)
+    static bool WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+#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/include/views/SkEventSink.h b/include/views/SkEventSink.h
new file mode 100644
index 0000000..8ef92b4
--- /dev/null
+++ b/include/views/SkEventSink.h
@@ -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.
+ */
+
+
+#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:
+    SK_DECLARE_INST_COUNT(SkEventSink)
+
+             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;
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkKey.h b/include/views/SkKey.h
new file mode 100644
index 0000000..a019c28
--- /dev/null
+++ b/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/include/views/SkOSMenu.h b/include/views/SkOSMenu.h
new file mode 100644
index 0000000..3e5ee43
--- /dev/null
+++ b/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/include/views/SkOSWindow_Android.h b/include/views/SkOSWindow_Android.h
new file mode 100644
index 0000000..9b72b48
--- /dev/null
+++ b/include/views/SkOSWindow_Android.h
@@ -0,0 +1,46 @@
+
+/*
+ * 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() {}
+
+    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_Mac.h b/include/views/SkOSWindow_Mac.h
new file mode 100644
index 0000000..d195cf1
--- /dev/null
+++ b/include/views/SkOSWindow_Mac.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 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);
+    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);
+    // 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;
+#if SK_SUPPORT_GPU
+    void*   fGLContext;
+#endif
+    typedef SkWindow INHERITED;
+};
+
+#endif
diff --git a/include/views/SkOSWindow_SDL.h b/include/views/SkOSWindow_SDL.h
new file mode 100644
index 0000000..037de3b
--- /dev/null
+++ b/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/include/views/SkOSWindow_Unix.h b/include/views/SkOSWindow_Unix.h
new file mode 100644
index 0000000..f1a4698
--- /dev/null
+++ b/include/views/SkOSWindow_Unix.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 SkOSWindow_Unix_DEFINED
+#define SkOSWindow_Unix_DEFINED
+
+#include <GL/glx.h>
+#include <X11/Xlib.h>
+
+#include "SkWindow.h"
+
+class SkEvent;
+
+struct SkUnixWindow {
+  Display* fDisplay;
+  Window fWin;
+  size_t fOSWin;
+  GC fGc;
+  GLXContext fGLContext;
+};
+
+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();
+
+    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);
+
+protected:
+    // 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:
+    void doPaint();
+    void mapWindowAndWait();
+
+    void closeWindow();
+    void initWindow(int newMSAASampleCount);
+
+    SkUnixWindow fUnixWindow;
+
+    // Needed for GL
+    XVisualInfo* fVi;
+    // 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
new file mode 100644
index 0000000..ff289bd
--- /dev/null
+++ b/include/views/SkOSWindow_Win.h
@@ -0,0 +1,95 @@
+
+/*
+ * Copyright 2006 The Android 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"
+
+#if SK_ANGLE
+#include "EGL/egl.h"
+#endif
+
+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);
+
+    enum SkBackEndTypes {
+        kNone_BackEndType,
+#if SK_SUPPORT_GPU
+        kNativeGL_BackEndType,
+#if SK_ANGLE
+        kANGLE_BackEndType,
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
+    };
+
+    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);
+
+    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);
+
+#if SK_SUPPORT_GPU
+    void*               fHGLRC;
+#if SK_ANGLE
+    EGLDisplay          fDisplay;
+    EGLContext          fContext;
+    EGLSurface          fSurface;
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
+
+    HMENU               fMBar;
+
+    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
new file mode 100755
index 0000000..eda7c5f
--- /dev/null
+++ b/include/views/SkOSWindow_iOS.h
@@ -0,0 +1,49 @@
+
+/*
+ * 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);
+
+    enum SkBackEndTypes {
+        kNone_BackEndType,
+        kNativeGL_BackEndType,
+    };
+
+    void    detach();
+    bool    attach(SkBackEndTypes attachType, int msaaSampleCount);
+    void    present();
+
+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(SkOSMenu*);
+    virtual void onSetTitle(const char[]);
+
+private:
+    void*   fHWND;
+    bool    fInvalEventIsPending;
+    void*   fNotifier;
+    typedef SkWindow INHERITED;
+};
+
+#endif
+
diff --git a/include/views/SkStackViewLayout.h b/include/views/SkStackViewLayout.h
new file mode 100644
index 0000000..f8d23de
--- /dev/null
+++ b/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/include/views/SkSystemEventTypes.h b/include/views/SkSystemEventTypes.h
new file mode 100644
index 0000000..bb2b5d5
--- /dev/null
+++ b/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/include/views/SkTextBox.h b/include/views/SkTextBox.h
new file mode 100644
index 0000000..e3b365d
--- /dev/null
+++ b/include/views/SkTextBox.h
@@ -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.
+ */
+
+
+#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
new file mode 100644
index 0000000..1ba3865
--- /dev/null
+++ b/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/include/views/SkView.h b/include/views/SkView.h
new file mode 100644
index 0000000..eb0621a
--- /dev/null
+++ b/include/views/SkView.h
@@ -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.
+ */
+
+
+#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
+     *  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.
+    */
+    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
+        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:
+        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.
+    */
+    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:
+        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
+        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/include/views/SkViewInflate.h b/include/views/SkViewInflate.h
new file mode 100644
index 0000000..b2cd1e6
--- /dev/null
+++ b/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/include/views/SkWidget.h b/include/views/SkWidget.h
new file mode 100644
index 0000000..c3b4530
--- /dev/null
+++ b/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/SkWindow.h b/include/views/SkWindow.h
new file mode 100644
index 0000000..f1d3881
--- /dev/null
+++ b/include/views/SkWindow.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 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);
+    // 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 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);
+    // 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;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+#if 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/include/views/android/AndroidKeyToSkKey.h b/include/views/android/AndroidKeyToSkKey.h
new file mode 100644
index 0000000..6bcb148
--- /dev/null
+++ b/include/views/android/AndroidKeyToSkKey.h
@@ -0,0 +1,35 @@
+
+/*
+ * Copyright 2011 Skia
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef _ANDROID_TO_SKIA_KEYCODES_H
+#define _ANDROID_TO_SKIA_KEYCODES_H
+
+#include "android/keycodes.h"
+#include "SkKey.h"
+
+// Convert an Android keycode to an SkKey.  This is an incomplete list, only
+// including keys used by the sample app.
+SkKey AndroidKeycodeToSkKey(int keycode) {
+    switch (keycode) {
+        case AKEYCODE_DPAD_LEFT:
+            return kLeft_SkKey;
+        case AKEYCODE_DPAD_RIGHT:
+            return kRight_SkKey;
+        case AKEYCODE_DPAD_UP:
+            return kUp_SkKey;
+        case AKEYCODE_DPAD_DOWN:
+            return kDown_SkKey;
+        case AKEYCODE_BACK:
+            return kBack_SkKey;
+        default:
+            return kNONE_SkKey;
+    }
+}
+
+#endif
diff --git a/include/views/animated/SkBorderView.h b/include/views/animated/SkBorderView.h
new file mode 100644
index 0000000..02f8725
--- /dev/null
+++ b/include/views/animated/SkBorderView.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 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..1270e14
--- /dev/null
+++ b/include/views/animated/SkScrollBarView.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 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/views/unix/XkeysToSkKeys.h b/include/views/unix/XkeysToSkKeys.h
new file mode 100644
index 0000000..30eb97d
--- /dev/null
+++ b/include/views/unix/XkeysToSkKeys.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.
+ */
+#include "X11/Xlib.h"
+#include "X11/keysym.h"
+
+#include "SkKey.h"
+
+#ifndef XKEYS_TOSKKEYS_H
+#define XKEYS_TOSKKEYS_H
+
+SkKey XKeyToSkKey(KeySym keysym) {
+    switch (keysym) {
+        case XK_BackSpace:
+            return kBack_SkKey;
+        case XK_Return:
+            return kOK_SkKey;
+        case XK_Home:
+            return kHome_SkKey;
+        case XK_End:
+            return kEnd_SkKey;
+        case XK_Right:
+            return kRight_SkKey;
+        case XK_Left:
+            return kLeft_SkKey;
+        case XK_Down:
+            return kDown_SkKey;
+        case XK_Up:
+            return kUp_SkKey;
+        default:
+            return kNONE_SkKey;
+    }
+}
+#endif
diff --git a/include/views/unix/keysym2ucs.h b/include/views/unix/keysym2ucs.h
new file mode 100644
index 0000000..255a930
--- /dev/null
+++ b/include/views/unix/keysym2ucs.h
@@ -0,0 +1,15 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/*
+ * This module converts keysym values into the corresponding ISO 10646-1
+ * (UCS, Unicode) values.
+ */
+
+#include <X11/X.h>
+
+long keysym2ucs(KeySym keysym);
diff --git a/include/xml/SkBML_WXMLParser.h b/include/xml/SkBML_WXMLParser.h
new file mode 100644
index 0000000..5d220cf
--- /dev/null
+++ b/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/include/xml/SkBML_XMLParser.h b/include/xml/SkBML_XMLParser.h
new file mode 100644
index 0000000..7a8d6a1
--- /dev/null
+++ b/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/include/xml/SkDOM.h b/include/xml/SkDOM.h
new file mode 100644
index 0000000..fd175f0
--- /dev/null
+++ b/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/include/xml/SkJS.h b/include/xml/SkJS.h
new file mode 100644
index 0000000..645e03b
--- /dev/null
+++ b/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/include/xml/SkXMLParser.h b/include/xml/SkXMLParser.h
new file mode 100644
index 0000000..1a90bf7
--- /dev/null
+++ b/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/include/xml/SkXMLWriter.h b/include/xml/SkXMLWriter.h
new file mode 100644
index 0000000..4e0eda1
--- /dev/null
+++ b/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/make.bat b/make.bat
new file mode 100644
index 0000000..fbe0a35
--- /dev/null
+++ b/make.bat
@@ -0,0 +1,26 @@
+@rem    Copyright 2011 Google Inc.

+@rem

+@rem    Use of this source code is governed by a BSD-style license that can be

+@rem    found in the LICENSE file.

+

+@rem Launches make.py on Windows, after setting Visual Studio environment variables.

+@rem See http://code.google.com/p/skia/wiki/GettingStartedOnWindows

+

+@if "%DevEnvDir%"=="" goto setup_env_vars

+

+:run_python

+@rem Run make.py and propagate its return value.

+python make.py %*

+@exit /B %ERRORLEVEL%

+

+:setup_env_vars

+@rem Visual Studio environment variables aren't set yet, so run vcvars32.bat

+@if "%VS100COMNTOOLS%"=="" goto error_no_VS100COMNTOOLS

+call "%VS100COMNTOOLS%..\..\VC\bin\vcvars32.bat"

+@if %ERRORLEVEL% neq 0 exit /B %ERRORLEVEL%

+@goto run_python

+

+:error_no_VS100COMNTOOLS

+@echo ERROR: VS100COMNTOOLS environment variable not set.

+@echo Are you sure Visual Studio 2010 is installed?

+@exit /B 1

diff --git a/make.py b/make.py
new file mode 100644
index 0000000..1e0b7e7
--- /dev/null
+++ b/make.py
@@ -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.

+

+# "Makefile" replacement to build skia for Windows.

+# More info at http://code.google.com/p/skia/wiki/DocRoot

+#

+# Some usage examples:

+#   make clean

+#   make tests

+#   make bench BUILDTYPE=Release

+#   make gm GYP_DEFINES=skia_scalar=fixed BUILDTYPE=Release

+#   make all

+

+import os

+import shutil

+import sys

+

+BUILDTYPE = 'Debug'

+

+# special targets

+TARGET_ALL = 'all'

+TARGET_CLEAN = 'clean'

+TARGET_GYP = 'gyp'

+LIST_OF_ALL_TARGETS = ['SampleApp', 'bench', 'gm', 'tests', 'tools']

+

+SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))

+OUT_SUBDIR = 'out'

+GYP_SUBDIR = 'gyp'

+

+

+# Simple functions that report what they are doing, and exit(1) on failure.

+def cd(path):

+    print '> cd %s' % path

+    if not os.path.isdir(path):

+        print 'directory %s does not exist' % path

+        sys.exit(1)

+    os.chdir(path)

+

+def rmtree(path):

+    print '> rmtree %s' % path

+    shutil.rmtree(path, ignore_errors=True)

+

+def mkdirs(path):

+    print '> mkdirs %s' % path

+    if not os.path.isdir(path):

+        os.makedirs(path)

+

+def runcommand(command):

+    print '> %s' % command

+    if os.system(command):

+        sys.exit(1)

+

+def MakeClean():

+    """Cross-platform "make clean" operation."""

+    cd(SCRIPT_DIR)

+    rmtree(OUT_SUBDIR)

+    # clean up the directory that XCode (on Mac) creates

+    rmtree('xcodebuild')

+

+

+def CheckWindowsEnvironment():

+    """For Windows: check environment variables needed for command-line build.

+

+    If those environment variables are missing, try to set them.

+    If environment variables can be set up, this function returns; otherwise,

+    it displays an error message and exits.

+    """

+    # If we already have the proper environment variables, nothing to do here.

+    try:

+        env_DevEnvDir = os.environ['DevEnvDir']

+        return  # found it, so we are done

+    except KeyError:

+        pass # go on and run the rest of this function

+

+    print ('\nCould not find Visual Studio environment variables.'

+           '\nPerhaps you have not yet run vcvars32.bat as described at'

+           '\nhttp://msdn.microsoft.com/en-us/library/f2ccy3wt.aspx ?')

+    found_path = None

+    try:

+        possible_path = os.path.abspath(os.path.join(

+            os.environ['VS100COMNTOOLS'], os.path.pardir, os.path.pardir,

+            'VC', 'bin', 'vcvars32.bat'))

+        if os.path.exists(possible_path):

+            found_path = possible_path

+    except KeyError:

+        pass

+    if found_path:

+        print '\nIt looks like you can run that script at:\n%s' % found_path

+    else:

+        print '\nUnable to find vcvars32.bat on your system.'

+    sys.exit(1)

+

+

+def MakeWindows(targets):

+    """For Windows: build as appropriate for the command line arguments.

+

+    parameters:

+        targets: build targets as a list of strings

+    """

+    CheckWindowsEnvironment()

+

+    # Run gyp_skia to prepare Visual Studio projects.

+    cd(SCRIPT_DIR)

+    runcommand('python gyp_skia')

+

+    # Prepare final output dir into which we will copy all binaries.

+    msbuild_working_dir = os.path.join(SCRIPT_DIR, OUT_SUBDIR, GYP_SUBDIR)

+    msbuild_output_dir = os.path.join(msbuild_working_dir, BUILDTYPE)

+    final_output_dir = os.path.join(SCRIPT_DIR, OUT_SUBDIR, BUILDTYPE)

+    mkdirs(final_output_dir)

+

+    for target in targets:

+        # We already built the gypfiles...

+        if target == TARGET_GYP:

+            continue

+

+        cd(msbuild_working_dir)

+        runcommand(

+            ('msbuild /nologo /property:Configuration=%s'

+            ' /target:%s /verbosity:minimal %s.sln') % (

+                BUILDTYPE, target, target))

+        runcommand('xcopy /y %s\* %s' % (

+            msbuild_output_dir, final_output_dir))

+

+

+def Make(args):

+    """Main function.

+

+    parameters:

+        args: command line arguments as a list of strings

+    """

+    # handle any variable-setting parameters or special targets

+    global BUILDTYPE

+    targets = []

+    for arg in args:

+        if arg == TARGET_ALL:

+            targets.extend(LIST_OF_ALL_TARGETS)

+        elif arg == TARGET_CLEAN:

+            MakeClean()

+        elif arg.startswith('BUILDTYPE='):

+            BUILDTYPE = arg[10:]

+        elif arg.startswith('GYP_DEFINES='):

+            os.environ['GYP_DEFINES'] = arg[12:]

+        else:

+            targets.append(arg)

+

+    # if there are no remaining targets, we're done

+    if not targets:

+        sys.exit(0)

+

+    # dispatch to appropriate Make<Platform>() variant.

+    if os.name == 'nt':

+        MakeWindows(targets)

+        sys.exit(0)

+    elif os.name == 'posix':

+        if sys.platform == 'darwin':

+            print 'Mac developers should not run this script; see ' \

+                'http://code.google.com/p/skia/wiki/GettingStartedOnMac'

+            sys.exit(1)

+        elif sys.platform == 'cygwin':

+            print 'Windows development on Cygwin is not currently supported; ' \

+                'see http://code.google.com/p/skia/wiki/GettingStartedOnWindows'

+            sys.exit(1)

+        else:

+            print 'Unix developers should not run this script; see ' \

+                'http://code.google.com/p/skia/wiki/GettingStartedOnLinux'

+            sys.exit(1)

+    else:

+        print 'unknown platform (os.name=%s, sys.platform=%s); see %s' % (

+            os.name, sys.platform, 'http://code.google.com/p/skia/wiki/DocRoot')

+        sys.exit(1)

+    sys.exit(0)

+

+

+# main()

+Make(sys.argv[1:])

+

+    

diff --git a/samplecode/ClockFaceView.cpp b/samplecode/ClockFaceView.cpp
new file mode 100644
index 0000000..a719608
--- /dev/null
+++ b/samplecode/ClockFaceView.cpp
@@ -0,0 +1,256 @@
+
+/*
+ * Copyright 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 "SkFlattenableBuffers.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) {}
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Dot2DPathEffect)
+
+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) : INHERITED(buffer)
+    {
+        fRadius = buffer.readScalar();
+        fPts = NULL;
+    }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
+        this->INHERITED::flatten(buffer);
+        buffer.writeScalar(fRadius);
+    }
+
+private:
+    SkScalar fRadius;
+    SkTDArray<SkPoint>* fPts;
+
+    typedef Sk2DPathEffect INHERITED;
+};
+
+class InverseFillPE : public SkPathEffect {
+public:
+    InverseFillPE() {}
+    virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*) SK_OVERRIDE {
+        *dst = src;
+        dst->setFillType(SkPath::kInverseWinding_FillType);
+        return true;
+    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(InverseFillPE)
+
+protected:
+    InverseFillPE(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+private:
+
+    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(SkScalarToFloat(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, SkFloatToScalar(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);
+
+        SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
+        SkPath path, dstPath;
+        orig.getTextPath("9", 1, 0, 0, &path);
+        pe->filterPath(&dstPath, path, &rec);
+
+        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, SkScalarToFloat(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
new file mode 100644
index 0000000..0b224a5
--- /dev/null
+++ b/samplecode/GMSampleView.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 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
new file mode 100644
index 0000000..f170bdb
--- /dev/null
+++ b/samplecode/OverView.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 "OverView.h"
+
+#include "SampleCode.h"
+
+#include "SkCanvas.h"
+#include "SkView.h"
+
+namespace {
+
+const int N = 8;
+const SkScalar kWidth = SkIntToScalar(640);
+const SkScalar kHeight = SkIntToScalar(480);
+const char gIsOverview[] = "is-overview";
+
+}  // namespace
+
+class OverView : public SkView {
+public:
+    OverView(int count, const SkViewFactory* factories[]);
+    virtual ~OverView();
+
+protected:
+    // Overridden from SkEventSink:
+    virtual bool onEvent(const SkEvent&) SK_OVERRIDE;
+    virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Overview");
+            return true;
+        }
+        if (evt->isType(gIsOverview)) {
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+
+    // Overridden from SkView:
+    virtual void onSizeChange() SK_OVERRIDE;
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        canvas->drawColor(SK_ColorLTGRAY);
+    }
+
+    virtual SkCanvas* beforeChildren(SkCanvas*) SK_OVERRIDE;
+
+    virtual bool onSendClickToChildren(SkScalar x, SkScalar y) SK_OVERRIDE {
+        return false;
+    }
+
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y) SK_OVERRIDE {
+        int ix = (int)(SkScalarDiv(x * N, kWidth));
+        int iy = (int)(SkScalarDiv(y * N, kHeight));
+        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));
+}
+
+bool is_overview(SkView* view) {
+    SkEvent isOverview(gIsOverview);
+    return view->doQuery(&isOverview);
+}
+
+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(kWidth, kHeight);
+        locX += kWidth;
+        if ((i % N) == N - 1) {
+            locY += kHeight;
+            locX = 0;
+        }
+    }
+}
+
+SkCanvas* OverView::beforeChildren(SkCanvas* canvas) {
+    canvas->scale(SK_Scalar1 / N, SK_Scalar1 / N);
+    return canvas;
+}
diff --git a/samplecode/OverView.h b/samplecode/OverView.h
new file mode 100644
index 0000000..3412b19
--- /dev/null
+++ b/samplecode/OverView.h
@@ -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.
+ */
+
+#ifndef SAMPLECODE_OVERVIEW_H_
+#define SAMPLECODE_OVERVIEW_H_
+
+class SkView;
+class SkViewFactory;
+
+SkView* create_overview(int, const SkViewFactory*[]);
+
+bool is_overview(SkView* view);
+
+#endif  // SAMPLECODE_OVERVIEW_H_
diff --git a/samplecode/Sample2PtRadial.cpp b/samplecode/Sample2PtRadial.cpp
new file mode 100644
index 0000000..073fd82
--- /dev/null
+++ b/samplecode/Sample2PtRadial.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 "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
new file mode 100644
index 0000000..196641e
--- /dev/null
+++ b/samplecode/SampleAAClip.cpp
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright 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/SampleAARectModes.cpp b/samplecode/SampleAARectModes.cpp
new file mode 100644
index 0000000..00edfba
--- /dev/null
+++ b/samplecode/SampleAARectModes.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright 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 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
new file mode 100644
index 0000000..e66f8df
--- /dev/null
+++ b/samplecode/SampleAARects.cpp
@@ -0,0 +1,198 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..acdb148
--- /dev/null
+++ b/samplecode/SampleAll.cpp
@@ -0,0 +1,645 @@
+
+/*
+ * Copyright 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 "SkFlattenableBuffers.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) {}
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Dot2DPathEffect)
+
+protected:
+    virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) {
+        dst->addCircle(loc.fX, loc.fY, fRadius);
+    }
+
+    Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+        fRadius = buffer.readScalar();
+    }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
+        this->INHERITED::flatten(buffer);
+        buffer.writeScalar(fRadius);
+    }
+
+private:
+    SkScalar fRadius;
+
+    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);
+}
+
+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
+};
+
+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 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((float) cos(index / 10.0f * 2 * 3.1415925358f));
+            SkScalar y = SkFloatToScalar((float) 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((unsigned int)index * 10),
+                                       SkIntToScalar((unsigned int)index * 2));
+        SkTDArray<SkPoint>(pos2);
+        pos2.setCount(asciiLength);
+        for (index = 0;  index < asciiLength; index++)
+            pos2[index].set(SkIntToScalar((unsigned int)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;
+    }
+
+    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/SampleAnimBlur.cpp b/samplecode/SampleAnimBlur.cpp
new file mode 100644
index 0000000..cada0cd
--- /dev/null
+++ b/samplecode/SampleAnimBlur.cpp
@@ -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.
+ */
+#include "SampleCode.h"
+#include "SkBlurMaskFilter.h"
+#include "SkColorPriv.h"
+#include "SkCanvas.h"
+#include "SkRandom.h"
+
+class AnimBlurView : public SampleView {
+public:
+    AnimBlurView() {
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "AnimBlur");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+
+        SkScalar blurRadius = SampleCode::GetAnimSinScalar(100 * SK_Scalar1,
+                                                           4 * SK_Scalar1,
+                                                           5 * SK_Scalar1);
+
+        SkScalar circleRadius = 3 * SK_Scalar1 +
+                                SampleCode::GetAnimSinScalar(150 * SK_Scalar1,
+                                                             25 * SK_Scalar1,
+                                                             3 * SK_Scalar1);
+
+        static const SkBlurMaskFilter::BlurStyle gStyles[] = {
+            SkBlurMaskFilter::kNormal_BlurStyle,
+            SkBlurMaskFilter::kInner_BlurStyle,
+            SkBlurMaskFilter::kSolid_BlurStyle,
+            SkBlurMaskFilter::kOuter_BlurStyle,
+        };
+        SkRandom random;
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gStyles); ++i) {
+            SkMaskFilter* mf = SkBlurMaskFilter::Create(blurRadius,
+                                       gStyles[i],
+                                       SkBlurMaskFilter::kHighQuality_BlurFlag);
+            SkPaint paint;
+            SkSafeUnref(paint.setMaskFilter(mf));
+            paint.setColor(random.nextU() | 0xff000000);
+            canvas->drawCircle(200 * SK_Scalar1 + 400 * (i % 2) * SK_Scalar1,
+                               200 * SK_Scalar1 + i / 2 * 400 * SK_Scalar1,
+                               circleRadius, paint);
+        }
+        this->inval(NULL);
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new AnimBlurView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SampleAnimatedGradient.cpp b/samplecode/SampleAnimatedGradient.cpp
new file mode 100644
index 0000000..c4a4ec5
--- /dev/null
+++ b/samplecode/SampleAnimatedGradient.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 "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);
+
diff --git a/samplecode/SampleAnimator.cpp b/samplecode/SampleAnimator.cpp
new file mode 100644
index 0000000..7b46d47
--- /dev/null
+++ b/samplecode/SampleAnimator.cpp
@@ -0,0 +1,175 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..df77063
--- /dev/null
+++ b/samplecode/SampleApp.cpp
@@ -0,0 +1,2476 @@
+/*
+ * Copyright 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 "SkGraphics.h"
+#include "SkImageEncoder.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkTime.h"
+#include "SkWindow.h"
+
+#include "SampleCode.h"
+#include "SkTypeface.h"
+
+#if SK_SUPPORT_GPU
+#include "gl/GrGLInterface.h"
+#include "gl/GrGLUtil.h"
+#include "GrRenderTarget.h"
+#include "GrContext.h"
+#include "SkGpuDevice.h"
+#else
+class GrContext;
+#endif
+
+#include "SkOSFile.h"
+#include "SkPDFDevice.h"
+#include "SkPDFDocument.h"
+#include "SkStream.h"
+
+#include "SkGPipe.h"
+#include "SamplePipeControllers.h"
+#include "OverView.h"
+#include "TransitionView.h"
+
+SK_DEFINE_INST_COUNT(SampleWindow::DeviceManager)
+
+extern SampleView* CreateSamplePictFileView(const char filename[]);
+
+class PictFileFactory : public SkViewFactory {
+    SkString fFilename;
+public:
+    PictFileFactory(const SkString& filename) : fFilename(filename) {}
+    virtual SkView* operator() () const SK_OVERRIDE {
+        return CreateSamplePictFileView(fFilename.c_str());
+    }
+};
+
+#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
+
+
+#define USE_ARROWS_FOR_ZOOM true
+
+#if SK_ANGLE
+//#define DEFAULT_TO_ANGLE 1
+#else
+//#define DEFAULT_TO_GPU 1
+#endif
+
+#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() {
+#if SK_SUPPORT_GPU
+        fCurContext = NULL;
+        fCurIntf = NULL;
+        fCurRenderTarget = NULL;
+        fMSAASampleCount = 0;
+#endif
+        fBackend = kNone_BackEndType;
+    }
+
+    virtual ~DefaultDeviceManager() {
+#if SK_SUPPORT_GPU
+        SkSafeUnref(fCurContext);
+        SkSafeUnref(fCurIntf);
+        SkSafeUnref(fCurRenderTarget);
+#endif
+    }
+
+    virtual void setUpBackend(SampleWindow* win, int msaaSampleCount) {
+        SkASSERT(kNone_BackEndType == fBackend);
+
+        fBackend = kNone_BackEndType;
+
+#if SK_SUPPORT_GPU
+        switch (win->getDeviceType()) {
+            case kRaster_DeviceType:
+                // fallthrough
+            case kPicture_DeviceType:
+                // fallthrough
+            case kGPU_DeviceType:
+                // fallthrough
+            case kNullGPU_DeviceType:
+                // all these guys use the native backend
+                fBackend = kNativeGL_BackEndType;
+                break;
+#if SK_ANGLE
+            case kANGLE_DeviceType:
+                // ANGLE is really the only odd man out
+                fBackend = kANGLE_BackEndType;
+                break;
+#endif // SK_ANGLE
+            default:
+                SkASSERT(false);
+                break;
+        }
+
+        bool result = win->attach(fBackend, msaaSampleCount);
+        if (!result) {
+            SkDebugf("Failed to initialize GL");
+            return;
+        }
+        fMSAASampleCount = msaaSampleCount;
+
+        SkASSERT(NULL == fCurIntf);
+        switch (win->getDeviceType()) {
+            case kRaster_DeviceType:
+                // fallthrough
+            case kPicture_DeviceType:
+                // fallthrough
+            case kGPU_DeviceType:
+                // all these guys use the native interface
+                fCurIntf = GrGLCreateNativeInterface();
+                break;
+#if SK_ANGLE
+            case kANGLE_DeviceType:
+                fCurIntf = GrGLCreateANGLEInterface();
+                break;
+#endif // SK_ANGLE
+            case kNullGPU_DeviceType:
+                fCurIntf = GrGLCreateNullInterface();
+                break;
+            default:
+                SkASSERT(false);
+                break;
+        }
+
+        SkASSERT(NULL == fCurContext);
+        fCurContext = GrContext::Create(kOpenGL_Shaders_GrEngine,
+                                        (GrPlatform3DContext) fCurIntf);
+
+        if (NULL == fCurContext || NULL == fCurIntf) {
+            // We need some context and interface to see results
+            SkSafeUnref(fCurContext);
+            SkSafeUnref(fCurIntf);
+            SkDebugf("Failed to setup 3D");
+
+            win->detach();
+        }
+#endif // SK_SUPPORT_GPU
+        // call windowSizeChanged to create the render target
+        this->windowSizeChanged(win);
+    }
+
+    virtual void tearDownBackend(SampleWindow *win) {
+#if SK_SUPPORT_GPU
+        SkSafeUnref(fCurContext);
+        fCurContext = NULL;
+
+        SkSafeUnref(fCurIntf);
+        fCurIntf = NULL;
+
+        SkSafeUnref(fCurRenderTarget);
+        fCurRenderTarget = NULL;
+#endif
+        win->detach();
+        fBackend = kNone_BackEndType;
+    }
+
+    virtual SkCanvas* createCanvas(SampleWindow::DeviceType dType,
+                                   SampleWindow* win) {
+        switch (dType) {
+            case kRaster_DeviceType:
+                // fallthrough
+            case kPicture_DeviceType:
+                // fallthrough
+#if SK_ANGLE
+            case kANGLE_DeviceType:
+#endif
+                break;
+#if SK_SUPPORT_GPU
+            case kGPU_DeviceType:
+            case kNullGPU_DeviceType:
+                if (fCurContext) {
+                    SkAutoTUnref<SkDevice> device(new SkGpuDevice(fCurContext,
+                                                                  fCurRenderTarget));
+                    return new SkCanvas(device);
+                } else {
+                    return NULL;
+                }
+                break;
+#endif
+            default:
+                SkASSERT(false);
+                return NULL;
+        }
+        return NULL;
+    }
+
+    virtual void publishCanvas(SampleWindow::DeviceType dType,
+                               SkCanvas* canvas,
+                               SampleWindow* win) {
+#if SK_SUPPORT_GPU
+        if (fCurContext) {
+            // in case we have queued drawing calls
+            fCurContext->flush();
+
+            if (kGPU_DeviceType != dType && kNullGPU_DeviceType != dType) {
+                // need to send the raster bits to the (gpu) window
+                fCurContext->setRenderTarget(fCurRenderTarget);
+                const SkBitmap& bm = win->getBitmap();
+                fCurRenderTarget->writePixels(0, 0, bm.width(), bm.height(),
+                                             kSkia8888_PM_GrPixelConfig,
+                                             bm.getPixels(),
+                                             bm.rowBytes());
+            }
+        }
+#endif
+
+        win->present();
+    }
+
+    virtual void windowSizeChanged(SampleWindow* win) {
+#if SK_SUPPORT_GPU
+        if (fCurContext) {
+            win->attach(fBackend, fMSAASampleCount);
+
+            GrPlatformRenderTargetDesc desc;
+            desc.fWidth = SkScalarRound(win->width());
+            desc.fHeight = SkScalarRound(win->height());
+            desc.fConfig = kSkia8888_PM_GrPixelConfig;
+            GR_GL_GetIntegerv(fCurIntf, GR_GL_SAMPLES, &desc.fSampleCnt);
+            GR_GL_GetIntegerv(fCurIntf, GR_GL_STENCIL_BITS, &desc.fStencilBits);
+            GrGLint buffer;
+            GR_GL_GetIntegerv(fCurIntf, GR_GL_FRAMEBUFFER_BINDING, &buffer);
+            desc.fRenderTargetHandle = buffer;
+
+            SkSafeUnref(fCurRenderTarget);
+            fCurRenderTarget = fCurContext->createPlatformRenderTarget(desc);
+        }
+#endif
+    }
+
+    virtual GrContext* getGrContext() {
+#if SK_SUPPORT_GPU
+        return fCurContext;
+#else
+        return NULL;
+#endif
+    }
+
+    virtual GrRenderTarget* getGrRenderTarget() SK_OVERRIDE {
+#if SK_SUPPORT_GPU
+        return fCurRenderTarget;
+#else
+        return NULL;
+#endif
+    }
+
+private:
+
+#if SK_SUPPORT_GPU
+    GrContext*              fCurContext;
+    const GrGLInterface*    fCurIntf;
+    GrRenderTarget*         fCurRenderTarget;
+    int fMSAASampleCount;
+#endif
+
+    SkOSWindow::SkBackEndTypes fBackend;
+
+    typedef SampleWindow::DeviceManager INHERITED;
+};
+
+///////////////
+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.
+static 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);
+}
+
+SkScalar SampleCode::GetAnimSinScalar(SkScalar amplitude,
+                                      SkScalar periodInSec,
+                                      SkScalar phaseInSec) {
+    if (!periodInSec) {
+        return 0;
+    }
+    double t = (double)gAnimTime / 1000.0 + phaseInSec;
+    t *= SkScalarToFloat(2 * SK_ScalarPI) / periodInSec;
+    amplitude = SK_ScalarHalf * amplitude;
+    return SkScalarMul(amplitude, SkDoubleToScalar(sin(t))) + amplitude;
+}
+
+GrContext* SampleCode::GetGr() {
+    return gSampleWindow ? gSampleWindow->getGrContext() : NULL;
+}
+
+// some GMs rely on having a skiagm::GetGr function defined
+namespace skiagm {
+    // FIXME: this should be moved into a header
+    GrContext* GetGr();
+    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,
+#if SK_SUPPORT_GPU
+        SampleWindow::kGPU_DeviceType,
+#if SK_ANGLE
+        SampleWindow::kANGLE_DeviceType,
+#endif // SK_ANGLE
+        SampleWindow::kRaster_DeviceType, // skip the null gpu device in normal cycling
+#endif // SK_SUPPORT_GPU
+        SampleWindow::kRaster_DeviceType
+    };
+    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gCT) == SampleWindow::kDeviceTypeCnt, array_size_mismatch);
+    return gCT[ct];
+}
+
+static void usage(const char * argv0) {
+    SkDebugf("%s [--slide sampleName] [-i resourcePath] [--msaa sampleCount] [--pictureDir dirPath] [--picture path]\n", argv0);
+    SkDebugf("    sampleName: sample at which to start.\n");
+    SkDebugf("    resourcePath: directory that stores image resources.\n");
+    SkDebugf("    msaa: request multisampling with the given sample count.\n");
+    SkDebugf("    dirPath: path to directory skia pictures are read from\n");
+    SkDebugf("    path: path to skia picture\n");
+}
+
+SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* devManager)
+    : INHERITED(hwnd)
+    , fDevManager(NULL) {
+
+    this->registerPictFileSamples(argv, argc);
+    this->registerPictFileSample(argv, argc);
+    SkGMRegistyToSampleRegistry();
+    {
+        const SkViewRegister* reg = SkViewRegister::Head();
+        while (reg) {
+            *fSamples.append() = reg->factory();
+            reg = reg->next();
+        }
+    }
+
+    const char* resourcePath = NULL;
+    fCurrIndex = -1;
+    fMSAASampleCount = 0;
+
+    const char* const commandName = argv[0];
+    char* const* stop = argv + argc;
+    for (++argv; argv < stop; ++argv) {
+        if (strcmp(*argv, "-i") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                resourcePath = *argv;
+            }
+        } else if (strcmp(*argv, "--slide") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                fCurrIndex = findByTitle(*argv);
+                if (fCurrIndex < 0) {
+                    fprintf(stderr, "Unknown sample \"%s\"\n", *argv);
+                    listTitles();
+                }
+            }
+        } else if (strcmp(*argv, "--msaa") == 0) {
+            ++argv;
+            if (argv < stop && **argv) {
+                fMSAASampleCount = atoi(*argv);
+            }
+        } else if (strcmp(*argv, "--list") == 0) {
+            listTitles();
+        }
+        else {
+            usage(commandName);
+        }
+    }
+
+    if (fCurrIndex < 0) {
+        SkString title;
+        if (readTitleFromPrefs(&title)) {
+            fCurrIndex = findByTitle(title.c_str());
+        }
+    }
+
+    if (fCurrIndex < 0) {
+        fCurrIndex = 0;
+    }
+
+    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;
+
+    fDeviceType = kRaster_DeviceType;
+
+#if DEFAULT_TO_GPU
+    fDeviceType = kGPU_DeviceType;
+#endif
+#if SK_ANGLE && DEFAULT_TO_ANGLE
+    fDeviceType = kANGLE_DeviceType;
+#endif
+
+    fUseClip = false;
+    fNClip = false;
+    fAnimating = false;
+    fRotate = false;
+    fPerspAnim = false;
+    fPerspAnimTime = 0;
+    fScale = false;
+    fRequestGrabImage = false;
+    fPipeState = SkOSMenu::kOffState;
+    fTilingState = SkOSMenu::kOffState;
+    fTileCount.set(1, 1);
+    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 = new SkOSMenu;
+    fAppMenu->setTitle("Global Settings");
+    int itemID;
+
+    itemID =fAppMenu->appendList("Device Type", "Device Type", sinkID, 0,
+                                "Raster", "Picture", "OpenGL",
+#if SK_ANGLE
+                                "ANGLE",
+#endif
+                                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->appendTriState("Pipe", "Pipe" , sinkID,
+                                                  fPipeState);
+    fAppMenu->assignKeyEquivalentToItem(fUsePipeMenuItemID, 'P');
+
+    itemID = fAppMenu->appendTriState("Tiling", "Tiling", sinkID, fTilingState);
+    fAppMenu->assignKeyEquivalentToItem(itemID, 't');
+
+#ifdef DEBUGGER
+    itemID = fAppMenu->appendSwitch("Debugger", "Debugger", sinkID, fDebugger);
+    fAppMenu->assignKeyEquivalentToItem(itemID, 'q');
+#endif
+    itemID = fAppMenu->appendSwitch("Slide Show", "Slide Show" , sinkID, false);
+    fAppMenu->assignKeyEquivalentToItem(itemID, 'a');
+    itemID = fAppMenu->appendSwitch("Clip", "Clip" , sinkID, fUseClip);
+    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);
+    fSlideMenu = new SkOSMenu;
+    this->addMenu(fSlideMenu);
+
+//    this->setConfig(SkBitmap::kRGB_565_Config);
+    this->setConfig(SkBitmap::kARGB_8888_Config);
+    this->setVisibleP(true);
+    this->setClipToBounds(false);
+
+    skiagm::GM::SetResourcePath(resourcePath);
+
+    this->loadView((*fSamples[fCurrIndex])());
+
+    fPDFData = NULL;
+
+    if (NULL == devManager) {
+        fDevManager = new DefaultDeviceManager();
+    } else {
+        devManager->ref();
+        fDevManager = devManager;
+    }
+    fDevManager->setUpBackend(this, fMSAASampleCount);
+
+    // 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);
+}
+
+static void make_filepath(SkString* path, const char* dir, const SkString& name) {
+    size_t len = strlen(dir);
+    path->set(dir);
+    if (len > 0 && dir[len - 1] != '/') {
+        path->append("/");
+    }
+    path->append(name);
+}
+
+void SampleWindow::registerPictFileSample(char** argv, int argc) {
+    const char* pict = NULL;
+
+    for (int i = 0; i < argc; ++i) {
+        if (!strcmp(argv[i], "--picture")) {
+            i += 1;
+            if (i < argc) {
+                pict = argv[i];
+                break;
+            }
+        }
+    }
+    if (pict) {
+        SkString path(pict);
+        *fSamples.append() = new PictFileFactory(path);
+    }
+}
+
+void SampleWindow::registerPictFileSamples(char** argv, int argc) {
+    const char* pictDir = NULL;
+
+    for (int i = 0; i < argc; ++i) {
+        if (!strcmp(argv[i], "--pictureDir")) {
+            i += 1;
+            if (i < argc) {
+                pictDir = argv[i];
+                break;
+            }
+        }
+    }
+    if (pictDir) {
+        SkOSFile::Iter iter(pictDir, "skp");
+        SkString filename;
+        while (iter.next(&filename)) {
+            SkString path;
+            make_filepath(&path, pictDir, filename);
+            *fSamples.append() = new PictFileFactory(path);
+        }
+    }
+}
+
+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;
+}
+
+void SampleWindow::listTitles() {
+    int count = fSamples.count();
+    SkDebugf("All Slides:\n");
+    for (int i = 0; i < count; i++) {
+        SkDebugf("    %s\n", getSampleTitle(i).c_str());
+    }
+}
+
+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) {
+    // update the animation time
+    if (!gAnimTimePrev && !gAnimTime) {
+        // first time make delta be 0
+        gAnimTime = SkTime::GetMSecs();
+        gAnimTimePrev = gAnimTime;
+    } else {
+        gAnimTimePrev = gAnimTime;
+        gAnimTime = SkTime::GetMSecs();
+    }
+
+    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 {
+        const SkScalar cw = SkScalarDiv(this->width(), SkIntToScalar(fTileCount.width()));
+        const SkScalar ch = SkScalarDiv(this->height(), SkIntToScalar(fTileCount.height()));
+
+        for (int y = 0; y < fTileCount.height(); ++y) {
+            for (int x = 0; x < fTileCount.width(); ++x) {
+                SkAutoCanvasRestore acr(canvas, true);
+                canvas->clipRect(SkRect::MakeXYWH(x * cw, y * ch, cw, ch));
+                this->INHERITED::draw(canvas);
+            }
+        }
+
+        if (!fTileCount.equals(1, 1)) {
+            SkPaint paint;
+            paint.setColor(0x60FF00FF);
+            paint.setStyle(SkPaint::kStroke_Style);
+            for (int y = 0; y < fTileCount.height(); ++y) {
+                for (int x = 0; x < fTileCount.width(); ++x) {
+                    canvas->drawRect(SkRect::MakeXYWH(x * cw, y * ch, cw, ch), paint);
+                }
+            }
+        }
+    }
+    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();
+    if (!m.invert(&m)) {
+        return;
+    }
+    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"
+
+#if 0 // UNUSED
+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;
+    }
+}
+#endif
+
+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:
+                // fallthrough
+#if SK_SUPPORT_GPU
+            case kGPU_DeviceType:
+                // fallthrough
+#if SK_ANGLE
+            case kANGLE_DeviceType:
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
+                canvas = this->INHERITED::beforeChildren(canvas);
+                break;
+            case kPicture_DeviceType:
+                fPicture = new SkPicture;
+                canvas = fPicture->beginRecording(9999, 9999);
+                break;
+#if SK_SUPPORT_GPU
+            case kNullGPU_DeviceType:
+                break;
+#endif
+            default:
+                SkASSERT(false);
+                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.png", 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
+        if (fPipeState != SkOSMenu::kOffState) {
+            fPipeState = SkOSMenu::kOffState;
+            (void)SampleView::SetUsePipe(curr, fPipeState);
+            fAppMenu->getItemByID(fUsePipeMenuItemID)->setTriState(fPipeState);
+            this->onUpdateMenu(fAppMenu);
+        }
+
+        //Reset any transformations
+        fGesture.stop();
+        fGesture.reset();
+
+        this->loadView(create_debugger(gTempDataStore.begin(),
+                                       gTempDataStore.count()));
+    }
+#endif
+}
+
+void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) {
+    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 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();
+    this->loadView(create_transition(curr_view(this), (*fSamples[fCurrIndex])(),
+                                     fTransitionPrev));
+    return true;
+}
+
+bool SampleWindow::nextSample() {
+    fCurrIndex = (fCurrIndex + 1) % fSamples.count();
+    this->loadView(create_transition(curr_view(this), (*fSamples[fCurrIndex])(),
+                                     fTransitionNext));
+    return true;
+}
+
+bool SampleWindow::goToSample(int i) {
+    fCurrIndex = (i) % fSamples.count();
+    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_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::FindTriState(evt, "Pipe", &fPipeState)) {
+#ifdef PIPE_NET
+        if (!fPipeState != SkOSMenu::kOnState)
+            gServer.disconnectAll();
+#endif
+        (void)SampleView::SetUsePipe(curr_view(this), fPipeState);
+        this->updateTitle();
+        this->inval(NULL);
+        return true;
+    }
+    if (SkOSMenu::FindTriState(evt, "Tiling", &fTilingState)) {
+        int nx = 1, ny = 1;
+        switch (fTilingState) {
+            case SkOSMenu::kOffState:   nx = 1; ny = 1; break;
+            case SkOSMenu::kMixedState: nx = 1; ny = 16; break;
+            case SkOSMenu::kOnState:    nx = 4; ny = 4; break;
+        }
+        fTileCount.set(nx, ny);
+        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) {
+            fPipeState = SkOSMenu::kOnState;
+            (void)SampleView::SetUsePipe(curr_view(this), fPipeState);
+        } else {
+            this->loadView((*fSamples[fCurrIndex])());
+        }
+        this->inval(NULL);
+        return true;
+    }
+#endif
+    return this->INHERITED::onEvent(evt);
+}
+
+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);
+}
+
+#if 0 // UNUSED
+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;
+        }
+    }
+}
+#endif
+
+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;
+#if SK_SUPPORT_GPU
+        case '\\':
+            this->setDeviceType(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;
+#endif
+        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)
+        return;
+
+    fDevManager->tearDownBackend(this);
+
+    fDeviceType = type;
+
+    fDevManager->setUpBackend(this, fMSAASampleCount);
+
+    this->updateTitle();
+    this->inval(NULL);
+}
+
+void SampleWindow::toggleSlideshow() {
+    fAnimating = !fAnimating;
+    this->postAnimatingEvent();
+    this->updateTitle();
+}
+
+void SampleWindow::toggleRendering() {
+    this->setDeviceType(cycle_devicetype(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:
+            if (this->previousSample()) {
+                return true;
+            }
+            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
+        fPipeState = SkOSMenu::kOnState;
+    }
+#endif
+    (void)SampleView::SetUsePipe(view, fPipeState);
+    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: ",
+#if SK_SUPPORT_GPU
+    "opengl: ",
+#if SK_ANGLE
+    "angle: ",
+#endif // SK_ANGLE
+    "null-gl: "
+#endif // SK_SUPPORT_GPU
+};
+SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gDeviceTypePrefix) == SampleWindow::kDeviceTypeCnt,
+                  array_size_mismatch);
+
+static const bool gDeviceTypeIsGPU[] = {
+    false,
+    false,
+#if SK_SUPPORT_GPU
+    true,
+#if SK_ANGLE
+    true,
+#endif // SK_ANGLE
+    true
+#endif // SK_SUPPORT_GPU
+};
+SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gDeviceTypeIsGPU) == SampleWindow::kDeviceTypeCnt,
+                  array_size_mismatch);
+
+
+static const char* trystate_str(SkOSMenu::TriState state,
+                                const char trueStr[], const char falseStr[]) {
+    if (SkOSMenu::kOnState == state) {
+        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(" %8.3f ms", fMeasureFPS_Time / (float)FPS_REPEAT_COUNT);
+    }
+    if (SampleView::IsSampleView(view)) {
+        switch (fPipeState) {
+            case SkOSMenu::kOnState:
+                title.prepend("<Pipe> ");
+                break;
+            case SkOSMenu::kMixedState:
+                title.prepend("<Tiled Pipe> ");
+                break;
+
+            default:
+                break;
+        }
+        title.prepend("! ");
+    }
+
+#if SK_SUPPORT_GPU
+    if (gDeviceTypeIsGPU[fDeviceType] &&
+        NULL != fDevManager &&
+        fDevManager->getGrRenderTarget()->numSamples() > 0) {
+        title.appendf(" [MSAA: %d]",
+                       fDevManager->getGrRenderTarget()->numSamples());
+    }
+#endif
+
+    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, SkOSMenu::TriState state) {
+    SkEvent evt;
+    evt.setS32(set_use_pipe_tag, state);
+    return view->doEvent(evt);
+}
+
+bool SampleView::onEvent(const SkEvent& evt) {
+    if (evt.isType(repeat_count_tag)) {
+        fRepeatCount = evt.getFast32();
+        return true;
+    }
+    int32_t pipeHolder;
+    if (evt.findS32(set_use_pipe_tag, &pipeHolder)) {
+        fPipeState = static_cast<SkOSMenu::TriState>(pipeHolder);
+        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);
+}
+
+
+class SimplePC : public SkGPipeController {
+public:
+    SimplePC(SkCanvas* target);
+    ~SimplePC();
+
+    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;
+
+    size_t        fTotalWritten;
+};
+
+SimplePC::SimplePC(SkCanvas* target) : fReader(target) {
+    fBlock = NULL;
+    fBlockSize = fBytesWritten = 0;
+    fStatus = SkGPipeReader::kDone_Status;
+    fTotalWritten = 0;
+    fAtomsWritten = 0;
+}
+
+SimplePC::~SimplePC() {
+//    SkASSERT(SkGPipeReader::kDone_Status == fStatus);
+    if (fTotalWritten) {
+        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;
+}
+
+void SampleView::draw(SkCanvas* canvas) {
+    if (SkOSMenu::kOffState == fPipeState) {
+        this->INHERITED::draw(canvas);
+    } else {
+        SkGPipeWriter writer;
+        SimplePC controller(canvas);
+        TiledPipeController tc(canvas->getDevice()->accessBitmap(false),
+                               &canvas->getTotalMatrix());
+        SkGPipeController* pc;
+        if (SkOSMenu::kMixedState == fPipeState) {
+            pc = &tc;
+        } else {
+            pc = &controller;
+        }
+        uint32_t flags = SkGPipeWriter::kCrossProcess_Flag;
+
+        canvas = writer.startRecording(pc, 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();
+    }
+}
+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);
+    }
+}
+
+// FIXME: this should be in a header
+SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv);
+SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) {
+    if (false) { // avoid bit rot, suppress warning
+        test();
+    }
+    return new SampleWindow(hwnd, argc, argv, NULL);
+}
+
+// FIXME: this should be in a header
+void get_preferred_size(int* x, int* y, int* width, int* height);
+void get_preferred_size(int* x, int* y, int* width, int* height) {
+    *x = 10;
+    *y = 50;
+    *width = 640;
+    *height = 480;
+}
+
+#ifdef SK_BUILD_FOR_IOS
+void save_args(int argc, char *argv[]) {
+}
+#endif
+
+// FIXME: this should be in a header
+void application_init();
+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();
+}
+
+// FIXME: this should be in a header
+void application_term();
+void application_term() {
+    SkEvent::Term();
+    SkGraphics::Term();
+}
diff --git a/samplecode/SampleApp.h b/samplecode/SampleApp.h
new file mode 100644
index 0000000..85392e0
--- /dev/null
+++ b/samplecode/SampleApp.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2011 Skia
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SampleApp_DEFINED
+#define SampleApp_DEFINED
+
+#include "SkOSMenu.h"
+#include "SkPath.h"
+#include "SkScalar.h"
+#include "SkTDArray.h"
+#include "SkTouchGesture.h"
+#include "SkWindow.h"
+
+class GrContext;
+class GrRenderTarget;
+
+class SkCanvas;
+class SkData;
+class SkEvent;
+class SkPicture;
+class SkTypeface;
+class SkViewFactory;
+
+class SampleWindow : public SkOSWindow {
+    SkTDArray<const SkViewFactory*> fSamples;
+public:
+    enum DeviceType {
+        kRaster_DeviceType,
+        kPicture_DeviceType,
+#if SK_SUPPORT_GPU
+        kGPU_DeviceType,
+#if SK_ANGLE
+        kANGLE_DeviceType,
+#endif // SK_ANGLE
+        kNullGPU_DeviceType,
+#endif // SK_SUPPORT_GPU
+
+        kDeviceTypeCnt
+    };
+    /**
+     * 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:
+        SK_DECLARE_INST_COUNT(DeviceManager)
+
+        virtual void setUpBackend(SampleWindow* win, int msaaSampleCount) = 0;
+
+        virtual void tearDownBackend(SampleWindow* win) = 0;
+
+        // called before drawing. should install correct device
+        // type on the canvas. Will skip drawing if returns false.
+        virtual SkCanvas* createCanvas(DeviceType dType, 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 (NULL if not built with GPU support)
+        virtual GrContext* getGrContext() = 0;
+
+        // return the GrRenderTarget backing gpu devices (NULL if not built with GPU support)
+        virtual GrRenderTarget* getGrRenderTarget() = 0;
+    private:
+        typedef SkRefCnt INHERITED;
+    };
+
+    SampleWindow(void* hwnd, int argc, char** argv, DeviceManager*);
+    virtual ~SampleWindow();
+
+    virtual SkCanvas* createCanvas() SK_OVERRIDE {
+        SkCanvas* canvas = NULL;
+        if (fDevManager) {
+            canvas = fDevManager->createCanvas(fDeviceType, this);
+        }
+        if (NULL == canvas) {
+            canvas = this->INHERITED::createCanvas();
+        }
+        return canvas;
+    }
+
+    virtual void draw(SkCanvas* canvas);
+
+    void setDeviceType(DeviceType type);
+    void toggleRendering();
+    void toggleSlideshow();
+    void toggleFPS();
+    void showOverview();
+
+    GrContext* getGrContext() const { return fDevManager->getGrContext(); }
+
+    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();
+
+    DeviceType getDeviceType() const { return fDeviceType; }
+
+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);
+
+    void registerPictFileSamples(char** argv, int argc);
+    void registerPictFileSample(char** argv, int argc);
+
+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;
+    SkISize fTileCount;
+
+
+    SkOSMenu::TriState fPipeState;  // Mixed uses a tiled pipe
+                                    // On uses a normal pipe
+                                    // Off uses no pipe
+    int  fUsePipeMenuItemID;
+    bool fDebugger;
+
+    // The following are for the 'fatbits' drawing
+    // Latest position of the mouse.
+    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;
+    SkOSMenu::TriState fTilingState;
+    unsigned   fFlipAxis;
+
+    int fMSAASampleCount;
+
+    int fScrollTestX, fScrollTestY;
+    SkScalar fZoomCenterX, fZoomCenterY;
+
+    //Stores global settings
+    SkOSMenu* fAppMenu; // We pass ownership to SkWindow, when we call addMenu
+    //Stores slide specific settings
+    SkOSMenu* fSlideMenu; // We pass ownership to SkWindow, when we call addMenu
+
+    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*);
+    void listTitles();
+
+    typedef SkOSWindow INHERITED;
+};
+
+#endif
diff --git a/samplecode/SampleArc.cpp b/samplecode/SampleArc.cpp
new file mode 100644
index 0000000..a87d280
--- /dev/null
+++ b/samplecode/SampleArc.cpp
@@ -0,0 +1,191 @@
+
+/*
+ * Copyright 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.5f));
+    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
new file mode 100644
index 0000000..b8bac0d
--- /dev/null
+++ b/samplecode/SampleAvoid.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 "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
new file mode 100644
index 0000000..f5e632c
--- /dev/null
+++ b/samplecode/SampleBigBlur.cpp
@@ -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.
+ */
+#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
new file mode 100644
index 0000000..6a68fa0
--- /dev/null
+++ b/samplecode/SampleBigGradient.cpp
@@ -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.
+ */
+#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
new file mode 100644
index 0000000..3f895fc
--- /dev/null
+++ b/samplecode/SampleBitmapRect.cpp
@@ -0,0 +1,248 @@
+
+/*
+ * Copyright 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"
+
+#if SK_SUPPORT_GPU
+#include "SkGpuDevice.h"
+#else
+class GrContext;
+#endif
+
+#define INT_SIZE        64
+#define SCALAR_SIZE     SkIntToScalar(INT_SIZE)
+
+static void make_bitmap(SkBitmap* bitmap) {
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, INT_SIZE, INT_SIZE);
+    bitmap->allocPixels();
+    SkCanvas canvas(*bitmap);
+
+    canvas.drawColor(SK_ColorRED);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    const SkPoint pts[] = { { 0, 0 }, { SCALAR_SIZE, SCALAR_SIZE } };
+    const SkColor colors[] = { SK_ColorWHITE, SK_ColorBLUE };
+    paint.setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+                                                   SkShader::kClamp_TileMode))->unref();
+    canvas.drawCircle(SCALAR_SIZE/2, SCALAR_SIZE/2, SCALAR_SIZE/2, paint);
+}
+
+static SkPoint unit_vec(int degrees) {
+    SkScalar rad = SkDegreesToRadians(SkIntToScalar(degrees));
+    SkScalar s, c;
+    s = SkScalarSinCos(rad, &c);
+    return SkPoint::Make(c, s);
+}
+
+static void bounce(SkScalar* value, SkScalar* delta, SkScalar min, SkScalar max) {
+    *value += *delta;
+    if (*value < min) {
+        *value = min;
+        *delta = - *delta;
+    } else if (*value > max) {
+        *value = max;
+        *delta = - *delta;
+    }
+}
+
+static void bounce_pt(SkPoint* pt, SkVector* vec, const SkRect& limit) {
+    bounce(&pt->fX, &vec->fX, limit.fLeft, limit.fRight);
+    bounce(&pt->fY, &vec->fY, limit.fTop, limit.fBottom);
+}
+
+class BitmapRectView : public SampleView {
+    SkPoint fSrcPts[2];
+    SkPoint fSrcVec[2];
+    SkRect  fSrcLimit;
+    SkRect  fDstR[2];
+
+    void bounce() {
+        bounce_pt(&fSrcPts[0], &fSrcVec[0], fSrcLimit);
+        bounce_pt(&fSrcPts[1], &fSrcVec[1], fSrcLimit);
+    }
+
+public:
+    BitmapRectView() {
+        this->setBGColor(SK_ColorGRAY);
+
+        fSrcPts[0].set(0, 0);
+        fSrcPts[1].set(SCALAR_SIZE, SCALAR_SIZE);
+
+        fSrcVec[0] = unit_vec(30);
+        fSrcVec[1] = unit_vec(107);
+
+        fSrcLimit.set(-SCALAR_SIZE/4, -SCALAR_SIZE/4,
+                      SCALAR_SIZE*5/4, SCALAR_SIZE*5/4);
+
+        fDstR[0] = SkRect::MakeXYWH(SkIntToScalar(10), SkIntToScalar(100),
+                                       SkIntToScalar(250), SkIntToScalar(300));
+        fDstR[1] = fDstR[0];
+        fDstR[1].offset(fDstR[0].width() * 5/4, 0);
+
+        fSrcPts[0].set(32, 32);
+        fSrcPts[1].set(90, 90);
+    }
+
+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) {
+        SkRect srcR;
+        srcR.set(fSrcPts[0], fSrcPts[1]);
+        srcR = SkRect::MakeXYWH(fSrcPts[0].fX, fSrcPts[0].fY, 32, 32);
+        srcR.offset(-srcR.width()/2, -srcR.height()/2);
+
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(SK_ColorYELLOW);
+
+        SkBitmap bitmap;
+        make_bitmap(&bitmap);
+
+        canvas->translate(20, 20);
+
+        canvas->drawBitmap(bitmap, 0, 0, &paint);
+        canvas->drawRect(srcR, paint);
+
+        for (int i = 0; i < 2; ++i) {
+            paint.setFilterBitmap(1 == i);
+            canvas->drawBitmapRectToRect(bitmap, &srcR, fDstR[i], &paint);
+            canvas->drawRect(fDstR[i], paint);
+        }
+
+        this->bounce();
+        this->inval(NULL);
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void make_big_bitmap(SkBitmap* bm) {
+    static const char gText[] =
+        "We the people, in order to form a more perfect union, establish justice,"
+        " ensure domestic tranquility, provide for the common defense, promote the"
+        " general welfare and ensure the blessings of liberty to ourselves and our"
+        " posterity, do ordain and establish this constitution for the United"
+        " States of America.";
+
+    const int BIG_H = 120;
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setTextSize(SkIntToScalar(BIG_H));
+
+    const int BIG_W = SkScalarRoundToInt(paint.measureText(gText, strlen(gText)));
+
+    bm->setConfig(SkBitmap::kARGB_8888_Config, BIG_W, BIG_H);
+    bm->allocPixels();
+    bm->eraseColor(SK_ColorWHITE);
+
+    SkCanvas canvas(*bm);
+
+    canvas.drawText(gText, strlen(gText), 0, paint.getTextSize()*4/5, paint);
+}
+
+class BitmapRectView2 : public SampleView {
+    SkBitmap fBitmap;
+
+    SkRect  fSrcR;
+    SkRect  fLimitR;
+    SkScalar fDX;
+    SkRect  fDstR[2];
+
+    void bounceMe() {
+        SkScalar width = fSrcR.width();
+        bounce(&fSrcR.fLeft, &fDX, fLimitR.fLeft, fLimitR.fRight - width);
+        fSrcR.fRight = fSrcR.fLeft + width;
+    }
+
+public:
+    BitmapRectView2() {
+        make_big_bitmap(&fBitmap);
+
+        this->setBGColor(SK_ColorGRAY);
+
+        fSrcR.fLeft = 0;
+        fSrcR.fTop = 0;
+        fSrcR.fRight = SkIntToScalar(fBitmap.height()) * 3;
+        fSrcR.fBottom = SkIntToScalar(fBitmap.height());
+
+        fLimitR.set(0, 0,
+                    SkIntToScalar(fBitmap.width()),
+                    SkIntToScalar(fBitmap.height()));
+
+        fDX = SK_Scalar1;
+
+        fDstR[0] = SkRect::MakeXYWH(SkIntToScalar(20), SkIntToScalar(20),
+                                    SkIntToScalar(600), SkIntToScalar(200));
+        fDstR[1] = fDstR[0];
+        fDstR[1].offset(0, fDstR[0].height() * 5/4);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "BigBitmapRect");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(SK_ColorYELLOW);
+
+        for (int i = 0; i < 2; ++i) {
+            paint.setFilterBitmap(1 == i);
+            canvas->drawBitmapRectToRect(fBitmap, &fSrcR, fDstR[i], &paint);
+            canvas->drawRect(fDstR[i], paint);
+        }
+
+        this->bounceMe();
+        this->inval(NULL);
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* F0() { return new BitmapRectView; }
+static SkView* F1() { return new BitmapRectView2; }
+static SkViewRegister gR0(F0);
+static SkViewRegister gR1(F1);
+
diff --git a/samplecode/SampleBlur.cpp b/samplecode/SampleBlur.cpp
new file mode 100644
index 0000000..1444949
--- /dev/null
+++ b/samplecode/SampleBlur.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 "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() {
+        if (false) { // avoid bit rot, suppress warning
+            fBM = make_bitmap();
+        }
+    }
+
+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
new file mode 100644
index 0000000..19ff6ca
--- /dev/null
+++ b/samplecode/SampleBox.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 "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);
+
diff --git a/samplecode/SampleCamera.cpp b/samplecode/SampleCamera.cpp
new file mode 100644
index 0000000..12e7e04
--- /dev/null
+++ b/samplecode/SampleCamera.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 "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
new file mode 100644
index 0000000..1444e1b
--- /dev/null
+++ b/samplecode/SampleCircle.cpp
@@ -0,0 +1,136 @@
+
+/*
+ * Copyright 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);
+        if (false) { // avoid bit rot, suppress warning
+            test_circlebounds(canvas);
+        }
+    }
+
+    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
new file mode 100644
index 0000000..c521f81
--- /dev/null
+++ b/samplecode/SampleClamp.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 "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
new file mode 100644
index 0000000..ce3736e
--- /dev/null
+++ b/samplecode/SampleClip.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 "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 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, SkToBool(aa));
+//                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
new file mode 100644
index 0000000..8bd5fbd
--- /dev/null
+++ b/samplecode/SampleCode.h
@@ -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.
+ */
+
+
+#ifndef SampleCode_DEFINED
+#define SampleCode_DEFINED
+
+#include "SkColor.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkView.h"
+#include "SkOSMenu.h"
+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);
+    // gives a sinusoidal value between 0 and amplitude
+    static SkScalar GetAnimSinScalar(SkScalar amplitude,
+                                     SkScalar periodInSec,
+                                     SkScalar phaseInSec = 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() : fPipeState(SkOSMenu::kOffState),
+            fBGColor(SK_ColorWHITE), fRepeatCount(1) {
+    }
+
+    void setBGColor(SkColor color) { fBGColor = color; }
+
+    static bool IsSampleView(SkView*);
+    static bool SetRepeatDraw(SkView*, int count);
+    static bool SetUsePipe(SkView*, SkOSMenu::TriState);
+
+    /**
+     *  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*);
+
+    SkOSMenu::TriState fPipeState;
+    SkColor fBGColor;
+
+private:
+    int fRepeatCount;
+
+    typedef SkView INHERITED;
+};
+
+#endif
+
diff --git a/samplecode/SampleColorFilter.cpp b/samplecode/SampleColorFilter.cpp
new file mode 100644
index 0000000..bbabea2
--- /dev/null
+++ b/samplecode/SampleColorFilter.cpp
@@ -0,0 +1,219 @@
+
+/*
+ * Copyright 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();
+
+        if (false) { // avoid bit rot, suppress warning
+            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
new file mode 100644
index 0000000..ff343dd
--- /dev/null
+++ b/samplecode/SampleComplexClip.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 "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
new file mode 100644
index 0000000..549e9e2
--- /dev/null
+++ b/samplecode/SampleConcavePaths.cpp
@@ -0,0 +1,153 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..18d9bdc
--- /dev/null
+++ b/samplecode/SampleCull.cpp
@@ -0,0 +1,196 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..2c16e54
--- /dev/null
+++ b/samplecode/SampleDash.cpp
@@ -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.
+ */
+#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
new file mode 100644
index 0000000..48759c0
--- /dev/null
+++ b/samplecode/SampleDecode.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 "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);
+            }
+            SkDELETE(codec);
+        }
+    }
+
+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
new file mode 100644
index 0000000..91f1622
--- /dev/null
+++ b/samplecode/SampleDegenerateTwoPtRadials.cpp
@@ -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.
+ */
+#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::CreateTwoPointConical(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;
+
+        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
new file mode 100644
index 0000000..7e5fc21
--- /dev/null
+++ b/samplecode/SampleDither.cpp
@@ -0,0 +1,185 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..b84d0dd
--- /dev/null
+++ b/samplecode/SampleDitherBitmap.cpp
@@ -0,0 +1,148 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..e1f62fe
--- /dev/null
+++ b/samplecode/SampleDraw.cpp
@@ -0,0 +1,380 @@
+
+/*
+ * Copyright 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/SampleEffects.cpp b/samplecode/SampleEffects.cpp
new file mode 100644
index 0000000..15ad809
--- /dev/null
+++ b/samplecode/SampleEffects.cpp
@@ -0,0 +1,118 @@
+
+/*
+ * Copyright 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"
+
+//#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]);
+        }
+
+        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
new file mode 100644
index 0000000..41c7455
--- /dev/null
+++ b/samplecode/SampleEmboss.cpp
@@ -0,0 +1,73 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..49ad7c5
--- /dev/null
+++ b/samplecode/SampleEmptyPath.cpp
@@ -0,0 +1,130 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..afcde03
--- /dev/null
+++ b/samplecode/SampleEncode.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 "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
new file mode 100644
index 0000000..b291834
--- /dev/null
+++ b/samplecode/SampleFillType.cpp
@@ -0,0 +1,97 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..75eb7d7
--- /dev/null
+++ b/samplecode/SampleFilter.cpp
@@ -0,0 +1,145 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..8582447
--- /dev/null
+++ b/samplecode/SampleFilter2.cpp
@@ -0,0 +1,123 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..d5c2add
--- /dev/null
+++ b/samplecode/SampleFontCache.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 "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
new file mode 100644
index 0000000..8494e75
--- /dev/null
+++ b/samplecode/SampleFontScalerTest.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 "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(SkFloatToScalar(-7.18397061e+15f),
+                       SkFloatToScalar(-1.53091184e+13f));
+            pts[2].set(SkFloatToScalar(-1.30077315e+16f),
+                       SkFloatToScalar(-2.77196141e+13f));
+            pts[3].set(SkFloatToScalar(-1.30077315e+16f),
+                       SkFloatToScalar(-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, SkIntToScalar(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
new file mode 100644
index 0000000..c082c4d
--- /dev/null
+++ b/samplecode/SampleFuzz.cpp
@@ -0,0 +1,382 @@
+
+/*
+ * Copyright 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);
+}
+
+#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 float huge() {
+    double d = 1e100;
+    float f = (float)d;
+    return f;
+}
+
+#if defined _WIN32
+#pragma warning ( pop )
+#endif
+
+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
new file mode 100644
index 0000000..f1a2dd9
--- /dev/null
+++ b/samplecode/SampleGradients.cpp
@@ -0,0 +1,185 @@
+
+/*
+ * Copyright 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));
+        if (false) { // avoid bit rot, suppress warning
+            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
new file mode 100644
index 0000000..2bb2fa2
--- /dev/null
+++ b/samplecode/SampleHairCurves.cpp
@@ -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.
+ */
+#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
new file mode 100644
index 0000000..59cb25c
--- /dev/null
+++ b/samplecode/SampleHairModes.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright 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 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
new file mode 100644
index 0000000..7aa4666
--- /dev/null
+++ b/samplecode/SampleHairline.cpp
@@ -0,0 +1,279 @@
+
+/*
+ * Copyright 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) { // avoid bit rot, suppress warning
+            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
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/samplecode/SampleImage.cpp
diff --git a/samplecode/SampleImageDir.cpp b/samplecode/SampleImageDir.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/samplecode/SampleImageDir.cpp
diff --git a/samplecode/SampleLCD.cpp b/samplecode/SampleLCD.cpp
new file mode 100644
index 0000000..7136448
--- /dev/null
+++ b/samplecode/SampleLCD.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 "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
new file mode 100644
index 0000000..78b9800
--- /dev/null
+++ b/samplecode/SampleLayerMask.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 "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
new file mode 100644
index 0000000..6d57c2a
--- /dev/null
+++ b/samplecode/SampleLayers.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 "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 void filter(SkPaint* p, SkDrawFilter::Type) SK_OVERRIDE {
+        fColor = p->getColor();
+        if (fColor == SK_ColorRED) {
+            p->setColor(SK_ColorGREEN);
+        }
+    }
+
+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/SampleLines.cpp b/samplecode/SampleLines.cpp
new file mode 100644
index 0000000..57d7050
--- /dev/null
+++ b/samplecode/SampleLines.cpp
@@ -0,0 +1,116 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..18b3720
--- /dev/null
+++ b/samplecode/SampleMeasure.cpp
@@ -0,0 +1,122 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..e0a8e4d
--- /dev/null
+++ b/samplecode/SampleMipMap.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 "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
+    };
+    bool fOnce;
+public:
+    MipMapView() {
+        fOnce = false;
+    }
+
+    void init() {
+        if (fOnce) {
+            return;
+        }
+        fOnce = true;
+
+        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) {
+        this->init();
+        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
new file mode 100644
index 0000000..c85976b
--- /dev/null
+++ b/samplecode/SampleMovie.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 "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/SampleOvalTest.cpp b/samplecode/SampleOvalTest.cpp
new file mode 100644
index 0000000..340acf6
--- /dev/null
+++ b/samplecode/SampleOvalTest.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 "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
new file mode 100644
index 0000000..3cf30ad
--- /dev/null
+++ b/samplecode/SampleOverflow.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 "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;
+    SkMatrix matrix;
+    matrix.reset();
+
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1370, 812);
+    bitmap.allocPixels();
+
+    SkCanvas canvas(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
+}
+
+#ifdef SK_SCALAR_IS_FLOATx // FIXME: unclear when if ever this can be enabled
+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;
+}
+#endif
+
+static void TestOverflowHitTest() {
+    SkPath path;
+
+#ifdef SK_SCALAR_IS_FLOATx // FIXME: unclear when if ever this can be enabled
+    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
new file mode 100644
index 0000000..7daeb1b
--- /dev/null
+++ b/samplecode/SamplePageFlip.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 "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 {
+    bool fOnce;
+public:
+
+    enum { N = SK_ARRAY_COUNT(gConfigs) };
+
+    pthread_t   fThreads[N];
+    SkBitmap    fBitmaps[N];
+
+    PageFlipView() {
+        gDone = false;
+        fOnce = false;
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+    void init() {
+        if (fOnce) {
+            return;
+        }
+        fOnce = true;
+
+        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);
+        }
+    }
+
+    virtual ~PageFlipView() {
+        if (!fOnce) {
+            return;
+        }
+        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) {
+        this->init();
+        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
new file mode 100644
index 0000000..90aaf4c
--- /dev/null
+++ b/samplecode/SamplePatch.cpp
@@ -0,0 +1,336 @@
+
+/*
+ * Copyright 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 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)) {
+            SampleCode::TitleR(evt, "Patch");
+            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
new file mode 100644
index 0000000..b07307a
--- /dev/null
+++ b/samplecode/SamplePath.cpp
@@ -0,0 +1,219 @@
+
+/*
+ * Copyright 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;
+    bool fOnce;
+
+    PathView() {
+        fOnce = false;
+    }
+
+    void init() {
+        if (fOnce) {
+            return;
+        }
+        fOnce = true;
+
+        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) {
+        this->init();
+        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
new file mode 100644
index 0000000..b999c39
--- /dev/null
+++ b/samplecode/SamplePathClip.cpp
@@ -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.
+ */
+#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 false;
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new PathClipView; }
+static SkViewRegister reg(MyFactory);
+
diff --git a/samplecode/SamplePathEffects.cpp b/samplecode/SamplePathEffects.cpp
new file mode 100644
index 0000000..35d3260
--- /dev/null
+++ b/samplecode/SamplePathEffects.cpp
@@ -0,0 +1,191 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..9c7cdc6
--- /dev/null
+++ b/samplecode/SamplePathFill.cpp
@@ -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.
+ */
+#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/SamplePictFile.cpp b/samplecode/SamplePictFile.cpp
new file mode 100644
index 0000000..014ae93
--- /dev/null
+++ b/samplecode/SamplePictFile.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 "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"
+
+class PictFileView : public SampleView {
+    SkString    fFilename;
+    SkPicture*  fPicture;
+
+    static SkPicture* LoadPicture(const char path[]) {
+        SkPicture* pic = NULL;
+
+        SkBitmap bm;
+        if (SkImageDecoder::DecodeFile(path, &bm)) {
+            bm.setImmutable();
+            pic = SkNEW(SkPicture);
+            SkCanvas* can = pic->beginRecording(bm.width(), bm.height());
+            can->drawBitmap(bm, 0, 0, NULL);
+            pic->endRecording();
+        } else {
+            SkFILEStream stream(path);
+            if (stream.isValid()) {
+                pic = SkNEW_ARGS(SkPicture, (&stream));
+            }
+
+            if (false) { // re-record
+                SkPicture p2;
+                pic->draw(p2.beginRecording(pic->width(), pic->height()));
+                p2.endRecording();
+
+                SkString path2(path);
+                path2.append(".new.skp");
+                SkFILEWStream writer(path2.c_str());
+                p2.serialize(&writer);
+            }
+        }
+        return pic;
+    }
+
+public:
+    PictFileView(const char name[] = NULL) : fFilename(name) {
+        fPicture = NULL;
+    }
+
+    virtual ~PictFileView() {
+        SkSafeUnref(fPicture);
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SkString name("P:");
+            name.append(fFilename);
+            SampleCode::TitleR(evt, name.c_str());
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    virtual void onDrawContent(SkCanvas* canvas) {
+        if (!fPicture) {
+            fPicture = LoadPicture(fFilename.c_str());
+        }
+        if (fPicture) {
+            canvas->drawPicture(*fPicture);
+        }
+    }
+
+private:
+    typedef SampleView INHERITED;
+};
+
+SampleView* CreateSamplePictFileView(const char filename[]);
+SampleView* CreateSamplePictFileView(const char filename[]) {
+    return new PictFileView(filename);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#if 0
+static SkView* MyFactory() { return new PictFileView; }
+static SkViewRegister reg(MyFactory);
+#endif
+
diff --git a/samplecode/SamplePicture.cpp b/samplecode/SamplePicture.cpp
new file mode 100644
index 0000000..17a0d75
--- /dev/null
+++ b/samplecode/SamplePicture.cpp
@@ -0,0 +1,197 @@
+
+/*
+ * Copyright 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(SkFloatToScalar(0.5f), SkFloatToScalar(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
new file mode 100644
index 0000000..705a7b1
--- /dev/null
+++ b/samplecode/SamplePoints.cpp
@@ -0,0 +1,85 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..88adae9
--- /dev/null
+++ b/samplecode/SamplePolyToPoly.cpp
@@ -0,0 +1,170 @@
+
+/*
+ * Copyright 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)) {
+            SampleCode::TitleR(evt, "PolyToPolyView");
+            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, SkFloatToScalar(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
new file mode 100644
index 0000000..269fc6a
--- /dev/null
+++ b/samplecode/SampleRegion.cpp
@@ -0,0 +1,418 @@
+
+/*
+ * Copyright 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"
+
+static void test_strokerect(SkCanvas* canvas) {
+    int width = 100;
+    int height = 100;
+
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kA8_Config, width*2, height*2);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+
+    SkScalar dx = 20;
+    SkScalar dy = 20;
+
+    SkPath path;
+    path.addRect(0.0f, 0.0f,
+                 SkIntToScalar(width), SkIntToScalar(height),
+                 SkPath::kCW_Direction);
+    SkRect r = SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
+
+    SkCanvas c(bitmap);
+    c.translate(dx, dy);
+
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(1);
+
+    // use the rect
+    c.clear(0);
+    c.drawRect(r, paint);
+    canvas->drawBitmap(bitmap, 0, 0, NULL);
+
+    // use the path
+    c.clear(0);
+    c.drawPath(path, paint);
+    canvas->drawBitmap(bitmap, SkIntToScalar(2*width), 0, NULL);
+}
+
+static void drawFadingText(SkCanvas* canvas,
+                           const char* text, size_t len, SkScalar x, SkScalar y,
+                           const SkPaint& paint) {
+    // Need a bounds for the text
+    SkRect bounds;
+    SkPaint::FontMetrics fm;
+
+    paint.getFontMetrics(&fm);
+    bounds.set(x, y + fm.fTop, x + paint.measureText(text, len), y + fm.fBottom);
+
+    // may need to outset bounds a little, to account for hinting and/or
+    // antialiasing
+    bounds.inset(-SkIntToScalar(2), -SkIntToScalar(2));
+
+    canvas->saveLayer(&bounds, NULL);
+    canvas->drawText(text, len, x, y, paint);
+
+    const SkPoint pts[] = {
+        { bounds.fLeft, y },
+        { bounds.fRight, y }
+    };
+    const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
+
+    // pos[1] value is where we start to fade, relative to the width
+    // of our pts[] array.
+    const SkScalar pos[] = { 0, SkFloatToScalar(0.9f), SK_Scalar1 };
+
+    SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos, 3,
+                                                 SkShader::kClamp_TileMode);
+    SkPaint p;
+    p.setShader(s)->unref();
+    p.setXfermodeMode(SkXfermode::kDstIn_Mode);
+    canvas->drawRect(bounds, p);
+
+    canvas->restore();
+}
+
+static void test_text(SkCanvas* canvas) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setTextSize(20);
+
+    const char* str = "Hamburgefons";
+    size_t len = strlen(str);
+    SkScalar x = 20;
+    SkScalar y = 20;
+
+    canvas->drawText(str, len, x, y, paint);
+
+    y += 20;
+
+    const SkPoint pts[] = { { x, y }, { x + paint.measureText(str, len), y } };
+    const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, 0 };
+    const SkScalar pos[] = { 0, 0.9f, 1 };
+    SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos,
+                                                 SK_ARRAY_COUNT(colors),
+                                                 SkShader::kClamp_TileMode);
+    paint.setShader(s)->unref();
+    canvas->drawText(str, len, x, y, paint);
+
+    y += 20;
+    paint.setShader(NULL);
+    drawFadingText(canvas, str, len, x, y, paint);
+}
+
+#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, 2, 0x00000001, 0x0000000c, 0x0000000d, 0x00000025, 0x7fffffff,
+        0x000001de, 1, 0x00000001, 0x00000025, 0x7fffffff,
+        0x000004b3, 1, 0x00000001, 0x00000026, 0x7fffffff,
+        0x000004b4, 1, 0x0000000c, 0x00000026, 0x7fffffff,
+        0x00000579, 1, 0x00000000, 0x0000013a, 0x7fffffff,
+        0x000005d8, 1, 0x00000000, 0x0000013b, 0x7fffffff,
+        0x7fffffff
+    };
+    make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA);
+
+    static int32_t dataB[] = {
+        0x000000b6,
+        0x000000c4, 1, 0x000000a1, 0x000000f0, 0x7fffffff,
+        0x000000d6, 0, 0x7fffffff,
+        0x000000e4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
+        0x000000e6, 0, 0x7fffffff,
+        0x000000f4, 2, 0x00000070, 0x00000079, 0x000000a1, 0x000000b0, 0x7fffffff,
+        0x000000f6, 0, 0x7fffffff,
+        0x00000104, 1, 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_base_rgn(SkRegion* rgn) {
+        rgn->setRect(fBase);
+        SkIRect r = fBase;
+        r.offset(75, 20);
+        rgn->op(r, SkRegion::kUnion_Op);
+    }
+
+    void build_rgn(SkRegion* rgn, SkRegion::Op op) {
+        build_base_rgn(rgn);
+        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);
+    }
+
+    static void drawstr(SkCanvas* canvas, const char text[], const SkPoint& loc,
+                        bool hilite) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(20));
+        paint.setColor(hilite ? SK_ColorRED : 0x40FF0000);
+        canvas->drawText(text, strlen(text), loc.fX, loc.fY, paint);
+    }
+
+    void drawPredicates(SkCanvas* canvas, const SkPoint pts[]) {
+        SkRegion rgn;
+        build_base_rgn(&rgn);
+
+        drawstr(canvas, "Intersects", pts[0], rgn.intersects(fRect));
+        drawstr(canvas, "Contains", pts[1], rgn.contains(fRect));
+    }
+
+    void drawOrig(SkCanvas* canvas, bool bg) {
+        SkRect      r;
+        SkPaint     paint;
+
+        paint.setStyle(SkPaint::kStroke_Style);
+        if (bg)
+            paint.setColor(0xFFBBBBBB);
+
+        SkRegion rgn;
+        build_base_rgn(&rgn);
+        paint_rgn(canvas, rgn, 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.writeToMemory(NULL);
+                SkASSERT(size <= sizeof(buffer));
+                size_t  size2 = tmp.writeToMemory(buffer);
+                SkASSERT(size == size2);
+
+                SkRegion    tmp3;
+                size2 = tmp3.readFromMemory(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) {
+        if (false) { // avoid bit rot, suppress warning
+            test_strokerect(canvas);
+            return;
+        }
+        if (false) { // avoid bit rot, suppress warning
+            test_text(canvas);
+            return;
+        }
+#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
+        const SkPoint origins[] = {
+            { 30*SK_Scalar1, 50*SK_Scalar1 },
+            { 150*SK_Scalar1, 50*SK_Scalar1 },
+        };
+        this->drawPredicates(canvas, origins);
+
+        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
new file mode 100644
index 0000000..54c79ef
--- /dev/null
+++ b/samplecode/SampleRepeatTile.cpp
@@ -0,0 +1,93 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..cada977
--- /dev/null
+++ b/samplecode/SampleShaderText.cpp
@@ -0,0 +1,216 @@
+
+/*
+ * Copyright 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);
+}
+
+static 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
new file mode 100644
index 0000000..78b63a3
--- /dev/null
+++ b/samplecode/SampleShaders.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 "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/SampleSkLayer.cpp b/samplecode/SampleSkLayer.cpp
new file mode 100644
index 0000000..e7c0ddb
--- /dev/null
+++ b/samplecode/SampleSkLayer.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 "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
new file mode 100644
index 0000000..b200473
--- /dev/null
+++ b/samplecode/SampleSlides.cpp
@@ -0,0 +1,735 @@
+
+/*
+ * Copyright 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 "SkFlattenableBuffers.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, SkFloatToScalar(1.5f));
+
+    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; }
+};
+
+static 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);
+        }
+    }
+}
+
+static 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);
+}
+
+static 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);
+}
+
+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
+};
+
+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;
+    bool fOnce;
+public:
+    SlideView() {
+        fOnce = false;
+    }
+
+    void init() {
+        if (fOnce) {
+            return;
+        }
+        fOnce = true;
+
+        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_" SK_SIZE_T_SPECIFIER ".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) {
+        this->init();
+        gProc[fIndex](canvas);
+    }
+
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        this->init();
+        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
new file mode 100644
index 0000000..be6e728
--- /dev/null
+++ b/samplecode/SampleSpiral.cpp
@@ -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.
+ */
+#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);
+
diff --git a/samplecode/SampleStrokePath.cpp b/samplecode/SampleStrokePath.cpp
new file mode 100644
index 0000000..ca5135d
--- /dev/null
+++ b/samplecode/SampleStrokePath.cpp
@@ -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.
+ */
+#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
new file mode 100644
index 0000000..edf9228
--- /dev/null
+++ b/samplecode/SampleStrokeRect.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 "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
new file mode 100644
index 0000000..8245f4e
--- /dev/null
+++ b/samplecode/SampleStrokeText.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 "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);
+        if (false) { // avoid bit rot, suppress warning
+            lettersToBitmap2(&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
new file mode 100644
index 0000000..ea317e1
--- /dev/null
+++ b/samplecode/SampleTests.cpp
@@ -0,0 +1,118 @@
+
+/*
+ * 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
new file mode 100644
index 0000000..8ff36e4
--- /dev/null
+++ b/samplecode/SampleText.cpp
@@ -0,0 +1,332 @@
+
+/*
+ * Copyright 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 "SkFlattenableBuffers.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 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;
+    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(ReduceNoise)
+
+private:
+    ReduceNoise(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(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);
+    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Darken)
+
+private:
+    Darken(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(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((unsigned int)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&);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPowerMode)
+
+private:
+    SkScalar fExp;          // user's value
+    uint8_t fTable[256];    // cache
+
+    void init(SkScalar exponent);
+    SkPowerMode(SkFlattenableReadBuffer& b) : INHERITED(b) {
+        // read the exponent
+        this->init(SkFixedToScalar(b.readFixed()));
+    }
+    virtual void flatten(SkFlattenableWriteBuffer& b) const SK_OVERRIDE {
+        this->INHERITED::flatten(b);
+        b.writeFixed(SkScalarToFixed(fExp));
+    }
+
+    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 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
new file mode 100644
index 0000000..ac24f08
--- /dev/null
+++ b/samplecode/SampleTextAlpha.cpp
@@ -0,0 +1,123 @@
+
+/*
+ * Copyright 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 "SkDevice.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)) {
+            SampleCode::TitleR(evt, "TextAlpha");
+            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);
+        }
+        if (false) { // avoid bit rot, suppress warning
+            check_for_nonwhite(canvas->getDevice()->accessBitmap(false), 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
new file mode 100644
index 0000000..a021dbd
--- /dev/null
+++ b/samplecode/SampleTextBox.cpp
@@ -0,0 +1,116 @@
+
+/*
+ * Copyright 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);
+
+#if defined(SK_BUILD_FOR_WIN) && defined(SK_FONTHOST_WIN_GDI)
+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() {
+#if defined(SK_BUILD_FOR_WIN) && defined(SK_FONTHOST_WIN_GDI)
+        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)) {
+            SampleCode::TitleR(evt, "TextBox");
+            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/SampleTextOnPath.cpp b/samplecode/SampleTextOnPath.cpp
new file mode 100644
index 0000000..976f3c8
--- /dev/null
+++ b/samplecode/SampleTextOnPath.cpp
@@ -0,0 +1,174 @@
+
+/*
+ * Copyright 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 "SkBlurDrawLooper.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkPathMeasure.h"
+
+#define REPEAT_COUNT    1
+
+static void textStrokePath(SkCanvas* canvas) {
+    SkPaint paint;
+    SkPath  path;
+    SkRect  rect;
+
+    canvas->save();
+    canvas->scale(SkIntToScalar(250),SkIntToScalar(250));
+
+    rect.set(SkFloatToScalar(0.0f),  SkFloatToScalar(0.21f),
+             SkFloatToScalar(0.78f), SkFloatToScalar(0.99f));
+
+    path.addArc(rect, SkIntToScalar(280), SkIntToScalar(350));
+
+    paint.setAntiAlias(true);
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setColor(0xFFFF0000);
+    paint.setTextSize(SkFloatToScalar(0.085f));
+    paint.setStrokeWidth(SkFloatToScalar(.005f));
+
+    canvas->drawPath(path, paint);
+
+    paint.setLooper(new SkBlurDrawLooper(SkFloatToScalar(0.002f),
+                                          SkFloatToScalar(0.0f),
+                                          SkFloatToScalar(0.0f),
+                                          (SkColor)0xFF000000))->unref();
+
+    const char* text = "DRAWING STROKED TEXT WITH A BLUR ON A PATH";
+    size_t      len = strlen(text);
+
+    canvas->drawTextOnPathHV(text, len, path, 0,
+                             SkFloatToScalar(-0.025f), paint);
+    canvas->restore();
+}
+
+static void textPathMatrix(SkCanvas* canvas) {
+    SkPaint paint;
+    SkPath  path;
+    SkMatrix matrix;
+
+    path.moveTo(SkIntToScalar(050), SkIntToScalar(200));
+    path.quadTo(SkIntToScalar(250), SkIntToScalar(000),
+                SkIntToScalar(450), SkIntToScalar(200));
+
+    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);
+
+    SkPathMeasure   meas(path, false);
+    SkScalar pathLen = meas.getLength();
+
+    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(-50), SkIntToScalar(-50));
+
+        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(48));
+
+        const char* text = "Hamburgefons";
+        size_t      len = strlen(text);
+
+        for (int j = 0; j < REPEAT_COUNT; j++) {
+            SkScalar x = fHOffset;
+
+            paint.setColor(SK_ColorBLACK);
+            canvas->drawTextOnPathHV(text, len, fPath,
+                                     x, paint.getTextSize()/2, paint);
+
+            paint.setColor(SK_ColorRED);
+            canvas->drawTextOnPathHV(text, len, fPath,
+                                     x + SkIntToScalar(50), 0, paint);
+
+            paint.setColor(SK_ColorBLUE);
+            canvas->drawTextOnPathHV(text, len, fPath,
+                         x + SkIntToScalar(100), -paint.getTextSize()/2, paint);
+        }
+
+        paint.setColor(SK_ColorGREEN);
+        paint.setStyle(SkPaint::kStroke_Style);
+        canvas->drawPath(fPath, paint);
+
+        canvas->translate(SkIntToScalar(275), 0);
+        textStrokePath(canvas);
+
+        canvas->translate(SkIntToScalar(-275), SkIntToScalar(250));
+        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
new file mode 100644
index 0000000..07dcb40
--- /dev/null
+++ b/samplecode/SampleTextureDomain.cpp
@@ -0,0 +1,116 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..e7b6a7f
--- /dev/null
+++ b/samplecode/SampleTiling.cpp
@@ -0,0 +1,174 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..d682317
--- /dev/null
+++ b/samplecode/SampleTinyBitmap.cpp
@@ -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.
+ */
+#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
new file mode 100644
index 0000000..d6adc16
--- /dev/null
+++ b/samplecode/SampleTriangles.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 "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
new file mode 100644
index 0000000..9bc8762
--- /dev/null
+++ b/samplecode/SampleTypeface.cpp
@@ -0,0 +1,135 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..8cc9afb
--- /dev/null
+++ b/samplecode/SampleUnitMapper.cpp
@@ -0,0 +1,173 @@
+
+/*
+ * Copyright 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);
+    }
+
+    bool invertPt(SkScalar x, SkScalar y, SkPoint* result) {
+        if (NULL == result)
+            return true;
+
+        SkMatrix m;
+        if (!fMatrix.invert(&m)) {
+            return false;
+        }
+
+        m.mapXY(x, y, result);
+        return true;
+    }
+
+    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) {
+            if (!invertPt(click->fCurr.fX, click->fCurr.fY,
+                          &fPts[fDragIndex])) {
+                return false;
+            }
+
+            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
new file mode 100644
index 0000000..bc02256
--- /dev/null
+++ b/samplecode/SampleVertices.cpp
@@ -0,0 +1,234 @@
+
+/*
+ * Copyright 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)) {
+            SampleCode::TitleR(evt, "Vertices");
+            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
new file mode 100644
index 0000000..323df80
--- /dev/null
+++ b/samplecode/SampleWarp.cpp
@@ -0,0 +1,474 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..f9df8f5
--- /dev/null
+++ b/samplecode/SampleWritePixels.cpp
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright 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/SampleXfermodesBlur.cpp b/samplecode/SampleXfermodesBlur.cpp
new file mode 100644
index 0000000..9d368a2
--- /dev/null
+++ b/samplecode/SampleXfermodesBlur.cpp
@@ -0,0 +1,235 @@
+
+/*
+ * Copyright 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 (size_t 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() {
+        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
new file mode 100644
index 0000000..1caff13
--- /dev/null
+++ b/samplecode/TransitionView.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "TransitionView.h"
+
+#include "OverView.h"
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkTime.h"
+#include "SkInterpolator.h"
+
+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 {
+    enum {
+        // kDurationMS = 500
+        kDurationMS = 1
+    };
+
+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, SkOSMenu::kOffState);
+        //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, SkOSMenu::kOffState);
+        //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, SkOSMenu::kOffState);
+            //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)) {
+            fPipeState = SkOSMenu::kOffState;
+        }
+
+        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[] = { SkFloatToScalar(0.8f), SkFloatToScalar(0.0f),
+                             SkFloatToScalar(0.0f), SK_Scalar1 };
+        fInterp.setKeyFrame(0, SkTime::GetMSecs(), fBegin, blend);
+        fInterp.setKeyFrame(1, SkTime::GetMSecs()+kDurationMS, 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) {
+#ifdef SK_BUILD_FOR_ANDROID
+    // Disable transitions for Android
+    return next;
+#else
+    return SkNEW_ARGS(TransitionView, (prev, next, direction));
+#endif
+}
diff --git a/samplecode/TransitionView.h b/samplecode/TransitionView.h
new file mode 100644
index 0000000..92e136a
--- /dev/null
+++ b/samplecode/TransitionView.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 SAMPLECODE_TRANSITIONVIEW_H_
+#define SAMPLECODE_TRANSITIONVIEW_H_
+
+class SkView;
+
+SkView* create_transition(SkView* prev, SkView* next, int direction);
+
+bool is_transition(SkView* view);
+
+#endif  // SAMPLECODE_TRANSITIONVIEW_H_
diff --git a/samplecode/samplecode_files.mk b/samplecode/samplecode_files.mk
new file mode 100644
index 0000000..cb757de
--- /dev/null
+++ b/samplecode/samplecode_files.mk
@@ -0,0 +1,67 @@
+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
new file mode 100644
index 0000000..ae310a5
--- /dev/null
+++ b/samplecode/vertexdump.cpp
@@ -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.
+ */
+#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..d41df7f
--- /dev/null
+++ b/skia.gyp
@@ -0,0 +1,47 @@
+# 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 http://code.google.com/p/skia/wiki/DocRoot
+#
+{
+  'targets': [
+    {
+      # Use this target to build everything provided by Skia.
+      'target_name': 'all',
+      'type': 'none',
+      'dependencies': [
+        # The minimal set of static libraries for basic Skia functionality.
+        'gyp/skia_base_libs.gyp:skia_base_libs',
+
+        'gyp/bench.gyp:bench',
+        'gyp/gm.gyp:gm',
+        'gyp/SampleApp.gyp:SampleApp',
+        'gyp/tests.gyp:tests',
+        'gyp/tools.gyp:tools',
+      ],
+      'conditions': [
+        ['skia_os == "android"', {
+          'dependencies': [
+            'gyp/android_system.gyp:SkiaAndroidApp',
+          ],
+        }],
+
+        # The debugger is not supported for iOS, Android and 32-bit Mac builds.
+        ['skia_os != "ios" and skia_os != "android" and (skia_os != "mac" or skia_arch_width == 64)', {
+          'dependencies': [ 'gyp/debugger.gyp:debugger' ],
+        }],
+      ],
+    },
+  ],
+}
+
+# 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
new file mode 100644
index 0000000..b7018b9
--- /dev/null
+++ b/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/src/animator/SkAnimate3DSchema.xsd b/src/animator/SkAnimate3DSchema.xsd
new file mode 100644
index 0000000..5063b75
--- /dev/null
+++ b/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/src/animator/SkAnimate3DSchema.xsx b/src/animator/SkAnimate3DSchema.xsx
new file mode 100644
index 0000000..ceb7d89
--- /dev/null
+++ b/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/src/animator/SkAnimateActive.cpp b/src/animator/SkAnimateActive.cpp
new file mode 100644
index 0000000..00ee9df
--- /dev/null
+++ b/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/src/animator/SkAnimateActive.h b/src/animator/SkAnimateActive.h
new file mode 100644
index 0000000..33d0164
--- /dev/null
+++ b/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/src/animator/SkAnimateBase.cpp b/src/animator/SkAnimateBase.cpp
new file mode 100644
index 0000000..0e56faa
--- /dev/null
+++ b/src/animator/SkAnimateBase.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 "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) {
+        SkDebugf("begin=\"%g\" ", SkScalarToFloat(SkScalarDiv(begin,1000)));
+    }
+}
+#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/src/animator/SkAnimateBase.h b/src/animator/SkAnimateBase.h
new file mode 100644
index 0000000..df8d38a
--- /dev/null
+++ b/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/src/animator/SkAnimateField.cpp b/src/animator/SkAnimateField.cpp
new file mode 100644
index 0000000..43f510e
--- /dev/null
+++ b/src/animator/SkAnimateField.cpp
@@ -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.
+ */
+
+
+#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\" ");
+        SkDebugf("dur=\"%g\" ", SkScalarToFloat(SkScalarDiv(dur,1000)));
+        if (repeat != SK_Scalar1)
+            SkDebugf("repeat=\"%g\" ", SkScalarToFloat(repeat));
+        //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;
+                SkDebugf("%g", SkScalarToFloat(blend[i]));
+            }
+            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/src/animator/SkAnimateMaker.cpp b/src/animator/SkAnimateMaker.cpp
new file mode 100644
index 0000000..ddde2eb
--- /dev/null
+++ b/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/src/animator/SkAnimateMaker.h b/src/animator/SkAnimateMaker.h
new file mode 100644
index 0000000..53a5521
--- /dev/null
+++ b/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/src/animator/SkAnimateProperties.h b/src/animator/SkAnimateProperties.h
new file mode 100644
index 0000000..b070640
--- /dev/null
+++ b/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/src/animator/SkAnimateSchema.xsd b/src/animator/SkAnimateSchema.xsd
new file mode 100644
index 0000000..f7af332
--- /dev/null
+++ b/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/src/animator/SkAnimateSchema.xsx b/src/animator/SkAnimateSchema.xsx
new file mode 100644
index 0000000..ceb7d89
--- /dev/null
+++ b/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/src/animator/SkAnimateSet.cpp b/src/animator/SkAnimateSet.cpp
new file mode 100644
index 0000000..f153b16
--- /dev/null
+++ b/src/animator/SkAnimateSet.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 "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) {
+        SkDebugf("dur=\"%g\" ", SkScalarToFloat(SkScalarDiv(dur,1000)));
+    }
+    //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/src/animator/SkAnimateSet.h b/src/animator/SkAnimateSet.h
new file mode 100644
index 0000000..c735a08
--- /dev/null
+++ b/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/src/animator/SkAnimator.cpp b/src/animator/SkAnimator.cpp
new file mode 100644
index 0000000..5d1f220
--- /dev/null
+++ b/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/src/animator/SkAnimatorScript.cpp b/src/animator/SkAnimatorScript.cpp
new file mode 100644
index 0000000..0e639f2
--- /dev/null
+++ b/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/src/animator/SkAnimatorScript.h b/src/animator/SkAnimatorScript.h
new file mode 100644
index 0000000..c8802a3
--- /dev/null
+++ b/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/src/animator/SkAnimatorScript2.cpp b/src/animator/SkAnimatorScript2.cpp
new file mode 100644
index 0000000..45ce3ce
--- /dev/null
+++ b/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/src/animator/SkAnimatorScript2.h b/src/animator/SkAnimatorScript2.h
new file mode 100644
index 0000000..c3995f6
--- /dev/null
+++ b/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/src/animator/SkBoundable.cpp b/src/animator/SkBoundable.cpp
new file mode 100644
index 0000000..7f36218
--- /dev/null
+++ b/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/src/animator/SkBoundable.h b/src/animator/SkBoundable.h
new file mode 100644
index 0000000..1e70505
--- /dev/null
+++ b/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/src/animator/SkBuildCondensedInfo.cpp b/src/animator/SkBuildCondensedInfo.cpp
new file mode 100644
index 0000000..8fb82c7
--- /dev/null
+++ b/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/src/animator/SkCondensedDebug.cpp b/src/animator/SkCondensedDebug.cpp
new file mode 100644
index 0000000..08764b6
--- /dev/null
+++ b/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/src/animator/SkCondensedRelease.cpp b/src/animator/SkCondensedRelease.cpp
new file mode 100644
index 0000000..60fa991
--- /dev/null
+++ b/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/src/animator/SkDisplayAdd.cpp b/src/animator/SkDisplayAdd.cpp
new file mode 100644
index 0000000..7fd5026
--- /dev/null
+++ b/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/src/animator/SkDisplayAdd.h b/src/animator/SkDisplayAdd.h
new file mode 100644
index 0000000..5883963
--- /dev/null
+++ b/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/src/animator/SkDisplayApply.cpp b/src/animator/SkDisplayApply.cpp
new file mode 100644
index 0000000..baa10e7
--- /dev/null
+++ b/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/src/animator/SkDisplayApply.h b/src/animator/SkDisplayApply.h
new file mode 100644
index 0000000..3a066a4
--- /dev/null
+++ b/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/src/animator/SkDisplayBounds.cpp b/src/animator/SkDisplayBounds.cpp
new file mode 100644
index 0000000..4c89481
--- /dev/null
+++ b/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/src/animator/SkDisplayBounds.h b/src/animator/SkDisplayBounds.h
new file mode 100644
index 0000000..bc3a987
--- /dev/null
+++ b/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/src/animator/SkDisplayEvent.cpp b/src/animator/SkDisplayEvent.cpp
new file mode 100644
index 0000000..303e42c
--- /dev/null
+++ b/src/animator/SkDisplayEvent.cpp
@@ -0,0 +1,326 @@
+
+/*
+ * Copyright 2006 The Android 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) {
+        SkDebugf("x=\"%g\" y=\"%g\" ", SkScalarToFloat(x), SkScalarToFloat(y));
+    }
+    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/src/animator/SkDisplayEvent.h b/src/animator/SkDisplayEvent.h
new file mode 100644
index 0000000..ef8ec68
--- /dev/null
+++ b/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) SK_OVERRIDE;
+    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/src/animator/SkDisplayEvents.cpp b/src/animator/SkDisplayEvents.cpp
new file mode 100644
index 0000000..c42fbdf
--- /dev/null
+++ b/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/src/animator/SkDisplayEvents.h b/src/animator/SkDisplayEvents.h
new file mode 100644
index 0000000..2874ac5
--- /dev/null
+++ b/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/src/animator/SkDisplayInclude.cpp b/src/animator/SkDisplayInclude.cpp
new file mode 100644
index 0000000..860264e
--- /dev/null
+++ b/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/src/animator/SkDisplayInclude.h b/src/animator/SkDisplayInclude.h
new file mode 100644
index 0000000..d3fe4da
--- /dev/null
+++ b/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/src/animator/SkDisplayInput.cpp b/src/animator/SkDisplayInput.cpp
new file mode 100644
index 0000000..7061aa8
--- /dev/null
+++ b/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/src/animator/SkDisplayInput.h b/src/animator/SkDisplayInput.h
new file mode 100644
index 0000000..79d82b5
--- /dev/null
+++ b/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/src/animator/SkDisplayList.cpp b/src/animator/SkDisplayList.cpp
new file mode 100644
index 0000000..39465f1
--- /dev/null
+++ b/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/src/animator/SkDisplayList.h b/src/animator/SkDisplayList.h
new file mode 100644
index 0000000..af98aef
--- /dev/null
+++ b/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/src/animator/SkDisplayMath.cpp b/src/animator/SkDisplayMath.cpp
new file mode 100644
index 0000000..bdf377b
--- /dev/null
+++ b/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/src/animator/SkDisplayMath.h b/src/animator/SkDisplayMath.h
new file mode 100644
index 0000000..faa929e
--- /dev/null
+++ b/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/src/animator/SkDisplayMovie.cpp b/src/animator/SkDisplayMovie.cpp
new file mode 100644
index 0000000..33123cb
--- /dev/null
+++ b/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/src/animator/SkDisplayMovie.h b/src/animator/SkDisplayMovie.h
new file mode 100644
index 0000000..a599fb6
--- /dev/null
+++ b/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/src/animator/SkDisplayNumber.cpp b/src/animator/SkDisplayNumber.cpp
new file mode 100644
index 0000000..82b658f
--- /dev/null
+++ b/src/animator/SkDisplayNumber.cpp
@@ -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.
+ */
+
+
+#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);
+
+#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) {
+        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;
+}
+
+#if defined _WIN32
+#pragma warning ( pop )
+#endif
diff --git a/src/animator/SkDisplayNumber.h b/src/animator/SkDisplayNumber.h
new file mode 100644
index 0000000..16c6f4f
--- /dev/null
+++ b/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/src/animator/SkDisplayPost.cpp b/src/animator/SkDisplayPost.cpp
new file mode 100644
index 0000000..3c9fa19
--- /dev/null
+++ b/src/animator/SkDisplayPost.cpp
@@ -0,0 +1,299 @@
+
+/*
+ * Copyright 2006 The Android 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) {
+        SkDebugf("delay=\"%g\" ", SkScalarToFloat(SkScalarDiv(delay, 1000)));
+    }
+//  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);
+                SkDebugf("float=\"%g\" ", SkScalarToFloat(scalar));
+                } 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/src/animator/SkDisplayPost.h b/src/animator/SkDisplayPost.h
new file mode 100644
index 0000000..57ae31c
--- /dev/null
+++ b/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) SK_OVERRIDE;
+    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/src/animator/SkDisplayRandom.cpp b/src/animator/SkDisplayRandom.cpp
new file mode 100644
index 0000000..31a20b5
--- /dev/null
+++ b/src/animator/SkDisplayRandom.cpp
@@ -0,0 +1,66 @@
+
+/*
+ * Copyright 2006 The Android 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);
+    SkDebugf("min=\"%g\" ", SkScalarToFloat(min));
+    SkDebugf("max=\"%g\" ", SkScalarToFloat(max));
+    SkDebugf("blend=\"%g\" ", SkScalarToFloat(blend));
+    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/src/animator/SkDisplayRandom.h b/src/animator/SkDisplayRandom.h
new file mode 100644
index 0000000..87956d2
--- /dev/null
+++ b/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/src/animator/SkDisplayScreenplay.cpp b/src/animator/SkDisplayScreenplay.cpp
new file mode 100644
index 0000000..463f948
--- /dev/null
+++ b/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/src/animator/SkDisplayScreenplay.h b/src/animator/SkDisplayScreenplay.h
new file mode 100644
index 0000000..0265548
--- /dev/null
+++ b/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/src/animator/SkDisplayType.cpp b/src/animator/SkDisplayType.cpp
new file mode 100644
index 0000000..dc52f0c
--- /dev/null
+++ b/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(ImageBaseBitmap);
+        CASE_NEW(Include);
+        CASE_NEW(Input);
+        CASE_DISPLAY_NEW(Int);
+        CASE_DEBUG_RETURN_NIL(Join);
+        CASE_NEW(Line);
+        CASE_NEW(LineTo);
+        CASE_NEW(DrawLinearGradient);
+        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(DrawRadialGradient);
+        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(DrawGradient);
+        CASE_GET_INFO(Group);
+        CASE_GET_INFO(HitClear);
+        CASE_GET_INFO(HitTest);
+        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(DrawLinearGradient);
+        // 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(DrawRadialGradient);
+        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_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_DrawLinearGradient   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_DrawRadialGradient   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_ImageBaseBitmap:
+        case SkType_Input:
+        case SkType_Line:
+        case SkType_LineTo:
+        case SkType_DrawLinearGradient:
+        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_DrawRadialGradient:
+        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/src/animator/SkDisplayType.h b/src/animator/SkDisplayType.h
new file mode 100644
index 0000000..474a65e
--- /dev/null
+++ b/src/animator/SkDisplayType.h
@@ -0,0 +1,206 @@
+
+/*
+ * Copyright 2006 The Android 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
+    #define SK_DUMP_ENABLED
+    #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_DrawGradient,
+    SkType_Group,
+    SkType_HitClear,
+    SkType_HitTest,
+    SkType_ImageBaseBitmap,
+    SkType_Include,
+    SkType_Input,
+    SkType_Int,
+    SkType_Join,
+    SkType_Line, // simple line primitive
+    SkType_LineTo, // used as part of path construction
+    SkType_DrawLinearGradient,
+    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_DrawRadialGradient,
+    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/src/animator/SkDisplayTypes.cpp b/src/animator/SkDisplayTypes.cpp
new file mode 100644
index 0000000..d320fc0
--- /dev/null
+++ b/src/animator/SkDisplayTypes.cpp
@@ -0,0 +1,217 @@
+
+/*
+ * Copyright 2006 The Android 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);
+    SkDebugf("value=\"%g\" />\n", SkScalarToFloat(value));
+}
+#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/src/animator/SkDisplayTypes.h b/src/animator/SkDisplayTypes.h
new file mode 100644
index 0000000..614018f
--- /dev/null
+++ b/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/src/animator/SkDisplayXMLParser.cpp b/src/animator/SkDisplayXMLParser.cpp
new file mode 100644
index 0000000..31be3da
--- /dev/null
+++ b/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/src/animator/SkDisplayXMLParser.h b/src/animator/SkDisplayXMLParser.h
new file mode 100644
index 0000000..c18e48f
--- /dev/null
+++ b/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/src/animator/SkDisplayable.cpp b/src/animator/SkDisplayable.cpp
new file mode 100644
index 0000000..753764b
--- /dev/null
+++ b/src/animator/SkDisplayable.cpp
@@ -0,0 +1,542 @@
+
+/*
+ * Copyright 2006 The Android 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:
+                            SkDebugf("%g", SkScalarToFloat(op->fScalar));
+                            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) {
+            SkDebugf("%s=\"[%g,%g]\" ", info->fName, SkScalarToFloat(op.fScalar), SkScalarToFloat(op2.fScalar));
+        }
+        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?
+            SkDebugf("%s=\"%g\"  ", info->fName, SkScalarToFloat(op.fScalar));
+        }
+        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) {
+            SkDebugf(" %s=\"%g\"  ", info->fName, SkScalarToFloat(SkScalarDiv(op.fS32, 1000)));
+        }
+    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/src/animator/SkDisplayable.h b/src/animator/SkDisplayable.h
new file mode 100644
index 0000000..5fc2d3e
--- /dev/null
+++ b/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/src/animator/SkDraw3D.cpp b/src/animator/SkDraw3D.cpp
new file mode 100644
index 0000000..6ec178f
--- /dev/null
+++ b/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/src/animator/SkDraw3D.h b/src/animator/SkDraw3D.h
new file mode 100644
index 0000000..f4bd82b
--- /dev/null
+++ b/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/src/animator/SkDrawBitmap.cpp b/src/animator/SkDrawBitmap.cpp
new file mode 100644
index 0000000..4604a71
--- /dev/null
+++ b/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 SkImageBaseBitmap_Properties {
+    SK_PROPERTY(height),
+    SK_PROPERTY(width)
+};
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkImageBaseBitmap::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(SkImageBaseBitmap);
+
+SkImageBaseBitmap::SkImageBaseBitmap() : fDirty(true), fUriBase(NULL) {
+    base64.fData = NULL;
+    base64.fLength = 0;
+}
+
+SkImageBaseBitmap::~SkImageBaseBitmap() {
+    delete[] base64.fData;
+}
+
+SkDisplayable* SkImageBaseBitmap::deepCopy(SkAnimateMaker* maker) {
+    SkDisplayable* copy = INHERITED::deepCopy(maker);
+    ((SkImageBaseBitmap*) copy)->fUriBase = ((SkImageBaseBitmap*) this)->fUriBase;
+    return copy;
+}
+
+void SkImageBaseBitmap::dirty() {
+    fDirty = true;
+}
+
+bool SkImageBaseBitmap::draw(SkAnimateMaker& maker) {
+    if (fDirty)
+        resolve();
+    return INHERITED::draw(maker);
+}
+
+bool SkImageBaseBitmap::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 SkImageBaseBitmap::onEndElement(SkAnimateMaker& maker) {
+    fUriBase = maker.fPrefix.c_str();
+}
+
+void SkImageBaseBitmap::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/src/animator/SkDrawBitmap.h b/src/animator/SkDrawBitmap.h
new file mode 100644
index 0000000..f9a9212
--- /dev/null
+++ b/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 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<SkImageBaseBitmap*>(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/src/animator/SkDrawBlur.cpp b/src/animator/SkDrawBlur.cpp
new file mode 100644
index 0000000..68996bc
--- /dev/null
+++ b/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/src/animator/SkDrawBlur.h b/src/animator/SkDrawBlur.h
new file mode 100644
index 0000000..220e211
--- /dev/null
+++ b/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/src/animator/SkDrawClip.cpp b/src/animator/SkDrawClip.cpp
new file mode 100644
index 0000000..521bbc5
--- /dev/null
+++ b/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/src/animator/SkDrawClip.h b/src/animator/SkDrawClip.h
new file mode 100644
index 0000000..6265775
--- /dev/null
+++ b/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/src/animator/SkDrawColor.cpp b/src/animator/SkDrawColor.cpp
new file mode 100644
index 0000000..dfe0622
--- /dev/null
+++ b/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_DrawLinearGradient || parent->getType() == SkType_DrawRadialGradient)
+        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/src/animator/SkDrawColor.h b/src/animator/SkDrawColor.h
new file mode 100644
index 0000000..281af0f
--- /dev/null
+++ b/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 SkDrawGradient;
+    typedef SkPaintPart INHERITED;
+};
+
+#endif // SkDrawColor_DEFINED
diff --git a/src/animator/SkDrawDash.cpp b/src/animator/SkDrawDash.cpp
new file mode 100644
index 0000000..0d93293
--- /dev/null
+++ b/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/src/animator/SkDrawDash.h b/src/animator/SkDrawDash.h
new file mode 100644
index 0000000..483d2a3
--- /dev/null
+++ b/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/src/animator/SkDrawDiscrete.cpp b/src/animator/SkDrawDiscrete.cpp
new file mode 100644
index 0000000..655c83d
--- /dev/null
+++ b/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/src/animator/SkDrawDiscrete.h b/src/animator/SkDrawDiscrete.h
new file mode 100644
index 0000000..bd33d2f
--- /dev/null
+++ b/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/src/animator/SkDrawEmboss.cpp b/src/animator/SkDrawEmboss.cpp
new file mode 100644
index 0000000..7e47ec2
--- /dev/null
+++ b/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/src/animator/SkDrawEmboss.h b/src/animator/SkDrawEmboss.h
new file mode 100644
index 0000000..50ce71a
--- /dev/null
+++ b/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/src/animator/SkDrawExtraPathEffect.cpp b/src/animator/SkDrawExtraPathEffect.cpp
new file mode 100644
index 0000000..65d9cba
--- /dev/null
+++ b/src/animator/SkDrawExtraPathEffect.cpp
@@ -0,0 +1,510 @@
+
+/*
+ * Copyright 2006 The Android 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* ) SK_OVERRIDE;
+    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* ) SK_OVERRIDE;
+    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) {
+    }
+
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
+
+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:
+    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/src/animator/SkDrawFull.cpp b/src/animator/SkDrawFull.cpp
new file mode 100644
index 0000000..762b3da
--- /dev/null
+++ b/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/src/animator/SkDrawFull.h b/src/animator/SkDrawFull.h
new file mode 100644
index 0000000..a2dcf49
--- /dev/null
+++ b/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/src/animator/SkDrawGradient.cpp b/src/animator/SkDrawGradient.cpp
new file mode 100644
index 0000000..2e751ca
--- /dev/null
+++ b/src/animator/SkDrawGradient.cpp
@@ -0,0 +1,226 @@
+
+/*
+ * Copyright 2006 The Android 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"
+
+static SkScalar SkUnitToScalar(U16CPU x) {
+#ifdef SK_SCALAR_IS_FLOAT
+    return x / 65535.0f;
+#else
+    return x + (x >> 8);
+#endif
+}
+
+static 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 SkDrawGradientUnitMapper : public SkUnitMapper {
+public:
+    SkDrawGradientUnitMapper(SkAnimateMaker* maker, const char* script) : fMaker(maker), fScript(script) {
+    }
+
+    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))
+            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 SkDrawGradient::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER_ARRAY(offsets, Float),
+    SK_MEMBER(unitMapper, String)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawGradient);
+
+SkDrawGradient::SkDrawGradient() : fUnitMapper(NULL) {
+}
+
+SkDrawGradient::~SkDrawGradient() {
+    for (int index = 0; index < fDrawColors.count(); index++)
+        delete fDrawColors[index];
+    delete fUnitMapper;
+}
+
+bool SkDrawGradient::add(SkAnimateMaker& , SkDisplayable* child) {
+    SkASSERT(child);
+    if (child->isColor()) {
+        SkDrawColor* color = (SkDrawColor*) child;
+        *fDrawColors.append() = color;
+        return true;
+    }
+    return false;
+}
+
+int SkDrawGradient::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 SkDrawGradient::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 SkDrawGradient::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 SkDrawGradientUnitMapper(&maker, unitMapper.c_str());
+    INHERITED::onEndElement(maker);
+}
+
+#if SK_USE_CONDENSED_INFO == 0
+
+const SkMemberInfo SkDrawLinearGradient::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER_ARRAY(points, Float),
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawLinearGradient);
+
+SkDrawLinearGradient::SkDrawLinearGradient() {
+}
+
+void SkDrawLinearGradient::onEndElement(SkAnimateMaker& maker)
+{
+    if (points.count() != 4)
+        maker.setErrorCode(SkDisplayXMLParserError::kGradientPointsLengthMustBeFour);
+    INHERITED::onEndElement(maker);
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawLinearGradient::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpRest(maker);
+    }
+#endif
+
+SkShader* SkDrawLinearGradient::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 SkDrawRadialGradient::fInfo[] = {
+    SK_MEMBER_INHERITED,
+    SK_MEMBER(center, Point),
+    SK_MEMBER(radius, Float)
+};
+
+#endif
+
+DEFINE_GET_MEMBER(SkDrawRadialGradient);
+
+SkDrawRadialGradient::SkDrawRadialGradient() : radius(0) {
+    center.set(0, 0);
+}
+
+#ifdef SK_DUMP_ENABLED
+void SkDrawRadialGradient::dump(SkAnimateMaker* maker) {
+    dumpBase(maker);
+    dumpRest(maker);
+}
+#endif
+
+SkShader* SkDrawRadialGradient::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/src/animator/SkDrawGradient.h b/src/animator/SkDrawGradient.h
new file mode 100644
index 0000000..d7fc694
--- /dev/null
+++ b/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 SkDrawGradient : public SkDrawShader {
+    DECLARE_PRIVATE_MEMBER_INFO(DrawGradient);
+    SkDrawGradient();
+    virtual ~SkDrawGradient();
+    virtual bool add(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
+#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 SkDrawLinearGradient : public SkDrawGradient {
+    DECLARE_MEMBER_INFO(DrawLinearGradient);
+    SkDrawLinearGradient();
+    virtual void onEndElement(SkAnimateMaker& );
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker*);
+#endif
+    virtual SkShader* getShader();
+protected:
+    SkTDScalarArray points;
+private:
+    typedef SkDrawGradient INHERITED;
+};
+
+class SkDrawRadialGradient : public SkDrawGradient {
+    DECLARE_MEMBER_INFO(DrawRadialGradient);
+    SkDrawRadialGradient();
+#ifdef SK_DUMP_ENABLED
+    virtual void dump(SkAnimateMaker*);
+#endif
+    virtual SkShader* getShader();
+protected:
+    SkPoint center;
+    SkScalar radius;
+private:
+    typedef SkDrawGradient INHERITED;
+};
+
+#endif // SkDrawGradient_DEFINED
+
diff --git a/src/animator/SkDrawGroup.cpp b/src/animator/SkDrawGroup.cpp
new file mode 100644
index 0000000..806da5e
--- /dev/null
+++ b/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/src/animator/SkDrawGroup.h b/src/animator/SkDrawGroup.h
new file mode 100644
index 0000000..a63a50e
--- /dev/null
+++ b/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) SK_OVERRIDE;
+    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/src/animator/SkDrawLine.cpp b/src/animator/SkDrawLine.cpp
new file mode 100644
index 0000000..d0ae7d9
--- /dev/null
+++ b/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/src/animator/SkDrawLine.h b/src/animator/SkDrawLine.h
new file mode 100644
index 0000000..e90c997
--- /dev/null
+++ b/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/src/animator/SkDrawMatrix.cpp b/src/animator/SkDrawMatrix.cpp
new file mode 100644
index 0000000..1d7b3e0
--- /dev/null
+++ b/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/src/animator/SkDrawMatrix.h b/src/animator/SkDrawMatrix.h
new file mode 100644
index 0000000..cb781e7
--- /dev/null
+++ b/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) SK_OVERRIDE;
+    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/src/animator/SkDrawOval.cpp b/src/animator/SkDrawOval.cpp
new file mode 100644
index 0000000..ab0fc81
--- /dev/null
+++ b/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/src/animator/SkDrawOval.h b/src/animator/SkDrawOval.h
new file mode 100644
index 0000000..afdc252
--- /dev/null
+++ b/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/src/animator/SkDrawPaint.cpp b/src/animator/SkDrawPaint.cpp
new file mode 100644
index 0000000..4d1bd8b
--- /dev/null
+++ b/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/src/animator/SkDrawPaint.h b/src/animator/SkDrawPaint.h
new file mode 100644
index 0000000..db95d34
--- /dev/null
+++ b/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/src/animator/SkDrawPath.cpp b/src/animator/SkDrawPath.cpp
new file mode 100644
index 0000000..858db54
--- /dev/null
+++ b/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*) {
+    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/src/animator/SkDrawPath.h b/src/animator/SkDrawPath.h
new file mode 100644
index 0000000..76e2e7e
--- /dev/null
+++ b/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) SK_OVERRIDE;
+    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*) SK_OVERRIDE;
+    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/src/animator/SkDrawPoint.cpp b/src/animator/SkDrawPoint.cpp
new file mode 100644
index 0000000..66d2b4e
--- /dev/null
+++ b/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/src/animator/SkDrawPoint.h b/src/animator/SkDrawPoint.h
new file mode 100644
index 0000000..0ecf447
--- /dev/null
+++ b/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/src/animator/SkDrawRectangle.cpp b/src/animator/SkDrawRectangle.cpp
new file mode 100644
index 0000000..7587e4b
--- /dev/null
+++ b/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/src/animator/SkDrawRectangle.h b/src/animator/SkDrawRectangle.h
new file mode 100644
index 0000000..321c4ee
--- /dev/null
+++ b/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/src/animator/SkDrawSaveLayer.cpp b/src/animator/SkDrawSaveLayer.cpp
new file mode 100644
index 0000000..43ec5c1
--- /dev/null
+++ b/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/src/animator/SkDrawSaveLayer.h b/src/animator/SkDrawSaveLayer.h
new file mode 100644
index 0000000..5c3e068
--- /dev/null
+++ b/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/src/animator/SkDrawShader.cpp b/src/animator/SkDrawShader.cpp
new file mode 100644
index 0000000..e3aa4da
--- /dev/null
+++ b/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/src/animator/SkDrawShader.h b/src/animator/SkDrawShader.h
new file mode 100644
index 0000000..b6a487a
--- /dev/null
+++ b/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/src/animator/SkDrawText.cpp b/src/animator/SkDrawText.cpp
new file mode 100644
index 0000000..b8d38b4
--- /dev/null
+++ b/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/src/animator/SkDrawText.h b/src/animator/SkDrawText.h
new file mode 100644
index 0000000..3ac2479
--- /dev/null
+++ b/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/src/animator/SkDrawTextBox.cpp b/src/animator/SkDrawTextBox.cpp
new file mode 100644
index 0000000..f920e8e
--- /dev/null
+++ b/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/src/animator/SkDrawTextBox.h b/src/animator/SkDrawTextBox.h
new file mode 100644
index 0000000..d8d7f0c
--- /dev/null
+++ b/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/src/animator/SkDrawTo.cpp b/src/animator/SkDrawTo.cpp
new file mode 100644
index 0000000..bc5cd6d
--- /dev/null
+++ b/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/src/animator/SkDrawTo.h b/src/animator/SkDrawTo.h
new file mode 100644
index 0000000..b6365af
--- /dev/null
+++ b/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/src/animator/SkDrawTransparentShader.cpp b/src/animator/SkDrawTransparentShader.cpp
new file mode 100644
index 0000000..bb5392a
--- /dev/null
+++ b/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/src/animator/SkDrawTransparentShader.h b/src/animator/SkDrawTransparentShader.h
new file mode 100644
index 0000000..df1c6eb
--- /dev/null
+++ b/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/src/animator/SkDrawable.cpp b/src/animator/SkDrawable.cpp
new file mode 100644
index 0000000..9e80c9d
--- /dev/null
+++ b/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/src/animator/SkDrawable.h b/src/animator/SkDrawable.h
new file mode 100644
index 0000000..6bb9608
--- /dev/null
+++ b/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/src/animator/SkDump.cpp b/src/animator/SkDump.cpp
new file mode 100644
index 0000000..563b0e1
--- /dev/null
+++ b/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/src/animator/SkDump.h b/src/animator/SkDump.h
new file mode 100644
index 0000000..3222f06
--- /dev/null
+++ b/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/src/animator/SkExtraPathEffects.xsd b/src/animator/SkExtraPathEffects.xsd
new file mode 100644
index 0000000..9592443
--- /dev/null
+++ b/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/src/animator/SkExtras.h b/src/animator/SkExtras.h
new file mode 100644
index 0000000..dcd3905
--- /dev/null
+++ b/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/src/animator/SkGetCondensedInfo.cpp b/src/animator/SkGetCondensedInfo.cpp
new file mode 100644
index 0000000..6d83b1d
--- /dev/null
+++ b/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/src/animator/SkHitClear.cpp b/src/animator/SkHitClear.cpp
new file mode 100644
index 0000000..70b3e73
--- /dev/null
+++ b/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/src/animator/SkHitClear.h b/src/animator/SkHitClear.h
new file mode 100644
index 0000000..9c40209
--- /dev/null
+++ b/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/src/animator/SkHitTest.cpp b/src/animator/SkHitTest.cpp
new file mode 100644
index 0000000..dc4e739
--- /dev/null
+++ b/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/src/animator/SkHitTest.h b/src/animator/SkHitTest.h
new file mode 100644
index 0000000..68d5cc5
--- /dev/null
+++ b/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/src/animator/SkIntArray.h b/src/animator/SkIntArray.h
new file mode 100644
index 0000000..4dac75a
--- /dev/null
+++ b/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/src/animator/SkMatrixParts.cpp b/src/animator/SkMatrixParts.cpp
new file mode 100644
index 0000000..221ac0a
--- /dev/null
+++ b/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/src/animator/SkMatrixParts.h b/src/animator/SkMatrixParts.h
new file mode 100644
index 0000000..51c9559
--- /dev/null
+++ b/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/src/animator/SkMemberInfo.cpp b/src/animator/SkMemberInfo.cpp
new file mode 100644
index 0000000..582b29a
--- /dev/null
+++ b/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/src/animator/SkMemberInfo.h b/src/animator/SkMemberInfo.h
new file mode 100644
index 0000000..007df60
--- /dev/null
+++ b/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/src/animator/SkOpArray.cpp b/src/animator/SkOpArray.cpp
new file mode 100644
index 0000000..94298cc
--- /dev/null
+++ b/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/src/animator/SkOpArray.h b/src/animator/SkOpArray.h
new file mode 100644
index 0000000..260bf78
--- /dev/null
+++ b/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/src/animator/SkOperand.h b/src/animator/SkOperand.h
new file mode 100644
index 0000000..0bd1fa3
--- /dev/null
+++ b/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/src/animator/SkOperand2.h b/src/animator/SkOperand2.h
new file mode 100644
index 0000000..f844b6b
--- /dev/null
+++ b/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/src/animator/SkOperandInterpolator.h b/src/animator/SkOperandInterpolator.h
new file mode 100644
index 0000000..82fa272
--- /dev/null
+++ b/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/src/animator/SkOperandIterpolator.cpp b/src/animator/SkOperandIterpolator.cpp
new file mode 100644
index 0000000..bc7d46b
--- /dev/null
+++ b/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/src/animator/SkPaintParts.cpp b/src/animator/SkPaintParts.cpp
new file mode 100644
index 0000000..2959238
--- /dev/null
+++ b/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/src/animator/SkPaintParts.h b/src/animator/SkPaintParts.h
new file mode 100644
index 0000000..964bc35
--- /dev/null
+++ b/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/src/animator/SkParseSVGPath.cpp b/src/animator/SkParseSVGPath.cpp
new file mode 100644
index 0000000..f020e2e
--- /dev/null
+++ b/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/src/animator/SkPathParts.cpp b/src/animator/SkPathParts.cpp
new file mode 100644
index 0000000..3060bd4
--- /dev/null
+++ b/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/src/animator/SkPathParts.h b/src/animator/SkPathParts.h
new file mode 100644
index 0000000..cc81cdd
--- /dev/null
+++ b/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/src/animator/SkPostParts.cpp b/src/animator/SkPostParts.cpp
new file mode 100644
index 0000000..b6549a2
--- /dev/null
+++ b/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/src/animator/SkPostParts.h b/src/animator/SkPostParts.h
new file mode 100644
index 0000000..4101b2b
--- /dev/null
+++ b/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/src/animator/SkScript.cpp b/src/animator/SkScript.cpp
new file mode 100644
index 0000000..92e8f3c
--- /dev/null
+++ b/src/animator/SkScript.cpp
@@ -0,0 +1,1896 @@
+
+/*
+ * Copyright 2006 The Android 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
+
+#include "SkFloatingPoint.h"
+
+#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
+    #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 }
+
+static const SkScriptNAnswer scriptTests[]  = {
+    testInt(1>1/2),
+    testInt((6+7)*8),
+    testInt(0&&1?2:3),
+    testInt(3*(4+5)),
+    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),
+    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),
+    // 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.),
+    // 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.'),
+    // 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.),
+    // 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)
+    , { "123.5", SkType_Float, 0, SkIntToScalar(123) + SK_Scalar1/2, DEF_STRING_ANSWER }
+};
+
+#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/src/animator/SkScript.h b/src/animator/SkScript.h
new file mode 100644
index 0000000..aa8d9a3
--- /dev/null
+++ b/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/src/animator/SkScript2.h b/src/animator/SkScript2.h
new file mode 100644
index 0000000..03eb92c
--- /dev/null
+++ b/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/src/animator/SkScriptCallBack.h b/src/animator/SkScriptCallBack.h
new file mode 100644
index 0000000..dcbaf11
--- /dev/null
+++ b/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/src/animator/SkScriptDecompile.cpp b/src/animator/SkScriptDecompile.cpp
new file mode 100644
index 0000000..995da87
--- /dev/null
+++ b/src/animator/SkScriptDecompile.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 "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);
+            SkDebugf(" scalar: %g", SkScalarToFloat(scalar));
+            } 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/src/animator/SkScriptRuntime.cpp b/src/animator/SkScriptRuntime.cpp
new file mode 100644
index 0000000..b211a71
--- /dev/null
+++ b/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/src/animator/SkScriptRuntime.h b/src/animator/SkScriptRuntime.h
new file mode 100644
index 0000000..3e73801
--- /dev/null
+++ b/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
diff --git a/src/animator/SkScriptTokenizer.cpp b/src/animator/SkScriptTokenizer.cpp
new file mode 100644
index 0000000..a1de5e5
--- /dev/null
+++ b/src/animator/SkScriptTokenizer.cpp
@@ -0,0 +1,1511 @@
+
+/*
+ * Copyright 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((float) exp1, (float) exp2), NULL }
+#else
+#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
+#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)),
+    testScalar(- -5.5- -1.5),
+    testScalar(1.0+5),
+    testInt((6+7)*8),
+    testInt(3*(4+5)),
+    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),
+    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),
+    // 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.),
+    // 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.'),
+    // 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.),
+    // 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)
+    , {    "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2, NULL }
+};
+
+#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);
+                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)));
+                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/SkSnapshot.cpp b/src/animator/SkSnapshot.cpp
new file mode 100644
index 0000000..4e78bbd
--- /dev/null
+++ b/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/src/animator/SkSnapshot.h b/src/animator/SkSnapshot.h
new file mode 100644
index 0000000..beb26ca
--- /dev/null
+++ b/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/src/animator/SkTDArray_Experimental.h b/src/animator/SkTDArray_Experimental.h
new file mode 100644
index 0000000..094d106
--- /dev/null
+++ b/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/src/animator/SkTextOnPath.cpp b/src/animator/SkTextOnPath.cpp
new file mode 100644
index 0000000..7bdb7fd
--- /dev/null
+++ b/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/src/animator/SkTextOnPath.h b/src/animator/SkTextOnPath.h
new file mode 100644
index 0000000..b0ce234
--- /dev/null
+++ b/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/src/animator/SkTextToPath.cpp b/src/animator/SkTextToPath.cpp
new file mode 100644
index 0000000..b38daff
--- /dev/null
+++ b/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/src/animator/SkTextToPath.h b/src/animator/SkTextToPath.h
new file mode 100644
index 0000000..cb2a15e
--- /dev/null
+++ b/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/src/animator/SkTime.cpp b/src/animator/SkTime.cpp
new file mode 100644
index 0000000..06ad9bd
--- /dev/null
+++ b/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/src/animator/SkTypedArray.cpp b/src/animator/SkTypedArray.cpp
new file mode 100644
index 0000000..e94e57d
--- /dev/null
+++ b/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/src/animator/SkTypedArray.h b/src/animator/SkTypedArray.h
new file mode 100644
index 0000000..e93b106
--- /dev/null
+++ b/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/src/animator/SkXMLAnimatorWriter.cpp b/src/animator/SkXMLAnimatorWriter.cpp
new file mode 100644
index 0000000..6d299b6
--- /dev/null
+++ b/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/src/animator/SkXMLAnimatorWriter.h b/src/animator/SkXMLAnimatorWriter.h
new file mode 100644
index 0000000..bd6ccf4
--- /dev/null
+++ b/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/src/animator/thingstodo.txt b/src/animator/thingstodo.txt
new file mode 100644
index 0000000..8d0d47a
--- /dev/null
+++ b/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/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h b/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
new file mode 100644
index 0000000..720b479
--- /dev/null
+++ b/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/src/core/Sk64.cpp b/src/core/Sk64.cpp
new file mode 100644
index 0000000..c530ed8
--- /dev/null
+++ b/src/core/Sk64.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2006 The Android 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 "SkMathPriv.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/src/core/SkAAClip.cpp b/src/core/SkAAClip.cpp
new file mode 100644
index 0000000..fce6683
--- /dev/null
+++ b/src/core/SkAAClip.cpp
@@ -0,0 +1,2179 @@
+
+/*
+ * Copyright 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;
+        fStopYOff = 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
+
+///////////////////////////////////////////////////////////////////////////////
+
+// 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;
+    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;
+
+    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];
+        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, 10,    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();
+
+    // 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;
+        }
+        if (R < riteZeros) {
+            riteZeros = R;
+        }
+        if (0 == (leftZeros | riteZeros)) {
+            // no trimming to do
+            return true;
+        }
+        yoff += 1;
+    }
+
+    SkASSERT(leftZeros || riteZeros);
+    if (width == leftZeros) {
+        SkASSERT(width == 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) {
+            if (initialCount) {
+                *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 SK_INIT_TO_AVOID_WARNING;
+    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
+        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];
+            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);
+
+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();
+    }
+}
+
+#if 0 // UNUSED
+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;
+}
+#endif
+
+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)) {
+                if (SkRegion::kIntersect_Op == op) {
+                    return this->setEmpty();
+                } else {    // kDifference
+                    return !this->isEmpty();
+                }
+            }
+            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->freeRuns();
+        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));
+
+    const uint8_t* row = fAAClip->findRow(y);
+    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[]) {
+
+    const uint8_t* row = fAAClip->findRow(y);
+    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 SK_INIT_TO_AVOID_WARNING;
+        const uint8_t* row = fAAClip->findRow(y, &lastY);
+        int dy = lastY - y + 1;
+        if (dy > height) {
+            dy = height;
+        }
+        height -= dy;
+
+        row = fAAClip->findX(row, x);
+        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(g, alpha),
+                       SkMulDiv255Round(b, 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) {
+    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 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);
+
+        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/src/core/SkAAClip.h b/src/core/SkAAClip.h
new file mode 100644
index 0000000..f2cde62
--- /dev/null
+++ b/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 = NULL) const;
+    const uint8_t* findX(const uint8_t data[], int x, int* initialCount = NULL) 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/src/core/SkAdvancedTypefaceMetrics.cpp b/src/core/SkAdvancedTypefaceMetrics.cpp
new file mode 100644
index 0000000..370616e
--- /dev/null
+++ b/src/core/SkAdvancedTypefaceMetrics.cpp
@@ -0,0 +1,319 @@
+
+/*
+ * Copyright 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"
+
+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
+#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));
+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,
+        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);
+
+// 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
new file mode 100644
index 0000000..116d132
--- /dev/null
+++ b/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/src/core/SkAnnotation.cpp b/src/core/SkAnnotation.cpp
new file mode 100644
index 0000000..8176761
--- /dev/null
+++ b/src/core/SkAnnotation.cpp
@@ -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.
+ */
+
+#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
new file mode 100644
index 0000000..31b52e3
--- /dev/null
+++ b/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/src/core/SkAutoKern.h b/src/core/SkAutoKern.h
new file mode 100644
index 0000000..0a08432
--- /dev/null
+++ b/src/core/SkAutoKern.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 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..7d061b5
--- /dev/null
+++ b/src/core/SkBBoxHierarchy.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 "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..6625a84
--- /dev/null
+++ b/src/core/SkBBoxHierarchy.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 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..c27940a
--- /dev/null
+++ b/src/core/SkBBoxHierarchyRecord.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 "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);
+}
+
diff --git a/src/core/SkBBoxHierarchyRecord.h b/src/core/SkBBoxHierarchyRecord.h
new file mode 100644
index 0000000..4741338
--- /dev/null
+++ b/src/core/SkBBoxHierarchyRecord.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 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;
+
+private:
+    typedef SkBBoxRecord INHERITED;
+};
+
+#endif
+
diff --git a/src/core/SkBBoxRecord.cpp b/src/core/SkBBoxRecord.cpp
new file mode 100644
index 0000000..5db77f5
--- /dev/null
+++ b/src/core/SkBBoxRecord.cpp
@@ -0,0 +1,253 @@
+
+/*
+ * Copyright 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::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 (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);
+    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 = {SkIntToScalar(left), SkIntToScalar(top), SkIntToScalar(left + bitmap.width()), SkIntToScalar(top + bitmap.height())};
+    this->handleBBox(bbox); // directly call handleBBox, matrix is ignored
+    INHERITED::drawBitmap(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;
+
+    if (paint) {
+        // account for stroking, path effects, shadows, etc
+        if (paint->canComputeFastBounds()) {
+            SkRect temp;
+            outBounds = paint->computeFastBounds(bounds, &temp);
+        } else {
+            // set bounds to current clip
+            if (!this->getClipBounds(&outBounds)) {
+                // current clip is empty
+                return false;
+            }
+        }
+    }
+
+    SkRect clip;
+
+    if (this->getClipBounds(&clip) && outBounds.intersect(clip)) {
+        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..e6b85db
--- /dev/null
+++ b/src/core/SkBBoxRecord.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 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 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
new file mode 100644
index 0000000..61a4c8f
--- /dev/null
+++ b/src/core/SkBitmap.cpp
@@ -0,0 +1,1466 @@
+
+/*
+ * 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 "SkOrderedReadBuffer.h"
+#include "SkOrderedWriteBuffer.h"
+#include "SkPixelRef.h"
+#include "SkThread.h"
+#include "SkUnPreMultiply.h"
+#include "SkUtils.h"
+#include "SkPackBits.h"
+#include <new>
+
+SK_DEFINE_INST_COUNT(SkBitmap::Allocator)
+
+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(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::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();
+
+    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 {
+    return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
+}
+
+void SkBitmap::setPixels(void* p, SkColorTable* 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();)
+}
+
+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 {
+    return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
+}
+
+void SkBitmap::notifyPixelsChanged() const {
+    SkASSERT(!this->isImmutable());
+    if (fPixelRef) {
+        fPixelRef->notifyPixelsChanged();
+    }
+}
+
+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) {
+        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());
+#ifndef SK_DISABLE_EXTRACTSUBSET_OPAQUE_FIX
+    dst.setIsOpaque(this->isOpaque());
+#endif
+
+    if (fPixelRef) {
+        // share the pixelref with a custom offset
+        dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
+    }
+    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);
+            if (dst->pixelRef()) {
+                dst->pixelRef()->fGenerationID = fPixelRef->getGenerationID();
+            }
+            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;
+    }
+
+    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());
+            // 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) {
+            if (dstConfig == fConfig) {
+                pixelRef->fGenerationID = fPixelRef->getGenerationID();
+            }
+            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;
+    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);
+
+        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;
+    }
+    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_REF_DATA
+};
+
+void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.writeInt(fWidth);
+    buffer.writeInt(fHeight);
+    buffer.writeInt(fRowBytes);
+    buffer.writeInt(fConfig);
+    buffer.writeBool(this->isOpaque());
+
+    if (fPixelRef) {
+        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.writeInt(SERIALIZE_PIXELTYPE_NONE);
+    } else {
+        buffer.writeInt(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.readInt();
+
+    this->setConfig((Config)config, width, height, rowBytes);
+    this->setIsOpaque(buffer.readBool());
+
+    int reftype = buffer.readInt();
+    switch (reftype) {
+        case SERIALIZE_PIXELTYPE_REF_DATA: {
+            size_t offset = buffer.readUInt();
+            SkPixelRef* pr = buffer.readFlattenableT<SkPixelRef>();
+            SkSafeUnref(this->setPixelRef(pr, offset));
+            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 | kImageIsImmutable_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/src/core/SkBitmapHeap.cpp b/src/core/SkBitmapHeap.cpp
new file mode 100644
index 0000000..936951c
--- /dev/null
+++ b/src/core/SkBitmapHeap.cpp
@@ -0,0 +1,399 @@
+
+/*
+ * Copyright 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) {
+        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..badbc9c
--- /dev/null
+++ b/src/core/SkBitmapHeap.h
@@ -0,0 +1,306 @@
+
+/*
+ * Copyright 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;
+};
+
+
+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
new file mode 100644
index 0000000..66d9d76
--- /dev/null
+++ b/src/core/SkBitmapProcShader.cpp
@@ -0,0 +1,345 @@
+
+/*
+ * Copyright 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 "SkFlattenableBuffers.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) {
+    buffer.readBitmap(&fRawBitmap);
+    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[]) 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) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.writeBitmap(fRawBitmap);
+    buffer.writeUInt(fState.fTileModeX);
+    buffer.writeUInt(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.getShaderProc32()) {
+        state.getShaderProc32()(state, x, y, dstC, count);
+        return;
+    }
+
+    uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA];
+    SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
+    SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32();
+    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;
+    }
+}
+
+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.getShaderProc16()) {
+        state.getShaderProc16()(state, x, y, dstC, count);
+        return;
+    }
+
+    uint32_t buffer[BUF_MAX];
+    SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
+    SkBitmapProcState::SampleProc16 sproc = state.getSampleProc16();
+    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"
+
+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() || bitmapIsTooBig(src)) {
+        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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h
new file mode 100644
index 0000000..23e0992
--- /dev/null
+++ b/src/core/SkBitmapProcShader.h
@@ -0,0 +1,49 @@
+
+/*
+ * Copyright 2006 The Android 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 ShadeProc asAShadeProc(void** ctx) SK_OVERRIDE;
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
+    virtual void beginSession();
+    virtual void endSession();
+    virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const;
+
+    static bool CanDo(const SkBitmap&, TileMode tx, TileMode ty);
+
+    // override from flattenable
+    virtual bool toDumpString(SkString* str) const;
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBitmapProcShader)
+
+protected:
+    SkBitmapProcShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    SkBitmap          fRawBitmap;   // experimental for RLE encoding
+    SkBitmapProcState fState;
+    uint32_t          fFlags;
+
+private:
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
new file mode 100644
index 0000000..283a1ec
--- /dev/null
+++ b/src/core/SkBitmapProcState.cpp
@@ -0,0 +1,437 @@
+
+/*
+ * Copyright 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 "SkColorPriv.h"
+#include "SkFilterProc.h"
+#include "SkPaint.h"
+#include "SkShader.h"   // for tilemodes
+#include "SkUtilsArm.h"
+
+#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
+
+#if !SK_ARM_NEON_IS_ALWAYS
+#define   NAME_WRAP(x)  x
+#include "SkBitmapProcState_filter.h"
+#include "SkBitmapProcState_procs.h"
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+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());
+    fInvSxFractionalInt = SkScalarToFractionalInt(m->getScaleX());
+    fInvKy          = SkScalarToFixed(m->getSkewY());
+    fInvKyFractionalInt = SkScalarToFractionalInt(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;
+    }
+
+#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,
+        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 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 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
+    index >>= 1;    // shift away any opaque/alpha distinction
+    fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
+
+    // our special-case shaderprocs
+    if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
+        if (clamp_clamp) {
+            fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
+        } else if (SkShader::kRepeat_TileMode == fTileModeX &&
+                   SkShader::kRepeat_TileMode == fTileModeY) {
+            fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_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
+    this->platformProcs();
+    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);
+}
+
+SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
+    if (fAlphaScale < 256) {
+        return NULL;
+    }
+    if (fInvType > SkMatrix::kTranslate_Mask) {
+        return NULL;
+    }
+    if (fDoFilter) {
+        return NULL;
+    }
+    if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
+        return NULL;
+    }
+
+    if (SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY) {
+        SkPoint pt;
+        fInvProc(*fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
+        // 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 Clamp_S32_D32_nofilter_trans_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)
+    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/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h
new file mode 100644
index 0000000..d9ac5c7
--- /dev/null
+++ b/src/core/SkBitmapProcState.h
@@ -0,0 +1,193 @@
+
+/*
+ * 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"
+
+#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 {
+
+    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 (*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;
+
+    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;
+
+    // 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();
+
+#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.
+    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 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[],
+                                  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);
+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
new file mode 100644
index 0000000..f7848f7
--- /dev/null
+++ b/src/core/SkBitmapProcState_filter.h
@@ -0,0 +1,81 @@
+
+/*
+ * 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).
+ */
+
+static inline void Filter_32_opaque(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(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);
+}
+
diff --git a/src/core/SkBitmapProcState_matrix.h b/src/core/SkBitmapProcState_matrix.h
new file mode 100644
index 0000000..cc65c2d
--- /dev/null
+++ b/src/core/SkBitmapProcState_matrix.h
@@ -0,0 +1,303 @@
+
+/*
+ * Copyright 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"
+#include "SkMathPriv.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
+
+// 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 |
+                             SkMatrix::kScale_Mask)) == 0);
+
+    PREAMBLE(s);
+    // we store y, x, x, x, x, x
+
+    const unsigned maxX = s.fBitmap->width() - 1;
+    SkFractionalInt fx;
+    {
+        SkPoint pt;
+        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
+        fx = SkScalarToFractionalInt(pt.fY);
+        const unsigned maxY = s.fBitmap->height() - 1;
+        *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 SkFractionalInt dx = s.fInvSxFractionalInt;
+
+#ifdef CHECK_FOR_DECAL
+    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(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(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
+        }
+        uint16_t* xx = (uint16_t*)xy;
+        for (i = (count & 3); i > 0; --i) {
+            *xx++ = TILEX_PROCF(SkFractionalIntToFixed(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);
+
+    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(SkFractionalIntToFixed(fy), maxY) << 16) |
+                 TILEX_PROCF(SkFractionalIntToFixed(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 SkFractionalInt dx = s.fInvSxFractionalInt;
+    SkFractionalInt 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 = SkScalarToFractionalInt(pt.fX) - (SkFixedToFractionalInt(one) >> 1);
+    }
+
+#ifdef CHECK_FOR_DECAL
+    if (can_truncate_to_fixed_for_decal(fx, dx, count, maxX)) {
+        decal_filter_scale(xy, SkFractionalIntToFixed(fx),
+                           SkFractionalIntToFixed(dx), count);
+    } else
+#endif
+    {
+        do {
+            SkFixed fixedFx = SkFractionalIntToFixed(fx);
+            *xy++ = PACK_FILTER_X_NAME(fixedFx, 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/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp
new file mode 100644
index 0000000..9fa3cd6
--- /dev/null
+++ b/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -0,0 +1,522 @@
+/* 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"
+#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
+ */
+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;
+}
+
+/*
+ *  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
+#include "SkBitmapProcState_matrix.h"
+
+#define MAKENAME(suffix)        RepeatX_RepeatY ## suffix
+#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.h"
+#endif
+
+#define MAKENAME(suffix)        GeneralXY ## suffix
+#define PREAMBLE(state)         SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; \
+                                SkBitmapProcState::FixedTileProc tileProcY = (state).fTileProcY; \
+                                SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcX = (state).fTileLowBitsProcX; \
+                                SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcY = (state).fTileLowBitsProcY
+#define PREAMBLE_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 < 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;
+}
+
+static inline U16CPU fixed_repeat(SkFixed x)
+{
+    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;
+    // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
+    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)
+        return fixed_clamp;
+    if (SkShader::kRepeat_TileMode == m)
+        return fixed_repeat;
+    SkASSERT(SkShader::kMirror_TileMode == m);
+    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)
+        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;
+
+    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;
+
+    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 (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 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 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..d43305b
--- /dev/null
+++ b/src/core/SkBitmapProcState_procs.h
@@ -0,0 +1,344 @@
+
+/*
+ * 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
new file mode 100644
index 0000000..b38eb77
--- /dev/null
+++ b/src/core/SkBitmapProcState_sample.h
@@ -0,0 +1,248 @@
+
+/*
+ * Copyright 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
+
+
+// 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) {
+    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/src/core/SkBitmapProcState_shaderproc.h b/src/core/SkBitmapProcState_shaderproc.h
new file mode 100644
index 0000000..cf15a50
--- /dev/null
+++ b/src/core/SkBitmapProcState_shaderproc.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.
+ */
+
+#include "SkMathPriv.h"
+
+#define SCALE_FILTER_NAME       MAKENAME(_filter_DX_shaderproc)
+
+// 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);
+    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/src/core/SkBitmapSampler.cpp b/src/core/SkBitmapSampler.cpp
new file mode 100644
index 0000000..755f717
--- /dev/null
+++ b/src/core/SkBitmapSampler.cpp
@@ -0,0 +1,417 @@
+
+/*
+ * Copyright 2006 The Android 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)
+        , fColor(0)
+    {
+        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)
+        , fProcTable(NULL)
+    {
+    }
+
+    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/src/core/SkBitmapSampler.h b/src/core/SkBitmapSampler.h
new file mode 100644
index 0000000..47ec331
--- /dev/null
+++ b/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/src/core/SkBitmapSamplerTemplate.h b/src/core/SkBitmapSamplerTemplate.h
new file mode 100644
index 0000000..7b56af7
--- /dev/null
+++ b/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/src/core/SkBitmapShader16BilerpTemplate.h b/src/core/SkBitmapShader16BilerpTemplate.h
new file mode 100644
index 0000000..435b806
--- /dev/null
+++ b/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/src/core/SkBitmapShaderTemplate.h b/src/core/SkBitmapShaderTemplate.h
new file mode 100644
index 0000000..20e5518
--- /dev/null
+++ b/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/src/core/SkBitmap_scroll.cpp b/src/core/SkBitmap_scroll.cpp
new file mode 100644
index 0000000..36e5f05
--- /dev/null
+++ b/src/core/SkBitmap_scroll.cpp
@@ -0,0 +1,123 @@
+
+/*
+ * Copyright 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 (this->isImmutable()) {
+        return false;
+    }
+
+    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;
+    }
+
+    this->notifyPixelsChanged();
+    return true;
+}
diff --git a/src/core/SkBlitBWMaskTemplate.h b/src/core/SkBlitBWMaskTemplate.h
new file mode 100644
index 0000000..15ed54e
--- /dev/null
+++ b/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/src/core/SkBlitMask.h b/src/core/SkBlitMask.h
new file mode 100644
index 0000000..ea1da25
--- /dev/null
+++ b/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/src/core/SkBlitMask_D32.cpp b/src/core/SkBlitMask_D32.cpp
new file mode 100644
index 0000000..4fb974b
--- /dev/null
+++ b/src/core/SkBlitMask_D32.cpp
@@ -0,0 +1,597 @@
+#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) {
+#if 0 // suppress warning
+    const uint32_t rbmask = gMask_00FF00FF;
+#endif
+    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
+        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,
+    };
+
+    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/src/core/SkBlitRow_D16.cpp b/src/core/SkBlitRow_D16.cpp
new file mode 100644
index 0000000..9453488
--- /dev/null
+++ b/src/core/SkBlitRow_D16.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright 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"
+#include "SkMathPriv.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/src/core/SkBlitRow_D32.cpp b/src/core/SkBlitRow_D32.cpp
new file mode 100644
index 0000000..c2cb855
--- /dev/null
+++ b/src/core/SkBlitRow_D32.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 "SkBlitRow.h"
+#include "SkBlitMask.h"
+#include "SkColorPriv.h"
+#include "SkUtils.h"
+
+#define UNROLL
+
+SkBlitRow::ColorRectProc PlatformColorRectProcFactory();
+
+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
+    }
+}
+
+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 {
+            *dst = SkPMSrcOver(*src, *dst);
+            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);
+        }
+    }
+}
+
+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
new file mode 100644
index 0000000..c32e812
--- /dev/null
+++ b/src/core/SkBlitRow_D4444.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright 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);
+
+            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/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
new file mode 100644
index 0000000..cdb2b62
--- /dev/null
+++ b/src/core/SkBlitter.cpp
@@ -0,0 +1,982 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkFilterShader.h"
+#include "SkFlattenableBuffers.h"
+#include "SkMask.h"
+#include "SkMaskFilter.h"
+#include "SkTemplatesPriv.h"
+#include "SkTLazy.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();
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sk3DShader)
+
+protected:
+    Sk3DShader(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+        fProxy = buffer.readFlattenableT<SkShader>();
+        fPMColor = buffer.readColor();
+        fMask = NULL;
+    }
+
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
+        this->INHERITED::flatten(buffer);
+        buffer.writeFlattenable(fProxy);
+        buffer.writeColor(fPMColor);
+    }
+
+private:
+    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;
+    }
+
+    SkShader* shader = origPaint.getShader();
+    SkColorFilter* cf = origPaint.getColorFilter();
+    SkXfermode* mode = origPaint.getXfermode();
+    Sk3DShader* shader3D = NULL;
+
+    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
+
+    if (origPaint.getMaskFilter() != NULL &&
+            origPaint.getMaskFilter()->getFormat() == SkMask::k3D_Format) {
+        shader3D = SkNEW_ARGS(Sk3DShader, (shader));
+        // 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())) {
+            case kSrcOver_XferInterp:
+                mode = NULL;
+                paint.writable()->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.writable()->setShader(shader)->unref();
+        } else if (cf) {
+            // if no shader && no xfermode, we just apply the colorfilter to
+            // our color and move on.
+            SkPaint* writablePaint = paint.writable();
+            writablePaint->setColor(cf->filterColor(paint->getColor()));
+            writablePaint->setColorFilter(NULL);
+            cf = NULL;
+        }
+    }
+
+    if (cf) {
+        SkASSERT(shader);
+        shader = SkNEW_ARGS(SkFilterShader, (shader, cf));
+        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);
+    }
+
+    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/src/core/SkBlitter.h b/src/core/SkBlitter.h
new file mode 100644
index 0000000..ce74a28
--- /dev/null
+++ b/src/core/SkBlitter.h
@@ -0,0 +1,166 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkBlitter_DEFINED
+#define SkBlitter_DEFINED
+
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkRefCnt.h"
+#include "SkRegion.h"
+#include "SkMask.h"
+
+/** SkBlitter and its subclasses are responsible for actually writing pixels
+    into memory. Besides efficiency, they handle clipping and antialiasing.
+*/
+class SkBlitter {
+public:
+    virtual ~SkBlitter();
+
+    /// Blit a horizontal run of one or more pixels.
+    virtual void blitH(int x, int y, int width);
+    /// Blit a horizontal run of antialiased pixels; runs[] is a *sparse*
+    /// zero-terminated run-length encoding of spans of constant alpha values.
+    virtual void blitAntiH(int x, int y, const SkAlpha antialias[],
+                           const int16_t runs[]);
+    /// Blit a vertical run of pixels with a constant alpha value.
+    virtual void blitV(int x, int y, int height, SkAlpha alpha);
+    /// Blit a solid rectangle one or more pixels wide.
+    virtual void blitRect(int x, int y, int width, int height);
+    /** Blit a rectangle with one alpha-blended column on the left,
+        width (zero or more) opaque pixels, and one alpha-blended column
+        on the right.
+        The result will always be at least two pixels wide.
+    */
+    virtual void blitAntiRect(int x, int y, int width, int height,
+                              SkAlpha leftAlpha, SkAlpha rightAlpha);
+    /// Blit a pattern of pixels defined by a rectangle-clipped mask;
+    /// typically used for text.
+    virtual void blitMask(const SkMask&, const SkIRect& clip);
+
+    /** If the blitter just sets a single value for each pixel, return the
+        bitmap it draws into, and assign value. If not, return NULL and ignore
+        the value parameter.
+    */
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
+
+    ///@name non-virtual helpers
+    void blitMaskRegion(const SkMask& mask, const SkRegion& clip);
+    void blitRectRegion(const SkIRect& rect, const SkRegion& clip);
+    void blitRegion(const SkRegion& clip);
+    ///@}
+
+    /** @name Factories
+        Return the correct blitter to use given the specified context.
+     */
+    static SkBlitter* Choose(const SkBitmap& device,
+                             const SkMatrix& matrix,
+                             const SkPaint& paint) {
+        return Choose(device, matrix, paint, NULL, 0);
+    }
+
+    static SkBlitter* Choose(const SkBitmap& device,
+                             const SkMatrix& matrix,
+                             const SkPaint& paint,
+                             void* storage, size_t storageSize);
+
+    static SkBlitter* ChooseSprite(const SkBitmap& device,
+                                   const SkPaint&,
+                                   const SkBitmap& src,
+                                   int left, int top,
+                                   void* storage, size_t storageSize);
+    ///@}
+
+private:
+};
+
+/** This blitter silently never draws anything.
+*/
+class SkNullBlitter : public SkBlitter {
+public:
+    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
+    virtual void blitAntiH(int x, int y, const SkAlpha[],
+                           const int16_t runs[]) SK_OVERRIDE;
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
+    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+    virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
+};
+
+/** Wraps another (real) blitter, and ensures that the real blitter is only
+    called with coordinates that have been clipped by the specified clipRect.
+    This means the caller need not perform the clipping ahead of time.
+*/
+class SkRectClipBlitter : public SkBlitter {
+public:
+    void init(SkBlitter* blitter, const SkIRect& clipRect) {
+        SkASSERT(!clipRect.isEmpty());
+        fBlitter = blitter;
+        fClipRect = clipRect;
+    }
+
+    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
+    virtual void blitAntiH(int x, int y, const SkAlpha[],
+                           const int16_t runs[]) SK_OVERRIDE;
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
+    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+    virtual void blitAntiRect(int x, int y, int width, int height,
+                     SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE;
+    virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
+
+private:
+    SkBlitter*  fBlitter;
+    SkIRect     fClipRect;
+};
+
+/** Wraps another (real) blitter, and ensures that the real blitter is only
+    called with coordinates that have been clipped by the specified clipRgn.
+    This means the caller need not perform the clipping ahead of time.
+*/
+class SkRgnClipBlitter : public SkBlitter {
+public:
+    void init(SkBlitter* blitter, const SkRegion* clipRgn) {
+        SkASSERT(clipRgn && !clipRgn->isEmpty());
+        fBlitter = blitter;
+        fRgn = clipRgn;
+    }
+
+    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
+    virtual void blitAntiH(int x, int y, const SkAlpha[],
+                           const int16_t runs[]) SK_OVERRIDE;
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
+    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+    virtual void blitAntiRect(int x, int y, int width, int height,
+                     SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE;
+    virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
+
+private:
+    SkBlitter*      fBlitter;
+    const SkRegion* fRgn;
+};
+
+/** Factory to set up the appropriate most-efficient wrapper blitter
+    to apply a clip. Returns a pointer to a member, so lifetime must
+    be managed carefully.
+*/
+class SkBlitterClipper {
+public:
+    SkBlitter*  apply(SkBlitter* blitter, const SkRegion* clip,
+                      const SkIRect* bounds = NULL);
+
+private:
+    SkNullBlitter       fNullBlitter;
+    SkRectClipBlitter   fRectBlitter;
+    SkRgnClipBlitter    fRgnBlitter;
+};
+
+#endif
diff --git a/src/core/SkBlitter_4444.cpp b/src/core/SkBlitter_4444.cpp
new file mode 100644
index 0000000..ec62523
--- /dev/null
+++ b/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/src/core/SkBlitter_A1.cpp b/src/core/SkBlitter_A1.cpp
new file mode 100644
index 0000000..5638477
--- /dev/null
+++ b/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/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp
new file mode 100644
index 0000000..92a4971
--- /dev/null
+++ b/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/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp
new file mode 100644
index 0000000..f944f44
--- /dev/null
+++ b/src/core/SkBlitter_ARGB32.cpp
@@ -0,0 +1,482 @@
+/*
+ * Copyright 2006 The Android 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();
+    fColorRect32Proc = SkBlitRow::ColorRectProcFactory();
+}
+
+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();
+
+    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);
+        }
+    }
+}
+
+#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::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;
+
+    if (fXfermode == NULL && (shader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+        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 {
+        SkPMColor* span = fBuffer;
+        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) {
+        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/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp
new file mode 100644
index 0000000..175409c
--- /dev/null
+++ b/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/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp
new file mode 100644
index 0000000..c2a0062
--- /dev/null
+++ b/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/src/core/SkBuffer.cpp b/src/core/SkBuffer.cpp
new file mode 100644
index 0000000..915264d
--- /dev/null
+++ b/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/src/core/SkBuffer.h b/src/core/SkBuffer.h
new file mode 100644
index 0000000..6722eb8
--- /dev/null
+++ b/src/core/SkBuffer.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.
+ */
+
+
+#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
new file mode 100644
index 0000000..18cec44
--- /dev/null
+++ b/src/core/SkCanvas.cpp
@@ -0,0 +1,2004 @@
+
+/*
+ * 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 "SkDeviceImageFilterProxy.h"
+#include "SkDraw.h"
+#include "SkDrawFilter.h"
+#include "SkDrawLooper.h"
+#include "SkMetaData.h"
+#include "SkPicture.h"
+#include "SkRasterClip.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
+    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;
+
+void SkCanvas::predrawNotify() {
+    if (fSurfaceBase) {
+        fSurfaceBase->aboutToDraw(this);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  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)
+
+    DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
+            : fNext(NULL) {
+        if (NULL != device) {
+            device->ref();
+            device->onAttachToCanvas(canvas);
+        }
+        fDevice = device;
+        fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
+    }
+
+    ~DeviceCM() {
+        if (NULL != fDevice) {
+            fDevice->onDetachFromCanvas();
+            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
+    }
+
+private:
+    SkMatrix    fMatrixStorage;
+};
+
+/*  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->fClipStack;
+        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;
+            SkDEBUGCODE(this->validate();)
+
+            fCurrLayer = rec->fNext;
+            if (fBounder) {
+                fBounder->setClip(fClip);
+            }
+            // fCurrLayer may be NULL now
+
+            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,
+                   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);
+    }
+
+    const SkPaint& paint() const {
+        SkASSERT(fPaint);
+        return *fPaint;
+    }
+
+    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;
+    SkCanvas*       fCanvas;
+    const SkPaint&  fOrigPaint;
+    SkDrawLooper*   fLooper;
+    SkDrawFilter*   fFilter;
+    const SkPaint*  fPaint;
+    int             fSaveCount;
+    bool            fDoClearImageFilter;
+    bool            fDone;
+    bool            fIsSimple;
+
+    bool doNext(SkDrawFilter::Type drawType);
+};
+
+bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
+    fPaint = NULL;
+    SkASSERT(!fIsSimple);
+    SkASSERT(fLooper || fFilter || fDoClearImageFilter);
+
+    SkPaint* paint = fLazyPaint.set(fOrigPaint);
+
+    if (fDoClearImageFilter) {
+        paint->setImageFilter(NULL);
+    }
+
+    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;
+
+    // if we only came in here for the imagefilter, mark us as done
+    if (!fLooper && !fFilter) {
+        fDone = true;
+    }
+
+    // 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_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);                           \
+        SkDrawIter          iter(this);
+
+#define LOOPER_END    }
+
+////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::init(SkDevice* device) {
+    fBounder = NULL;
+    fLocalBoundsCompareType.setEmpty();
+    fLocalBoundsCompareTypeDirty = true;
+    fDeviceCMDirty = false;
+    fSaveLayerCount = 0;
+    fMetaData = NULL;
+
+    fMCRec = (MCRec*)fMCStack.push_back();
+    new (fMCRec) MCRec(NULL, 0);
+
+    fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
+    fMCRec->fTopLayer = fMCRec->fLayer;
+    fMCRec->fNext = NULL;
+
+    fSurfaceBase = NULL;
+
+    return this->setDevice(device);
+}
+
+SkCanvas::SkCanvas()
+: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+    inc_canvas();
+
+    this->init(NULL);
+}
+
+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 == fSaveLayerCount);
+
+    this->internalRestore();    // restore the last, since we're going away
+
+    SkSafeUnref(fBounder);
+    SkDELETE(fMetaData);
+
+    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;
+}
+
+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() {
+    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
+    MCRec* rec = (MCRec*) fMCStack.front();
+    SkASSERT(rec && rec->fLayer);
+    return rec->fLayer->fDevice;
+}
+
+SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
+    if (updateMatrixClip) {
+        const_cast<SkCanvas*>(this)->updateDeviceCMCache();
+    }
+    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;
+    }
+
+    if (device) {
+        device->onAttachToCanvas(this);
+    }
+    if (rootDevice) {
+        rootDevice->onDetachFromCanvas();
+    }
+
+    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.
+    */
+
+    SkIRect bounds;
+    if (device) {
+        bounds.set(0, 0, device->width(), device->height());
+    } else {
+        bounds.setEmpty();
+    }
+    // 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;
+}
+
+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) {
+        if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
+                                SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
+            device->accessBitmap(true);
+            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);
+        } else {
+            SkRasterClip clip(totalClip);
+            do {
+                layer->updateMC(totalMatrix, clip, fClipStack, &clip);
+            } while ((layer = layer->fNext) != NULL);
+        }
+        fDeviceCMDirty = false;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) {
+    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);
+
+    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())) {
+            if (justForImageFilter) {
+                // early exit if the layer was just for the imageFilter
+                return count;
+            }
+            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, this));
+    device->unref();
+
+    layer->fNext = fMCRec->fTopLayer;
+    fMCRec->fLayer = layer;
+    fMCRec->fTopLayer = layer;    // this field is NOT an owner of layer
+
+    fSaveLayerCount += 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;
+
+    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->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
+                                     layer->fPaint);
+            // reset this, since internalDrawDevice will have set it to true
+            fDeviceCMDirty = true;
+
+            SkASSERT(fSaveLayerCount > 0);
+            fSaveLayerCount -= 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 fSaveLayerCount > 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);
+}
+
+void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
+                                  const SkPaint* paint) {
+    SkPaint tmp;
+    if (NULL == paint) {
+        tmp.setDither(true);
+        paint = &tmp;
+    }
+
+    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 && !dstDev->canHandleImageFilter(filter)) {
+            SkDeviceImageFilterProxy proxy(dstDev);
+            SkBitmap dst;
+            const SkBitmap& src = srcDev->accessBitmap(false);
+            if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
+                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);
+        }
+    }
+    LOOPER_END
+}
+
+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_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;
+    return fMCRec->fMatrix->preTranslate(dx, dy);
+}
+
+bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preScale(sx, sy);
+}
+
+bool SkCanvas::rotate(SkScalar degrees) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preRotate(degrees);
+}
+
+bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preSkew(sx, sy);
+}
+
+bool SkCanvas::concat(const SkMatrix& matrix) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = true;
+    return fMCRec->fMatrix->preConcat(matrix);
+}
+
+void SkCanvas::setMatrix(const SkMatrix& matrix) {
+    fDeviceCMDirty = true;
+    fLocalBoundsCompareTypeDirty = 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) {
+#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;
+
+    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) {
+#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;
+
+    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;
+
+    // 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(), op);
+
+    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::B2TIter                iter(fClipStack);
+    const SkClipStack::B2TIter::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::replayClips(ClipVisitor* visitor) const {
+    SkClipStack::B2TIter                iter(fClipStack);
+    const SkClipStack::B2TIter::Clip*   clip;
+
+    SkRect empty = { 0, 0, 0, 0 };
+    while ((clip = iter.next()) != NULL) {
+        if (clip->fPath) {
+            visitor->clipPath(*clip->fPath, clip->fOp, clip->fDoAA);
+        } else if (clip->fRect) {
+            visitor->clipRect(*clip->fRect, clip->fOp, clip->fDoAA);
+        } else {
+            visitor->clipRect(empty, SkRegion::kIntersect_Op, false);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+
+    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();
+
+        // 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) const {
+    return path.isEmpty() || this->quickReject(path.getBounds());
+}
+
+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) 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 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
+        // 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();
+}
+
+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;
+    }
+
+    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)
+
+    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))) {
+            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.isFinite()) {
+        return;
+    }
+
+    if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
+        SkRect storage;
+        const SkRect& bounds = path.getBounds();
+        if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
+            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)) {
+            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 SkRect* src,
+                                      const SkRect& dst, const SkPaint* paint) {
+    if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
+        return;
+    }
+
+    if (NULL == paint || paint->canComputeFastBounds()) {
+        SkRect storage;
+        const SkRect* bounds = &dst;
+        if (paint) {
+            bounds = &paint->computeFastBounds(dst, &storage);
+        }
+        if (this->quickReject(*bounds)) {
+            return;
+        }
+    }
+
+    SkLazyPaint lazy;
+    if (NULL == paint) {
+        paint = lazy.init();
+    }
+
+    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* 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)) {
+            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 SkScalar srcX[4] = {
+        0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), w
+    };
+    const SkScalar srcY[4] = {
+        0, SkIntToScalar(c.fTop), SkIntToScalar(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];
+    }
+
+    for (int y = 0; y < 3; y++) {
+        SkRect s, d;
+
+        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);
+}
+
+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))) {
+            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))) {
+                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))) {
+            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) {
+    picture.draw(this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+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(); }
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas::ClipVisitor::~ClipVisitor() { }
+
+
diff --git a/src/core/SkChunkAlloc.cpp b/src/core/SkChunkAlloc.cpp
new file mode 100644
index 0000000..25ef4bc
--- /dev/null
+++ b/src/core/SkChunkAlloc.cpp
@@ -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.
+ */
+
+#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);
+    }
+
+    static void FreeChain(Block* block) {
+        while (block) {
+            Block* next = block->fNext;
+            sk_free(block);
+            block = next;
+        }
+    };
+
+    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) {
+    if (minSize < MIN_CHUNKALLOC_BLOCK_SIZE) {
+        minSize = MIN_CHUNKALLOC_BLOCK_SIZE;
+    }
+
+    fBlock = NULL;
+    fMinSize = minSize;
+    fChunkSize = fMinSize;
+    fTotalCapacity = 0;
+    fBlockCount = 0;
+}
+
+SkChunkAlloc::~SkChunkAlloc() {
+    this->reset();
+}
+
+void SkChunkAlloc::reset() {
+    Block::FreeChain(fBlock);
+    fBlock = NULL;
+    fChunkSize = fMinSize;  // reset to our initial minSize
+    fTotalCapacity = 0;
+    fBlockCount = 0;
+}
+
+SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) {
+    size_t size = bytes;
+    if (size < fChunkSize) {
+        size = fChunkSize;
+    }
+
+    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;
+}
+
+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);
+    char* ptr = block->fFreePtr;
+
+    block->fFreeSize -= bytes;
+    block->fFreePtr = ptr + 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/SkClipStack.cpp b/src/core/SkClipStack.cpp
new file mode 100644
index 0000000..0f2d632
--- /dev/null
+++ b/src/core/SkClipStack.cpp
@@ -0,0 +1,882 @@
+
+/*
+ * Copyright 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 "SkThread.h"
+
+#include <new>
+
+
+// 0-2 are reserved for invalid, empty & wide-open
+int32_t SkClipStack::gGenID = 3;
+
+struct SkClipStack::Rec {
+    enum State {
+        kEmpty_State,
+        kRect_State,
+        kPath_State
+    };
+
+    SkPath          fPath;
+    SkRect          fRect;
+    int             fSaveCount;
+    SkRegion::Op    fOp;
+    State           fState;
+    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;
+    bool                    fIsIntersectionOfRects;
+
+    int                     fGenID;
+
+    Rec(int saveCount)
+    : fGenID(kInvalidGenID) {
+        fSaveCount = saveCount;
+        this->setEmpty();
+    }
+
+    Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA)
+        : fRect(rect)
+        , fGenID(kInvalidGenID) {
+        fSaveCount = saveCount;
+        fOp = op;
+        fState = kRect_State;
+        fDoAA = doAA;
+        // bounding box members are updated in a following updateBound call
+    }
+
+    Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA)
+        : fPath(path)
+        , fGenID(kInvalidGenID) {
+        fRect.setEmpty();
+        fSaveCount = saveCount;
+        fOp = op;
+        fState = kPath_State;
+        fDoAA = doAA;
+        // bounding box members are updated in a following updateBound call
+    }
+
+    void setEmpty() {
+        fState = kEmpty_State;
+        fFiniteBound.setEmpty();
+        fFiniteBoundType = kNormal_BoundsType;
+        fIsIntersectionOfRects = false;
+        fGenID = kEmptyGenID;
+    }
+
+    void checkEmpty() {
+        SkASSERT(fFiniteBound.isEmpty());
+        SkASSERT(kNormal_BoundsType == fFiniteBoundType);
+        SkASSERT(!fIsIntersectionOfRects);
+        SkASSERT(kEmptyGenID == fGenID);
+    }
+
+    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 canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const {
+        if (kEmpty_State == fState && (
+                    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);
+    }
+
+    /**
+     * 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 {
+        SkASSERT(kRect_State == fState);
+
+        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;
+    }
+
+
+    /**
+     * 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
+    };
+
+    // a mirror of CombineBoundsRevDiff
+    void 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();
+                }
+                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::Rec::CombineBoundsDiff Invalid fill combination");
+                break;
+        }
+    }
+
+    void 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::Rec::CombineBoundsXOR Invalid fill combination");
+                break;
+        }
+    }
+
+    // a mirror of CombineBoundsIntersection
+    void CombineBoundsUnion(int combination, const SkRect& prevFinite) {
+
+        switch (combination) {
+            case kInvPrev_InvCur_FillCombo:
+                if (!fFiniteBound.intersect(prevFinite)) {
+                    fFiniteBound.setEmpty();
+                }
+                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::Rec::CombineBoundsUnion Invalid fill combination");
+                break;
+        }
+    }
+
+    // a mirror of CombineBoundsUnion
+    void 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();
+                }
+                break;
+            default:
+                SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsIntersection Invalid fill combination");
+                break;
+        }
+    }
+
+    // a mirror of CombineBoundsDiff
+    void 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();
+                }
+                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::Rec::CombineBoundsRevDiff Invalid fill combination");
+                break;
+        }
+    }
+
+    void updateBound(const Rec* prior) {
+
+        // First, optimistically update the current Rec's bound information
+        // with the current clip's bound
+        fIsIntersectionOfRects = false;
+        if (kRect_State == fState) {
+            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_State == fState);
+
+            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 set up the previous Rec'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 Rec'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 kDefaultRecordAllocCnt = 8;
+
+SkClipStack::SkClipStack()
+    : fDeque(sizeof(Rec), kDefaultRecordAllocCnt)
+    , fSaveCount(0) {
+}
+
+SkClipStack::SkClipStack(const SkClipStack& b)
+    : fDeque(sizeof(Rec), kDefaultRecordAllocCnt) {
+    *this = b;
+}
+
+SkClipStack::SkClipStack(const SkRect& r)
+    : fDeque(sizeof(Rec), kDefaultRecordAllocCnt)
+    , fSaveCount(0) {
+    if (!r.isEmpty()) {
+        this->clipDevRect(r, SkRegion::kReplace_Op, false);
+    }
+}
+
+SkClipStack::SkClipStack(const SkIRect& r)
+    : fDeque(sizeof(Rec), kDefaultRecordAllocCnt)
+    , 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;
+    }
+    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() {
+    // 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()) {
+        Rec* rec = (Rec*)fDeque.back();
+        rec->~Rec();
+        fDeque.pop_back();
+    }
+
+    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;
+        }
+        this->purgeClip(rec);
+        rec->~Rec();
+        fDeque.pop_back();
+    }
+}
+
+void SkClipStack::getBounds(SkRect* canvFiniteBound,
+                            BoundsType* boundType,
+                            bool* isIntersectionOfRects) const {
+    SkASSERT(NULL != canvFiniteBound && NULL != boundType);
+
+    Rec* rec = (Rec*)fDeque.back();
+
+    if (NULL == rec) {
+        // 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 = rec->fFiniteBound;
+    *boundType = rec->fFiniteBoundType;
+    if (NULL != isIntersectionOfRects) {
+        *isIntersectionOfRects = rec->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);
+    }
+}
+
+void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+
+    int32_t genID = GetNextGenID();
+
+    // Use reverse iterator instead of back because Rect path may need previous
+    SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
+    Rec* rec = (Rec*) iter.prev();
+
+    if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
+        switch (rec->fState) {
+            case Rec::kEmpty_State:
+                rec->checkEmpty();
+                return;
+            case Rec::kRect_State:
+                if (rec->rectRectIntersectAllowed(rect, doAA)) {
+                    this->purgeClip(rec);
+                    if (!rec->fRect.intersect(rect)) {
+                        rec->setEmpty();
+                        return;
+                    }
+
+                    rec->fDoAA = doAA;
+                    Rec* prev = (Rec*) iter.prev();
+                    rec->updateBound(prev);
+                    rec->fGenID = genID;
+                    return;
+                }
+                break;
+            case Rec::kPath_State:
+                if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
+                    this->purgeClip(rec);
+                    rec->setEmpty();
+                    return;
+                }
+                break;
+        }
+    }
+    new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
+    ((Rec*) fDeque.back())->updateBound(rec);
+    ((Rec*) fDeque.back())->fGenID = genID;
+
+    if (rec && rec->fSaveCount == fSaveCount) {
+        this->purgeClip(rec);
+    }
+}
+
+void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+    SkRect alt;
+    if (path.isRect(&alt)) {
+        return this->clipDevRect(alt, op, doAA);
+    }
+
+    int32_t genID = GetNextGenID();
+
+    Rec* rec = (Rec*)fDeque.back();
+    if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
+        const SkRect& pathBounds = path.getBounds();
+        switch (rec->fState) {
+            case Rec::kEmpty_State:
+                rec->checkEmpty();
+                return;
+            case Rec::kRect_State:
+                if (!SkRect::Intersects(rec->fRect, pathBounds)) {
+                    this->purgeClip(rec);
+                    rec->setEmpty();
+                    return;
+                }
+                break;
+            case Rec::kPath_State:
+                if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
+                    this->purgeClip(rec);
+                    rec->setEmpty();
+                    return;
+                }
+                break;
+        }
+    }
+    new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
+    ((Rec*) fDeque.back())->updateBound(rec);
+    ((Rec*) fDeque.back())->fGenID = genID;
+
+    if (rec && rec->fSaveCount == fSaveCount) {
+        this->purgeClip(rec);
+    }
+}
+
+void SkClipStack::clipEmpty() {
+
+    Rec* rec = (Rec*) fDeque.back();
+
+    if (rec && rec->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
+        switch (rec->fState) {
+            case Rec::kEmpty_State:
+                rec->checkEmpty();
+                return;
+            case Rec::kRect_State:
+            case Rec::kPath_State:
+                this->purgeClip(rec);
+                rec->setEmpty();
+                return;
+        }
+    }
+    new (fDeque.push_back()) Rec(fSaveCount);
+
+    if (rec && rec->fSaveCount == fSaveCount) {
+        this->purgeClip(rec);
+    }
+}
+
+bool SkClipStack::isWideOpen() const {
+    if (0 == fDeque.count()) {
+        return true;
+    }
+
+    const Rec* back = (const Rec*) fDeque.back();
+    return kInsideOut_BoundsType == back->fFiniteBoundType &&
+           back->fFiniteBound.isEmpty();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkClipStack::Iter::Iter() : fStack(NULL) {
+}
+
+bool operator==(const SkClipStack::Iter::Clip& a,
+                const SkClipStack::Iter::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::Iter::Clip& a,
+                const SkClipStack::Iter::Clip& b) {
+    return !(a == b);
+}
+
+SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
+    : fStack(&stack) {
+    this->reset(stack, startLoc);
+}
+
+const SkClipStack::Iter::Clip* SkClipStack::Iter::updateClip(
+                        const SkClipStack::Rec* rec) {
+    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;
+    fClip.fGenID = rec->fGenID;
+    return &fClip;
+}
+
+const SkClipStack::Iter::Clip* SkClipStack::Iter::next() {
+    const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
+    if (NULL == rec) {
+        return NULL;
+    }
+
+    return this->updateClip(rec);
+}
+
+const SkClipStack::Iter::Clip* SkClipStack::Iter::prev() {
+    const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.prev();
+    if (NULL == rec) {
+        return NULL;
+    }
+
+    return this->updateClip(rec);
+}
+
+const SkClipStack::Iter::Clip* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
+
+    if (NULL == fStack) {
+        return NULL;
+    }
+
+    fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
+
+    const SkClipStack::Rec* rec = NULL;
+
+    for (rec = (const SkClipStack::Rec*) fIter.prev();
+         NULL != rec;
+         rec = (const SkClipStack::Rec*) fIter.prev()) {
+
+        if (op == rec->fOp) {
+            // The Deque's iterator is actually one pace ahead of the
+            // returned value. So while "rec" 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;
+        }
+    }
+
+    if (NULL == rec) {
+        // There were no "op" clips
+        fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
+    }
+
+    return this->next();
+}
+
+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 'rec' will never be used again. Purge it.
+void SkClipStack::purgeClip(Rec* rec) {
+    SkASSERT(NULL != rec);
+
+    for (int i = 0; i < fCallbackData.count(); ++i) {
+        (*fCallbackData[i].fCallback)(rec->fGenID, fCallbackData[i].fData);
+    }
+
+    // Invalidate rec's gen ID so handlers can detect already handled records
+    rec->fGenID = kInvalidGenID;
+}
+
+int32_t SkClipStack::GetNextGenID() {
+    return sk_atomic_inc(&gGenID);
+}
+
+int32_t SkClipStack::getTopmostGenID() const {
+
+    if (fDeque.empty()) {
+        return kInvalidGenID;
+    }
+
+    Rec* rec = (Rec*)fDeque.back();
+    return rec->fGenID;
+}
diff --git a/src/core/SkColor.cpp b/src/core/SkColor.cpp
new file mode 100644
index 0000000..cb5f15c
--- /dev/null
+++ b/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/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
new file mode 100644
index 0000000..fee34ed
--- /dev/null
+++ b/src/core/SkColorFilter.cpp
@@ -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.
+ */
+
+#include "SkColorFilter.h"
+#include "SkFilterShader.h"
+#include "SkFlattenableBuffers.h"
+#include "SkShader.h"
+#include "SkUnPreMultiply.h"
+
+SK_DEFINE_INST_COUNT(SkColorFilter)
+
+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 = buffer.readFlattenableT<SkShader>();
+    fFilter = buffer.readFlattenableT<SkColorFilter>();
+}
+
+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) const {
+    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/src/core/SkColorTable.cpp b/src/core/SkColorTable.cpp
new file mode 100644
index 0000000..1375c58
--- /dev/null
+++ b/src/core/SkColorTable.cpp
@@ -0,0 +1,157 @@
+
+/*
+ * 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 "SkColorTable.h"
+#include "SkFlattenableBuffers.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+SK_DEFINE_INST_COUNT(SkColorTable)
+
+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;)
+}
+
+// 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();
+    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(sk_atomic_dec(&fColorLockCount);)
+    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;)
+
+    fFlags = buffer.readUInt();
+    fCount = buffer.getArrayCount();
+    fColors = (SkPMColor*)sk_malloc_throw(fCount * sizeof(SkPMColor));
+    const uint32_t countRead = buffer.readColorArray(fColors);
+    SkASSERT((unsigned)fCount <= 256);
+    SkASSERT(countRead == fCount);
+}
+
+void SkColorTable::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.writeUInt(fFlags);
+    buffer.writeColorArray(fColors, fCount);
+}
diff --git a/src/core/SkComposeShader.cpp b/src/core/SkComposeShader.cpp
new file mode 100644
index 0000000..e925ab5
--- /dev/null
+++ b/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 "SkFlattenableBuffers.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 = buffer.readFlattenableT<SkShader>();
+    if (NULL == fShaderA) {
+        fShaderA = SkNEW_ARGS(SkColorShader, (0));
+    }
+    fShaderB = buffer.readFlattenableT<SkShader>();
+    if (NULL == fShaderB) {
+        fShaderB = SkNEW_ARGS(SkColorShader, (0));
+    }
+    fMode = buffer.readFlattenableT<SkXfermode>();
+}
+
+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) const {
+    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/src/core/SkConcaveToTriangles.cpp
new file mode 100644
index 0000000..4821e69
--- /dev/null
+++ b/src/core/SkConcaveToTriangles.cpp
@@ -0,0 +1,962 @@
+
+/*
+ * Copyright 2009 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// This is an implementation of the triangulation algorithm described by Alain
+// Fournier and Delfin Montuno, in "Triangulating Simple Polygons and Equivalent
+// Problems", in the ACM Transactions on Graphics, vol. 3, no. 2, April 1984,
+// pp. 153-174.
+//
+// No new vertices are created in the triangulation: triangles are constructed
+// only from the points in the original polygon, so there is no possibility for
+// cracks to develop from finite precision arithmetic.
+////////////////////////////////////////////////////////////////////////////////
+
+// TODO:
+// - RemoveDegenerateTrapezoids() was added to make the code robust, but it
+//   unfortunately introduces T-vertices. Make it robust without T-vertices.
+// - It should be easy enough to detect whether the outer contour is right- or
+//   left-handed by looking at the top vertex, which is available in the
+//   pre-sort during trapezoidization. Use this information in angleIsConvex()
+//   to allowed either handedness outer contour. In either case, though, holes
+//   need to have the opposite orientation.
+// - SkTHeapSort was broken, so I wrote a bubble sort so that I could make other
+//   things work. Use SkQSort() instead.
+// - The ActiveTrapezoid array does a linear search which is O(n) inefficient.
+//   Use SkSearch to implement O(log n) binary search and insertion sort.
+// - There is no need to use SkTDArray for everything. Use SkAutoTMalloc for
+//   everything else.
+
+#include "SkConcaveToTriangles.h"
+#include "SkTDArray.h"
+#include "SkGeometry.h"
+#include "SkTSort.h"
+
+// This is used to prevent runaway code bugs, and can probably be removed after
+// the code has been proven robust.
+#define kMaxCount 1000
+
+#define DEBUG
+#ifdef DEBUG
+//------------------------------------------------------------------------------
+// Debugging support
+//------------------------------------------------------------------------------
+
+#include <cstdio>
+#include <stdarg.h>
+
+static int gDebugLevel = 0;   // This enables debug reporting.
+
+static void DebugPrintf(const char *format, ...) {
+    if (gDebugLevel > 0) {
+        va_list ap;
+        va_start(ap, format);
+        vprintf(format, ap);
+        va_end(ap);
+    }
+}
+
+static void FailureMessage(const char *format, ...) {
+    if (1) {
+        printf("FAILURE: ");
+        va_list ap;
+        va_start(ap, format);
+        vprintf(format, ap);
+        va_end(ap);
+    }
+}
+#else  // !DEBUG
+inline void DebugPrintf(const char *format, ...) {}
+inline void FailureMessage(const char *format, ...) {}
+#endif  // DEBUG
+
+
+// Forward declaration.
+class Vertex;
+
+
+//------------------------------------------------------------------------------
+// The Trapezoid (actually, up to two of them) is embedded into a Vertex, whose
+// point() provides the top of the Trapezoid, whereas the bottom, and the left
+// and right edges, are stored in the Trapezoid. The edges are represented by
+// their tail point; the head is the successive point modulo the number of
+// points in the polygon. Only the Y coordinate of the top and bottom are
+// relevant.
+//------------------------------------------------------------------------------
+class Trapezoid {
+public:
+    const Vertex* left()   const    { return fLeft;   }
+    const Vertex* right()  const    { return fRight;  }
+    const Vertex* bottom() const    { return fBottom; }
+    Vertex* left()                  { return fLeft;   }
+    Vertex* right()                 { return fRight;  }
+    Vertex* bottom()                { return fBottom; }
+    void   setLeft(Vertex *left)    { fLeft   = left;   }
+    void  setRight(Vertex *right)   { fRight  = right;  }
+    void setBottom(Vertex *bottom)  { fBottom = bottom; }
+    void nullify()                  { setBottom(NULL); }
+
+    bool operator<(Trapezoid &t1)   { return compare(t1) < 0; }
+    bool operator>(Trapezoid &t1)   { return compare(t1) > 0; }
+
+private:
+    Vertex *fLeft, *fRight, *fBottom;
+
+    // These return a number that is less than, equal to, or greater than zero
+    // depending on whether the trapezoid or point is to the left, on, or to the
+    // right.
+    SkScalar compare(const Trapezoid &t1) const;
+    SkScalar compare(const SkPoint &p) const;
+};
+
+
+//------------------------------------------------------------------------------
+// The ActiveTrapezoids are a sorted list containing the currently active
+// trapezoids, i.e. those that have the top, left, and right, but still need the
+// bottom. This could use some optimization, to reduce search time from O(n) to
+// O(log n).
+//------------------------------------------------------------------------------
+class ActiveTrapezoids {
+public:
+    ActiveTrapezoids() { fTrapezoids.setCount(0); }
+
+    size_t count() const { return fTrapezoids.count(); }
+
+    // Select an unused trapezoid from the Vertex vt, initialize it with the
+    // left and right edges, and insert it into the sorted list.
+    bool insertNewTrapezoid(Vertex *vt, Vertex *left, Vertex *right);
+
+    // Remove the specified Trapezoids from the active list.
+    void remove(Trapezoid *t);
+
+    // Determine whether the given point lies within any active trapezoid, and
+    // return a pointer to that Trapezoid.
+    bool withinActiveTrapezoid(const SkPoint &pt, Trapezoid **tp);
+
+    // Find an active trapezoid that contains the given edge.
+    Trapezoid* getTrapezoidWithEdge(const Vertex *edge);
+
+private:
+    // Insert the specified Trapezoid into the sorted list.
+    void insert(Trapezoid *t);
+
+    // The sorted list of active trapezoids. This is O(n), and would benefit
+    // a 2-3 tree o achieve O(log n).
+    SkTDArray<Trapezoid*> fTrapezoids;  // Fournier suggests a 2-3 tree instead.
+};
+
+
+//------------------------------------------------------------------------------
+// The Vertex is used to communicate information between the various phases of
+// triangulation.
+//------------------------------------------------------------------------------
+class Vertex {
+public:
+    enum VertexType { MONOTONE, CONVEX, CONCAVE };
+
+    Trapezoid fTrap0;
+    Trapezoid fTrap1;
+
+    const SkPoint &point() const        { return fPoint; }
+    void setPoint(const SkPoint &point) { fPoint = point; }
+
+    // The next and previous vertices around the polygon.
+    Vertex *next()                      { return fNext; }
+    Vertex *prev()                      { return fPrev; }
+    const Vertex *next() const          { return fNext; }
+    const Vertex *prev() const          { return fPrev; }
+    void setNext(Vertex *next)          { fNext = next; }
+    void setPrev(Vertex *prev)          { fPrev = prev; }
+
+    void setDone(bool done)             { fDone = done; }
+    bool done() const                   { return fDone; }
+
+    // Trapezoid accessors return non-null for any complete trapezoids.
+    void trapezoids(Trapezoid **trap0, Trapezoid **trap1) {
+        *trap0 = (fTrap0.bottom() != NULL) ? &fTrap0 : NULL;
+        *trap1 = (fTrap1.bottom() != NULL) ? &fTrap1 : NULL;
+    }
+
+    // Eliminate a trapezoid.
+    void nullifyTrapezoid() {
+        if (fTrap1.bottom() != NULL) {
+            DebugPrintf("Unexpected non-null second trapezoid.\n");
+            fTrap1.nullify();
+            return;
+        }
+        fTrap0.nullify();
+    }
+
+    // Determine whether the edge specified by this Vertex shares the given top
+    // and bottom.
+    bool shareEdge(Vertex *top, Vertex *bottom);
+
+    // Determines whether the angle specified by { prev, this, next } is convex.
+    // Note that collinear is considered to be convex.
+    bool angleIsConvex();
+
+    // Remove this Vertex from the { prev, next } linked list.
+    void delink() {
+        Vertex *p = prev(),
+               *n = next();
+        p->setNext(n);
+        n->setPrev(p);
+    }
+
+    // Return a number that is less than, equal to, or greater than zero
+    // depending on whether the point is to the left, on, or to the right of the
+    // edge that has this Vertex as a base.
+    SkScalar compare(const SkPoint &pt) const;
+
+    // Classify the vertex, and return its sorted edges.
+    VertexType classify(Vertex **e0, Vertex **e1);
+
+    // This helps determine unimonotonicity.
+    Vertex *diagonal();
+
+private:
+    SkPoint fPoint;
+    Vertex *fNext;
+    Vertex *fPrev;
+    bool fDone;
+};
+
+
+bool Vertex::angleIsConvex() {
+    SkPoint vPrev = prev()->point() - point(),
+            vNext = next()->point() - point();
+    // TODO(turk): There might be overflow in fixed-point.
+    return SkPoint::CrossProduct(vNext, vPrev) >= 0;
+}
+
+
+bool Vertex::shareEdge(Vertex *top, Vertex *bottom) {
+    return (((this == top)    && (this->next() == bottom)) ||
+            ((this == bottom) && (this->next() == top)));
+}
+
+
+SkScalar Vertex::compare(const SkPoint &pt) const  {
+    SkPoint ve = next()->point() - point(),
+            vp = pt              - point();
+    SkScalar d;
+    if (ve.fY == 0) {
+        // Return twice the distance to the center of the horizontal edge.
+        d = ve.fX + pt.fX - next()->point().fX;
+    } else {
+        // Return the distance to the edge, scaled by the edge length.
+        d = SkPoint::CrossProduct(ve, vp);
+        if (ve.fY > 0) d = -d;
+    }
+    return d;
+}
+
+
+SkScalar Trapezoid::compare(const SkPoint &pt) const {
+    SkScalar d = left()->compare(pt);
+    if (d <= 0)
+        return d;   // Left of the left edge.
+    d = right()->compare(pt);
+    if (d >= 0)
+        return d;   // Right of the right edge.
+    return 0;       // Somewhere between the left and the right edges.
+}
+
+
+
+SkScalar Trapezoid::compare(const Trapezoid &t1) const {
+#if 1
+    SkScalar d = left()->compare(t1.left()->point());
+    if (d == 0)
+        d = right()->compare(t1.right()->point());
+    return d;
+#else
+    SkScalar dl =  left()->compare( t1.left()->point()),
+             dr = right()->compare(t1.right()->point());
+    if (dl < 0 && dr < 0)
+        return dr;
+    if (dl > 0 && dr > 0)
+        return dl;
+    return 0;
+#endif
+}
+
+
+Trapezoid* ActiveTrapezoids::getTrapezoidWithEdge(const Vertex *edge) {
+    DebugPrintf("Entering getTrapezoidWithEdge(): looking through %d\n",
+           fTrapezoids.count());
+    DebugPrintf("trying to find %p: ", edge);
+    Trapezoid **tp;
+    for (tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) {
+        SkASSERT(tp != NULL);
+        SkASSERT(*tp != NULL);
+        DebugPrintf("%p and %p, ", (**tp).left(), (**tp).right());
+        if ((**tp).left() == edge || (**tp).right() == edge) {
+            DebugPrintf("\ngetTrapezoidWithEdge found the trapezoid\n");
+            return *tp;
+        }
+    }
+    DebugPrintf("getTrapezoidWithEdge found no trapezoid\n");
+    return NULL;
+}
+
+
+bool ActiveTrapezoids::insertNewTrapezoid(Vertex *vt,
+                                          Vertex *left,
+                                          Vertex *right) {
+    DebugPrintf("Inserting a trapezoid...");
+    if (vt->fTrap0.left() == NULL && vt->fTrap0.right() == NULL) {
+        vt->fTrap0.setLeft(left);
+        vt->fTrap0.setRight(right);
+        insert(&vt->fTrap0);
+    } else if (vt->fTrap1.left() == NULL && vt->fTrap1.right() == NULL) {
+        DebugPrintf("a second one...");
+        vt->fTrap1.setLeft(left);
+        vt->fTrap1.setRight(right);
+        if (vt->fTrap1 < vt->fTrap0) {  // Keep trapezoids sorted.
+            remove(&vt->fTrap0);
+            Trapezoid t = vt->fTrap0;
+            vt->fTrap0 = vt->fTrap1;
+            vt->fTrap1 = t;
+            insert(&vt->fTrap0);
+        }
+        insert(&vt->fTrap1);
+    } else {
+        FailureMessage("More than 2 trapezoids requested for a vertex\n");
+        return false;
+    }
+    DebugPrintf(" done. %d incomplete trapezoids\n", fTrapezoids.count());
+    return true;
+}
+
+
+void ActiveTrapezoids::insert(Trapezoid *t) {
+    Trapezoid **tp;
+    for (tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp)
+        if (**tp > *t)
+            break;
+    fTrapezoids.insert(tp - fTrapezoids.begin(), 1, &t);
+    // SHOULD VERIFY THAT ALL TRAPEZOIDS ARE PROPERLY SORTED
+}
+
+
+void ActiveTrapezoids::remove(Trapezoid *t) {
+    DebugPrintf("Removing a trapezoid...");
+    for (Trapezoid **tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) {
+        if (*tp == t) {
+            fTrapezoids.remove(tp - fTrapezoids.begin());
+            DebugPrintf(" done.\n");
+            return;
+        }
+    }
+    DebugPrintf(" Arghh! Panic!\n");
+    SkASSERT(t == 0);   // Cannot find t in active trapezoid list.
+}
+
+
+bool ActiveTrapezoids::withinActiveTrapezoid(const SkPoint &pt,
+                                             Trapezoid **trap) {
+    DebugPrintf("Entering withinActiveTrapezoid()\n");
+    // This is where a good search data structure would be helpful.
+    Trapezoid **t;
+    for (t = fTrapezoids.begin(); t < fTrapezoids.end(); ++t) {
+        if ((**t).left()->compare(pt) <= 0) {
+            // The point is to the left of the left edge of this trapezoid.
+            DebugPrintf("withinActiveTrapezoid: Before a trapezoid\n");
+            *trap = *t;     // Return the place where a new trapezoid would go.
+// We have a bug with the sorting -- look through everything.
+            continue;
+//             return false;   // Outside all trapezoids, since they are sorted.
+        }
+        // The point is to the right of the left edge of this trapezoid.
+
+        if ((**t).right()->compare(pt) < 0) {
+            // The point is to the left of the right edge.
+            DebugPrintf("withinActiveTrapezoid: Within an Active Trapezoid\n");
+            *trap = *t;
+            return true;
+        }
+    }
+
+    // The point is to the right of all trapezoids.
+    DebugPrintf("withinActiveTrapezoid: After all trapezoids\n");
+    *trap = NULL;
+    return false;
+}
+
+
+Vertex* Vertex::diagonal() {
+    Vertex *diag = NULL;
+    if (fTrap0.bottom() != NULL) {
+        if (!fTrap0.left() ->shareEdge(this, fTrap0.bottom()) &&
+            !fTrap0.right()->shareEdge(this, fTrap0.bottom())
+        ) {
+            diag = fTrap0.bottom();
+            fTrap0 = fTrap1;
+            fTrap1.nullify();
+        } else if (fTrap1.bottom() != NULL &&
+                  !fTrap1.left() ->shareEdge(this, fTrap1.bottom()) &&
+                  !fTrap1.right()->shareEdge(this, fTrap1.bottom())
+        ) {
+            diag = fTrap1.bottom();
+            fTrap1.nullify();
+        }
+    }
+    return diag;
+}
+
+
+// We use this to sort the edges vertically for a scan-conversion type of
+// operation.
+class VertexPtr {
+public:
+    Vertex *vt;
+};
+
+
+static bool operator<(VertexPtr &v0, VertexPtr &v1) {
+    // DebugPrintf("< %p %p\n", &v0, &v1);
+    if (v0.vt->point().fY < v1.vt->point().fY)  return true;
+    if (v0.vt->point().fY > v1.vt->point().fY)  return false;
+    if (v0.vt->point().fX < v1.vt->point().fX)  return true;
+    else                                        return false;
+}
+
+
+#if 0 // UNUSED
+static bool operator>(VertexPtr &v0, VertexPtr &v1) {
+    // DebugPrintf("> %p %p\n", &v0, &v1);
+    if (v0.vt->point().fY > v1.vt->point().fY)  return true;
+    if (v0.vt->point().fY < v1.vt->point().fY)  return false;
+    if (v0.vt->point().fX > v1.vt->point().fX)  return true;
+    else                                        return false;
+}
+#endif
+
+static void SetVertexPoints(size_t numPts, const SkPoint *pt, Vertex *vt) {
+    for (; numPts-- != 0; ++pt, ++vt)
+        vt->setPoint(*pt);
+}
+
+
+static void InitializeVertexTopology(size_t numPts, Vertex *v1) {
+    Vertex *v0 = v1 + numPts - 1, *v_1 = v0 - 1;
+    for (; numPts-- != 0; v_1 = v0, v0 = v1++) {
+        v0->setPrev(v_1);
+        v0->setNext(v1);
+    }
+}
+
+Vertex::VertexType Vertex::classify(Vertex **e0, Vertex **e1) {
+    VertexType type;
+    SkPoint vPrev, vNext;
+    vPrev.fX = prev()->point().fX - point().fX;
+    vPrev.fY = prev()->point().fY - point().fY;
+    vNext.fX = next()->point().fX - point().fX;
+    vNext.fY = next()->point().fY - point().fY;
+
+    // This can probably be simplified, but there are enough potential bugs,
+    // we will leave it expanded until all cases are tested appropriately.
+    if (vPrev.fY < 0) {
+        if (vNext.fY > 0) {
+            // Prev comes from above, Next goes below.
+            type = MONOTONE;
+            *e0 = prev();
+            *e1 = this;
+        } else if (vNext.fY < 0) {
+            // The are both above: sort so that e0 is on the left.
+            type = CONCAVE;
+            if (SkPoint::CrossProduct(vPrev, vNext) <= 0) {
+                *e0 = this;
+                *e1 = prev();
+            } else {
+                *e0 = prev();
+                *e1 = this;
+            }
+        } else {
+            DebugPrintf("### py < 0, ny = 0\n");
+            if (vNext.fX < 0) {
+                type = CONCAVE;
+                *e0 = this;     // flat to the left
+                *e1 = prev();   // concave on the right
+            } else {
+                type = CONCAVE;
+                *e0 = prev();   // concave to the left
+                *e1 = this;     // flat to the right
+            }
+        }
+    } else if (vPrev.fY > 0) {
+        if (vNext.fY < 0) {
+            // Next comes from above, Prev goes below.
+            type = MONOTONE;
+            *e0 = this;
+            *e1 = prev();
+        } else if (vNext.fY > 0) {
+            // They are both below: sort so that e0 is on the left.
+            type = CONVEX;
+            if (SkPoint::CrossProduct(vPrev, vNext) <= 0) {
+                *e0 = prev();
+                *e1 = this;
+            } else {
+                *e0 = this;
+                *e1 = prev();
+            }
+        } else {
+            DebugPrintf("### py > 0, ny = 0\n");
+            if (vNext.fX < 0) {
+                type = MONOTONE;
+                *e0 = this;     // flat to the left
+                *e1 = prev();   // convex on the right - try monotone first
+            } else {
+                type = MONOTONE;
+                *e0 = prev();   // convex to the left - try monotone first
+                *e1 = this;     // flat to the right
+            }
+        }
+    } else {  // vPrev.fY == 0
+        if (vNext.fY < 0) {
+            DebugPrintf("### py = 0, ny < 0\n");
+            if (vPrev.fX < 0) {
+                type = CONCAVE;
+                *e0 = prev();   // flat to the left
+                *e1 = this;     // concave on the right
+            } else {
+                type = CONCAVE;
+                *e0 = this;     // concave on the left - defer
+                *e1 = prev();   // flat to the right
+            }
+        } else if (vNext.fY > 0) {
+            DebugPrintf("### py = 0, ny > 0\n");
+            if (vPrev.fX < 0) {
+                type = MONOTONE;
+                *e0 = prev();   // flat to the left
+                *e1 = this;     // convex on the right - try monotone first
+            } else {
+                type = MONOTONE;
+                *e0 = this;     // convex to the left - try monotone first
+                *e1 = prev();   // flat to the right
+            }
+        } else {
+            DebugPrintf("### py = 0, ny = 0\n");
+            // First we try concave, then monotone, then convex.
+            if (vPrev.fX <= vNext.fX) {
+                type = CONCAVE;
+                *e0 = prev();   // flat to the left
+                *e1 = this;     // flat to the right
+            } else {
+                type = CONCAVE;
+                *e0 = this;     // flat to the left
+                *e1 = prev();   // flat to the right
+            }
+        }
+    }
+    return type;
+}
+
+
+#ifdef DEBUG
+static const char* GetVertexTypeString(Vertex::VertexType type) {
+    const char *typeStr = NULL;
+    switch (type) {
+        case Vertex::MONOTONE:
+            typeStr = "MONOTONE";
+            break;
+        case Vertex::CONCAVE:
+            typeStr = "CONCAVE";
+            break;
+        case Vertex::CONVEX:
+            typeStr = "CONVEX";
+            break;
+    }
+    return typeStr;
+}
+
+
+static void PrintVertices(size_t numPts, Vertex *vt) {
+    DebugPrintf("\nVertices:\n");
+    for (size_t i = 0; i < numPts; i++) {
+        Vertex *e0, *e1;
+        Vertex::VertexType type = vt[i].classify(&e0, &e1);
+        DebugPrintf("%2d: (%.7g, %.7g), prev(%d), next(%d), "
+                    "type(%s), left(%d), right(%d)",
+                    i, vt[i].point().fX, vt[i].point().fY,
+                    vt[i].prev() - vt, vt[i].next() - vt,
+                    GetVertexTypeString(type), e0 - vt, e1 - vt);
+        Trapezoid *trap[2];
+        vt[i].trapezoids(trap, trap+1);
+        for (int j = 0; j < 2; ++j) {
+            if (trap[j] != NULL) {
+                DebugPrintf(", trap(L=%d, R=%d, B=%d)",
+                            trap[j]->left()   - vt,
+                            trap[j]->right()  - vt,
+                            trap[j]->bottom() - vt);
+            }
+        }
+        DebugPrintf("\n");
+    }
+}
+
+
+static void PrintVertexPtrs(size_t numPts, VertexPtr *vp, Vertex *vtBase) {
+    DebugPrintf("\nSorted Vertices:\n");
+    for (size_t i = 0; i < numPts; i++) {
+        Vertex *e0, *e1;
+        Vertex *vt = vp[i].vt;
+        Vertex::VertexType type = vt->classify(&e0, &e1);
+        DebugPrintf("%2d: %2d: (%.7g, %.7g), prev(%d), next(%d), "
+                    "type(%s), left(%d), right(%d)",
+                    i, vt - vtBase, vt->point().fX, vt->point().fY,
+                    vt->prev() - vtBase, vt->next() - vtBase,
+                    GetVertexTypeString(type), e0 - vtBase, e1 - vtBase);
+        Trapezoid *trap[2];
+        vt->trapezoids(trap, trap+1);
+        for (int j = 0; j < 2; ++j) {
+            if (trap[j] != NULL) {
+                DebugPrintf(", trap(L=%d, R=%d, B=%d)",
+                            trap[j]->left()   - vtBase,
+                            trap[j]->right()  - vtBase,
+                            trap[j]->bottom() - vtBase);
+            }
+        }
+        DebugPrintf("\n");
+    }
+}
+#else  // !DEBUG
+inline void PrintVertices(size_t numPts, Vertex *vt) {}
+inline void PrintVertexPtrs(size_t numPts, VertexPtr *vp, Vertex *vtBase) {}
+#endif  // !DEBUG
+
+
+// SkTHeapSort is broken, so we use a bubble sort in the meantime.
+template <typename T>
+void BubbleSort(T *array, size_t count) {
+    bool sorted;
+    size_t count_1 = count - 1;
+    do {
+        sorted = true;
+        for (size_t i = 0; i < count_1; ++i) {
+            if (array[i + 1] < array[i]) {
+                T t = array[i];
+                array[i] = array[i + 1];
+                array[i + 1] = t;
+                sorted = false;
+            }
+        }
+    } while (!sorted);
+}
+
+
+// Remove zero-height trapezoids.
+static void RemoveDegenerateTrapezoids(size_t numVt, Vertex *vt) {
+    for (; numVt-- != 0; vt++) {
+        Trapezoid *traps[2];
+        vt->trapezoids(traps, traps+1);
+        if (traps[1] != NULL &&
+                vt->point().fY >= traps[1]->bottom()->point().fY) {
+            traps[1]->nullify();
+            traps[1] = NULL;
+        }
+        if (traps[0] != NULL &&
+                vt->point().fY >= traps[0]->bottom()->point().fY) {
+            if (traps[1] != NULL) {
+                *traps[0] = *traps[1];
+                traps[1]->nullify();
+            } else {
+                traps[0]->nullify();
+            }
+        }
+    }
+}
+
+
+// Enhance the polygon with trapezoids.
+static bool ConvertPointsToVertices(size_t numPts, const SkPoint *pts, Vertex *vta) {
+    DebugPrintf("ConvertPointsToVertices()\n");
+
+    // Clear everything.
+    DebugPrintf("Zeroing vertices\n");
+    sk_bzero(vta, numPts * sizeof(*vta));
+
+    // Initialize vertices.
+    DebugPrintf("Initializing vertices\n");
+    SetVertexPoints(numPts, pts, vta);
+    InitializeVertexTopology(numPts, vta);
+
+    PrintVertices(numPts, vta);
+
+    SkTDArray<VertexPtr> vtptr;
+    vtptr.setCount(numPts);
+    for (int i = numPts; i-- != 0;)
+        vtptr[i].vt = vta + i;
+    PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta);
+    DebugPrintf("Sorting vertrap ptr array [%d] %p %p\n", vtptr.count(),
+        &vtptr[0], &vtptr[vtptr.count() - 1]
+    );
+//  SkTHeapSort(vtptr.begin(), vtptr.count());
+    BubbleSort(vtptr.begin(), vtptr.count());
+    DebugPrintf("Done sorting\n");
+    PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta);
+
+    DebugPrintf("Traversing sorted vertrap ptrs\n");
+    ActiveTrapezoids incompleteTrapezoids;
+    for (VertexPtr *vtpp = vtptr.begin(); vtpp < vtptr.end(); ++vtpp) {
+        DebugPrintf("%d: sorted vertrap %d\n",
+                    vtpp - vtptr.begin(), vtpp->vt - vta);
+        Vertex *vt = vtpp->vt;
+        Vertex *e0, *e1;
+        Trapezoid *t;
+        switch (vt->classify(&e0, &e1)) {
+            case Vertex::MONOTONE:
+            monotone:
+                DebugPrintf("MONOTONE %d %d\n", e0 - vta, e1 - vta);
+                // We should find one edge.
+                t = incompleteTrapezoids.getTrapezoidWithEdge(e0);
+                if (t == NULL) {      // One of the edges is flat.
+                    DebugPrintf("Monotone: cannot find a trapezoid with e0: "
+                                "trying convex\n");
+                    goto convex;
+                }
+                t->setBottom(vt);     // This trapezoid is now complete.
+                incompleteTrapezoids.remove(t);
+
+                if (e0 == t->left())  // Replace the left edge.
+                    incompleteTrapezoids.insertNewTrapezoid(vt, e1, t->right());
+                else                  // Replace the right edge.
+                    incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), e1);
+                break;
+
+            case Vertex::CONVEX:      // Start of a new trapezoid.
+            convex:
+                // We don't expect to find any edges.
+                DebugPrintf("CONVEX %d %d\n", e0 - vta, e1 - vta);
+                if (incompleteTrapezoids.withinActiveTrapezoid(
+                        vt->point(), &t)) {
+                    // Complete trapezoid.
+                    SkASSERT(t != NULL);
+                    t->setBottom(vt);
+                    incompleteTrapezoids.remove(t);
+                    // Introduce two new trapezoids.
+                    incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), e0);
+                    incompleteTrapezoids.insertNewTrapezoid(vt, e1, t->right());
+                } else {
+                    // Insert a new trapezoid.
+                    incompleteTrapezoids.insertNewTrapezoid(vt, e0, e1);
+                }
+                break;
+
+            case Vertex::CONCAVE:   // End of a trapezoid.
+                DebugPrintf("CONCAVE %d %d\n", e0 - vta, e1 - vta);
+                // We should find two edges.
+                t = incompleteTrapezoids.getTrapezoidWithEdge(e0);
+                if (t == NULL) {
+                    DebugPrintf("Concave: cannot find a trapezoid with e0: "
+                                " trying monotone\n");
+                    goto monotone;
+                }
+                SkASSERT(t != NULL);
+                if (e0 == t->left() && e1 == t->right()) {
+                    DebugPrintf(
+                            "Concave edges belong to the same trapezoid.\n");
+                    // Edges belong to the same trapezoid.
+                    // Complete trapezoid & transfer it from the active list.
+                    t->setBottom(vt);
+                    incompleteTrapezoids.remove(t);
+                } else {  // Edges belong to different trapezoids
+                    DebugPrintf(
+                            "Concave edges belong to different trapezoids.\n");
+                    // Complete left and right trapezoids.
+                    Trapezoid *s = incompleteTrapezoids.getTrapezoidWithEdge(
+                                                                            e1);
+                    if (s == NULL) {
+                        DebugPrintf(
+                                "Concave: cannot find a trapezoid with e1: "
+                                " trying monotone\n");
+                        goto monotone;
+                    }
+                    t->setBottom(vt);
+                    s->setBottom(vt);
+                    incompleteTrapezoids.remove(t);
+                    incompleteTrapezoids.remove(s);
+
+                    // Merge the two trapezoids into one below this vertex.
+                    incompleteTrapezoids.insertNewTrapezoid(vt, t->left(),
+                                                                s->right());
+                }
+                break;
+        }
+    }
+
+    RemoveDegenerateTrapezoids(numPts, vta);
+
+    DebugPrintf("Done making trapezoids\n");
+    PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta);
+
+    size_t k = incompleteTrapezoids.count();
+    if (k > 0) {
+        FailureMessage("%d incomplete trapezoids\n", k);
+        return false;
+    }
+    return true;
+}
+
+
+inline void appendTriangleAtVertex(const Vertex *v,
+                                   SkTDArray<SkPoint> *triangles) {
+    SkPoint *p = triangles->append(3);
+    p[0] = v->prev()->point();
+    p[1] = v->point();
+    p[2] = v->next()->point();
+    DebugPrintf(
+          "Appending triangle: { (%.7g, %.7g), (%.7g, %.7g), (%.7g, %.7g) }\n",
+          p[0].fX, p[0].fY,
+          p[1].fX, p[1].fY,
+          p[2].fX, p[2].fY
+    );
+}
+
+
+static size_t CountVertices(const Vertex *first, const Vertex *last) {
+    DebugPrintf("Counting vertices: ");
+    size_t count = 1;
+    for (; first != last; first = first->next()) {
+        ++count;
+        SkASSERT(count <= kMaxCount);
+        if (count >= kMaxCount) {
+            FailureMessage("Vertices do not seem to be in a linked chain\n");
+            break;
+        }
+    }
+    return count;
+}
+
+
+static bool operator<(const SkPoint &p0, const SkPoint &p1) {
+    if (p0.fY < p1.fY)  return true;
+    if (p0.fY > p1.fY)  return false;
+    if (p0.fX < p1.fX)  return true;
+    else                return false;
+}
+
+
+static void PrintLinkedVertices(size_t n, Vertex *vertices) {
+    DebugPrintf("%d vertices:\n", n);
+    Vertex *v;
+    for (v = vertices; n-- != 0; v = v->next())
+        DebugPrintf("   (%.7g, %.7g)\n", v->point().fX, v->point().fY);
+    if (v != vertices)
+        FailureMessage("Vertices are not in a linked chain\n");
+}
+
+
+// Triangulate an unimonotone chain.
+static bool TriangulateMonotone(Vertex *first, Vertex *last,
+                         SkTDArray<SkPoint> *triangles) {
+    DebugPrintf("TriangulateMonotone()\n");
+
+    size_t numVertices = CountVertices(first, last);
+    if (numVertices == kMaxCount) {
+        FailureMessage("Way too many vertices: %d:\n", numVertices);
+        PrintLinkedVertices(numVertices, first);
+        return false;
+    }
+
+    Vertex *start = first;
+    // First find the point with the smallest Y.
+    DebugPrintf("TriangulateMonotone: finding bottom\n");
+    int count = kMaxCount;    // Maximum number of vertices.
+    for (Vertex *v = first->next(); v != first && count-- > 0; v = v->next())
+        if (v->point() < start->point())
+            start = v;
+    if (count <= 0) {
+        FailureMessage("TriangulateMonotone() was given disjoint chain\n");
+        return false;   // Something went wrong.
+    }
+
+    // Start at the far end of the long edge.
+    if (start->prev()->point() < start->next()->point())
+        start = start->next();
+
+    Vertex *current = start->next();
+    while (numVertices >= 3) {
+        if (current->angleIsConvex()) {
+            DebugPrintf("Angle %p is convex\n", current);
+            // Print the vertices
+            PrintLinkedVertices(numVertices, start);
+
+            appendTriangleAtVertex(current, triangles);
+            if (triangles->count() > kMaxCount * 3) {
+                FailureMessage("An extraordinarily large number of triangles "
+                               "were generated\n");
+                return false;
+            }
+            Vertex *save = current->prev();
+            current->delink();
+            current = (save == start || save == start->prev()) ? start->next()
+                                                               : save;
+            --numVertices;
+        } else {
+            if (numVertices == 3) {
+                FailureMessage("Convexity error in TriangulateMonotone()\n");
+                appendTriangleAtVertex(current, triangles);
+                return false;
+            }
+            DebugPrintf("Angle %p is concave\n", current);
+            current = current->next();
+        }
+    }
+    return true;
+}
+
+
+// Split the polygon into sets of unimonotone chains, and eventually call
+// TriangulateMonotone() to convert them into triangles.
+static bool Triangulate(Vertex *first, Vertex *last, SkTDArray<SkPoint> *triangles) {
+    DebugPrintf("Triangulate()\n");
+    Vertex *currentVertex = first;
+    while (!currentVertex->done()) {
+        currentVertex->setDone(true);
+        Vertex *bottomVertex = currentVertex->diagonal();
+        if (bottomVertex != NULL) {
+            Vertex *saveNext = currentVertex->next();
+            Vertex *savePrev = bottomVertex->prev();
+            currentVertex->setNext(bottomVertex);
+            bottomVertex->setPrev(currentVertex);
+            currentVertex->nullifyTrapezoid();
+            bool success = Triangulate(bottomVertex, currentVertex, triangles);
+            currentVertex->setDone(false);
+            bottomVertex->setDone(false);
+            currentVertex->setNext(saveNext);
+            bottomVertex->setPrev(savePrev);
+            bottomVertex->setNext(currentVertex);
+            currentVertex->setPrev(bottomVertex);
+            return Triangulate(currentVertex, bottomVertex, triangles)
+                   && success;
+        } else {
+            currentVertex = currentVertex->next();
+        }
+    }
+    return TriangulateMonotone(first, last, triangles);
+}
+
+
+bool SkConcaveToTriangles(size_t numPts,
+                          const SkPoint pts[],
+                          SkTDArray<SkPoint> *triangles) {
+    DebugPrintf("SkConcaveToTriangles()\n");
+
+    SkTDArray<Vertex> vertices;
+    vertices.setCount(numPts);
+    if (!ConvertPointsToVertices(numPts, pts, vertices.begin()))
+        return false;
+
+    triangles->setReserve(numPts);
+    triangles->setCount(0);
+    return Triangulate(vertices.begin(), vertices.end() - 1, triangles);
+}
diff --git a/src/core/SkConcaveToTriangles.h b/src/core/SkConcaveToTriangles.h
new file mode 100644
index 0000000..e7eb09b
--- /dev/null
+++ b/src/core/SkConcaveToTriangles.h
@@ -0,0 +1,31 @@
+
+/*
+ * Copyright 2009 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkConcaveToTriangles_DEFINED
+#define SkConcaveToTriangles_DEFINED
+
+#include "SkPoint.h"
+#include "SkTDArray.h"
+
+
+// Triangulate a polygon.
+// The polygon can be convex or concave, and can have holes or multiple contours
+// of arbitrary recursion.
+// The holes must have opposite orientation of the outer contours, whereas
+// islands within the holes must have the same orientation as the outer contour.
+// Contours should be joined by zero-thickness double-edges, to mimic a single
+// polygon.  The polygon should not be self-intersecting.
+// Currently, the outer contour must be right-handed, i.e. it should be oriented
+// in the direction that rotates the X-axis to the Y-axis.
+bool SkConcaveToTriangles(size_t count,
+                          const SkPoint pts[],
+                          SkTDArray<SkPoint> *triangles);
+
+
+#endif  // SkConcaveToTriangles_DEFINED
diff --git a/src/core/SkConfig8888.cpp b/src/core/SkConfig8888.cpp
new file mode 100644
index 0000000..f889f20
--- /dev/null
+++ b/src/core/SkConfig8888.cpp
@@ -0,0 +1,282 @@
+#include "SkConfig8888.h"
+#include "SkMathPriv.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/src/core/SkConfig8888.h b/src/core/SkConfig8888.h
new file mode 100644
index 0000000..a891370
--- /dev/null
+++ b/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/src/core/SkCordic.cpp b/src/core/SkCordic.cpp
new file mode 100644
index 0000000..00dd76e
--- /dev/null
+++ b/src/core/SkCordic.cpp
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2006 The Android 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 "SkMathPriv.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
+
+#include "SkFloatingPoint.h"
+
+void SkCordic_UnitTest()
+{
+#if defined(SK_SUPPORT_UNITTEST)
+    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/src/core/SkCordic.h b/src/core/SkCordic.h
new file mode 100644
index 0000000..aa703eb
--- /dev/null
+++ b/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/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
new file mode 100644
index 0000000..48f85b0
--- /dev/null
+++ b/src/core/SkCoreBlitters.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2006 The Android 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;
+    SkBlitRow::ColorRectProc fColorRect32Proc;
+
+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 blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+    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/src/core/SkCubicClipper.cpp b/src/core/SkCubicClipper.cpp
new file mode 100644
index 0000000..3db2feb
--- /dev/null
+++ b/src/core/SkCubicClipper.cpp
@@ -0,0 +1,163 @@
+
+/*
+ * 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() {
+    fClip.setEmpty();
+}
+
+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/src/core/SkCubicClipper.h b/src/core/SkCubicClipper.h
new file mode 100644
index 0000000..c52eabe
--- /dev/null
+++ b/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/src/core/SkData.cpp b/src/core/SkData.cpp
new file mode 100644
index 0000000..3e0c71a
--- /dev/null
+++ b/src/core/SkData.cpp
@@ -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.
+ */
+
+#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;
+    fSize = size;
+    fReleaseProc = proc;
+    fReleaseProcContext = context;
+}
+
+SkData::~SkData() {
+    if (fReleaseProc) {
+        fReleaseProc(fPtr, fSize, fReleaseProcContext);
+    }
+}
+
+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) {
+        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));
+}
+
+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
new file mode 100644
index 0000000..4b33836
--- /dev/null
+++ b/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/src/core/SkDeque.cpp b/src/core/SkDeque.cpp
new file mode 100644
index 0000000..d210dcf
--- /dev/null
+++ b/src/core/SkDeque.cpp
@@ -0,0 +1,308 @@
+
+/*
+ * Copyright 2006 The Android 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"
+
+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
+
+    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, 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, int allocCount)
+        : fElemSize(elemSize)
+        , fInitialStorage(storage)
+        , fCount(0)
+        , fAllocCount(allocCount) {
+    SkASSERT(storageSize == 0 || storage != NULL);
+    SkASSERT(allocCount >= 1);
+
+    if (storageSize >= sizeof(Block) + elemSize) {
+        fFrontBlock = (Block*)storage;
+        fFrontBlock->init(storageSize);
+    } else {
+        fFrontBlock = NULL;
+    }
+    fBackBlock = fFrontBlock;
+    fFront = fBack = NULL;
+}
+
+SkDeque::~SkDeque() {
+    Block* head = fFrontBlock;
+    Block* initialHead = (Block*)fInitialStorage;
+
+    while (head) {
+        Block* next = head->fNext;
+        if (head != initialHead) {
+            this->freeBlock(head);
+        }
+        head = next;
+    }
+}
+
+void* SkDeque::push_front() {
+    fCount += 1;
+
+    if (NULL == fFrontBlock) {
+        fFrontBlock = this->allocateBlock(fAllocCount);
+        fBackBlock = fFrontBlock;     // update our linklist
+    }
+
+    Block*  first = fFrontBlock;
+    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?
+            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 == fBackBlock) {
+        fBackBlock = this->allocateBlock(fAllocCount);
+        fFrontBlock = fBackBlock; // update our linklist
+    }
+
+    Block*  last = fBackBlock;
+    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?
+            last = this->allocateBlock(fAllocCount);
+            last->fPrev = fBackBlock;
+            fBackBlock->fNext = last;
+            fBackBlock = last;
+            goto INIT_CHUNK;
+        }
+    }
+
+    last->fEnd = end;
+    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;
+
+    Block*  first = fFrontBlock;
+
+    SkASSERT(first != NULL);
+
+    if (first->fBegin == NULL) {  // we were marked empty from before
+        first = first->fNext;
+        first->fPrev = NULL;
+        this->freeBlock(fFrontBlock);
+        fFrontBlock = first;
+        SkASSERT(first != NULL);    // else we popped too far
+    }
+
+    char* begin = first->fBegin + fElemSize;
+    SkASSERT(begin <= first->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;
+        }
+    }
+}
+
+void SkDeque::pop_back() {
+    SkASSERT(fCount > 0);
+    fCount -= 1;
+
+    Block* last = fBackBlock;
+
+    SkASSERT(last != NULL);
+
+    if (last->fEnd == NULL) {  // we were marked empty from before
+        last = last->fPrev;
+        last->fNext = NULL;
+        this->freeBlock(fBackBlock);
+        fBackBlock = 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;
+        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::Iter::Iter() : fCurBlock(NULL), fPos(NULL), fElemSize(0) {}
+
+SkDeque::Iter::Iter(const SkDeque& d, IterStart startLoc) {
+    this->reset(d, startLoc);
+}
+
+// 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 <= fCurBlock->fEnd);
+        if (next == fCurBlock->fEnd) { // exhausted this chunk, move to next
+            do {
+                fCurBlock = fCurBlock->fNext;
+            } while (fCurBlock != NULL && fCurBlock->fBegin == NULL);
+            next = fCurBlock ? fCurBlock->fBegin : NULL;
+        }
+        fPos = next;
+    }
+    return pos;
+}
+
+// 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;
+    }
+    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..071a89c
--- /dev/null
+++ b/src/core/SkDescriptor.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 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
new file mode 100644
index 0000000..7da9a6b
--- /dev/null
+++ b/src/core/SkDevice.cpp
@@ -0,0 +1,534 @@
+
+/*
+ * Copyright 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 "SkRasterClip.h"
+#include "SkRect.h"
+#include "SkShader.h"
+
+SK_DEFINE_INST_COUNT(SkDevice)
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define CHECK_FOR_NODRAW_ANNOTATION(paint) \
+    do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice::SkDevice(const SkBitmap& bitmap)
+    : fBitmap(bitmap)
+#ifdef SK_DEBUG
+    , fAttachedToCanvas(false)
+#endif
+{
+    fOrigin.setZero();
+    fMetaData = NULL;
+}
+
+SkDevice::SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque)
+#ifdef SK_DEBUG
+    : fAttachedToCanvas(false)
+#endif
+{
+    fOrigin.setZero();
+    fMetaData = NULL;
+
+    fBitmap.setConfig(config, width, height);
+    fBitmap.allocPixels();
+    fBitmap.setIsOpaque(isOpaque);
+    if (!isOpaque) {
+        fBitmap.eraseColor(0);
+    }
+}
+
+SkDevice::~SkDevice() {
+    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) {
+    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::canHandleImageFilter(SkImageFilter*) {
+    return false;
+}
+
+bool SkDevice::filterImage(SkImageFilter* filter, 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);
+    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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) {
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
+    draw.drawRect(r, paint);
+}
+
+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);
+}
+
+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::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
+                              const SkRect* src, const SkRect& dst,
+                              const SkPaint& paint) {
+#ifdef SK_SUPPORT_INT_SRCRECT_DRAWBITMAPRECT
+    SkMatrix matrix;
+    // Compute matrix from the two rectangles
+    {
+        SkRect tmpSrc;
+        if (src) {
+            tmpSrc = *src;
+            // if the extract process clipped off the top or left of the
+            // original, we adjust for that here to get the position right.
+            if (tmpSrc.fLeft > 0) {
+                tmpSrc.fRight -= tmpSrc.fLeft;
+                tmpSrc.fLeft = 0;
+            }
+            if (tmpSrc.fTop > 0) {
+                tmpSrc.fBottom -= tmpSrc.fTop;
+                tmpSrc.fTop = 0;
+            }
+        } else {
+            tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
+                       SkIntToScalar(bitmap.height()));
+        }
+        matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+    }
+
+    // ensure that src is "valid" before we pass it to our internal routines
+    // and to SkDevice. i.e. sure it is contained inside the original bitmap.
+    SkIRect isrcStorage;
+    SkIRect* isrcPtr = NULL;
+    if (src) {
+        src->roundOut(&isrcStorage);
+        if (!isrcStorage.intersect(0, 0, bitmap.width(), bitmap.height())) {
+            return;
+        }
+        isrcPtr = &isrcStorage;
+    }
+
+    this->drawBitmap(draw, bitmap, isrcPtr, matrix, paint);
+#else
+    SkMatrix    matrix;
+    SkRect      bitmapBounds, tmpSrc, tmpDst;
+    SkBitmap    tmpBitmap;
+
+    bitmapBounds.set(0, 0,
+                     SkIntToScalar(bitmap.width()),
+                     SkIntToScalar(bitmap.height()));
+
+    // 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);
+        }
+    }
+
+    // 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);
+#endif
+}
+
+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/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
new file mode 100644
index 0000000..abb78ac
--- /dev/null
+++ b/src/core/SkDeviceProfile.cpp
@@ -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.
+ */
+
+
+#include "SkDeviceProfile.h"
+
+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
+
+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/src/core/SkDeviceProfile.h b/src/core/SkDeviceProfile.h
new file mode 100644
index 0000000..2a3f4bb
--- /dev/null
+++ b/src/core/SkDeviceProfile.h
@@ -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.
+ */
+
+#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
new file mode 100644
index 0000000..e733aad
--- /dev/null
+++ b/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/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
new file mode 100644
index 0000000..885a810
--- /dev/null
+++ b/src/core/SkDraw.cpp
@@ -0,0 +1,2649 @@
+
+/*
+ * Copyright 2006 The Android 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 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 {
+    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.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) {
+            // 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;)
+
+    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
+
+    {
+        SkScalar coverage;
+        if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
+            if (SK_Scalar1 == coverage) {
+                paint.writable()->setStrokeWidth(0);
+            } 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
+                SkPaint* writablePaint = paint.writable();
+                writablePaint->setStrokeWidth(0);
+                writablePaint->setAlpha(newAlpha);
+            }
+        }
+    }
+
+    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);
+
+    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)) {
+        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 "SkTextToPathIter.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);
+
+    SkMatrix    matrix;
+    matrix.setScale(iter.getPathScale(), iter.getPathScale());
+    matrix.postTranslate(x, y);
+
+    const SkPath* iterPath;
+    SkScalar xpos, prevXPos = 0;
+
+    while (iter.next(&iterPath, &xpos)) {
+        matrix.postTranslate(xpos - prevXPos, 0);
+        if (iterPath) {
+            const SkPaint& pnt = iter.getPaint();
+            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;
+    }
+
+    // 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()) ||
+        (0 == paint.getStrokeWidth() && SkPaint::kStroke_Style == paint.getStyle())) {
+        this->drawText_asPaths(text, byteLength, x, y, paint);
+        return;
+    }
+
+    SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
+
+    const SkMatrix* matrix = fMatrix;
+
+    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;
+
+    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
+        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);
+
+                    proc(d1g,
+                         SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf,
+                         SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf,
+                         glyph);
+                }
+                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;
+            }
+        }
+    }
+}
+
+#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;
+
+        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;
+            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);
+    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 (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);
+            }
+        }
+    }
+}
+
+#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);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTriColorShader)
+
+protected:
+    SkTriColorShader(SkFlattenableReadBuffer& buffer) : SkShader(buffer) {}
+
+private:
+    SkMatrix    fDstToUnit;
+    SkPMColor   fColors[3];
+
+    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));
+}
+
+#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 = SkIPoint::Make(0, 0);
+    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,
+                           SkPaint::Style style) {
+    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);
+    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,
+                        SkPaint::Style style) {
+    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, style);
+    }
+
+    return true;
+}
diff --git a/src/core/SkDrawProcs.h b/src/core/SkDrawProcs.h
new file mode 100644
index 0000000..ba1a875
--- /dev/null
+++ b/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/src/core/SkEdge.cpp b/src/core/SkEdge.cpp
new file mode 100644
index 0000000..fff3dbc
--- /dev/null
+++ b/src/core/SkEdge.cpp
@@ -0,0 +1,498 @@
+
+/*
+ * Copyright 2006 The Android 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).
+*/
+
+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,
+                    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);
+    //fCubicDShift only set for cubics
+    fCurveCount = SkToS8(1 << shift);
+
+    /*
+     *  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 = 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
+    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;
+        }
+
+        // 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;
+    } while (count < 0 && !success);
+
+    fCx         = newx;
+    fCy         = newy;
+    fCurveCount = SkToS8(count);
+    return success;
+}
+
+
+
diff --git a/src/core/SkEdge.h b/src/core/SkEdge.h
new file mode 100644
index 0000000..5367862
--- /dev/null
+++ b/src/core/SkEdge.h
@@ -0,0 +1,133 @@
+
+/*
+ * Copyright 2006 The Android 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"
+#include "SkFDot6.h"
+#include "SkMath.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);
+    // 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);
+
+    inline bool intersectsClip(const SkIRect& clip) const {
+        SkASSERT(fFirstY < clip.fBottom);
+        return fLastY >= clip.fTop;
+    }
+
+#ifdef SK_DEBUG
+    void dump() const {
+        SkDebugf("edge: firstY:%d lastY:%d x:%g dx:%g w:%d\n", fFirstY, fLastY, SkFixedToFloat(fX), SkFixedToFloat(fDX), fWinding);
+    }
+
+    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();
+};
+
+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);
+
+    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;
+    return 1;
+}
+
+
+#endif
diff --git a/src/core/SkEdgeBuilder.cpp b/src/core/SkEdgeBuilder.cpp
new file mode 100644
index 0000000..ad9eeb7
--- /dev/null
+++ b/src/core/SkEdgeBuilder.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 "SkEdgeBuilder.h"
+#include "SkPath.h"
+#include "SkEdge.h"
+#include "SkEdgeClipper.h"
+#include "SkLineClipper.h"
+#include "SkGeometry.h"
+
+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], 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::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;
+
+    if (iclip) {
+        SkRect clip;
+        setShiftedClip(&clip, *iclip, shiftUp);
+        SkEdgeClipper clipper;
+
+        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);
+                    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, 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:
+                    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;
+            }
+        }
+    }
+    fEdgeList = fList.begin();
+    return fList.count();
+}
+
diff --git a/src/core/SkEdgeBuilder.h b/src/core/SkEdgeBuilder.h
new file mode 100644
index 0000000..b296f77
--- /dev/null
+++ b/src/core/SkEdgeBuilder.h
@@ -0,0 +1,51 @@
+
+/*
+ * 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();
+
+    // 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 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
new file mode 100644
index 0000000..9fd6a0e
--- /dev/null
+++ b/src/core/SkEdgeClipper.cpp
@@ -0,0 +1,534 @@
+
+/*
+ * 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 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 {
+            // 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 to clean up imprecise numerics in the chop
+            clamp_le(tmp[1].fY, clip.fBottom);
+            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 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 {
+            // 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 to clean up imprecise numerics in the chop
+            clamp_le(tmp[1].fX, clip.fRight);
+            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;
+            clamp_ge(tmp[4].fY, clip.fTop);
+            clamp_ge(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);
+            tmp[3].fY = clip.fBottom;
+            clamp_le(tmp[2].fY, clip.fBottom);
+            clamp_le(tmp[1].fY, tmp[2].fY);
+
+            pts[1] = tmp[1];
+            pts[2] = tmp[2];
+            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;
+            clamp_ge(tmp[4].fX, clip.fLeft);
+            clamp_ge(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);
+            tmp[3].fX = clip.fRight;
+            clamp_le(tmp[2].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 {
+            // 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/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..9b8e4d0
--- /dev/null
+++ b/src/core/SkFDot6.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 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/SkFP.h b/src/core/SkFP.h
new file mode 100644
index 0000000..1d1507a
--- /dev/null
+++ b/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/src/core/SkFilterProc.cpp b/src/core/SkFilterProc.cpp
new file mode 100644
index 0000000..2903849
--- /dev/null
+++ b/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/src/core/SkFilterProc.h b/src/core/SkFilterProc.h
new file mode 100644
index 0000000..4dfdf60
--- /dev/null
+++ b/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/src/core/SkFilterShader.h b/src/core/SkFilterShader.h
new file mode 100644
index 0000000..f637584
--- /dev/null
+++ b/src/core/SkFilterShader.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 SkFilterShader_DEFINED
+#define SkFilterShader_DEFINED
+
+#include "SkShader.h"
+
+class SkColorFilter;
+
+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();
+
+    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
new file mode 100644
index 0000000..39fc724
--- /dev/null
+++ b/src/core/SkFlate.cpp
@@ -0,0 +1,141 @@
+
+/*
+ * 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_HAS_ZLIB
+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 {
+
+#ifdef SK_SYSTEM_ZLIB
+#include <zlib.h>
+#else
+#include SK_ZLIB_INCLUDE
+#endif
+
+// 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/src/core/SkFlattenable.cpp b/src/core/SkFlattenable.cpp
new file mode 100644
index 0000000..b4efe91
--- /dev/null
+++ b/src/core/SkFlattenable.cpp
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkFlattenable.h"
+#include "SkPtrRecorder.h"
+
+SK_DEFINE_INST_COUNT(SkFlattenable)
+
+///////////////////////////////////////////////////////////////////////////////
+
+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()
+        in their code.
+    */
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkNamedFactorySet::SkNamedFactorySet() : fNextAddedFactory(0) {}
+
+uint32_t SkNamedFactorySet::find(SkFlattenable::Factory factory) {
+    uint32_t index = fFactorySet.find(factory);
+    if (index > 0) {
+        return index;
+    }
+    const char* name = SkFlattenable::FactoryToName(factory);
+    if (NULL == name) {
+        return 0;
+    }
+    *fNames.append() = name;
+    return fFactorySet.add(factory);
+}
+
+const char* SkNamedFactorySet::getNextAddedFactoryName() {
+    if (fNextAddedFactory < fNames.count()) {
+        return fNames[fNextAddedFactory++];
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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  1024
+
+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;
+}
+
+#ifdef 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[]) {
+#ifdef 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) {
+#ifdef 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;
+}
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
new file mode 100644
index 0000000..b5cc4f1
--- /dev/null
+++ b/src/core/SkFloat.cpp
@@ -0,0 +1,393 @@
+
+/*
+ * 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 "SkMathPriv.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"
+#include "SkFloatingPoint.h"
+
+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;
+
+    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
diff --git a/src/core/SkFloat.h b/src/core/SkFloat.h
new file mode 100644
index 0000000..74cd19e
--- /dev/null
+++ b/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/src/core/SkFloatBits.cpp b/src/core/SkFloatBits.cpp
new file mode 100644
index 0000000..54a1d20
--- /dev/null
+++ b/src/core/SkFloatBits.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright 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 "SkMathPriv.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;
+    }
+}
+
+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;
+}
+
diff --git a/src/core/SkFontHost.cpp b/src/core/SkFontHost.cpp
new file mode 100644
index 0000000..c1836ac
--- /dev/null
+++ b/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/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
new file mode 100644
index 0000000..1c1a4f1
--- /dev/null
+++ b/src/core/SkGeometry.cpp
@@ -0,0 +1,1362 @@
+
+/*
+ * Copyright 2006 The Android 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) {
+    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/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
new file mode 100644
index 0000000..5904edd
--- /dev/null
+++ b/src/core/SkGlyphCache.cpp
@@ -0,0 +1,742 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkPath.h"
+#include "SkTemplates.h"
+#include "SkTLS.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->countPoints() * 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::invokeAndRemoveAuxProcs() {
+    AuxProcRec* rec = fAuxProcList;
+    while (rec) {
+        rec->fProc(rec->fData);
+        AuxProcRec* next = rec->fNext;
+        SkDELETE(rec);
+        rec = next;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#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)
+    #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:
+    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
+    }
+
+    ~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
+    SkGlyphCache*   fHash[HASH_COUNT];
+#endif
+
+#ifdef SK_DEBUG
+    void validate() const;
+#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);
+    }
+};
+
+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 = 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();
+    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();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) {
+    if (cache) {
+        while (cache->fNext) {
+            cache = cache->fNext;
+        }
+    }
+    return cache;
+}
+
+#ifdef SK_DEBUG
+void SkGlyphCache_Globals::validate() const {
+    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);
+    }
+    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
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTypefaceCache.h"
+
+size_t SkGraphics::GetFontCacheLimit() {
+    return getSharedGlobals().getFontCacheLimit();
+}
+
+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
new file mode 100644
index 0000000..472bcde
--- /dev/null
+++ b/src/core/SkGlyphCache.h
@@ -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.
+ */
+
+
+#ifndef SkGlyphCache_DEFINED
+#define SkGlyphCache_DEFINED
+
+#include "SkBitmap.h"
+#include "SkChunkAlloc.h"
+#include "SkDescriptor.h"
+#include "SkGlyph.h"
+#include "SkScalerContext.h"
+#include "SkTemplates.h"
+#include "SkTDArray.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);
+
+    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);
+    }
+
+#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);
+
+    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/src/core/SkGraphics.cpp b/src/core/SkGraphics.cpp
new file mode 100644
index 0000000..53d0318
--- /dev/null
+++ b/src/core/SkGraphics.cpp
@@ -0,0 +1,172 @@
+
+/*
+ * Copyright 2006 The Android 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() {
+    SkFlattenable::InitializeFlattenables();
+#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
+}
+
+void SkGraphics::Term() {
+    PurgeFontCache();
+    SkPaint::Term();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
new file mode 100644
index 0000000..45ad024
--- /dev/null
+++ b/src/core/SkImageFilter.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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"
+#include "stdarg.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(int inputCount, ...)
+  : fInputCount(inputCount), fInputs(new SkImageFilter*[inputCount]) {
+    va_list ap;
+    va_start(ap, inputCount);
+    for (int i = 0; i < inputCount; ++i) {
+        fInputs[i] = va_arg(ap, SkImageFilter*);
+        SkSafeRef(fInputs[i]);
+    }
+    va_end(ap);
+}
+
+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;
+}
+
+GrTexture* SkImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* texture, const SkRect& rect) {
+    return NULL;
+}
+
+bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+                                   SkIRect* dst) {
+    *dst = src;
+    return true;
+}
+
+bool SkImageFilter::asNewCustomStage(GrCustomStage**, 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..d22639e
--- /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"
+
+#ifdef SK_ENABLE_INST_COUNT
+bool gPrintInstCount = false;
+#endif
diff --git a/src/core/SkLineClipper.cpp b/src/core/SkLineClipper.cpp
new file mode 100644
index 0000000..7b7746f
--- /dev/null
+++ b/src/core/SkLineClipper.cpp
@@ -0,0 +1,296 @@
+
+/*
+ * Copyright 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"
+
+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;
+    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);
+
+        // 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);
+#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
+
+#ifdef SK_SCALAR_IS_FLOAT
+// 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
+
+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) {
+        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/src/core/SkMMapStream.cpp b/src/core/SkMMapStream.cpp
new file mode 100644
index 0000000..1579044
--- /dev/null
+++ b/src/core/SkMMapStream.cpp
@@ -0,0 +1,78 @@
+
+/*
+ * Copyright 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
+    fSize = 0;
+
+    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/src/core/SkMallocPixelRef.cpp b/src/core/SkMallocPixelRef.cpp
new file mode 100644
index 0000000..d700983
--- /dev/null
+++ b/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 "SkFlattenableBuffers.h"
+
+SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size,
+                                   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);
+    if (fOwnPixels) {
+        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.writeByteArray(fStorage, fSize);
+    buffer.writeBool(fCTable != NULL);
+    if (fCTable) {
+        buffer.writeFlattenable(fCTable);
+    }
+}
+
+SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer, NULL) {
+    fSize = buffer.getArrayCount();
+    fStorage = sk_malloc_throw(fSize);
+    buffer.readByteArray(fStorage);
+    if (buffer.readBool()) {
+        fCTable = buffer.readFlattenableT<SkColorTable>();
+    } else {
+        fCTable = NULL;
+    }
+    fOwnPixels = true;
+
+    this->setPreLocked(fStorage, fCTable);
+}
diff --git a/src/core/SkMask.cpp b/src/core/SkMask.cpp
new file mode 100644
index 0000000..c290b57
--- /dev/null
+++ b/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/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
new file mode 100644
index 0000000..f27ebd1
--- /dev/null
+++ b/src/core/SkMaskFilter.cpp
@@ -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.
+ */
+
+
+#include "SkMaskFilter.h"
+#include "SkBlitter.h"
+#include "SkBounder.h"
+#include "SkDraw.h"
+#include "SkRasterClip.h"
+
+SK_DEFINE_INST_COUNT(SkMaskFilter)
+
+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, SkPaint::Style style) {
+    SkMask  srcM, dstM;
+
+    if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode,
+                            style)) {
+        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/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..a092b20
--- /dev/null
+++ b/src/core/SkMaskGamma.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright 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)
+
+    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 << kLuminanceBits_Max); ++i) {
+            U8CPU lum = sk_t_scale255<kLuminanceBits_Max>(i);
+            SkTMaskGamma_build_correcting_lut(fGammaTables[i], lum, contrast,
+                                              paintConvert, paintGamma,
+                                              deviceConvert, deviceGamma);
+        }
+    }
+
+    /** Given a color, returns the closest cannonical color. */
+    static SkColor cannonicalColor(SkColor color) {
+        return SkColorSetRGB(
+                   sk_t_scale255<kLuminanceBits_R>(SkColorGetR(color) >> (8 - kLuminanceBits_R)),
+                   sk_t_scale255<kLuminanceBits_G>(SkColorGetG(color) >> (8 - kLuminanceBits_G)),
+                   sk_t_scale255<kLuminanceBits_B>(SkColorGetB(color) >> (8 - kLuminanceBits_B)));
+    }
+
+    /** 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);
+
+private:
+    enum LuminanceBits {
+        kLuminanceBits_R = R_LUM_BITS,
+        kLuminanceBits_G = G_LUM_BITS,
+        kLuminanceBits_B = B_LUM_BITS,
+        kLuminanceBits_Max = 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 << kLuminanceBits_Max][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.
+ */
+template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend {
+private:
+    SkTMaskPreBlend(SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>* parent,
+                    const uint8_t* r,
+                    const uint8_t* g,
+                    const uint8_t* b)
+    : fParent(parent), fR(r), fG(g), fB(b) {
+        SkSafeRef(parent);
+    }
+    SkAutoTUnref<SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> > fParent;
+    friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>;
+public:
+    /**
+     * 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(that.fParent.get()), fR(that.fR), fG(that.fG), fB(that.fB) {
+        SkSafeRef(fParent.get());
+    }
+    ~SkTMaskPreBlend() { }
+    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) {
+    return fIsLinear ? SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(
+                          NULL, NULL, NULL, NULL)
+                      : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(
+                          this,
+                          fGammaTables[SkColorGetR(color) >> (8 - kLuminanceBits_Max)],
+                          fGammaTables[SkColorGetG(color) >> (8 - kLuminanceBits_Max)],
+                          fGammaTables[SkColorGetB(color) >> (8 - kLuminanceBits_Max)]);
+}
+
+///@{
+/**
+ *  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
new file mode 100644
index 0000000..ccd96e9
--- /dev/null
+++ b/src/core/SkMath.cpp
@@ -0,0 +1,536 @@
+/*
+ * 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 "SkMathPriv.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/src/core/SkMathPriv.h b/src/core/SkMathPriv.h
new file mode 100644
index 0000000..11ad1ba
--- /dev/null
+++ b/src/core/SkMathPriv.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 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
new file mode 100644
index 0000000..4cd27e4
--- /dev/null
+++ b/src/core/SkMatrix.cpp
@@ -0,0 +1,1803 @@
+
+/*
+ * Copyright 2006 The Android 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 {
+#ifdef SK_SCALAR_SLOW_COMPARES
+    if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
+            SkScalarAs2sCompliment(fMat[kMPersp1]) |
+            (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
+        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(kOnlyPerspectiveValid_Mask | kUnknown_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)) {
+        return SkToU8(kORableMasks);
+    }
+
+    if (SkScalarAs2sCompliment(fMat[kMTransX]) |
+            SkScalarAs2sCompliment(fMat[kMTransY])) {
+        mask |= kTranslate_Mask;
+    }
+#else
+    if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 ||
+        fMat[kMPersp2] != kMatrix22Elem) {
+        // 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) {
+        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) {
+        // 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;
+
+        // 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.
+
+        // map non-zero to 1
+        m01 = m01 != 0;
+        m10 = m10 != 0;
+
+        int dp0 = 0 == (m00 | m11) ;  // true if both are 0
+        int ds1 = m01 & m10;        // true if both are 1
+
+        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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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;
+
+        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;
+    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;
+        }
+
+        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
+        } 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(fTypeMask);
+
+        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::writeToMemory(void* buffer) const {
+    // TODO write less for simple matrices
+    if (buffer) {
+        memcpy(buffer, fMat, 9 * sizeof(SkScalar));
+    }
+    return 9 * sizeof(SkScalar);
+}
+
+uint32_t SkMatrix::readFromMemory(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 {
+    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
+}
diff --git a/src/core/SkMemory_stdlib.cpp b/src/core/SkMemory_stdlib.cpp
new file mode 100644
index 0000000..17bf6a9
--- /dev/null
+++ b/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/src/core/SkMetaData.cpp b/src/core/SkMetaData.cpp
new file mode 100644
index 0000000..338ec5e
--- /dev/null
+++ b/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/src/core/SkOrderedReadBuffer.cpp b/src/core/SkOrderedReadBuffer.cpp
new file mode 100644
index 0000000..0a7bd90
--- /dev/null
+++ b/src/core/SkOrderedReadBuffer.cpp
@@ -0,0 +1,255 @@
+
+/*
+ * Copyright 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) {
+    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();
+            *bitmap = *fBitmapStorage->getBitmap(index);
+            fBitmapStorage->releaseRef(index);
+        } else {
+            bitmap->unflatten(*this);
+        }
+    }
+}
+
+SkTypeface* SkOrderedReadBuffer::readTypeface() {
+
+    uint32_t index = fReader.readU32();
+    if (0 == index || index > (unsigned)fTFCount) {
+        if (index) {
+            SkDebugf("====== typeface index %d\n", index);
+        }
+        return NULL;
+    } else {
+        SkASSERT(fTFArray);
+        return fTFArray[index - 1];
+    }
+}
+
+SkFlattenable* SkOrderedReadBuffer::readFlattenable() {
+    SkFlattenable::Factory factory = NULL;
+
+    if (fFactoryCount > 0) {
+        int32_t index = fReader.readU32();
+        if (0 == index) {
+            return NULL; // writer failed to give us the flattenable
+        }
+        index -= 1;     // we stored the index-base-1
+        SkASSERT(index < fFactoryCount);
+        factory = fFactoryArray[index];
+    } else if (fFactoryTDArray) {
+        int32_t index = fReader.readU32();
+        if (0 == index) {
+            return NULL; // writer failed to give us the flattenable
+        }
+        index -= 1;     // we stored the index-base-1
+        factory = (*fFactoryTDArray)[index];
+    } else {
+        factory = (SkFlattenable::Factory)readFunctionPtr();
+        if (NULL == factory) {
+            return NULL; // writer failed to give us the flattenable
+        }
+    }
+
+    // if we get here, factory may still be null, but if that is the case, the
+    // failure was ours, not the writer.
+    SkFlattenable* obj = NULL;
+    uint32_t sizeRecorded = fReader.readU32();
+    if (factory) {
+        uint32_t offset = fReader.offset();
+        obj = (*factory)(*this);
+        // check that we read the amount we expected
+        uint32_t sizeRead = fReader.offset() - offset;
+        if (sizeRecorded != sizeRead) {
+            // we could try to fix up the offset...
+            sk_throw();
+        }
+    } else {
+        // we must skip the remaining data
+        fReader.skip(sizeRecorded);
+    }
+    return obj;
+}
diff --git a/src/core/SkOrderedReadBuffer.h b/src/core/SkOrderedReadBuffer.h
new file mode 100644
index 0000000..b3fde17
--- /dev/null
+++ b/src/core/SkOrderedReadBuffer.h
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkOrderedReadBuffer_DEFINED
+#define SkOrderedReadBuffer_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkBitmap.h"
+#include "SkBitmapHeap.h"
+#include "SkFlattenableBuffers.h"
+#include "SkPath.h"
+#include "SkReader32.h"
+#include "SkSerializationHelpers.h"
+
+class SkOrderedReadBuffer : public SkFlattenableReadBuffer {
+public:
+    SkOrderedReadBuffer();
+    SkOrderedReadBuffer(const void* data, size_t size);
+    SkOrderedReadBuffer(SkStream* stream);
+    virtual ~SkOrderedReadBuffer();
+
+    virtual SkOrderedReadBuffer* getOrderedBinaryBuffer() SK_OVERRIDE { return this; }
+
+    SkReader32* getReader32() { return &fReader; }
+
+    uint32_t size() { return fReader.size(); }
+    uint32_t offset() { return fReader.offset(); }
+    bool eof() { return fReader.eof(); }
+    const void* skip(size_t size) { return fReader.skip(size); }
+
+    // primitives
+    virtual bool readBool() SK_OVERRIDE;
+    virtual SkColor readColor() SK_OVERRIDE;
+    virtual SkFixed readFixed() SK_OVERRIDE;
+    virtual int32_t readInt() SK_OVERRIDE;
+    virtual SkScalar readScalar() SK_OVERRIDE;
+    virtual uint32_t readUInt() SK_OVERRIDE;
+    virtual int32_t read32() SK_OVERRIDE;
+
+    // strings -- the caller is responsible for freeing the string contents
+    virtual char* readString() SK_OVERRIDE;
+    virtual void* readEncodedString(size_t* length, SkPaint::TextEncoding encoding) SK_OVERRIDE;
+
+    // common data structures
+    virtual SkFlattenable* readFlattenable() SK_OVERRIDE;
+    virtual void readPoint(SkPoint* point) SK_OVERRIDE;
+    virtual void readMatrix(SkMatrix* matrix) SK_OVERRIDE;
+    virtual void readIRect(SkIRect* rect) SK_OVERRIDE;
+    virtual void readRect(SkRect* rect) SK_OVERRIDE;
+    virtual void readRegion(SkRegion* region) SK_OVERRIDE;
+    virtual void readPath(SkPath* path) SK_OVERRIDE;
+
+    // binary data and arrays
+    virtual uint32_t readByteArray(void* value) SK_OVERRIDE;
+    virtual uint32_t readColorArray(SkColor* colors) SK_OVERRIDE;
+    virtual uint32_t readIntArray(int32_t* values) SK_OVERRIDE;
+    virtual uint32_t readPointArray(SkPoint* points) SK_OVERRIDE;
+    virtual uint32_t readScalarArray(SkScalar* values) SK_OVERRIDE;
+
+    // helpers to get info about arrays and binary data
+    virtual uint32_t getArrayCount() SK_OVERRIDE;
+
+    virtual void readBitmap(SkBitmap* bitmap) SK_OVERRIDE;
+    virtual SkTypeface* readTypeface() SK_OVERRIDE;
+
+    void setBitmapStorage(SkBitmapHeapReader* bitmapStorage) {
+        SkRefCnt_SafeAssign(fBitmapStorage, bitmapStorage);
+    }
+
+    void setTypefaceArray(SkTypeface* array[], int count) {
+        fTFArray = array;
+        fTFCount = count;
+    }
+
+    /**
+     *  Call this with a pre-loaded array of Factories, in the same order as
+     *  were created/written by the writer. SkPicture uses this.
+     */
+    void setFactoryPlayback(SkFlattenable::Factory array[], int count) {
+        fFactoryTDArray = NULL;
+        fFactoryArray = array;
+        fFactoryCount = count;
+    }
+
+    /**
+     *  Call this with an initially empty array, so the reader can cache each
+     *  factory it sees by name. Used by the pipe code in conjunction with
+     *  SkOrderedWriteBuffer::setNamedFactoryRecorder.
+     */
+    void setFactoryArray(SkTDArray<SkFlattenable::Factory>* array) {
+        fFactoryTDArray = array;
+        fFactoryArray = NULL;
+        fFactoryCount = 0;
+    }
+
+    /**
+     *  Provide a function to decode an SkBitmap from an SkStream. Only used if the writer encoded
+     *  the SkBitmap. If the proper decoder cannot be used, a red bitmap with the appropriate size
+     *  will be used.
+     */
+    void setBitmapDecoder(SkSerializationHelpers::DecodeBitmap bitmapDecoder) {
+        fBitmapDecoder = bitmapDecoder;
+    }
+
+private:
+    SkReader32 fReader;
+    void* fMemoryPtr;
+
+    SkBitmapHeapReader* fBitmapStorage;
+    SkTypeface** fTFArray;
+    int        fTFCount;
+
+    SkTDArray<SkFlattenable::Factory>* fFactoryTDArray;
+    SkFlattenable::Factory* fFactoryArray;
+    int                     fFactoryCount;
+
+    SkSerializationHelpers::DecodeBitmap fBitmapDecoder;
+
+    typedef SkFlattenableReadBuffer INHERITED;
+};
+
+#endif // SkOrderedReadBuffer_DEFINED
diff --git a/src/core/SkOrderedWriteBuffer.cpp b/src/core/SkOrderedWriteBuffer.cpp
new file mode 100644
index 0000000..bdd9516
--- /dev/null
+++ b/src/core/SkOrderedWriteBuffer.cpp
@@ -0,0 +1,263 @@
+
+/*
+ * Copyright 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 "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) {
+    return fWriter.readFromStream(stream, length);
+}
+
+bool SkOrderedWriteBuffer::writeToStream(SkWStream* stream) {
+    return fWriter.writeToStream(stream);
+}
+
+void SkOrderedWriteBuffer::writeBitmap(const SkBitmap& bitmap) {
+    bool encoded = false;
+    if (fBitmapEncoder != NULL) {
+        SkDynamicMemoryWStream pngStream;
+        if (fBitmapEncoder(&pngStream, bitmap)) {
+            encoded = true;
+            if (encoded) {
+                uint32_t offset = fWriter.bytesWritten();
+                // Write the length to indicate that the bitmap was encoded successfully.
+                size_t length = pngStream.getOffset();
+                this->writeUInt(length);
+                // Now write the stream.
+                if (pngStream.read(fWriter.reservePad(length), 0, length)) {
+                    // Write the width and height in case the reader does not have a decoder.
+                    this->writeInt(bitmap.width());
+                    this->writeInt(bitmap.height());
+                } else {
+                    // Writing the stream failed, so go back to original state to store another way.
+                    fWriter.rewindToOffset(offset);
+                    encoded = false;
+                }
+            }
+        }
+    }
+    if (!encoded) {
+        // Bitmap was not encoded. Record a zero, implying that the reader need not decode.
+        this->writeUInt(0);
+        if (fBitmapHeap) {
+            fWriter.write32(fBitmapHeap->insert(bitmap));
+        } else {
+            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::writeFlattenable(SkFlattenable* flattenable) {
+    /*
+     *  If we have a factoryset, then the first 32bits tell us...
+     *       0: failure to write the flattenable
+     *      >0: (1-based) index into the SkFactorySet or SkNamedFactorySet
+     *  If we don't have a factoryset, then the first "ptr" is either the
+     *  factory, or null for failure.
+     *
+     *  The distinction is important, since 0-index is 32bits (always), but a
+     *  0-functionptr might be 32 or 64 bits.
+     */
+
+    SkFlattenable::Factory factory = NULL;
+    if (flattenable) {
+        factory = flattenable->getFactory();
+    }
+    if (NULL == factory) {
+        if (fFactorySet != NULL || fNamedFactorySet != NULL) {
+            this->write32(0);
+        } else {
+            this->writeFunctionPtr(NULL);
+        }
+        return;
+    }
+
+    /*
+     *  We can write 1 of 3 versions of the flattenable:
+     *  1.  function-ptr : this is the fastest for the reader, but assumes that
+     *      the writer and reader are in the same process.
+     *  2.  index into fFactorySet : This is assumes the writer will later
+     *      resolve the function-ptrs into strings for its reader. SkPicture
+     *      does exactly this, by writing a table of names (matching the indices)
+     *      up front in its serialized form.
+     *  3.  index into fNamedFactorySet. fNamedFactorySet will also store the
+     *      name. SkGPipe uses this technique so it can write the name to its
+     *      stream before writing the flattenable.
+     */
+    if (fFactorySet) {
+        this->write32(fFactorySet->add(factory));
+    } else if (fNamedFactorySet) {
+        int32_t index = fNamedFactorySet->find(factory);
+        this->write32(index);
+        if (0 == index) {
+            return;
+        }
+    } else {
+        this->writeFunctionPtr((void*)factory);
+    }
+
+    // make room for the size of the flattened object
+    (void)fWriter.reserve(sizeof(uint32_t));
+    // record the current size, so we can subtract after the object writes.
+    uint32_t offset = fWriter.size();
+    // now flatten the object
+    flattenObject(flattenable, *this);
+    uint32_t objSize = fWriter.size() - offset;
+    // record the obj's size
+    *fWriter.peek32(offset - sizeof(uint32_t)) = objSize;
+}
diff --git a/src/core/SkOrderedWriteBuffer.h b/src/core/SkOrderedWriteBuffer.h
new file mode 100644
index 0000000..681efed
--- /dev/null
+++ b/src/core/SkOrderedWriteBuffer.h
@@ -0,0 +1,105 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkOrderedWriteBuffer_DEFINED
+#define SkOrderedWriteBuffer_DEFINED
+
+#include "SkFlattenableBuffers.h"
+
+#include "SkRefCnt.h"
+#include "SkBitmap.h"
+#include "SkBitmapHeap.h"
+#include "SkPath.h"
+#include "SkSerializationHelpers.h"
+#include "SkWriter32.h"
+
+class SkFlattenable;
+class SkFactorySet;
+class SkNamedFactorySet;
+class SkRefCntSet;
+
+class SkOrderedWriteBuffer : public SkFlattenableWriteBuffer {
+public:
+    SkOrderedWriteBuffer(size_t minSize);
+    SkOrderedWriteBuffer(size_t minSize, void* initialStorage,
+                         size_t storageSize);
+    virtual ~SkOrderedWriteBuffer();
+
+    virtual bool isOrderedBinaryBuffer() SK_OVERRIDE { return true; }
+    virtual SkOrderedWriteBuffer* getOrderedBinaryBuffer() SK_OVERRIDE { return this; }
+
+    SkWriter32* getWriter32() { return &fWriter; }
+
+    void writeToMemory(void* dst) { fWriter.flatten(dst); }
+    uint32_t* reserve(size_t size) { return fWriter.reserve(size); }
+    uint32_t size() { return fWriter.size(); }
+
+    virtual void writeByteArray(const void* data, size_t size) SK_OVERRIDE;
+    virtual void writeBool(bool value) SK_OVERRIDE;
+    virtual void writeFixed(SkFixed value) SK_OVERRIDE;
+    virtual void writeScalar(SkScalar value) SK_OVERRIDE;
+    virtual void writeScalarArray(const SkScalar* value, uint32_t count) SK_OVERRIDE;
+    virtual void writeInt(int32_t value) SK_OVERRIDE;
+    virtual void writeIntArray(const int32_t* value, uint32_t count) SK_OVERRIDE;
+    virtual void writeUInt(uint32_t value) SK_OVERRIDE;
+    virtual void write32(int32_t value) SK_OVERRIDE;
+    virtual void writeString(const char* value) SK_OVERRIDE;
+    virtual void writeEncodedString(const void* value, size_t byteLength,
+                                    SkPaint::TextEncoding encoding) SK_OVERRIDE;
+
+    virtual void writeFlattenable(SkFlattenable* flattenable) SK_OVERRIDE;
+    virtual void writeColor(const SkColor& color) SK_OVERRIDE;
+    virtual void writeColorArray(const SkColor* color, uint32_t count) SK_OVERRIDE;
+    virtual void writePoint(const SkPoint& point) SK_OVERRIDE;
+    virtual void writePointArray(const SkPoint* point, uint32_t count) SK_OVERRIDE;
+    virtual void writeMatrix(const SkMatrix& matrix) SK_OVERRIDE;
+    virtual void writeIRect(const SkIRect& rect)SK_OVERRIDE;
+    virtual void writeRect(const SkRect& rect) SK_OVERRIDE;
+    virtual void writeRegion(const SkRegion& region) SK_OVERRIDE;
+    virtual void writePath(const SkPath& path) SK_OVERRIDE;
+    virtual size_t writeStream(SkStream* stream, size_t length) SK_OVERRIDE;
+
+    virtual void writeBitmap(const SkBitmap& bitmap) SK_OVERRIDE;
+    virtual void writeTypeface(SkTypeface* typeface) SK_OVERRIDE;
+
+    virtual bool writeToStream(SkWStream*) SK_OVERRIDE;
+
+    SkFactorySet* setFactoryRecorder(SkFactorySet*);
+    SkNamedFactorySet* setNamedFactoryRecorder(SkNamedFactorySet*);
+
+    SkRefCntSet* getTypefaceRecorder() const { return fTFSet; }
+    SkRefCntSet* setTypefaceRecorder(SkRefCntSet*);
+
+    void setBitmapHeap(SkBitmapHeap* bitmapHeap) {
+        SkRefCnt_SafeAssign(fBitmapHeap, bitmapHeap);
+    }
+
+    /**
+     * Provide a function to encode an SkBitmap to an SkStream. writeBitmap will attempt to use
+     * bitmapEncoder to store the SkBitmap. Takes priority over the SkBitmapHeap. 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.
+     */
+    void setBitmapEncoder(SkSerializationHelpers::EncodeBitmap bitmapEncoder) {
+        fBitmapEncoder = bitmapEncoder;
+    }
+
+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
new file mode 100644
index 0000000..7a1444b
--- /dev/null
+++ b/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/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
new file mode 100644
index 0000000..c517663
--- /dev/null
+++ b/src/core/SkPaint.cpp
@@ -0,0 +1,2372 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPaint.h"
+#include "SkAnnotation.h"
+#include "SkColorFilter.h"
+#include "SkFontHost.h"
+#include "SkImageFilter.h"
+#include "SkMaskFilter.h"
+#include "SkMaskGamma.h"
+#include "SkPathEffect.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkScalar.h"
+#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)
+//#define SK_REPORT_API_RANGE_CHECK
+
+#ifdef SK_BUILD_FOR_ANDROID
+#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;
+    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;
+    fCapType    = kDefault_Cap;
+    fJoinType   = kDefault_Join;
+    fTextAlign  = kLeft_Align;
+    fStyle      = kFill_Style;
+    fTextEncoding = kUTF8_TextEncoding;
+    fHinting    = SkPaintDefaults_Hinting;
+    fPrivFlags  = 0;
+#ifdef SK_BUILD_FOR_ANDROID
+    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);
+    SkSafeRef(fAnnotation);
+}
+
+SkPaint::~SkPaint() {
+    SkSafeUnref(fTypeface);
+    SkSafeUnref(fPathEffect);
+    SkSafeUnref(fShader);
+    SkSafeUnref(fXfermode);
+    SkSafeUnref(fMaskFilter);
+    SkSafeUnref(fColorFilter);
+    SkSafeUnref(fRasterizer);
+    SkSafeUnref(fLooper);
+    SkSafeUnref(fImageFilter);
+    SkSafeUnref(fAnnotation);
+}
+
+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);
+    SkSafeRef(src.fAnnotation);
+
+    SkSafeUnref(fTypeface);
+    SkSafeUnref(fPathEffect);
+    SkSafeUnref(fShader);
+    SkSafeUnref(fXfermode);
+    SkSafeUnref(fMaskFilter);
+    SkSafeUnref(fColorFilter);
+    SkSafeUnref(fRasterizer);
+    SkSafeUnref(fLooper);
+    SkSafeUnref(fImageFilter);
+    SkSafeUnref(fAnnotation);
+
+#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) {
+    this->setFlags(SkSetClearMask(fFlags, doAA, kAntiAlias_Flag));
+}
+
+void SkPaint::setDither(bool doDither) {
+    this->setFlags(SkSetClearMask(fFlags, doDither, kDither_Flag));
+}
+
+void SkPaint::setSubpixelText(bool doSubpixel) {
+    this->setFlags(SkSetClearMask(fFlags, doSubpixel, kSubpixelText_Flag));
+}
+
+void SkPaint::setLCDRenderText(bool doLCDRender) {
+    this->setFlags(SkSetClearMask(fFlags, doLCDRender, kLCDRenderText_Flag));
+}
+
+void SkPaint::setEmbeddedBitmapText(bool doEmbeddedBitmapText) {
+    this->setFlags(SkSetClearMask(fFlags, doEmbeddedBitmapText, kEmbeddedBitmapText_Flag));
+}
+
+void SkPaint::setAutohinted(bool useAutohinter) {
+    this->setFlags(SkSetClearMask(fFlags, useAutohinter, kAutoHinting_Flag));
+}
+
+void SkPaint::setLinearText(bool doLinearText) {
+    this->setFlags(SkSetClearMask(fFlags, doLinearText, kLinearText_Flag));
+}
+
+void SkPaint::setVerticalText(bool doVertical) {
+    this->setFlags(SkSetClearMask(fFlags, doVertical, kVerticalText_Flag));
+}
+
+void SkPaint::setUnderlineText(bool doUnderline) {
+    this->setFlags(SkSetClearMask(fFlags, doUnderline, kUnderlineText_Flag));
+}
+
+void SkPaint::setStrikeThruText(bool doStrikeThru) {
+    this->setFlags(SkSetClearMask(fFlags, doStrikeThru, kStrikeThruText_Flag));
+}
+
+void SkPaint::setFakeBoldText(bool doFakeBold) {
+    this->setFlags(SkSetClearMask(fFlags, doFakeBold, kFakeBoldText_Flag));
+}
+
+void SkPaint::setDevKernText(bool doDevKern) {
+    this->setFlags(SkSetClearMask(fFlags, doDevKern, kDevKernText_Flag));
+}
+
+void SkPaint::setFilterBitmap(bool doFilter) {
+    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;
+}
+
+#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);
+        fTextEncoding = encoding;
+    } else {
+#ifdef SK_REPORT_API_RANGE_CHECK
+        SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding);
+#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;
+}
+
+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"
+#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) {
+    SkGlyphCache* cache;
+    descriptorProc(NULL, DetachDescProc, &cache, true);
+
+    const SkGlyph& glyph = cache->getUnicharMetrics(text);
+
+    SkGlyphCache::AttachCache(cache);
+    return glyph;
+}
+
+const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId) {
+    SkGlyphCache* cache;
+    descriptorProc(NULL, DetachDescProc, &cache, true);
+
+    const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphId);
+
+    SkGlyphCache::AttachCache(cache);
+    return glyph;
+}
+
+const void* SkPaint::findImage(const SkGlyph& glyph) {
+    // See ::detachCache()
+    SkGlyphCache* cache;
+    descriptorProc(NULL, DetachDescProc, &cache, true);
+
+    const void* image = cache->findImage(glyph);
+
+    SkGlyphCache::AttachCache(cache);
+    return image;
+}
+#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 kUTF32_TextEncoding:
+            return byteLength >> 2;
+        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;
+        }
+        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");
+    }
+    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;
+        }
+        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;
+    }
+    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_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);
+    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_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);
+    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_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 += 4;
+    }
+    if (!needFullMetrics && !this->isDevKernText()) {
+        index += 8;
+    }
+
+    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_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);
+    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_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 += 4;
+    }
+
+    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);
+    SkMatrix            matrix;
+    SkScalar            prevXPos = 0;
+
+    matrix.setScale(iter.getPathScale(), iter.getPathScale());
+    matrix.postTranslate(x, y);
+    path->reset();
+
+    SkScalar        xpos;
+    const SkPath*   iterPath;
+    while (iter.next(&iterPath, &xpos)) {
+        matrix.postTranslate(xpos - prevXPos, 0);
+        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,
+                            SkOrderedWriteBuffer* buffer) {
+    buffer->writeToMemory(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;
+}
+
+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))
+
+// 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
+}
+
+//#define SK_GAMMA_SRGB
+#ifndef SK_GAMMA_CONTRAST
+    /**
+     * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
+     * With lower values small text appears washed out (though correctly so).
+     * With higher values lcd fringing is worse and the smoothing effect of
+     * partial coverage is diminished.
+     */
+    #define SK_GAMMA_CONTRAST (0.5f)
+#endif
+#ifndef SK_GAMMA_EXPONENT
+    #define SK_GAMMA_EXPONENT (2.2f)
+#endif
+
+void SkScalerContext::MakeRec(const SkPaint& paint,
+                              const SkMatrix* deviceMatrix, Rec* rec) {
+    SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
+
+    SkTypeface* typeface = paint.getTypeface();
+    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());
+        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;
+
+    if (paint.isFakeBoldText()) {
+#ifdef SK_USE_FREETYPE_EMBOLDEN
+        flags |= SkScalerContext::kEmbolden_Flag;
+#else
+        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));
+
+    rec->setLuminanceColor(computeLuminanceColor(paint));
+#ifdef SK_GAMMA_SRGB
+    rec->setDeviceGamma(0);
+    rec->setPaintGamma(0);
+#else
+    rec->setDeviceGamma(SkFloatToScalar(SK_GAMMA_EXPONENT));
+    rec->setPaintGamma(SkFloatToScalar(SK_GAMMA_EXPONENT));
+#endif
+    rec->setContrast(SkFloatToScalar(SK_GAMMA_CONTRAST));
+    rec->fReservedAlign = 0;
+
+    /*  Allow the fonthost to modify our rec before we use it as a key into the
+        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, 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 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(const SkPaint& paint, SkScalerContext::Rec* rec) {
+    /**
+     *  If we're asking for A8, we force the colorlum to be gray, since 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: {
+            // filter down the luminance color to a finite number of bits
+            SkColor color = rec->getLuminanceColor();
+            rec->setLuminanceColor(SkMaskGamma::cannonicalColor(color));
+            break;
+        }
+        case SkMask::kA8_Format: {
+            // filter down the luminance to a single component, since A8 can't
+            // use per-component information
+
+            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
+            color = SkColorSetRGB(lum, lum, lum);
+            rec->setLuminanceColor(SkMaskGamma::cannonicalColor(color));
+            break;
+        }
+        case SkMask::kBW_Format:
+            // No need to differentiate gamma if we're BW
+            rec->setLuminanceColor(0);
+            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) {
+        rec.setLuminanceColor(0);
+    }
+
+    size_t          descSize = sizeof(rec);
+    int             entryCount = 1;
+    SkPathEffect*   pe = this->getPathEffect();
+    SkMaskFilter*   mf = this->getMaskFilter();
+    SkRasterizer*   ra = this->getRasterizer();
+
+    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);
+        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(*this, &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;
+}
+
+/**
+ * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
+ */
+//static
+SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
+    SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
+    SkMaskGamma* maskGamma = cachedMaskGamma(rec.getContrast(),
+                                             rec.getPaintGamma(),
+                                             rec.getDeviceGamma());
+    return maskGamma->preBlend(rec.getLuminanceColor());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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
+// 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);
+
+/*  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->getAnnotation()) |
+        asint(this->getImageFilter())) {
+        flatFlags |= kHasEffects_FlatFlag;
+    }
+
+
+    if (buffer.isOrderedBinaryBuffer()) {
+        SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
+        uint32_t* ptr = buffer.getOrderedBinaryBuffer()->reserve(kPODPaintSize);
+
+        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
+    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());
+        buffer.writeFlattenable(this->getAnnotation());
+    }
+}
+
+void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
+    fPrivFlags = 0;
+
+    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);
+
+        // 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++);
+
+        // 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));
+
+        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));
+    } 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());
+    } else {
+        this->setTypeface(NULL);
+    }
+
+    if (flatFlags & kHasEffects_FlatFlag) {
+        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);
+        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 {
+    SkStrokeRec rec(*this);
+
+    const SkPath* srcPtr = &src;
+    SkPath tmpPath;
+
+    if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec)) {
+        srcPtr = &tmpPath;
+    }
+
+    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;
+        }
+    }
+    return !rec.isHairlineStyle();
+}
+
+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);
+        return *storage;
+    }
+
+    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
+            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;
+    }
+
+    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)
+                                    : fPaint(paint) {
+    fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
+                                                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
+    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);
+}
+
+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 (path) {
+                *path = fCache->findPath(glyph);
+            }
+        } else {
+            if (path) {
+                *path = NULL;
+            }
+        }
+        if (xpos) {
+            *xpos = fXPos;
+        }
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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.
+
+SK_DEFINE_INST_COUNT(SkDrawLooper)
+
+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/src/core/SkPaintDefaults.h b/src/core/SkPaintDefaults.h
new file mode 100644
index 0000000..3ea1cd3
--- /dev/null
+++ b/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/src/core/SkPath.cpp b/src/core/SkPath.cpp
new file mode 100644
index 0000000..eb0e485
--- /dev/null
+++ b/src/core/SkPath.cpp
@@ -0,0 +1,2682 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkBuffer.h"
+#include "SkMath.h"
+#include "SkPathRef.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
+
+////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  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);
+}
+
+class SkAutoDisableOvalCheck {
+public:
+    SkAutoDisableOvalCheck(SkPath* path) : fPath(path) {
+        fSaved = fPath->fIsOval;
+    }
+
+    ~SkAutoDisableOvalCheck() {
+        fPath->fIsOval = fSaved;
+    }
+
+private:
+    SkPath* fPath;
+    bool    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).
+
+    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;
+            fPath->fIsFinite = fPath->fBounds.isFinite();
+        } else if (!fDirty) {
+            joinNoEmptyChecks(&fPath->fBounds, fRect);
+            fPath->fBoundsIsDirty = false;
+            fPath->fIsFinite = fPath->fBounds.isFinite();
+        }
+    }
+
+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();
+    }
+};
+
+// 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 {
+        return bounds->setBoundsCheck(ref.points(), 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()
+#if SK_DEBUG_PATH_REF
+    : fPathRef(SkPathRef::CreateEmpty(), this)
+#else
+    : fPathRef(SkPathRef::CreateEmpty())
+#endif
+    , fFillType(kWinding_FillType)
+    , fBoundsIsDirty(true) {
+    fConvexity = kUnknown_Convexity;
+    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)
+#if SK_DEBUG_PATH_REF
+    : fPathRef(this)
+#endif
+{
+    SkDEBUGCODE(src.validate();)
+    src.fPathRef.get()->ref();
+    fPathRef.reset(src.fPathRef.get());
+    fBounds         = src.fBounds;
+    fFillType       = src.fFillType;
+    fBoundsIsDirty  = src.fBoundsIsDirty;
+    fConvexity      = src.fConvexity;
+    fIsFinite       = src.fIsFinite;
+    fSegmentMask    = src.fSegmentMask;
+    fLastMoveToIndex = src.fLastMoveToIndex;
+    fIsOval         = src.fIsOval;
+#ifdef SK_BUILD_FOR_ANDROID
+    fGenerationID = src.fGenerationID;
+    fSourcePath = NULL;
+#endif
+}
+
+SkPath::~SkPath() {
+    SkDEBUGCODE(this->validate();)
+}
+
+SkPath& SkPath::operator=(const SkPath& src) {
+    SkDEBUGCODE(src.validate();)
+
+    if (this != &src) {
+        src.fPathRef.get()->ref();
+        fPathRef.reset(src.fPathRef.get());
+        fBounds         = src.fBounds;
+        fFillType       = src.fFillType;
+        fBoundsIsDirty  = src.fBoundsIsDirty;
+        fConvexity      = src.fConvexity;
+        fIsFinite       = src.fIsFinite;
+        fSegmentMask    = src.fSegmentMask;
+        fLastMoveToIndex = src.fLastMoveToIndex;
+        fIsOval         = src.fIsOval;
+        GEN_ID_INC;
+    }
+    SkDEBUGCODE(this->validate();)
+    return *this;
+}
+
+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.
+
+    // 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.fPathRef.get() == *b.fPathRef.get());
+}
+
+void SkPath::swap(SkPath& other) {
+    SkASSERT(&other != NULL);
+
+    if (this != &other) {
+        SkTSwap<SkRect>(fBounds, other.fBounds);
+        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>(fSegmentMask, other.fSegmentMask);
+        SkTSwap<int>(fLastMoveToIndex, other.fLastMoveToIndex);
+        SkTSwap<SkBool8>(fIsOval, other.fIsOval);
+        SkTSwap<SkBool8>(fIsFinite, other.fIsFinite);
+        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();)
+
+    fPathRef.reset(SkPathRef::CreateEmpty());
+    GEN_ID_INC;
+    fBoundsIsDirty = true;
+    fConvexity = kUnknown_Convexity;
+    fSegmentMask = 0;
+    fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
+    fIsOval = false;
+}
+
+void SkPath::rewind() {
+    SkDEBUGCODE(this->validate();)
+
+    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 == 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
+  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 SkPoint* pts = fPathRef->points();
+    int verbCnt = fPathRef->countVerbs();
+    int currVerb = 0;
+    while (currVerb < verbCnt) {
+        switch (fPathRef->atVerb(currVerb++)) {
+            case kClose_Verb:
+                pts = fPathRef->points();
+                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::countPoints() const {
+    return fPathRef->countPoints();
+}
+
+int SkPath::getPoints(SkPoint dst[], int max) const {
+    SkDEBUGCODE(this->validate();)
+
+    SkASSERT(max >= 0);
+    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)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 = fPathRef->countPoints();
+    if (count > 0) {
+        if (lastPt) {
+            *lastPt = fPathRef->atPoint(count - 1);
+        }
+        return true;
+    }
+    if (lastPt) {
+        lastPt->set(0, 0);
+    }
+    return false;
+}
+
+void SkPath::setLastPt(SkScalar x, SkScalar y) {
+    SkDEBUGCODE(this->validate();)
+
+    int count = fPathRef->countPoints();
+    if (count == 0) {
+        this->moveTo(x, y);
+    } else {
+        fIsOval = false;
+        SkPathRef::Editor ed(&fPathRef);
+        ed.atPoint(count-1)->set(x, y);
+        GEN_ID_INC;
+    }
+}
+
+void SkPath::computeBounds() const {
+    SkDEBUGCODE(this->validate();)
+    SkASSERT(fBoundsIsDirty);
+
+    fIsFinite = compute_pt_bounds(&fBounds, *fPathRef.get());
+    fBoundsIsDirty = false;
+}
+
+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; \
+        fIsOval = false;                 \
+    } while (0)
+
+#define DIRTY_AFTER_EDIT_NO_CONVEXITY_CHANGE    \
+    do {                                        \
+        fBoundsIsDirty = true;                  \
+    } while (0)
+
+void SkPath::incReserve(U16CPU inc) {
+    SkDEBUGCODE(this->validate();)
+    SkPathRef::Editor(&fPathRef, inc, inc);
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkPath::moveTo(SkScalar x, SkScalar y) {
+    SkDEBUGCODE(this->validate();)
+
+    SkPathRef::Editor ed(&fPathRef);
+
+    // remember our index
+    fLastMoveToIndex = ed.pathRef()->countPoints();
+
+    ed.growForVerb(kMove_Verb)->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 (fPathRef->countVerbs() == 0) {
+            x = y = 0;
+        } else {
+            const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
+            x = pt.fX;
+            y = pt.fY;
+        }
+        this->moveTo(x, y);
+    }
+}
+
+void SkPath::lineTo(SkScalar x, SkScalar y) {
+    SkDEBUGCODE(this->validate();)
+
+    this->injectMoveToIfNeeded();
+
+    SkPathRef::Editor ed(&fPathRef);
+    ed.growForVerb(kLine_Verb)->set(x, y);
+    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();
+
+    SkPathRef::Editor ed(&fPathRef);
+    SkPoint* pts = ed.growForVerb(kQuad_Verb);
+    pts[0].set(x1, y1);
+    pts[1].set(x2, y2);
+    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();
+
+    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);
+    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 = fPathRef->countVerbs();
+    if (count > 0) {
+        switch (fPathRef->atVerb(count - 1)) {
+            case kLine_Verb:
+            case kQuad_Verb:
+            case kCubic_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;
+        }
+    }
+
+    // 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();
+}
+
+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();)
+}
+
+#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();
+}
+
+bool SkPath::hasOnlyMoveTos() const {
+    int count = fPathRef->countVerbs();
+    const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
+    for (int i = 0; i < count; ++i) {
+        if (*verbs == kLine_Verb ||
+            *verbs == kQuad_Verb ||
+            *verbs == kCubic_Verb) {
+            return false;
+        }
+        ++verbs;
+    }
+    return true;
+}
+
+void SkPath::addOval(const SkRect& oval, Direction dir) {
+    /* 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();
+
+    SkAutoDisableOvalCheck adoc(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);
+    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();
+}
+
+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;
+        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]) {
+
+    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;
+    }
+
+    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 (fPathRef->countVerbs() == 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) {
+    SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints());
+
+    fIsOval = false;
+
+    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.fPathRef->countVerbs();
+    // exit early if the path is empty, or just has a moveTo.
+    if (vcount < 2) {
+        return;
+    }
+
+    SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
+
+    fIsOval = false;
+
+    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]) {
+            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.fPathRef->countVerbs();
+    // exit early if the path is empty, or just has a moveTo.
+    if (vcount < 2) {
+        return;
+    }
+
+    SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
+
+    fIsOval = false;
+
+    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;
+        }
+        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) {
+    SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs());
+
+    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 < verbsEnd) {
+        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, false)) != 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;
+            }
+        }
+
+        dst->swap(tmp);
+        SkPathRef::Editor ed(&dst->fPathRef);
+        matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
+    } else {
+        /*
+         *  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->fFillType = fFillType;
+            dst->fSegmentMask = fSegmentMask;
+            dst->fConvexity = fConvexity;
+        }
+
+        // It's an oval only if it stays a rect.
+        dst->fIsOval = fIsOval && matrix.rectStaysRect();
+
+        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.fPathRef->points();
+    fVerbs = path.fPathRef->verbs();
+    fVerbStop = path.fPathRef->verbsMemBegin();
+    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 - 1)) {
+        verbs -= 1; // skip the initial moveto
+    }
+
+    while (verbs > stop) {
+        // verbs points one beyond the current verb, decrement first.
+        unsigned v = *(--verbs);
+        if (kMove_Verb == v) {
+            break;
+        }
+        if (kClose_Verb == v) {
+            return true;
+        }
+    }
+    return false;
+}
+
+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.
+        // (consider SkPoint is a 2-dimension float point).
+        if (SkScalarIsNaN(fLastPt.fX) || SkScalarIsNaN(fLastPt.fY) ||
+            SkScalarIsNaN(fMoveTo.fX) || SkScalarIsNaN(fMoveTo.fY)) {
+            return kClose_Verb;
+        }
+
+        pts[0] = fLastPt;
+        pts[1] = fMoveTo;
+        fLastPt = fMoveTo;
+        fCloseLine = true;
+        return kLine_Verb;
+    } else {
+        pts[0] = fMoveTo;
+        return kClose_Verb;
+    }
+}
+
+const SkPoint& SkPath::Iter::cons_moveTo() {
+    if (fSegmentState == kAfterMove_SegmentState) {
+        // Set the first return pt to the move pt
+        fSegmentState = kAfterPrimitive_SegmentState;
+        return fMoveTo;
+    } else {
+        SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
+         // Set the first return pt to the last pt of the previous primitive.
+        return fPts[-1];
+    }
+}
+
+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 - 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--;
+                fPts++;
+                break;
+
+            case kClose_Verb:
+                // 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--;
+                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::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(ptsParam)) {
+                return kLine_Verb;
+            }
+            fNeedClose = false;
+            return kClose_Verb;
+        }
+        return kDone_Verb;
+    }
+
+    // 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++; // move back one verb
+                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;
+            pts[0] = *srcPts;
+            srcPts += 1;
+            fSegmentState = kAfterMove_SegmentState;
+            fLastPt = fMoveTo;
+            fNeedClose = fForceClose;
+            break;
+        case kLine_Verb:
+            pts[0] = this->cons_moveTo();
+            pts[1] = srcPts[0];
+            fLastPt = srcPts[0];
+            fCloseLine = false;
+            srcPts += 1;
+            break;
+        case kQuad_Verb:
+            pts[0] = this->cons_moveTo();
+            memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
+            fLastPt = srcPts[1];
+            srcPts += 2;
+            break;
+        case kCubic_Verb:
+            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++; // move back one verb
+            } 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.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;
+    }
+
+    // fVerbs points one beyond next verb so decrement first.
+    unsigned verb = *(--fVerbs);
+    const SkPoint* srcPts = fPts;
+
+    switch (verb) {
+        case kMove_Verb:
+            pts[0] = *srcPts;
+            fMoveTo = srcPts[0];
+            fLastPt = fMoveTo;
+            srcPts += 1;
+            break;
+        case kLine_Verb:
+            pts[0] = fLastPt;
+            pts[1] = srcPts[0];
+            fLastPt = srcPts[0];
+            srcPts += 1;
+            break;
+        case kQuad_Verb:
+            pts[0] = fLastPt;
+            memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
+            fLastPt = srcPts[1];
+            srcPts += 2;
+            break;
+        case kCubic_Verb:
+            pts[0] = fLastPt;
+            memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
+            fLastPt = srcPts[2];
+            srcPts += 3;
+            break;
+        case kClose_Verb:
+            fLastPt = fMoveTo;
+            pts[0] = fMoveTo;
+            break;
+    }
+    fPts = srcPts;
+    return (Verb)verb;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+    Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
+*/
+
+uint32_t SkPath::writeToMemory(void* storage) const {
+    SkDEBUGCODE(this->validate();)
+
+    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);
+
+    buffer.write32(packed);
+
+    fPathRef->writeToBuffer(&buffer);
+
+    buffer.write(&bounds, sizeof(bounds));
+
+    buffer.padToAlign4();
+    return buffer.pos();
+}
+
+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();
+    fIsFinite = (packed >> kIsFinite_SerializationShift) & 1;
+    fIsOval = (packed >> kIsOval_SerializationShift) & 1;
+    fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
+    fFillType = (packed >> kFillType_SerializationShift) & 0xFF;
+    fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xFF;
+
+#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;
+
+    SkDEBUGCODE(this->validate();)
+    return buffer.pos();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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, false)) != kDone_Verb) {
+        switch (verb) {
+            case kMove_Verb:
+                SkDebugf("  path: moveTo [%g %g]\n",
+                        SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY));
+                break;
+            case kLine_Verb:
+                SkDebugf("  path: lineTo [%g %g]\n",
+                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY));
+                break;
+            case kQuad_Verb:
+                SkDebugf("  path: quadTo [%g %g] [%g %g]\n",
+                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
+                        SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY));
+                break;
+            case kCubic_Verb:
+                SkDebugf("  path: cubeTo [%g %g] [%g %g] [%g %g]\n",
+                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
+                        SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY),
+                        SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY));
+                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);
+
+    if (!fBoundsIsDirty) {
+        SkRect bounds;
+
+        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
+            // 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;
+    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;
+            case kQuad_Verb:
+                mask |= kQuad_SegmentMask;
+                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
+
+///////////////////////////////////////////////////////////////////////////////
+
+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 SkPathRef& pathRef);
+
+    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 SkPathRef& pathRef) {
+    fStopVerbs = pathRef.verbsMemBegin();
+    fDone = false;
+    fCurrPt = pathRef.points();
+    fCurrVerb = pathRef.verbs();
+    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[~0]) {
+            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
+
+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
+ *  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(*fPathRef.get());
+
+    // 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 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)) {
+                return true;
+            }
+            if (convex_dir_test<double, toDouble>(n, pts, dir)) {
+                return true;
+            } else {
+                return false;
+            }
+        } 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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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
new file mode 100644
index 0000000..d706e8d
--- /dev/null
+++ b/src/core/SkPathEffect.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 "SkPathEffect.h"
+#include "SkPath.h"
+#include "SkFlattenableBuffers.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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_INST_COUNT(SkPathEffect)
+
+void SkPathEffect::computeFastBounds(SkRect* dst, const SkRect& src) {
+    *dst = src;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeFlattenable(fPE0);
+    buffer.writeFlattenable(fPE1);
+}
+
+SkPairPathEffect::SkPairPathEffect(SkFlattenableReadBuffer& buffer) {
+    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,
+                                     SkStrokeRec* rec) {
+    // 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, rec)) {
+        ptr = &tmp;
+    }
+    return fPE0->filterPath(dst, *ptr, rec);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkSumPathEffect::filterPath(SkPath* dst, const SkPath& src,
+                                 SkStrokeRec* rec) {
+    // use bit-or so that we always call both, even if the first one succeeds
+    return fPE0->filterPath(dst, src, rec) | fPE1->filterPath(dst, src, rec);
+}
diff --git a/src/core/SkPathHeap.cpp b/src/core/SkPathHeap.cpp
new file mode 100644
index 0000000..bd5269d
--- /dev/null
+++ b/src/core/SkPathHeap.cpp
@@ -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.
+ */
+#include "SkPathHeap.h"
+#include "SkPath.h"
+#include "SkStream.h"
+#include "SkFlattenableBuffers.h"
+#include <new>
+
+SK_DEFINE_INST_COUNT(SkPathHeap)
+
+#define kPathCount  64
+
+SkPathHeap::SkPathHeap() : fHeap(kPathCount * sizeof(SkPath)) {
+}
+
+SkPathHeap::SkPathHeap(SkFlattenableReadBuffer& buffer)
+            : fHeap(kPathCount * sizeof(SkPath)) {
+    const int count = buffer.readInt();
+
+    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;
+        buffer.readPath(p);
+        *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.writeInt(count);
+    SkPath** iter = fPaths.begin();
+    SkPath** stop = fPaths.end();
+    while (iter < stop) {
+        buffer.writePath(**iter);
+        iter++;
+    }
+}
+
diff --git a/src/core/SkPathHeap.h b/src/core/SkPathHeap.h
new file mode 100644
index 0000000..f043222
--- /dev/null
+++ b/src/core/SkPathHeap.h
@@ -0,0 +1,51 @@
+
+/*
+ * 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:
+    SK_DECLARE_INST_COUNT(SkPathHeap)
+
+    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;
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
+
diff --git a/src/core/SkPathMeasure.cpp b/src/core/SkPathMeasure.cpp
new file mode 100644
index 0000000..8b1ec2d
--- /dev/null
+++ b/src/core/SkPathMeasure.cpp
@@ -0,0 +1,537 @@
+
+/*
+ * 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;
+}
+
+// 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]);
+        SkScalar prevD = distance;
+        distance += d;
+        if (distance > prevD) {
+            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]);
+        SkScalar prevD = distance;
+        distance += d;
+        if (distance > prevD) {
+            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        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 {
+        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: {
+                SkScalar d = SkPoint::Distance(pts[0], pts[1]);
+                SkASSERT(d >= 0);
+                SkScalar prevD = distance;
+                distance += d;
+                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: {
+                SkScalar prevD = distance;
+                distance = this->compute_quad_segs(pts, distance, 0,
+                                                   kMaxTValue, ptIndex);
+                if (distance > prevD) {
+                    fPts.append(2, pts + 1);
+                    ptIndex += 2;
+                }
+            } break;
+
+            case SkPath::kCubic_Verb: {
+                SkScalar prevD = distance;
+                distance = this->compute_cubic_segs(pts, distance, 0,
+                                                    kMaxTValue, ptIndex);
+                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;
+        }
+    } 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 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));
+            }
+            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 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 (startT == stopT) {
+        return; // should we report this, to undo a moveTo?
+    }
+
+    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/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
new file mode 100644
index 0000000..cc73bda
--- /dev/null
+++ b/src/core/SkPicture.cpp
@@ -0,0 +1,326 @@
+
+/*
+ * 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 "SkDevice.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"
+#include "SkRTree.h"
+#include "SkBBoxHierarchyRecord.h"
+
+SK_DEFINE_INST_COUNT(SkPicture)
+
+#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);
+}
+
+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;
+
+        /*  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,
+                                    uint32_t recordingFlags) {
+    if (fPlayback) {
+        SkDELETE(fPlayback);
+        fPlayback = NULL;
+    }
+
+    if (NULL != fRecord) {
+        fRecord->unref();
+        fRecord = NULL;
+    }
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kNo_Config, width, height);
+    SkAutoTUnref<SkDevice> dev(SkNEW_ARGS(SkDevice, (bm)));
+
+    if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
+        SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(width),
+                                           SkIntToScalar(height));
+        SkRTree* tree = SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
+                                        aspectRatio);
+        SkASSERT(NULL != tree);
+        fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (recordingFlags, tree, dev));
+        tree->unref();
+    } else {
+        fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags, dev));
+    }
+    fRecord->beginRecording();
+
+    fWidth = width;
+    fHeight = height;
+
+    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) {
+            fRecord->endRecording();
+            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"
+
+// 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
+#define PICTURE_VERSION     9
+
+SkPicture::SkPicture(SkStream* stream, bool* success, SkSerializationHelpers::DecodeBitmap decoder) : SkRefCnt() {
+    if (success) {
+        *success = false;
+    }
+    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()) {
+        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, SkSerializationHelpers::EncodeBitmap encoder) const {
+    SkPicturePlayback* playback = fPlayback;
+
+    if (NULL == playback && fRecord) {
+        playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
+    }
+
+    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, encoder);
+        // 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/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
new file mode 100644
index 0000000..8f6e089
--- /dev/null
+++ b/src/core/SkPictureFlat.cpp
@@ -0,0 +1,148 @@
+
+/*
+ * Copyright 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 "SkChecksum.h"
+#include "SkColorFilter.h"
+#include "SkDrawLooper.h"
+#include "SkMaskFilter.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkTypeface.h"
+#include "SkXfermode.h"
+
+SK_DEFINE_INST_COUNT(SkFlatController)
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypefacePlayback::SkTypefacePlayback() : fCount(0), fArray(NULL) {}
+
+SkTypefacePlayback::~SkTypefacePlayback() {
+    this->reset(NULL);
+}
+
+void SkTypefacePlayback::reset(const SkRefCntSet* rec) {
+    for (int i = 0; i < fCount; i++) {
+        SkASSERT(fArray[i]);
+        fArray[i]->unref();
+    }
+    SkDELETE_ARRAY(fArray);
+
+    if (rec!= NULL && rec->count() > 0) {
+        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 SkTypefacePlayback::setCount(int count) {
+    this->reset(NULL);
+
+    fCount = count;
+    fArray = SkNEW_ARRAY(SkRefCnt*, count);
+    sk_bzero(fArray, count * sizeof(SkRefCnt*));
+}
+
+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->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
new file mode 100644
index 0000000..2c8aa2c
--- /dev/null
+++ b/src/core/SkPictureFlat.h
@@ -0,0 +1,657 @@
+
+/*
+ * 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
+
+//#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,
+    CONCAT,
+    DRAW_BITMAP,
+    DRAW_BITMAP_MATRIX,
+    DRAW_BITMAP_NINE,
+    DRAW_BITMAP_RECT_TO_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_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,
+
+    LAST_DRAWTYPE_ENUM = TRANSLATE
+};
+
+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 SkTypefacePlayback {
+public:
+    SkTypefacePlayback();
+    virtual ~SkTypefacePlayback();
+
+    int count() const { return fCount; }
+
+    void reset(const SkRefCntSet*);
+
+    void setCount(int count);
+    SkRefCnt* set(int index, SkRefCnt*);
+
+    void setupBuffer(SkOrderedReadBuffer& buffer) const {
+        buffer.setTypefaceArray((SkTypeface**)fArray, fCount);
+    }
+
+protected:
+    int fCount;
+    SkRefCnt** fArray;
+};
+
+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(SkOrderedReadBuffer& buffer) const {
+        buffer.setFactoryPlayback(fArray, fCount);
+    }
+
+private:
+    int fCount;
+    SkFlattenable::Factory* fArray;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+//
+// 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:
+    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:
+    /**
+     * 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 SkFlatData {
+public:
+    /**
+     *  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) {}
+
+        if (a_ptr == stop) {    // sentinel
+            SkASSERT(b->dataStop() == b_ptr);
+            return 0;
+        }
+        SkASSERT(a_ptr < a->dataStop());
+        SkASSERT(b_ptr < b->dataStop());
+        return (*a_ptr < *b_ptr) ? -1 : 1;
+    }
+
+    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);
+    }
+
+private:
+    int fIndex;
+
+    // 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;
+    }
+};
+
+template <class T>
+class SkFlatDictionary {
+public:
+    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));
+    }
+
+    virtual ~SkFlatDictionary() {
+        fController->unref();
+    }
+
+    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();
+    }
+
+    /**
+     * Given a pointer to an array of type T we allocate the array and fill it
+     * with the unflattened dictionary contents. The return value is the size of
+     * the allocated array.
+     */
+    int unflattenDictionary(T*& array) const {
+        int elementCount = fData.count();
+        if (elementCount > 0) {
+            array = SkNEW_ARRAY(T, elementCount);
+            this->unflattenIntoArray(array);
+        }
+        return elementCount;
+    }
+
+    /**
+     *  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;
+    }
+
+protected:
+    void (*fFlattenProc)(SkOrderedWriteBuffer&, const void*);
+    void (*fUnflattenProc)(SkOrderedReadBuffer&, void*);
+
+private:
+    void unflattenIntoArray(T* array) const {
+        const int count = fData.count();
+        const SkFlatData** iter = fData.begin();
+        for (int i = 0; i < count; ++i) {
+            const SkFlatData* element = iter[i];
+            int index = element->index() - 1;
+            SkASSERT((unsigned)index < (unsigned)count);
+            element->unflatten(&array[index], fUnflattenProc,
+                               fController->getBitmapHeap(),
+                               fController->getTypefacePlayback());
+        }
+    }
+
+
+    SkFlatController * const     fController;
+    int                          fNextIndex;
+    SkTDArray<const SkFlatData*> fData;
+
+    const SkFlatData* findAndReturnFlat(const T& element) {
+        SkFlatData* flat = SkFlatData::Create(fController, &element, fNextIndex, fFlattenProc);
+
+        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;
+    }
+
+
+    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;
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Some common dictionaries are defined here for both reference and convenience
+///////////////////////////////////////////////////////////////////////////////
+
+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:
+    SkChunkAlloc               fHeap;
+    SkRefCntSet*               fTypefaceSet;
+    mutable SkTypefacePlayback fTypefacePlayback;
+};
+
+class SkBitmapDictionary : public SkFlatDictionary<SkBitmap> {
+public:
+    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;
+    }
+
+    static void flattenMatrix(SkOrderedWriteBuffer& buffer, const void* obj) {
+        buffer.getWriter32()->writeMatrix(*(SkMatrix*)obj);
+    }
+
+    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
new file mode 100644
index 0000000..5d4098a
--- /dev/null
+++ b/src/core/SkPicturePlayback.cpp
@@ -0,0 +1,1445 @@
+
+/*
+ * Copyright 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 "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).
+ */
+#define SPEW_CLIP_SKIPPINGx
+
+SkPicturePlayback::SkPicturePlayback() {
+    this->init();
+}
+
+SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record, bool deepCopy) {
+#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) {
+        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);
+        SkASSERT(!fOpData);
+        fOpData = SkData::NewFromMalloc(buffer, size);
+    }
+
+    // copy over the refcnt dictionary to our reader
+    record.fFlattenableHeap.setupPlaybacks();
+
+    fBitmaps = record.fBitmapHeap->extractBitmaps();
+    fMatrices = record.fMatrices.unflattenToArray();
+    fPaints = record.fPaints.unflattenToArray();
+    fRegions = record.fRegions.unflattenToArray();
+
+    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<SkPicture* >& pictures = record.getPictureRefs();
+    fPictureCount = pictures.count();
+    if (fPictureCount > 0) {
+        fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
+        for (int i = 0; i < fPictureCount; i++) {
+            if (deepCopy) {
+                fPictureRefs[i] = pictures[i]->clone();
+            } else {
+                fPictureRefs[i] = pictures[i];
+                fPictureRefs[i]->ref();
+            }
+        }
+    }
+
+#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, SkPictCopyInfo* deepCopyInfo) {
+    this->init();
+
+    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) {
+
+        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(src.fPaints->count());
+
+            SkDEBUGCODE(int heapSize = SafeCount(fBitmapHeap.get());)
+            for (int i = 0; i < src.fPaints->count(); i++) {
+                deepCopyInfo->paintData[i] = SkFlatData::Create(&deepCopyInfo->controller,
+                                                                &src.fPaints->at(i), 0,
+                                                                &SkFlattenObjectProc<SkPaint>);
+            }
+            SkASSERT(SafeCount(fBitmapHeap.get()) == heapSize);
+
+            // needed to create typeface playback
+            deepCopyInfo->controller.setupPlaybacks();
+            deepCopyInfo->initialized = true;
+        }
+
+        fPaints = SkTRefArray<SkPaint>::Create(src.fPaints->count());
+        SkASSERT(deepCopyInfo->paintData.count() == src.fPaints->count());
+        for (int i = 0; i < src.fPaints->count(); i++) {
+            deepCopyInfo->paintData[i]->unflatten(&fPaints->writableAt(i),
+                                                  &SkUnflattenObjectProc<SkPaint>,
+                                                  deepCopyInfo->controller.getBitmapHeap(),
+                                                  deepCopyInfo->controller.getTypefacePlayback());
+        }
+
+    } else {
+        fBitmaps = SkSafeRef(src.fBitmaps);
+        fPaints = SkSafeRef(src.fPaints);
+    }
+
+    fPictureCount = src.fPictureCount;
+    fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
+    for (int i = 0; i < fPictureCount; i++) {
+        if (deepCopyInfo) {
+            fPictureRefs[i] = src.fPictureRefs[i]->clone();
+        } else {
+            fPictureRefs[i] = src.fPictureRefs[i];
+            fPictureRefs[i]->ref();
+        }
+    }
+}
+
+void SkPicturePlayback::init() {
+    fBitmaps = NULL;
+    fMatrices = NULL;
+    fPaints = NULL;
+    fPictureRefs = NULL;
+    fRegions = NULL;
+    fPictureCount = 0;
+    fOpData = NULL;
+    fFactoryPlayback = NULL;
+    fBoundingHierarchy = NULL;
+    fStateTree = NULL;
+}
+
+SkPicturePlayback::~SkPicturePlayback() {
+    fOpData->unref();
+
+    SkSafeUnref(fBitmaps);
+    SkSafeUnref(fMatrices);
+    SkSafeUnref(fPaints);
+    SkSafeUnref(fRegions);
+    SkSafeUnref(fBoundingHierarchy);
+    SkSafeUnref(fStateTree);
+
+    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",
+             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));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#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')
+
+// 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_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(SkOrderedWriteBuffer& buffer, uint32_t tag,
+                         uint32_t size) {
+    buffer.writeUInt(tag);
+    buffer.writeUInt(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::flattenToBuffer(SkOrderedWriteBuffer& buffer) const {
+    int i, n;
+
+    if ((n = SafeCount(fBitmaps)) > 0) {
+        writeTagSize(buffer, PICT_BITMAP_BUFFER_TAG, n);
+        for (i = 0; i < n; i++) {
+            buffer.writeBitmap((*fBitmaps)[i]);
+        }
+    }
+
+    if ((n = SafeCount(fMatrices)) > 0) {
+        writeTagSize(buffer, PICT_MATRIX_BUFFER_TAG, n);
+        for (i = 0; i < n; i++) {
+            buffer.writeMatrix((*fMatrices)[i]);
+        }
+
+    }
+
+    if ((n = SafeCount(fPaints)) > 0) {
+        writeTagSize(buffer, PICT_PAINT_BUFFER_TAG, n);
+        for (i = 0; i < n; i++) {
+            buffer.writePaint((*fPaints)[i]);
+        }
+    }
+
+    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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  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 rbMask;
+}
+
+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 true;    // success
+}
+
+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) {
+                buffer.readBitmap(&fBitmaps->writableAt(i));
+            }
+        } 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();
+
+    *isValid = false;   // wait until we're done parsing to mark as true
+    for (;;) {
+        uint32_t tag = stream->readU32();
+        if (PICT_EOF_TAG == tag) {
+            break;
+        }
+
+        uint32_t size = stream->readU32();
+        if (!this->parseStreamTag(stream, info, tag, size, decoder)) {
+            return; // we're invalid
+        }
+    }
+    *isValid = true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#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
+
+    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 off = it.draw();
+        if (off == SK_MaxU32) {
+            return;
+        }
+        reader.setOffset(off);
+    }
+
+    // Record this, so we can concat w/ it if we encounter a setMatrix()
+    SkMatrix initialMatrix = canvas.getTotalMatrix();
+
+    while (!reader.eof()) {
+        switch (reader.readInt()) {
+            case CLIP_PATH: {
+                const SkPath& path = getPath(reader);
+                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.clipPath(path, op, doAA) && offsetToRestore) {
+#ifdef SPEW_CLIP_SKIPPING
+                    skipPath.recordSkip(offsetToRestore - reader.offset());
+#endif
+                    reader.setOffset(offsetToRestore);
+                }
+            } break;
+            case CLIP_REGION: {
+                const SkRegion& region = getRegion(reader);
+                uint32_t packed = reader.readInt();
+                SkRegion::Op op = ClipParams_unpackRegionOp(packed);
+                size_t offsetToRestore = reader.readInt();
+                SkASSERT(!offsetToRestore || \
+                    offsetToRestore >= reader.offset());
+                if (!canvas.clipRegion(region, op) && offsetToRestore) {
+#ifdef SPEW_CLIP_SKIPPING
+                    skipRegion.recordSkip(offsetToRestore - reader.offset());
+#endif
+                    reader.setOffset(offsetToRestore);
+                }
+            } break;
+            case CLIP_RECT: {
+                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 = reader.readInt();
+                SkASSERT(!offsetToRestore || \
+                    offsetToRestore >= reader.offset());
+                if (!canvas.clipRect(rect, op, doAA) && offsetToRestore) {
+#ifdef SPEW_CLIP_SKIPPING
+                    skipRect.recordSkip(offsetToRestore - reader.offset());
+#endif
+                    reader.setOffset(offsetToRestore);
+                }
+            } break;
+            case CONCAT:
+                canvas.concat(*getMatrix(reader));
+                break;
+            case DRAW_BITMAP: {
+                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_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(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(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(reader.readInt());
+                break;
+            case DRAW_DATA: {
+                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_PAINT:
+                canvas.drawPaint(*getPaint(reader));
+                break;
+            case DRAW_PATH: {
+                const SkPaint& paint = *getPaint(reader);
+                canvas.drawPath(getPath(reader), paint);
+            } break;
+            case DRAW_PICTURE:
+                canvas.drawPicture(getPicture(reader));
+                break;
+            case DRAW_POINTS: {
+                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(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(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(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(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)) {
+                    canvas.drawPosTextH(text.text(), text.length(), xpos,
+                                        constY, paint);
+                }
+            } break;
+            case DRAW_RECT: {
+                const SkPaint& paint = *getPaint(reader);
+                canvas.drawRect(reader.skipT<SkRect>(), paint);
+            } break;
+            case DRAW_SPRITE: {
+                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(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(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])) {
+                    canvas.drawText(text.text(), text.length(), ptr[0], ptr[1],
+                                    paint);
+                }
+            } break;
+            case DRAW_TEXT_ON_PATH: {
+                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(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*)reader.skip(
+                                                    vCount * sizeof(SkPoint));
+                }
+                if (flags & DRAW_VERTICES_HAS_COLORS) {
+                    colors = (const SkColor*)reader.skip(
+                                                    vCount * sizeof(SkColor));
+                }
+                if (flags & DRAW_VERTICES_HAS_INDICES) {
+                    iCount = reader.readInt();
+                    indices = (const uint16_t*)reader.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(reader.readScalar());
+                break;
+            case SAVE:
+                canvas.save((SkCanvas::SaveFlags) reader.readInt());
+                break;
+            case SAVE_LAYER: {
+                const SkRect* boundsPtr = getRectPtr(reader);
+                const SkPaint* paint = getPaint(reader);
+                canvas.saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) reader.readInt());
+                } break;
+            case SCALE: {
+                SkScalar sx = reader.readScalar();
+                SkScalar sy = reader.readScalar();
+                canvas.scale(sx, sy);
+            } break;
+            case SET_MATRIX: {
+                SkMatrix matrix;
+                matrix.setConcat(initialMatrix, *getMatrix(reader));
+                canvas.setMatrix(matrix);
+            } break;
+            case SKEW: {
+                SkScalar sx = reader.readScalar();
+                SkScalar sy = reader.readScalar();
+                canvas.skew(sx, sy);
+            } break;
+            case TRANSLATE: {
+                SkScalar dx = reader.readScalar();
+                SkScalar dy = reader.readScalar();
+                canvas.translate(dx, dy);
+            } break;
+            default:
+                SkASSERT(0);
+        }
+
+        if (it.isValid()) {
+            uint32_t off = it.draw();
+            if (off == SK_MaxU32) {
+                break;
+            }
+            reader.setOffset(off);
+        }
+    }
+
+#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 / reader.offset(), skipRect.fCount, skipPath.fCount,
+             skipRegion.fCount);
+    }
+#endif
+//    this->dumpSize();
+}
+
+void SkPicturePlayback::abort() {
+    SkASSERT(!"not supported");
+//    fReader.skip(fReader.size() - fReader.offset());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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 = fOpData.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/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
new file mode 100644
index 0000000..24bce4e
--- /dev/null
+++ b/src/core/SkPicturePlayback.h
@@ -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.
+ */
+#ifndef SkPicturePlayback_DEFINED
+#define SkPicturePlayback_DEFINED
+
+#include "SkPicture.h"
+#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 "SkPictureFlat.h"
+#include "SkSerializationHelpers.h"
+
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkThread.h"
+#endif
+
+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, 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*, SkSerializationHelpers::EncodeBitmap) 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(SkReader32& reader) {
+        int index = reader.readInt();
+        return (*fBitmaps)[index];
+    }
+
+    const SkMatrix* getMatrix(SkReader32& reader) {
+        int index = reader.readInt();
+        if (index == 0) {
+            return NULL;
+        }
+        return &(*fMatrices)[index - 1];
+    }
+
+    const SkPath& getPath(SkReader32& reader) {
+        return (*fPathHeap)[reader.readInt() - 1];
+    }
+
+    SkPicture& getPicture(SkReader32& reader) {
+        int index = reader.readInt();
+        SkASSERT(index > 0 && index <= fPictureCount);
+        return *fPictureRefs[index - 1];
+    }
+
+    const SkPaint* getPaint(SkReader32& reader) {
+        int index = reader.readInt();
+        if (index == 0) {
+            return NULL;
+        }
+        return &(*fPaints)[index - 1];
+    }
+
+    const SkRect* getRectPtr(SkReader32& reader) {
+        if (reader.readBool()) {
+            return &reader.skipT<SkRect>();
+        } else {
+            return NULL;
+        }
+    }
+
+    const SkIRect* getIRectPtr(SkReader32& reader) {
+        if (reader.readBool()) {
+            return &reader.skipT<SkIRect>();
+        } else {
+            return NULL;
+        }
+    }
+
+    const SkRegion& getRegion(SkReader32& reader) {
+        int index = reader.readInt();
+        return (*fRegions)[index - 1];
+    }
+
+    void getText(SkReader32& reader, TextContainer* text) {
+        size_t length = text->fByteLength = reader.readInt();
+        text->fText = (const char*)reader.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:    // 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:
+    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;
+
+    SkBBoxHierarchy* fBoundingHierarchy;
+    SkPictureStateTree* fStateTree;
+
+    SkTypefacePlayback fTFPlayback;
+    SkFactoryPlayback* fFactoryPlayback;
+#ifdef SK_BUILD_FOR_ANDROID
+    SkMutex fDrawMutex;
+#endif
+};
+
+#endif
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
new file mode 100644
index 0000000..edc67a0
--- /dev/null
+++ b/src/core/SkPictureRecord.cpp
@@ -0,0 +1,889 @@
+
+/*
+ * Copyright 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"
+#include "SkPixelRef.h"
+#include "SkBBoxHierarchy.h"
+#include "SkPictureStateTree.h"
+
+#define MIN_WRITER_SIZE 16384
+#define HEAP_BLOCK_SIZE 4096
+
+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);
+
+    fBitmapHeap = SkNEW(SkBitmapHeap);
+    fFlattenableHeap.setBitmapStorage(fBitmapHeap);
+    fPathHeap = NULL;   // lazy allocate
+    fFirstSavedLayerIndex = kNoSavedLayerIndex;
+
+    fInitialSaveCount = kNoInitialSave;
+}
+
+SkPictureRecord::~SkPictureRecord() {
+    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);
+
+    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);
+
+    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;
+}
+
+// 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,
+        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_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_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) {
+    // Some unexplained crashes in Chrome may be caused by this. Disabling
+    // for now to see if it helps.
+    // crbug.com/147406
+#ifdef SK_DISABLE_PICTURE_PEEPHOLE_OPTIMIZATION
+    return false;
+#endif
+
+#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;
+    }
+
+    if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
+        fFirstSavedLayerIndex = kNoSavedLayerIndex;
+    }
+
+    if (!collapseSaveClipRestore(&fWriter, fRestoreOffsetStack.top())) {
+        fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
+        this->addDraw(RESTORE);
+    }
+
+    fRestoreOffsetStack.pop();
+
+    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::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).
+        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;
+}
+
+bool SkPictureRecord::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+    addDraw(CLIP_RECT);
+    addRect(rect);
+    addInt(ClipParams_pack(op, doAA));
+    recordRestoreOffsetPlaceholder(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));
+    recordRestoreOffsetPlaceholder(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));
+    recordRestoreOffsetPlaceholder(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::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
+                            const SkRect& dst, const SkPaint* paint) {
+    addDraw(DRAW_BITMAP_RECT_TO_RECT);
+    addPaintPtr(paint);
+    addBitmap(bitmap);
+    addRectPtr(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::addBitmap(const SkBitmap& bitmap) {
+    addInt(fBitmapHeap->insert(bitmap));
+}
+
+void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
+    addMatrixPtr(&matrix);
+}
+
+void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
+    this->addInt(matrix ? fMatrices.find(*matrix) : 0);
+}
+
+void SkPictureRecord::addPaint(const SkPaint& paint) {
+    addPaintPtr(&paint);
+}
+
+void SkPictureRecord::addPaintPtr(const SkPaint* paint) {
+    this->addInt(paint ? fPaints.find(*paint) : 0);
+}
+
+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(fRegions.find(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
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
new file mode 100644
index 0000000..c36c24a
--- /dev/null
+++ b/src/core/SkPictureRecord.h
@@ -0,0 +1,200 @@
+
+/*
+ * 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 SkPictureStateTree;
+class SkBBoxHierarchy;
+
+class SkPictureRecord : public SkCanvas {
+public:
+    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;
+    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 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,
+                                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<SkPicture* >& getPictureRefs() const {
+        return fPictureRefs;
+    }
+
+    void setFlags(uint32_t recordFlags) {
+        fRecordFlags = recordFlags;
+    }
+
+    const SkWriter32& writeStream() const {
+        return fWriter;
+    }
+
+    void beginRecording();
+    void endRecording();
+
+private:
+    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
+        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(const SkBitmap& bitmap);
+
+#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
+
+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;
+
+private:
+    SkBitmapHeap* fBitmapHeap;
+    SkChunkFlatController fFlattenableHeap;
+
+    SkMatrixDictionary fMatrices;
+    SkPaintDictionary fPaints;
+    SkRegionDictionary fRegions;
+
+    SkPathHeap* fPathHeap;  // reference counted
+    SkWriter32 fWriter;
+
+    // we ref each item in these arrays
+    SkTDArray<SkPicture*> fPictureRefs;
+
+    uint32_t fRecordFlags;
+    int fInitialSaveCount;
+
+    friend class SkPicturePlayback;
+    friend class SkPictureTester; // for unit testing
+
+    typedef SkCanvas INHERITED;
+};
+
+#endif
diff --git a/src/core/SkPictureStateTree.cpp b/src/core/SkPictureStateTree.cpp
new file mode 100644
index 0000000..9299f8c
--- /dev/null
+++ b/src/core/SkPictureStateTree.cpp
@@ -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.
+ */
+
+#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..bcf2aee
--- /dev/null
+++ b/src/core/SkPictureStateTree.h
@@ -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.
+ */
+
+#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) { 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
new file mode 100644
index 0000000..69bbad7
--- /dev/null
+++ b/src/core/SkPixelRef.cpp
@@ -0,0 +1,181 @@
+
+/*
+ * Copyright 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 "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
+
+#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
+
+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...
+    int index = sk_atomic_inc(&gPixelRefMutexRingIndex);
+    return &gPixelRefMutexRing[index & (PIXELREF_MUTEX_RING_COUNT - 1)];
+#else
+    return &gPixelRefMutex;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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)
+        : INHERITED(buffer) {
+    this->setMutex(mutex);
+    fPixels = NULL;
+    fColorTable = NULL; // we do not track ownership of this
+    fLockCount = 0;
+    fIsImmutable = buffer.readBool();
+    fGenerationID = buffer.readUInt();
+    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 {
+    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() {
+    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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_BUILD_FOR_ANDROID
+void SkPixelRef::globalRef(void* data) {
+    this->ref();
+}
+
+void SkPixelRef::globalUnref() {
+    this->unref();
+}
+#endif
diff --git a/src/core/SkPoint.cpp b/src/core/SkPoint.cpp
new file mode 100644
index 0000000..ebbe3a4
--- /dev/null
+++ b/src/core/SkPoint.cpp
@@ -0,0 +1,473 @@
+
+/*
+ * 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);
+}
+
+#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) {
+    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;
+}
+
+SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
+    return sk_float_sqrt(getLengthSquared(dx, dy));
+}
+
+bool SkPoint::setLength(float x, float y, float length) {
+    float mag2;
+    if (!isLengthNearlyZero(x, y, &mag2)) {
+        float scale = length / sk_float_sqrt(mag2);
+        fX = x * scale;
+        fY = y * scale;
+        return true;
+    }
+    return false;
+}
+
+#else
+
+#include "Sk64.h"
+
+// 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
+    // 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 *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    tmp;
+    getLengthSquared(dx, dy, &tmp);
+    return tmp.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/src/core/SkProcSpriteBlitter.cpp b/src/core/SkProcSpriteBlitter.cpp
new file mode 100644
index 0000000..a481920
--- /dev/null
+++ b/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/src/core/SkPtrRecorder.cpp b/src/core/SkPtrRecorder.cpp
new file mode 100644
index 0000000..896e62d
--- /dev/null
+++ b/src/core/SkPtrRecorder.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 "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();
+    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, Cmp>(fList.begin(), count, pair, sizeof(pair));
+    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, Cmp>(fList.begin(), count, pair, sizeof(pair));
+    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/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
new file mode 100644
index 0000000..9e04887
--- /dev/null
+++ b/src/core/SkQuadClipper.cpp
@@ -0,0 +1,129 @@
+
+/*
+ * 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() {
+    fClip.setEmpty();
+}
+
+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/src/core/SkQuadClipper.h b/src/core/SkQuadClipper.h
new file mode 100644
index 0000000..c0b8695
--- /dev/null
+++ b/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/src/core/SkRTree.cpp b/src/core/SkRTree.cpp
new file mode 100644
index 0000000..42c1c99
--- /dev/null
+++ b/src/core/SkRTree.cpp
@@ -0,0 +1,476 @@
+
+/*
+ * Copyright 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) {
+
+            SkQSort(sorts[i][j], children, children + fMaxChildren, &RectLessThan);
+
+            // 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)) {
+        SkQSort(sorts[axis][sortSide], children, children + fMaxChildren, &RectLessThan);
+    }
+
+    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
+        SkQSort<int, Branch>(level, 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
+            SkQSort<int, Branch>(level, 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..1f16648
--- /dev/null
+++ b/src/core/SkRTree.h
@@ -0,0 +1,183 @@
+
+/*
+ * Copyright 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
+    static bool RectLessThan(SortSide const& side, const Branch lhs, const Branch rhs) {
+        return lhs.fBounds.*side < rhs.fBounds.*side;
+    }
+
+    static bool RectLessX(int&, const Branch lhs, const Branch rhs) {
+        return ((lhs.fBounds.fRight - lhs.fBounds.fLeft) >> 1) <
+               ((rhs.fBounds.fRight - lhs.fBounds.fLeft) >> 1);
+    }
+
+    static bool RectLessY(int&, const Branch lhs, const Branch rhs) {
+        return ((lhs.fBounds.fBottom - lhs.fBounds.fTop) >> 1) <
+               ((rhs.fBounds.fBottom - lhs.fBounds.fTop) >> 1);
+    }
+
+    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
new file mode 100644
index 0000000..4312717
--- /dev/null
+++ b/src/core/SkRasterClip.cpp
@@ -0,0 +1,294 @@
+/*
+ * 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;
+    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() {
+    SkDEBUGCODE(this->validate();)
+}
+
+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();
+    fIsEmpty = true;
+    fIsRect = false;
+    return false;
+}
+
+bool SkRasterClip::setRect(const SkIRect& rect) {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+
+    fIsBW = true;
+    fAA.setEmpty();
+    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) {
+        (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();
+        }
+        (void)fAA.setPath(path, &clip, doAA);
+    }
+    return this->updateCacheAndReturnNonEmpty();
+}
+
+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);
+
+    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) {
+        (void)fBW.op(rgn, op);
+    } else {
+        SkAAClip tmp;
+        tmp.setRegion(rgn);
+        (void)fAA.op(tmp, op);
+    }
+    return this->updateCacheAndReturnNonEmpty();
+}
+
+bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
+    AUTO_RASTERCLIP_VALIDATE(*this);
+    clip.validate();
+
+    if (this->isBW() && clip.isBW()) {
+        (void)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();
+        }
+        (void)fAA.op(*other, op);
+    }
+    return this->updateCacheAndReturnNonEmpty();
+}
+
+/**
+ *  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 (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;
+        }
+    }
+
+    if (fIsBW && !doAA) {
+        SkIRect ir;
+        r.round(&ir);
+        (void)fBW.op(ir, op);
+    } else {
+        if (fIsBW) {
+            this->convertToAA();
+        }
+        (void)fAA.op(r, op, doAA);
+    }
+    return this->updateCacheAndReturnNonEmpty();
+}
+
+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();
+    }
+    dst->updateCacheAndReturnNonEmpty();
+}
+
+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;
+    (void)this->updateCacheAndReturnNonEmpty();
+}
+
+#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();
+
+    SkASSERT(this->computeIsEmpty() == fIsEmpty);
+    SkASSERT(this->computeIsRect() == fIsRect);
+}
+#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/src/core/SkRasterClip.h b/src/core/SkRasterClip.h
new file mode 100644
index 0000000..7e015aa
--- /dev/null
+++ b/src/core/SkRasterClip.h
@@ -0,0 +1,164 @@
+/*
+ * 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 {
+        SkASSERT(this->computeIsEmpty() == fIsEmpty);
+        return fIsEmpty;
+    }
+
+    bool isRect() const {
+        SkASSERT(this->computeIsRect() == fIsRect);
+        return fIsRect;
+    }
+
+    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;
+    // 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();
+};
+
+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/src/core/SkRasterizer.cpp b/src/core/SkRasterizer.cpp
new file mode 100644
index 0000000..c6ddc08
--- /dev/null
+++ b/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"
+
+SK_DEFINE_INST_COUNT(SkRasterizer)
+
+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,
+                              SkPaint::kFill_Style);
+}
+
diff --git a/src/core/SkRect.cpp b/src/core/SkRect.cpp
new file mode 100644
index 0000000..1d8a13e
--- /dev/null
+++ b/src/core/SkRect.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 "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
+
+// 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 {
+#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;
+
+        l = r = pts[0].fX;
+        t = b = pts[0].fY;
+
+        // 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(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
+        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,
+                       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/src/core/SkRefCnt.cpp b/src/core/SkRefCnt.cpp
new file mode 100644
index 0000000..773e0d9
--- /dev/null
+++ b/src/core/SkRefCnt.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 "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
new file mode 100644
index 0000000..53b099b
--- /dev/null
+++ b/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/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
new file mode 100644
index 0000000..0dcacd8
--- /dev/null
+++ b/src/core/SkRegion.cpp
@@ -0,0 +1,1459 @@
+
+/*
+ * Copyright 2006 The Android 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"
+#include "SkUtils.h"
+
+/* Region Layout
+ *
+ *  TOP
+ *
+ *  [ Bottom, X-Intervals, [Left, Right]..., X-Sentinel ]
+ *  ...
+ *
+ *  Y-Sentinel
+ */
+
+SkDEBUGCODE(int32_t gRgnAllocCounter;)
+
+/////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*  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]);
+    }
+#endif
+    runs += intervals * 2 + 1;
+    return const_cast<SkRegion::RunType*>(runs);
+}
+
+bool SkRegion::RunsAreARect(const SkRegion::RunType runs[], int count,
+                            SkIRect* bounds) {
+    assert_sentinel(runs[0], false);    // top
+    SkASSERT(count >= kRectRegionRuns);
+
+    if (count == kRectRegionRuns) {
+        assert_sentinel(runs[1], false);    // bottom
+        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[3] < runs[4]);    // valid width
+
+        bounds->set(runs[3], runs[0], runs[4], runs[1]);
+        return true;
+    }
+    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, 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;
+}
+
+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());
+        maxT = fRunHead->getIntervalCount() * 2;
+    }
+    *itop = fBounds.fTop;
+    *ibot = fBounds.fBottom;
+    return maxT;
+}
+
+static bool isRunCountEmpty(int count) {
+    return count <= 2;
+}
+
+bool SkRegion::setRuns(RunType runs[], int count) {
+    SkDEBUGCODE(this->validate();)
+    SkASSERT(count > 0);
+
+    if (isRunCountEmpty(count)) {
+    //  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
+        // 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);    // intervalcount
+            assert_sentinel(runs[3], false);    // left
+            assert_sentinel(runs[4], false);    // right
+        }
+
+        assert_sentinel(stop[-1], true);
+        assert_sentinel(stop[-2], true);
+
+        // 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 (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) {
+        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();)
+
+    return true;
+}
+
+void SkRegion::BuildRectRuns(const SkIRect& bounds,
+                             RunType runs[kRectRegionRuns]) {
+    runs[0] = bounds.fTop;
+    runs[1] = bounds.fBottom;
+    runs[2] = 1;    // 1 interval for this scanline
+    runs[3] = bounds.fLeft;
+    runs[4] = bounds.fRight;
+    runs[5] = kRunTypeSentinel;
+    runs[6] = kRunTypeSentinel;
+}
+
+bool SkRegion::contains(int32_t x, int32_t y) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (!fBounds.contains(x, y)) {
+        return false;
+    }
+    if (this->isRect()) {
+        return true;
+    }
+
+    SkASSERT(this->isComplex());
+    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;
+}
+
+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()) {
+        return true;
+    }
+
+    SkASSERT(this->isComplex());
+    const RunType* scanline = fRunHead->findScanline(r.fTop);
+
+    do {
+        if (!scanline_contains(scanline, r.fLeft, r.fRight)) {
+            return false;
+        }
+        scanline = scanline_next(scanline);
+    } while (r.fBottom >= scanline[0]);
+    return true;
+}
+
+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()) {
+        tmpStorage[0] = kRunTypeSentinel;
+        *intervals = 0;
+    } else if (this->isRect()) {
+        BuildRectRuns(fBounds, tmpStorage);
+        *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;
+    }
+
+    SkIRect sect;
+    if (!sect.intersect(fBounds, r)) {
+        return false;
+    }
+    if (this->isRect()) {
+        return true;
+    }
+
+    const RunType* scanline = fRunHead->findScanline(sect.fTop);
+    do {
+        if (scanline_intersects(scanline, sect.fLeft, sect.fRight)) {
+            return true;
+        }
+        scanline = scanline_next(scanline);
+    } while (sect.fBottom >= scanline[0]);
+    return false;
+}
+
+bool SkRegion::intersects(const SkRegion& rgn) const {
+    if (this->isEmpty() || rgn.isEmpty()) {
+        return false;
+    }
+
+    if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
+        return false;
+    }
+
+    bool weAreARect = this->isRect();
+    bool theyAreARect = rgn.isRect();
+
+    if (weAreARect && theyAreARect) {
+        return true;
+    }
+    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();)
+    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);
+            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;
+            *druns++ = *sruns++;    // copy intervalCount;
+            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[]) {
+        // 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 &&
+            (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[-2] = (SkRegion::RunType)(bottom);
+                start[-1] = len >> 1;
+                fPrevDst = start;
+                fPrevLen = len;
+            }
+        }
+    }
+
+    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:
+    SkRegion::RunType*  fStartDst;
+    SkRegion::RunType*  fPrevDst;
+    size_t              fPrevLen;
+    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,
+                   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.
+        // 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;
+        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 (quickExit && !oper.isEmpty()) {
+            return QUICK_EXIT_TRUE_COUNT;
+        }
+
+        if (a_flush) {
+            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_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();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  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 INTERVALCOUNT LEFT RIGHT SENTINEL) + SENTINEL
+ */
+static int intervals_to_count(int intervals) {
+    return 1 + intervals * 5 + 1;
+}
+
+/*  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_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);
+}
+
+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 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) {
+        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 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 setEmptyCheck(result);
+        }
+        if (a_rect & b_rect) {
+            return setRectCheck(result, bounds);
+        }
+        break;
+
+    case kUnion_Op:
+        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 setRegionCheck(result, *rgnb);
+        }
+        if (b_empty) {
+            return setRegionCheck(result, *rgna);
+        }
+        break;
+    default:
+        SkDEBUGFAIL("unknown region op");
+        return false;
+    }
+
+    RunType tmpA[kRectRegionRuns];
+    RunType tmpB[kRectRegionRuns];
+
+    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_intervals, b_intervals);
+    SkAutoSTMalloc<256, RunType> array(dstCount);
+
+#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);
+
+    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::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);
+            }
+        }
+        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.write32(fRunHead->getYSpanCount());
+            buffer.write32(fRunHead->getIntervalCount());
+            buffer.write(fRunHead->readonly_runs(),
+                         fRunHead->fRunCount * sizeof(RunType));
+        }
+    }
+    return buffer.pos();
+}
+
+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 {
+            int32_t ySpanCount = buffer.readS32();
+            int32_t intervalCount = buffer.readS32();
+            tmp.allocateRuns(count, ySpanCount, intervalCount);
+            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
+
+// 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;
+
+    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 runs;
+}
+
+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 {
+        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;
+                int ySpanCount, intervalCount;
+                compute_bounds(run, stop - run, &bounds, &ySpanCount, &intervalCount);
+
+                SkASSERT(bounds == fBounds);
+                SkASSERT(ySpanCount > 0);
+                SkASSERT(fRunHead->getYSpanCount() == ySpanCount);
+           //     SkASSERT(intervalCount > 1);
+                SkASSERT(fRunHead->getIntervalCount() == intervalCount);
+            }
+        }
+    }
+}
+
+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[3], fRuns[0], fRuns[4], fRuns[1]);
+            fRuns += 5;
+            // Now fRuns points to the 2nd interval (or x-sentinel)
+        }
+    }
+}
+
+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
+            int intervals = runs[1];
+            if (0 == intervals) {    // empty line
+                fRect.fTop = runs[0];
+                runs += 3;
+            } else {
+                fRect.fTop = fRect.fBottom;
+            }
+
+            fRect.fBottom = runs[0];
+            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;
+        }
+    }
+    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();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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 = 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;
+            }
+        }
+    }
+}
+
+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/src/core/SkRegionPriv.h b/src/core/SkRegionPriv.h
new file mode 100644
index 0000000..91b3a2e
--- /dev/null
+++ b/src/core/SkRegionPriv.h
@@ -0,0 +1,241 @@
+
+/*
+ * Copyright 2006 The Android 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;)
+
+#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;
+
+    /**
+     *  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));
+
+        SkASSERT(count >= SkRegion::kRectRegionRuns);
+
+        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;
+    }
+
+    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;
+    }
+
+    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, 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) {
+                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
new file mode 100644
index 0000000..58812eb
--- /dev/null
+++ b/src/core/SkRegion_path.cpp
@@ -0,0 +1,491 @@
+
+/*
+ * Copyright 2006 The Android 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:
+    /*
+     *  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 {
+            // add final +1 for the x-sentinel
+            return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount + 1);
+        }
+    };
+    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);
+    // 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);
+
+    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;
+        *runs++ = count >> 1;   // intervalCount
+        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, false)) != 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());
+        tmp.fRunHead->computeRunBounds(&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);
+    }
+    qsort(edges.begin(), edges.count(), sizeof(Edge), SkCastForQSort(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/src/core/SkRegion_rects.cpp b/src/core/SkRegion_rects.cpp
new file mode 100644
index 0000000..4121080
--- /dev/null
+++ b/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/src/core/SkScalar.cpp b/src/core/SkScalar.cpp
new file mode 100644
index 0000000..c48d389
--- /dev/null
+++ b/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/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
new file mode 100644
index 0000000..0a46d20
--- /dev/null
+++ b/src/core/SkScalerContext.cpp
@@ -0,0 +1,779 @@
+
+/*
+ * Copyright 2006 The Android 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 "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 {
+    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) {
+        SkOrderedReadBuffer   buffer(data, len);
+        obj = buffer.readFlattenable();
+        SkASSERT(buffer.offset() == buffer.size());
+    }
+    return obj;
+}
+
+SkScalerContext::SkScalerContext(const SkDescriptor* desc)
+    : 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)
+    , fMaskPreBlend(SkScalerContext::GetMaskPreBlend(fRec))
+{
+#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
+}
+
+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)
+    uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID, rec.fOrigFontID);
+    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::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
+/*  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) {
+    SkScalerContext* ctx = this;
+    unsigned glyphID;
+    for (;;) {
+        glyphID = ctx->generateCharToGlyph(uni);
+        if (glyphID) {
+            break;  // found it
+        }
+        ctx = ctx->getNextContext();
+        if (NULL == ctx) {
+            SkDebugf("--- no context for char %x\n", uni);
+            // just return the original context (this)
+            return this->fBaseGlyphCount;
+        }
+    }
+    return ctx->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) {
+    SkScalerContext* ctx = this;
+    unsigned glyphID;
+    for (;;) {
+        glyphID = ctx->generateCharToGlyph(uni);
+        if (glyphID) {
+            break;  // found it
+        }
+        ctx = ctx->getNextContext();
+        if (NULL == 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)) {
+            if (dst.fBounds.isEmpty() || !dst.fBounds.is16Bit()) {
+                goto SK_ERROR;
+            }
+            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;
+}
+
+template<bool APPLY_PREBLEND>
+static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst, 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;
+
+    const uint8_t* maskPreBlendR = NULL;
+    const uint8_t* maskPreBlendG = NULL;
+    const uint8_t* maskPreBlendB = NULL;
+    if (APPLY_PREBLEND) {
+        maskPreBlendR = maskPreBlend->fR;
+        maskPreBlendG = maskPreBlend->fG;
+        maskPreBlendB = maskPreBlend->fB;
+    }
+
+    for (int y = 0; y < height; ++y) {
+        const uint8_t* srcP = src.getAddr8(0, y);
+        for (int x = 0; x < width; ++x) {
+            U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendR);
+            U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendG);
+            U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendB);
+            dstP[x] = SkPack888ToRGB16(r, g, b);
+        }
+        dstP = (uint16_t*)((char*)dstP + dstRB);
+    }
+}
+
+template<bool APPLY_PREBLEND>
+static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst, 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;
+
+    const uint8_t* maskPreBlendR = NULL;
+    const uint8_t* maskPreBlendG = NULL;
+    const uint8_t* maskPreBlendB = NULL;
+    if (APPLY_PREBLEND) {
+        maskPreBlendR = maskPreBlend->fR;
+        maskPreBlendG = maskPreBlend->fG;
+        maskPreBlendB = maskPreBlend->fB;
+    }
+
+    for (int y = 0; y < height; ++y) {
+        const uint8_t* srcP = src.getAddr8(0, y);
+        for (int x = 0; x < width; ++x) {
+            U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendR);
+            U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendG);
+            U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlendB);
+            dstP[x] = SkPackARGB32(0xFF, r, g, b);
+        }
+        dstP = (SkPMColor*)((char*)dstP + dstRB);
+    }
+}
+
+static void generateMask(const SkMask& mask, const SkPath& path, SkMaskGamma::PreBlend* maskPreBlend) {
+    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;
+    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:
+                if (maskPreBlend) {
+                    pack3xHToLCD16<true>(bm, mask, maskPreBlend);
+                } else {
+                    pack3xHToLCD16<false>(bm, mask, maskPreBlend);
+                }
+                break;
+            case SkMask::kLCD32_Format:
+                if (maskPreBlend) {
+                    pack3xHToLCD32<true>(bm, mask, maskPreBlend);
+                } else {
+                    pack3xHToLCD32<false>(bm, mask, maskPreBlend);
+                }
+                break;
+            default:
+                SkDEBUGFAIL("bad format for copyback");
+        }
+    }
+}
+
+static void applyLUTToA8Glyph(const SkGlyph& glyph, const uint8_t* lut) {
+      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] = lut[dst[x]];
+          }
+          dst += rowBytes;
+      }
+}
+
+void SkScalerContext::getImage(const SkGlyph& origGlyph) {
+    const SkGlyph*  glyph = &origGlyph;
+    SkGlyph         tmpGlyph;
+    SkMaskGamma::PreBlend* maskPreBlend = fMaskPreBlend.fG ? &fMaskPreBlend : NULL;
+
+    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;
+        maskPreBlend = NULL;
+    }
+
+    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;
+            }
+            //apply maskPreBlend to a8 (if not NULL)
+            if (maskPreBlend) {
+              applyLUTToA8Glyph(*glyph, maskPreBlend->fG);
+            }
+        } else {
+            generateMask(mask, devPath, maskPreBlend);
+            //apply maskPreBlend to a8 (if not NULL) -- already applied to lcd.
+            if (maskPreBlend && mask.fFormat == SkMask::kA8_Format) {
+                applyLUTToA8Glyph(*glyph, maskPreBlend->fG);
+            }
+        }
+    } else {
+        this->getGlyphContext(*glyph)->generateImage(*glyph, maskPreBlend);
+    }
+
+    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);
+
+            /* 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. */
+            //applyLUTToA8Glyph(origGlyph, fMaskPreBlend.fG);
+        }
+    }
+}
+
+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);
+        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
+
+        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, &rec)) {
+                localPath.swap(effectPath);
+            }
+        }
+
+        if (rec.needToApply()) {
+            SkPath strokePath;
+            if (rec.applyToPath(&strokePath, localPath)) {
+                localPath.swap(strokePath);
+            }
+        }
+
+        // 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 SkScalerContextRec::getMatrixFrom2x2(SkMatrix* dst) const {
+    dst->reset();
+    dst->setScaleX(fPost2x2[0][0]);
+    dst->setSkewX( fPost2x2[0][1]);
+    dst->setSkewY( fPost2x2[1][0]);
+    dst->setScaleY(fPost2x2[1][1]);
+}
+
+void SkScalerContextRec::getLocalMatrix(SkMatrix* m) const {
+    m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize);
+    if (fPreSkewX) {
+        m->postSkew(fPreSkewX, 0);
+    }
+}
+
+void SkScalerContextRec::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, SkMaskGamma::PreBlend* maskPreBlend) {}
+    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/src/core/SkScalerContext.h b/src/core/SkScalerContext.h
new file mode 100644
index 0000000..8175582
--- /dev/null
+++ b/src/core/SkScalerContext.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2006 The Android 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
+    //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
+
+    //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(0x00000000);
+        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) {
+        SkScalerContext* ctx = this;
+        while (NULL != ctx) {
+            if (ctx->generateCharToGlyph(uni)) {
+                return ctx->fRec.fFontID;
+            }
+            ctx = ctx->getNextContext();
+        }
+        return 0;
+    }
+#endif
+
+    static inline void MakeRec(const SkPaint&, 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&, SkMaskGamma::PreBlend* maskPreBlend) = 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:
+    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;
+
+    // converts linear masks to gamma correcting masks.
+    SkMaskGamma::PreBlend fMaskPreBlend;
+};
+
+#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
new file mode 100644
index 0000000..30626f7
--- /dev/null
+++ b/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/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
new file mode 100644
index 0000000..82402a7
--- /dev/null
+++ b/src/core/SkScanPriv.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 SkScanPriv_DEFINED
+#define SkScanPriv_DEFINED
+
+#include "SkScan.h"
+#include "SkBlitter.h"
+
+class SkScanClipper {
+public:
+    SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& bounds,
+                  bool skipRejectTest = false);
+
+    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/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
new file mode 100644
index 0000000..fdd0c8e
--- /dev/null
+++ b/src/core/SkScan_AntiPath.cpp
@@ -0,0 +1,747 @@
+
+/*
+ * Copyright 2006 The Android 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
+
+    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_USE_LEGACY_AA_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) {
+    aa <<= 8 - 2*SHIFT;
+#ifdef SK_USE_LEGACY_AA_COVERAGE
+    aa -= aa >> (8 - SHIFT - 1);
+#endif
+    return aa;
+}
+
+/** 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
+}
+
+#if 0 // UNUSED
+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;
+}
+#endif
+
+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;
+}
+
+// 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
+
+static void add_aa_span(uint8_t* alpha, U8CPU startAlpha, int middleCount,
+                        U8CPU stopAlpha, U8CPU maxValue) {
+    SkASSERT(middleCount >= 0);
+
+    saturated_add(alpha, startAlpha);
+    alpha += 1;
+
+    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[]
+    saturated_add(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 {
+        fb = SCALE - fb;
+        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/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
new file mode 100644
index 0000000..92245d4
--- /dev/null
+++ b/src/core/SkScan_Antihair.cpp
@@ -0,0 +1,903 @@
+
+/*
+ * 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;
+}
+
+#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
+
+static bool canConvertFDot6ToFixed(SkFDot6 x) {
+    const int maxDot6 = SK_MaxS32 >> (16 - 6);
+    return SkAbs32(x) <= maxDot6;
+}
+
+static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
+                             const SkIRect* clip, SkBlitter* blitter) {
+    // 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
+            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 = 0;  // so we don't draw this last column
+            }
+
+            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 = 0;  // so we don't draw this last row
+            }
+
+            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 };
+
+#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());
+        /*  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
+        // 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) {
+        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/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp
new file mode 100644
index 0000000..69095c0
--- /dev/null
+++ b/src/core/SkScan_Hairline.cpp
@@ -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.
+ */
+
+
+#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);
+}
+
+static bool canConvertFDot6ToFixed(SkFDot6 x) {
+    const int maxDot6 = SK_MaxS32 >> (16 - 6);
+    return SkAbs32(x) <= maxDot6;
+}
+
+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 };
+
+#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)
+        r.set(clip->getBounds());
+        if (!SkLineClipper::IntersectLine(pts, r, pts)) {
+            return;
+        }
+    }
+
+    SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
+    SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
+    SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
+    SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
+
+    SkASSERT(canConvertFDot6ToFixed(x0));
+    SkASSERT(canConvertFDot6ToFixed(y0));
+    SkASSERT(canConvertFDot6ToFixed(x1));
+    SkASSERT(canConvertFDot6ToFixed(y1));
+
+    if (clip) {
+        // now perform clipping again, as the rounding to dot6 can wiggle us
+        // our rects are really dot6 rects, but since we've already used
+        // lineclipper, we know they will fit in 32bits (26.6)
+        const SkIRect& bounds = clip->getBounds();
+
+        clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop),
+                  SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom));
+        ptsR.set(x0, y0, x1, y1);
+        ptsR.sort();
+
+        // outset the right and bottom, to account for how hairlines are
+        // actually drawn, which may hit the pixel to the right or below of
+        // the coordinate
+        ptsR.fRight += SK_FDot6One;
+        ptsR.fBottom += SK_FDot6One;
+
+        if (!SkIRect::Intersects(ptsR, clipR)) {
+            return;
+        }
+        if (clip->isRect() && clipR.contains(ptsR)) {
+            clip = NULL;
+        } else {
+            blitter = clipper.apply(blitter, clip);
+        }
+    }
+
+    SkFDot6 dx = x1 - x0;
+    SkFDot6 dy = y1 - y0;
+
+    if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal
+        if (x0 > x1) {   // we want to go left-to-right
+            SkTSwap<SkFDot6>(x0, x1);
+            SkTSwap<SkFDot6>(y0, y1);
+        }
+        int ix0 = SkFDot6Round(x0);
+        int ix1 = SkFDot6Round(x1);
+        if (ix0 == ix1) {// too short to draw
+            return;
+        }
+
+        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, false)) != 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/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
new file mode 100644
index 0000000..68ac7ff
--- /dev/null
+++ b/src/core/SkScan_Path.cpp
@@ -0,0 +1,736 @@
+/*
+ * Copyright 2006 The Android 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"
+#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
+
+#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
+
+#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;
+        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);
+    }
+}
+#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++) {
+        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()) {
+            /*
+             *  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;
+    }
+
+    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);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  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, bool skipRejectTest) {
+    fBlitter = NULL;     // null means blit nothing
+    fClipRect = NULL;
+
+    if (clip) {
+        fClipRect = &clip->getBounds();
+        if (!skipRejectTest && !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, path.isInverseFillType());
+
+    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/src/core/SkShader.cpp b/src/core/SkShader.cpp
new file mode 100644
index 0000000..7e3a92b
--- /dev/null
+++ b/src/core/SkShader.cpp
@@ -0,0 +1,358 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkFlattenableBuffers.h"
+#include "SkPaint.h"
+#include "SkMallocPixelRef.h"
+
+SK_DEFINE_INST_COUNT(SkShader)
+
+SkShader::SkShader() : fLocalMatrix(NULL) {
+    SkDEBUGCODE(fInSession = false;)
+}
+
+SkShader::SkShader(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer), fLocalMatrix(NULL) {
+    if (buffer.readBool()) {
+        SkMatrix matrix;
+        buffer.readMatrix(&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) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeBool(fLocalMatrix != NULL);
+    if (fLocalMatrix) {
+        buffer.writeMatrix(*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;
+}
+
+SkShader::ShadeProc SkShader::asAShadeProc(void** ctx) {
+    return NULL;
+}
+
+#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*) const {
+    return kNone_BitmapType;
+}
+
+SkShader::GradientType SkShader::asAGradient(GradientInfo* info) const {
+    return kNone_GradientType;
+}
+
+bool SkShader::asNewCustomStage(GrContext*, GrSamplerState*) const {
+    return false;
+}
+
+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.readBool();
+    if (fInheritColor) {
+        return;
+    }
+    fColor = b.readColor();
+}
+
+void SkColorShader::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeBool(fInheritColor);
+    if (fInheritColor) {
+        return;
+    }
+    buffer.writeColor(fColor);
+}
+
+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[]) 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"
+
+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");
+}
diff --git a/src/core/SkSinTable.h b/src/core/SkSinTable.h
new file mode 100644
index 0000000..ac26b9e
--- /dev/null
+++ b/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/src/core/SkSpriteBlitter.h b/src/core/SkSpriteBlitter.h
new file mode 100644
index 0000000..c1dbfa2
--- /dev/null
+++ b/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/src/core/SkSpriteBlitterTemplate.h b/src/core/SkSpriteBlitterTemplate.h
new file mode 100644
index 0000000..c43e582
--- /dev/null
+++ b/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/src/core/SkSpriteBlitter_ARGB32.cpp b/src/core/SkSpriteBlitter_ARGB32.cpp
new file mode 100644
index 0000000..80d3225
--- /dev/null
+++ b/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/src/core/SkSpriteBlitter_RGB16.cpp b/src/core/SkSpriteBlitter_RGB16.cpp
new file mode 100644
index 0000000..1a8404f
--- /dev/null
+++ b/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/src/core/SkStream.cpp b/src/core/SkStream.cpp
new file mode 100644
index 0000000..f7b1fb4
--- /dev/null
+++ b/src/core/SkStream.cpp
@@ -0,0 +1,775 @@
+
+/*
+ * Copyright 2006 The Android 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"
+
+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()
+{
+    // 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;
+    }
+}
+
+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()
+{
+}
+
+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->write32(data->size());
+        this->write(data->data(), data->size());
+    } else {
+        this->write32(0);
+    }
+    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/src/core/SkString.cpp b/src/core/SkString.cpp
new file mode 100644
index 0000000..fee614c
--- /dev/null
+++ b/src/core/SkString.cpp
@@ -0,0 +1,605 @@
+
+/*
+ * Copyright 2006 The Android 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 = 512;
+
+#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 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' + (int32_t) (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;
+}
+
+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;
+}
+
+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/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
new file mode 100644
index 0000000..0ab1016
--- /dev/null
+++ b/src/core/SkStroke.cpp
@@ -0,0 +1,704 @@
+
+/*
+ * 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(const SkPath& src,
+                  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();
+        }
+    }
+    // since we may re-use fInner, we rewind instead of reset, to save on
+    // reallocating its internal storage.
+    fInner.rewind();
+    fSegmentCount = -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPathStroker::SkPathStroker(const SkPath& src,
+                             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;
+
+    // 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) {
+    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;
+
+        }
+
+#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;
+            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);
+            }
+        }
+#endif
+    }
+
+    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
+
+// 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);
+
+    AutoTmpPath tmp(src, &dst);
+
+    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(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, false)) != 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();
+    }
+}
+
diff --git a/src/core/SkStroke.h b/src/core/SkStroke.h
new file mode 100644
index 0000000..46e6ba1
--- /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 "SkPoint.h"
+#include "SkPaint.h"
+
+struct SkRect;
+class SkPath;
+
+/** \class SkStroke
+    SkStroke is the utility class that constructs paths by stroking
+    geometries (lines, rects, ovals, roundrects, paths). This is
+    invoked when a geometry or text is drawn in a canvas with the
+    kStroke_Mask bit set in the paint.
+*/
+class SkStroke {
+public:
+    SkStroke();
+    SkStroke(const SkPaint&);
+    SkStroke(const SkPaint&, SkScalar width);   // width overrides paint.getStrokeWidth()
+
+    SkPaint::Cap    getCap() const { return (SkPaint::Cap)fCap; }
+    void        setCap(SkPaint::Cap);
+
+    SkPaint::Join   getJoin() const { return (SkPaint::Join)fJoin; }
+    void        setJoin(SkPaint::Join);
+
+    void    setMiterLimit(SkScalar);
+    void    setWidth(SkScalar);
+
+    bool    getDoFill() const { return SkToBool(fDoFill); }
+    void    setDoFill(bool doFill) { fDoFill = SkToU8(doFill); }
+
+    void    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/SkStrokerPriv.cpp b/src/core/SkStrokerPriv.cpp
new file mode 100644
index 0000000..0a0a581
--- /dev/null
+++ b/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/src/core/SkStrokerPriv.h b/src/core/SkStrokerPriv.h
new file mode 100644
index 0000000..e67d677
--- /dev/null
+++ b/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/src/core/SkTLS.cpp b/src/core/SkTLS.cpp
new file mode 100755
index 0000000..e4f6188
--- /dev/null
+++ b/src/core/SkTLS.cpp
@@ -0,0 +1,124 @@
+#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
new file mode 100644
index 0000000..801c0d6
--- /dev/null
+++ b/src/core/SkTSearch.cpp
@@ -0,0 +1,115 @@
+
+/*
+ * Copyright 2006 The Android 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);
+    }
+}
+
diff --git a/src/core/SkTSort.h b/src/core/SkTSort.h
new file mode 100644
index 0000000..db961d0
--- /dev/null
+++ b/src/core/SkTSort.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 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);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+static T** SkTQSort_Partition(T** left, T** right, T** pivot) {
+    T* pivotValue = *pivot;
+    SkTSwap(*pivot, *right);
+    T** newPivot = left;
+    while (left < right) {
+        if (**left < *pivotValue) {
+            SkTSwap(*left, *newPivot);
+            newPivot += 1;
+        }
+        left += 1;
+    }
+    SkTSwap(*newPivot, *right);
+    return newPivot;
+}
+
+template <typename T> void SkTQSort(T** left, T** right) {
+    if (left >= right) {
+        return;
+    }
+    T** pivot = left + ((right - left) >> 1);
+    pivot = SkTQSort_Partition(left, right, pivot);
+    SkTQSort(left, pivot - 1);
+    SkTQSort(pivot + 1, right);
+}
+
+template <typename T>
+static T* SkTQSort_Partition(T* left, T* right, T* pivot) {
+    T pivotValue = *pivot;
+    SkTSwap(*pivot, *right);
+    T* newPivot = left;
+    while (left < right) {
+        if (*left < pivotValue) {
+            SkTSwap(*left, *newPivot);
+            newPivot += 1;
+        }
+        left += 1;
+    }
+    SkTSwap(*newPivot, *right);
+    return newPivot;
+}
+
+template <typename T> void SkTQSort(T* left, T* right) {
+    if (left >= right) {
+        return;
+    }
+    T* pivot = left + ((right - left) >> 1);
+    pivot = SkTQSort_Partition(left, right, pivot);
+    SkTQSort(left, pivot - 1);
+    SkTQSort(pivot + 1, right);
+}
+
+template <typename S, typename T>
+static T* SkTQSort_Partition(S& context, T* left, T* right, T* pivot,
+                             bool (*lessThan)(S&, const T, const T)) {
+    T pivotValue = *pivot;
+    SkTSwap(*pivot, *right);
+    T* newPivot = left;
+    while (left < right) {
+        if (lessThan(context, *left, pivotValue)) {
+            SkTSwap(*left, *newPivot);
+            newPivot += 1;
+        }
+        left += 1;
+    }
+    SkTSwap(*newPivot, *right);
+    return newPivot;
+}
+
+template <typename S, typename T>
+void SkQSort(S& context, T* left, T* right,
+             bool (*lessThan)(S& , const T, const T)) {
+    if (left >= right) {
+        return;
+    }
+    T* pivot = left + ((right - left) >> 1);
+    pivot = SkTQSort_Partition(context, left, right, pivot, lessThan);
+    SkQSort(context, left, pivot - 1, lessThan);
+    SkQSort(context, pivot + 1, right, lessThan);
+}
+
+#endif
diff --git a/src/core/SkTemplatesPriv.h b/src/core/SkTemplatesPriv.h
new file mode 100644
index 0000000..79ae609
--- /dev/null
+++ b/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/src/core/SkTextFormatParams.h b/src/core/SkTextFormatParams.h
new file mode 100644
index 0000000..dac4aef
--- /dev/null
+++ b/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/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/SkTypeface.cpp b/src/core/SkTypeface.cpp
new file mode 100644
index 0000000..94bb237
--- /dev/null
+++ b/src/core/SkTypeface.cpp
@@ -0,0 +1,137 @@
+
+/*
+ * 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"
+
+SK_DEFINE_INST_COUNT(SkTypeface)
+
+//#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
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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.
+    static SkTypeface* gDefaultTypeface;
+
+    if (NULL == gDefaultTypeface) {
+        gDefaultTypeface =
+        SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
+    }
+    return gDefaultTypeface;
+}
+
+uint32_t SkTypeface::UniqueID(const SkTypeface* face) {
+    if (NULL == face) {
+        face = GetDefaultTypeface();
+    }
+    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, style);
+}
+
+SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style s) {
+    return SkFontHost::CreateTypeface(family, NULL, 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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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
new file mode 100644
index 0000000..ee5ced9
--- /dev/null
+++ b/src/core/SkTypefaceCache.cpp
@@ -0,0 +1,148 @@
+
+/*
+ * Copyright 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,
+                          bool strong) {
+    if (fArray.count() >= TYPEFACE_CACHE_LIMIT) {
+        this->purge(TYPEFACE_CACHE_LIMIT >> 2);
+    }
+
+    Rec* rec = fArray.append();
+    rec->fFace = face;
+    rec->fRequestedStyle = requestedStyle;
+    rec->fStrong = strong;
+    if (strong) {
+        face->ref();
+    } else {
+        face->weak_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::findByProcAndRef(FindProc proc, void* ctx) const {
+    const Rec* curr = fArray.begin();
+    const Rec* stop = fArray.end();
+    while (curr < stop) {
+        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;
+    }
+    return NULL;
+}
+
+void SkTypefaceCache::purge(int numToPurge) {
+    int count = fArray.count();
+    int i = 0;
+    while (i < count) {
+        SkTypeface* face = fArray[i].fFace;
+        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) {
+                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,
+                          bool strong) {
+    SkAutoMutexAcquire ama(gMutex);
+    Get().add(face, requestedStyle, strong);
+}
+
+SkTypeface* SkTypefaceCache::FindByID(SkFontID fontID) {
+    SkAutoMutexAcquire ama(gMutex);
+    return Get().findByID(fontID);
+}
+
+SkTypeface* SkTypefaceCache::FindByProcAndRef(FindProc proc, void* ctx) {
+    SkAutoMutexAcquire ama(gMutex);
+    SkTypeface* typeface = Get().findByProcAndRef(proc, ctx);
+    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().findByProcAndRef(DumpProc, NULL);
+#endif
+}
+
diff --git a/src/core/SkTypefaceCache.h b/src/core/SkTypefaceCache.h
new file mode 100644
index 0000000..b6cab81
--- /dev/null
+++ b/src/core/SkTypefaceCache.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 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. The typeface may be in the disposed state.
+     */
+    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,
+                    bool strong = true);
+
+    /**
+     *  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, bool strong = true);
+    SkTypeface* findByID(SkFontID findID) 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;
+};
+
+#endif
diff --git a/src/core/SkUnPreMultiply.cpp b/src/core/SkUnPreMultiply.cpp
new file mode 100644
index 0000000..ad87f8a
--- /dev/null
+++ b/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/src/core/SkUtils.cpp b/src/core/SkUtils.cpp
new file mode 100644
index 0000000..fe498ce
--- /dev/null
+++ b/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/src/core/SkUtilsArm.cpp b/src/core/SkUtilsArm.cpp
new file mode 100644
index 0000000..1c49316
--- /dev/null
+++ b/src/core/SkUtilsArm.cpp
@@ -0,0 +1,175 @@
+
+/*
+ * 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 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
+
+    // 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);
+
+    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..9ae648a
--- /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 bool sk_cpu_arm_has_neon(void) {
+    return false;
+}
+#elif SK_ARM_NEON_IS_ALWAYS
+static 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
new file mode 100644
index 0000000..6a84927
--- /dev/null
+++ b/src/core/SkWriter32.cpp
@@ -0,0 +1,381 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkWriter32.h"
+
+struct SkWriter32::Block {
+    Block*  fNext;
+    size_t  fSizeOfBlock;      // total space allocated (after this)
+    size_t  fAllocatedSoFar;    // space used so far
+
+    size_t  available() const { return fSizeOfBlock - fAllocatedSoFar; }
+    char*   base() { return (char*)(this + 1); }
+    const char* base() const { return (const char*)(this + 1); }
+
+    uint32_t* alloc(size_t size) {
+        SkASSERT(SkAlign4(size) == size);
+        SkASSERT(this->available() >= size);
+        void* ptr = this->base() + fAllocatedSoFar;
+        fAllocatedSoFar += size;
+        SkASSERT(fAllocatedSoFar <= fSizeOfBlock);
+        return (uint32_t*)ptr;
+    }
+
+    uint32_t* peek32(size_t offset) {
+        SkASSERT(offset <= fAllocatedSoFar + 4);
+        void* ptr = this->base() + offset;
+        return (uint32_t*)ptr;
+    }
+
+    void rewind() {
+        fNext = NULL;
+        fAllocatedSoFar = 0;
+        // keep fSizeOfBlock as is
+    }
+
+    static Block* Create(size_t size) {
+        SkASSERT(SkAlign4(size) == size);
+        Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
+        block->fNext = NULL;
+        block->fSizeOfBlock = size;
+        block->fAllocatedSoFar = 0;
+        return block;
+    }
+
+    static Block* CreateFromStorage(void* storage, size_t size) {
+        SkASSERT(SkIsAlign4((intptr_t)storage));
+        Block* block = (Block*)storage;
+        block->fNext = NULL;
+        block->fSizeOfBlock = size - sizeof(Block);
+        block->fAllocatedSoFar = 0;
+        return block;
+    }
+
+};
+
+#define MIN_BLOCKSIZE   (sizeof(SkWriter32::Block) + sizeof(intptr_t))
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkWriter32::SkWriter32(size_t minSize, void* storage, size_t storageSize) {
+    fMinSize = minSize;
+    fSize = 0;
+    fSingleBlock = NULL;
+    fSingleBlockSize = 0;
+
+    storageSize &= ~3;  // trunc down to multiple of 4
+    if (storageSize >= MIN_BLOCKSIZE) {
+        fHead = fTail = Block::CreateFromStorage(storage, storageSize);
+        fHeadIsExternalStorage = true;
+    } else {
+        fHead = fTail = NULL;
+        fHeadIsExternalStorage = false;
+    }
+}
+
+SkWriter32::~SkWriter32() {
+    this->reset();
+}
+
+void SkWriter32::reset() {
+    Block* block = fHead;
+
+    if (fHeadIsExternalStorage) {
+        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);
+        block = next;
+    }
+
+    fSize = 0;
+    fSingleBlock = NULL;
+    if (fHeadIsExternalStorage) {
+        SkASSERT(fHead);
+        fHead->rewind();
+        fTail = fHead;
+    } else {
+        fHead = fTail = 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) {
+    SkDEBUGCODE(this->validate();)
+
+    SkASSERT(SkAlign4(offset) == offset);
+    SkASSERT(offset <= fSize);
+
+    if (fSingleBlock) {
+        return (uint32_t*)(fSingleBlock + offset);
+    }
+
+    Block* block = fHead;
+    SkASSERT(NULL != block);
+
+    while (offset >= block->fAllocatedSoFar) {
+        offset -= block->fAllocatedSoFar;
+        block = block->fNext;
+        SkASSERT(NULL != block);
+    }
+    return block->peek32(offset);
+}
+
+void SkWriter32::rewindToOffset(size_t offset) {
+    if (offset >= fSize) {
+        return;
+    }
+    if (0 == offset) {
+        this->reset(NULL, 0);
+        return;
+    }
+
+    SkDEBUGCODE(this->validate();)
+
+    SkASSERT(SkAlign4(offset) == offset);
+    SkASSERT(offset <= fSize);
+    fSize = offset;
+
+    if (fSingleBlock) {
+        return;
+    }
+
+    // Similar to peek32, except that we free up any following blocks
+    Block* block = fHead;
+    SkASSERT(NULL != block);
+    while (offset >= block->fAllocatedSoFar) {
+        offset -= block->fAllocatedSoFar;
+        block = block->fNext;
+        SkASSERT(NULL != block);
+    }
+
+    // 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 {
+    if (fSingleBlock) {
+        memcpy(dst, fSingleBlock, fSize);
+        return;
+    }
+
+    const Block* block = fHead;
+    SkDEBUGCODE(size_t total = 0;)
+
+    while (block) {
+        size_t allocated = block->fAllocatedSoFar;
+        memcpy(dst, block->base(), allocated);
+        dst = (char*)dst + allocated;
+        block = block->fNext;
+
+        SkDEBUGCODE(total += allocated;)
+        SkASSERT(total <= fSize);
+    }
+    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) {
+    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) {
+            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->fAllocatedSoFar)) {
+            return false;
+        }
+        block = block->fNext;
+    }
+    return true;
+}
+
+#ifdef SK_DEBUG
+void SkWriter32::validate() const {
+    SkASSERT(SkIsAlign4(fSize));
+    SkASSERT(SkIsAlign4(fSingleBlockSize));
+
+    if (fSingleBlock) {
+        SkASSERT(fSize <= fSingleBlockSize);
+        return;
+    }
+
+    size_t accum = 0;
+    const Block* block = fHead;
+    while (block) {
+        SkASSERT(SkIsAlign4(block->fSizeOfBlock));
+        SkASSERT(SkIsAlign4(block->fAllocatedSoFar));
+        SkASSERT(block->fAllocatedSoFar <= block->fSizeOfBlock);
+        accum += block->fAllocatedSoFar;
+        SkASSERT(accum <= fSize);
+        block = block->fNext;
+    }
+    SkASSERT(accum == fSize);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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);
+    {
+        // 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);
+}
+
+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/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
new file mode 100644
index 0000000..aeed9cc
--- /dev/null
+++ b/src/core/SkXfermode.cpp
@@ -0,0 +1,1205 @@
+
+/*
+ * Copyright 2006 The Android 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"
+#include "SkFlattenableBuffers.h"
+#include "SkMathPriv.h"
+
+SK_DEFINE_INST_COUNT(SkXfermode)
+
+#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) {
+    fProc = NULL;
+    if (!buffer.isCrossProcess()) {
+        fProc = (SkXfermodeProc)buffer.readFunctionPtr();
+    }
+}
+
+void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    if (!buffer.isCrossProcess()) {
+        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;
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkProcCoeffXfermode)
+
+protected:
+    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
+        fSrcCoeff = rec.fSC;
+        fDstCoeff = rec.fDC;
+        // now update our function-ptr in the super class
+        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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkClearXfermode)
+
+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;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSrcXfermode)
+
+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;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstInXfermode)
+
+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;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstOutXfermode)
+
+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/src/device/xps/SkXPSDevice.cpp b/src/device/xps/SkXPSDevice.cpp
new file mode 100644
index 0000000..8e62d49
--- /dev/null
+++ b/src/device/xps/SkXPSDevice.cpp
@@ -0,0 +1,2427 @@
+/*
+ * 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(&localMatrix);
+        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(&localMatrix);
+            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, &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
new file mode 100644
index 0000000..98345d0
--- /dev/null
+++ b/src/effects/Sk1DPathEffect.cpp
@@ -0,0 +1,199 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkFlattenableBuffers.h"
+#include "SkPathMeasure.h"
+
+bool Sk1DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*) {
+    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
+        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)
+        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,
+                                    SkStrokeRec* rec) {
+    if (fAdvance > 0) {
+        rec->setFillStyle();
+        return this->INHERITED::filterPath(dst, src, rec);
+    }
+    return false;
+}
+
+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;
+
+        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
+
+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:
+                if (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:
+                if (morphpoints(dstP, &srcP[1], 2, meas, dist)) {
+                    dst->quadTo(dstP[0], dstP[1]);
+                }
+                break;
+            case SkPath::kCubic_Verb:
+                if (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) {
+        buffer.readPath(&fPath);
+        fInitialOffset = buffer.readScalar();
+        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) {
+    return fInitialOffset;
+}
+
+void SkPath1DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fAdvance);
+    if (fAdvance > 0) {
+        buffer.writePath(fPath);
+        buffer.writeScalar(fInitialOffset);
+        buffer.writeUInt(fStyle);
+    }
+}
+
+SkScalar SkPath1DPathEffect::next(SkPath* dst, SkScalar distance,
+                                  SkPathMeasure& meas) {
+    switch (fStyle) {
+        case kTranslate_Style: {
+            SkPoint pos;
+            if (meas.getPosTan(distance, &pos, NULL)) {
+                dst->addPath(fPath, pos.fX, pos.fY);
+            }
+        } break;
+        case kRotate_Style: {
+            SkMatrix matrix;
+            if (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;
+}
diff --git a/src/effects/Sk2DPathEffect.cpp b/src/effects/Sk2DPathEffect.cpp
new file mode 100644
index 0000000..e4d7821
--- /dev/null
+++ b/src/effects/Sk2DPathEffect.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 "Sk2DPathEffect.h"
+#include "SkFlattenableBuffers.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+
+Sk2DPathEffect::Sk2DPathEffect(const SkMatrix& mat) : fMatrix(mat) {
+    fMatrixIsInvertible = mat.invert(&fInverse);
+}
+
+bool Sk2DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*) {
+    if (!fMatrixIsInvertible) {
+        return false;
+    }
+
+    SkPath  tmp;
+    SkIRect ir;
+
+    src.transform(fInverse, &tmp);
+    tmp.getBounds().round(&ir);
+    if (!ir.isEmpty()) {
+        this->begin(ir, dst);
+
+        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) {
+    if (!fMatrixIsInvertible) {
+        return;
+    }
+
+    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) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeMatrix(fMatrix);
+}
+
+Sk2DPathEffect::Sk2DPathEffect(SkFlattenableReadBuffer& buffer) {
+    buffer.readMatrix(&fMatrix);
+    fMatrixIsInvertible = fMatrix.invert(&fInverse);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkLine2DPathEffect::filterPath(SkPath *dst, const SkPath &src, SkStrokeRec *rec) {
+    if (this->INHERITED::filterPath(dst, src, rec)) {
+        rec->setStrokeStyle(fWidth);
+        return true;
+    }
+    return false;
+}
+
+void SkLine2DPathEffect::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]);
+    }
+}
+
+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)
+    : INHERITED(m), fPath(p) {
+}
+
+SkPath2DPathEffect::SkPath2DPathEffect(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    buffer.readPath(&fPath);
+}
+
+void SkPath2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePath(fPath);
+}
+
+void SkPath2DPathEffect::next(const SkPoint& loc, int u, int v, SkPath* dst) {
+    dst->addPath(fPath, loc.fX, loc.fY);
+}
diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp
new file mode 100644
index 0000000..c999ce0
--- /dev/null
+++ b/src/effects/SkArithmeticMode.cpp
@@ -0,0 +1,172 @@
+#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;
+
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
+
+private:
+    SkScalar fK[4];
+};
+
+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 && !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));
+                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
+}
+
+#if 0 // UNUSED
+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
+}
+#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 (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/src/effects/SkAvoidXfermode.cpp b/src/effects/SkAvoidXfermode.cpp
new file mode 100644
index 0000000..89ae1f9
--- /dev/null
+++ b/src/effects/SkAvoidXfermode.cpp
@@ -0,0 +1,232 @@
+
+/*
+ * Copyright 2006 The Android 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"
+#include "SkFlattenableBuffers.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.readColor();
+    fDistMul = buffer.readUInt();
+    fMode = (Mode)buffer.readUInt();
+}
+
+void SkAvoidXfermode::flatten(SkFlattenableWriteBuffer& buffer) const
+{
+    this->INHERITED::flatten(buffer);
+
+    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)
+{
+    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] = SkFourByteInterp256(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
+}
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..1fa3c0d
--- /dev/null
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -0,0 +1,278 @@
+/*
+ * 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 "SkGr.h"
+#include "SkGrPixelRef.h"
+#include "gl/GrGLProgramStage.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::kMultiply_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(2, 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 GrGLProgramStage {
+public:
+    GrGLBlendEffect(const GrProgramStageFactory& factory,
+                    const GrCustomStage& stage);
+    virtual ~GrGLBlendEffect();
+
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE {}
+
+    static inline StageKey GenKey(const GrCustomStage& s, const GrGLCaps&);
+
+private:
+    typedef GrGLProgramStage INHERITED;
+    SkBlendImageFilter::Mode fMode;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrBlendEffect : public GrSingleTextureEffect {
+public:
+    GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground);
+    virtual ~GrBlendEffect();
+
+    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+    const GrProgramStageFactory& getFactory() const;
+    SkBlendImageFilter::Mode mode() const { return fMode; }
+
+    typedef GrGLBlendEffect GLProgramStage;
+    static const char* Name() { return "Blend"; }
+
+private:
+    typedef GrSingleTextureEffect INHERITED;
+    SkBlendImageFilter::Mode fMode;
+};
+
+// FIXME:  This should be refactored with SkSingleInputImageFilter's version.
+static GrTexture* getInputResultAsTexture(SkImageFilter::Proxy* proxy,
+                                          SkImageFilter* input,
+                                          GrTexture* src,
+                                          const SkRect& rect) {
+    GrTexture* resultTex;
+    if (!input) {
+        resultTex = src;
+    } else if (input->canFilterImageGPU()) {
+        // onFilterImageGPU() already refs the result, so just return it here.
+        return input->onFilterImageGPU(proxy, src, rect);
+    } else {
+        SkBitmap srcBitmap, result;
+        srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, src->width(), src->height());
+        srcBitmap.setPixelRef(new SkGrPixelRef(src))->unref();
+        SkIPoint offset;
+        if (input->filterImage(proxy, srcBitmap, SkMatrix(), &result, &offset)) {
+            if (result.getTexture()) {
+                resultTex = (GrTexture*) result.getTexture();
+            } else {
+                resultTex = GrLockCachedBitmapTexture(src->getContext(), result, NULL);
+                SkSafeRef(resultTex);
+                GrUnlockCachedBitmapTexture(resultTex);
+                return resultTex;
+            }
+        } else {
+            resultTex = src;
+        }
+    }
+    SkSafeRef(resultTex);
+    return resultTex;
+}
+
+GrTexture* SkBlendImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) {
+    SkAutoTUnref<GrTexture> background(getInputResultAsTexture(proxy, getBackgroundInput(), src, rect));
+    SkAutoTUnref<GrTexture> foreground(getInputResultAsTexture(proxy, getForegroundInput(), src, rect));
+    GrContext* context = src->getContext();
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = SkScalarCeilToInt(rect.width());
+    desc.fHeight = SkScalarCeilToInt(rect.height());
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    GrAutoScratchTexture ast(context, desc);
+    GrTexture* dst = ast.detach();
+
+    GrContext::AutoMatrix am;
+    am.setIdentity(context);
+
+    GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
+    GrContext::AutoClip ac(context, rect);
+
+    GrMatrix sampleM;
+    sampleM.setIDiv(background->width(), background->height());
+    GrPaint paint;
+    paint.colorSampler(0)->setCustomStage(SkNEW_ARGS(GrSingleTextureEffect, (background.get())), sampleM)->unref();
+    paint.colorSampler(1)->setCustomStage(SkNEW_ARGS(GrBlendEffect, (fMode, foreground.get())), sampleM)->unref();
+    context->drawRect(paint, rect);
+    return dst;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrBlendEffect::GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground)
+    : INHERITED(foreground), fMode(mode) {
+}
+
+GrBlendEffect::~GrBlendEffect() {
+}
+
+bool GrBlendEffect::isEqual(const GrCustomStage& sBase) const {
+    const GrBlendEffect& s = static_cast<const GrBlendEffect&>(sBase);
+    return INHERITED::isEqual(sBase) &&
+           fMode == s.fMode;
+}
+
+const GrProgramStageFactory& GrBlendEffect::getFactory() const {
+    return GrTProgramStageFactory<GrBlendEffect>::getInstance();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLBlendEffect::GrGLBlendEffect(const GrProgramStageFactory& factory,
+                                 const GrCustomStage& stage)
+    : GrGLProgramStage(factory),
+      fMode(static_cast<const GrBlendEffect&>(stage).mode()) {
+}
+
+GrGLBlendEffect::~GrGLBlendEffect() {
+}
+
+void GrGLBlendEffect::emitFS(GrGLShaderBuilder* builder,
+                             const char* outputColor,
+                             const char* inputColor,
+                             const TextureSamplerArray& samplers) {
+    SkString* code = &builder->fFSCode;
+    const char* bgColor = inputColor;
+    const char* fgColor = "fgColor";
+    code->appendf("\t\tvec4 %s = ", fgColor);
+    builder->appendTextureLookup(code, samplers[0]);
+    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;
+    }
+}
+
+GrGLProgramStage::StageKey GrGLBlendEffect::GenKey(const GrCustomStage& s, const GrGLCaps&) {
+    return static_cast<const GrBlendEffect&>(s).mode();
+}
+#endif
diff --git a/src/effects/SkBlurDrawLooper.cpp b/src/effects/SkBlurDrawLooper.cpp
new file mode 100644
index 0000000..e6e2ffd
--- /dev/null
+++ b/src/effects/SkBlurDrawLooper.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 "SkBlurDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkFlattenableBuffers.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), fState(kDone) {
+
+    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.readColor();
+    fBlur = buffer.readFlattenableT<SkMaskFilter>();
+    fColorFilter = buffer.readFlattenableT<SkColorFilter>();
+    fBlurFlags = buffer.readUInt() & kAll_BlurFlag;
+}
+
+SkBlurDrawLooper::~SkBlurDrawLooper() {
+    SkSafeUnref(fBlur);
+    SkSafeUnref(fColorFilter);
+}
+
+void SkBlurDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fDx);
+    buffer.writeScalar(fDy);
+    buffer.writeColor(fBlurColor);
+    buffer.writeFlattenable(fBlur);
+    buffer.writeFlattenable(fColorFilter);
+    buffer.writeUInt(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;
+    }
+}
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
new file mode 100644
index 0000000..fb76269
--- /dev/null
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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 "SkBitmap.h"
+#include "SkBlurImageFilter.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#endif
+
+SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer) {
+    fSigma.fWidth = buffer.readScalar();
+    fSigma.fHeight = buffer.readScalar();
+}
+
+SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input)
+    : INHERITED(input), fSigma(SkSize::Make(sigmaX, sigmaY)) {
+    SkASSERT(sigmaX >= 0 && sigmaY >= 0);
+}
+
+void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    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* 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;
+    }
+
+    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;
+}
+
+GrTexture* SkBlurImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) {
+#if SK_SUPPORT_GPU
+    SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(proxy, src, rect));
+    return src->getContext()->gaussianBlur(input.get(), false, rect,
+                                           fSigma.width(), fSigma.height());
+#else
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return NULL;
+#endif
+}
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
new file mode 100644
index 0000000..f545d8c
--- /dev/null
+++ b/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/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h
new file mode 100644
index 0000000..3709dee
--- /dev/null
+++ b/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/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
new file mode 100644
index 0000000..4af3bfc
--- /dev/null
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -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.
+ */
+
+
+#include "SkBlurMaskFilter.h"
+#include "SkBlurMask.h"
+#include "SkFlattenableBuffers.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;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
+
+private:
+    SkScalar                    fRadius;
+    SkBlurMaskFilter::BlurStyle fBlurStyle;
+    uint32_t                    fBlurFlags;
+
+    SkBlurMaskFilterImpl(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    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);
+}
+
+SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer)
+        : SkMaskFilter(buffer) {
+    fRadius = buffer.readScalar();
+    fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt();
+    fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag;
+    SkASSERT(fRadius >= 0);
+    SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
+}
+
+void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fRadius);
+    buffer.writeInt(fBlurStyle);
+    buffer.writeUInt(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/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp
new file mode 100755
index 0000000..22b5d12
--- /dev/null
+++ b/src/effects/SkColorFilterImageFilter.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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(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) {
+    SkImageFilter* parent = getInput(0);
+    SkScalar colorMatrix[20];
+    SkBitmap src;
+    SkColorFilter* cf;
+    if (parent && fColorFilter->asColorMatrix(colorMatrix)) {
+        SkColorFilter* parentColorFilter;
+        SkScalar parentMatrix[20];
+        while (parent && (parentColorFilter = parent->asColorFilter())
+                      && parentColorFilter->asColorMatrix(parentMatrix)
+                      && !matrix_needs_clamping(parentMatrix)) {
+            SkScalar combinedMatrix[20];
+            mult_color_matrix(parentMatrix, colorMatrix, combinedMatrix);
+            memcpy(colorMatrix, combinedMatrix, 20 * sizeof(SkScalar));
+            parent = parent->getInput(0);
+        }
+        if (!parent || !parent->filterImage(proxy, source, matrix, &src, loc)) {
+            src = source;
+        }
+        cf = SkNEW_ARGS(SkColorMatrixFilter, (colorMatrix));
+    } else {
+        src = this->getInputResult(proxy, source, matrix, loc);
+        cf = fColorFilter;
+        cf->ref();
+    }
+
+    SkAutoTUnref<SkDevice> device(proxy->createDevice(src.width(), src.height()));
+    SkCanvas canvas(device.get());
+    SkPaint paint;
+
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    paint.setColorFilter(cf)->unref();
+    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
new file mode 100644
index 0000000..aae7868
--- /dev/null
+++ b/src/effects/SkColorFilters.cpp
@@ -0,0 +1,493 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkFlattenableBuffers.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;
+        this->updateCache();
+    }
+
+    SkModeColorFilter(SkColor color, SkXfermode::Mode mode) {
+        fColor = color;
+        fMode = mode;
+        this->updateCache();
+    };
+
+    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) SK_OVERRIDE {
+        if (ILLEGAL_XFERMODE_MODE == fMode) {
+            return false;
+        }
+
+        if (color) {
+            *color = fColor;
+        }
+        if (mode) {
+            *mode = fMode;
+        }
+        return true;
+    }
+
+    virtual uint32_t getFlags() SK_OVERRIDE {
+        return fProc16 ? (kAlphaUnchanged_Flag | kHasFilter16_Flag) : 0;
+    }
+
+    virtual void filterSpan(const SkPMColor shader[], int count,
+                            SkPMColor result[]) 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[]) 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) const SK_OVERRIDE {
+        this->INHERITED::flatten(buffer);
+        buffer.writeColor(fColor);
+        buffer.writeUInt(fMode);
+    }
+
+    SkModeColorFilter(SkFlattenableReadBuffer& buffer) {
+        fColor = buffer.readColor();
+        fMode = (SkXfermode::Mode)buffer.readUInt();
+        this->updateCache();
+    }
+
+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;
+};
+
+class Src_SkModeColorFilter : public SkModeColorFilter {
+public:
+    Src_SkModeColorFilter(SkColor color) : INHERITED(color, SkXfermode::kSrc_Mode) {}
+
+    virtual uint32_t getFlags() {
+        if (SkGetPackedA32(this->getPMColor()) == 0xFF) {
+            return kAlphaUnchanged_Flag | kHasFilter16_Flag;
+        } else {
+            return 0;
+        }
+    }
+
+    virtual void filterSpan(const SkPMColor shader[], int count,
+                            SkPMColor result[]) {
+        sk_memset32(result, this->getPMColor(), count);
+    }
+
+    virtual void filterSpan16(const uint16_t shader[], int count,
+                              uint16_t result[]) {
+        SkASSERT(this->getFlags() & kHasFilter16_Flag);
+        sk_memset16(result, SkPixel32ToPixel16(this->getPMColor()), count);
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Src_SkModeColorFilter)
+
+protected:
+    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(this->getPMColor()) == 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, this->getPMColor());
+    }
+
+    virtual void filterSpan16(const uint16_t shader[], int count,
+                              uint16_t result[]) {
+        SkASSERT(this->getFlags() & kHasFilter16_Flag);
+        sk_memset16(result, SkPixel32ToPixel16(this->getPMColor()), count);
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SrcOver_SkModeColorFilter)
+
+protected:
+    SrcOver_SkModeColorFilter(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer), fColor32Proc(NULL) {}
+
+private:
+
+    SkBlitRow::ColorProc fColor32Proc;
+
+    typedef SkModeColorFilter INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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(SkModeColorFilter, (color, mode));
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static inline unsigned pin(unsigned value, unsigned max) {
+    if (value > max) {
+        value = max;
+    }
+    return value;
+}
+
+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;
+        }
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingColorFilter)
+
+protected:
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
+        this->INHERITED::flatten(buffer);
+        buffer.writeColor(fMul);
+        buffer.writeColor(fAdd);
+    }
+
+    SkLightingColorFilter(SkFlattenableReadBuffer& buffer) {
+        fMul = buffer.readColor();
+        fAdd = buffer.readColor();
+    }
+
+    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;
+        }
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingColorFilter_JustAdd)
+
+protected:
+    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;
+        }
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingColorFilter_JustMul)
+
+protected:
+    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);
+        }
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingColorFilter_SingleMul)
+
+protected:
+    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;
+        }
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingColorFilter_NoPin)
+
+protected:
+    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) const SK_OVERRIDE {}
+
+    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(SkModeColorFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Src_SkModeColorFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SrcOver_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/src/effects/SkColorMatrix.cpp b/src/effects/SkColorMatrix.cpp
new file mode 100644
index 0000000..98e2865
--- /dev/null
+++ b/src/effects/SkColorMatrix.cpp
@@ -0,0 +1,162 @@
+
+/*
+ * Copyright 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
new file mode 100644
index 0000000..2102e9b
--- /dev/null
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -0,0 +1,319 @@
+
+/*
+ * Copyright 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 "SkFlattenableBuffers.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::initState(const SkScalar* SK_RESTRICT src) {
+    int32_t* array = fState.fArray;
+    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 (int 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(const SkColorMatrix& cm) : fMatrix(cm) {
+    this->initState(cm.fMat);
+}
+
+SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
+    memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar));
+    this->initState(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) const {
+    this->INHERITED::flatten(buffer);
+    SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20);
+    buffer.writeScalarArray(fMatrix.fMat, 20);
+}
+
+SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    SkASSERT(buffer.getArrayCount() == 20);
+    buffer.readScalarArray(fMatrix.fMat);
+    this->initState(fMatrix.fMat);
+}
+
+bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) {
+    if (matrix) {
+        memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar));
+    }
+    return true;
+}
diff --git a/src/effects/SkCornerPathEffect.cpp b/src/effects/SkCornerPathEffect.cpp
new file mode 100644
index 0000000..1ce62ec
--- /dev/null
+++ b/src/effects/SkCornerPathEffect.cpp
@@ -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.
+ */
+
+
+#include "SkCornerPathEffect.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkFlattenableBuffers.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,
+                                    SkStrokeRec*) {
+    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, false)) {
+            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;
+}
+
+void SkCornerPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fRadius);
+}
+
+SkCornerPathEffect::SkCornerPathEffect(SkFlattenableReadBuffer& buffer) {
+    fRadius = buffer.readScalar();
+}
diff --git a/src/effects/SkDashPathEffect.cpp b/src/effects/SkDashPathEffect.cpp
new file mode 100644
index 0000000..3add0d7
--- /dev/null
+++ b/src/effects/SkDashPathEffect.cpp
@@ -0,0 +1,258 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkFlattenableBuffers.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 count) {
+    for (int i = 0; i < count; ++i) {
+        if (phase > intervals[i]) {
+            phase -= intervals[i];
+        } else {
+            *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,
+                                   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;
+
+    // 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);
+        }
+        SkASSERT(phase >= 0 && phase < len);
+
+        fInitialDashLength = FindFirstInterval(intervals, phase,
+                                               &fInitialDashIndex, count);
+
+        SkASSERT(fInitialDashLength >= 0);
+        SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
+    } else {
+        fInitialDashLength = -1;    // signal bad dash intervals
+    }
+}
+
+SkDashPathEffect::~SkDashPathEffect() {
+    sk_free(fIntervals);
+}
+
+class SpecialLineRec {
+public:
+    bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec,
+              SkScalar pathLength,
+              int intervalCount, SkScalar intervalLength) {
+        if (rec->isHairlineStyle() || !src.isLine(fPts)) {
+            return false;
+        }
+
+        // can relax this in the future, if we handle square and round caps
+        if (SkPaint::kButt_Cap != rec->getCap()) {
+            return false;
+        }
+
+        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) {
+    // we do nothing if the src wants to be filled, or if our dashlength is 0
+    if (rec->isFillStyle() || fInitialDashLength < 0) {
+        return false;
+    }
+
+    SkPathMeasure   meas(src, false);
+    const SkScalar* intervals = fIntervals;
+
+    SpecialLineRec lineRec;
+    const bool specialLine = lineRec.init(src, dst, rec, meas.getLength(),
+                                          fCount >> 1, fIntervalLength);
+
+    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;
+
+                if (specialLine) {
+                    lineRec.addSegment(distance, distance + dlen, dst);
+                } else {
+                    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) const {
+    SkASSERT(fInitialDashLength >= 0);
+
+    this->INHERITED::flatten(buffer);
+    buffer.writeInt(fInitialDashIndex);
+    buffer.writeScalar(fInitialDashLength);
+    buffer.writeScalar(fIntervalLength);
+    buffer.writeBool(fScaleToFit);
+    buffer.writeScalarArray(fIntervals, fCount);
+}
+
+SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
+    return SkNEW_ARGS(SkDashPathEffect, (buffer));
+}
+
+SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    fInitialDashIndex = buffer.readInt();
+    fInitialDashLength = buffer.readScalar();
+    fIntervalLength = buffer.readScalar();
+    fScaleToFit = buffer.readBool();
+
+    fCount = buffer.getArrayCount();
+    fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
+    buffer.readScalarArray(fIntervals);
+}
diff --git a/src/effects/SkDiscretePathEffect.cpp b/src/effects/SkDiscretePathEffect.cpp
new file mode 100644
index 0000000..8a7c307
--- /dev/null
+++ b/src/effects/SkDiscretePathEffect.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 "SkDiscretePathEffect.h"
+#include "SkFlattenableBuffers.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,
+                                      SkStrokeRec* rec) {
+    bool doFill = rec->isFillStyle();
+
+    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;
+            }
+
+            if (meas.getPosTan(distance, &p, &v)) {
+                Perterb(&p, v, SkScalarMul(rand.nextSScalar1(), scale));
+                dst->moveTo(p);
+            }
+            while (--n >= 0) {
+                distance += delta;
+                if (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;
+}
+
+void SkDiscretePathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fSegLength);
+    buffer.writeScalar(fPerterb);
+}
+
+SkDiscretePathEffect::SkDiscretePathEffect(SkFlattenableReadBuffer& buffer) {
+    fSegLength = buffer.readScalar();
+    fPerterb = buffer.readScalar();
+}
diff --git a/src/effects/SkEmbossMask.cpp b/src/effects/SkEmbossMask.cpp
new file mode 100644
index 0000000..300494b
--- /dev/null
+++ b/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/src/effects/SkEmbossMask.h b/src/effects/SkEmbossMask.h
new file mode 100644
index 0000000..15e2474
--- /dev/null
+++ b/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/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp
new file mode 100644
index 0000000..6480f50
--- /dev/null
+++ b/src/effects/SkEmbossMaskFilter.cpp
@@ -0,0 +1,133 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkFlattenableBuffers.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;
+}
+
+SkEmbossMaskFilter::SkEmbossMaskFilter(SkFlattenableReadBuffer& buffer)
+        : SkMaskFilter(buffer) {
+    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) const {
+    this->INHERITED::flatten(buffer);
+
+    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
new file mode 100644
index 0000000..0d331ee
--- /dev/null
+++ b/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/SkKernel33MaskFilter.cpp b/src/effects/SkKernel33MaskFilter.cpp
new file mode 100644
index 0000000..1568a4f
--- /dev/null
+++ b/src/effects/SkKernel33MaskFilter.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 "SkKernel33MaskFilter.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.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) const {
+    this->INHERITED::flatten(wb);
+    wb.writeInt(fPercent256);
+}
+
+SkKernel33ProcMaskFilter::SkKernel33ProcMaskFilter(SkFlattenableReadBuffer& rb)
+        : SkMaskFilter(rb) {
+    fPercent256 = rb.readInt();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) const {
+    this->INHERITED::flatten(wb);
+    wb.writeIntArray(&fKernel[0][0], 9);
+    wb.writeInt(fShift);
+}
+
+SkKernel33MaskFilter::SkKernel33MaskFilter(SkFlattenableReadBuffer& rb)
+        : SkKernel33ProcMaskFilter(rb) {
+    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
new file mode 100644
index 0000000..c27908f
--- /dev/null
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -0,0 +1,247 @@
+
+/*
+ * Copyright 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 "SkFlattenableBuffers.h"
+#include "SkLayerDrawLooper.h"
+#include "SkPaint.h"
+#include "SkUnPreMultiply.h"
+
+SK_DEFINE_INST_COUNT(SkLayerDrawLooper)
+
+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) const {
+    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.fFlagsMask);
+        buffer.writeInt(rec->fInfo.fPaintBits);
+        buffer.writeInt(rec->fInfo.fColorMode);
+        buffer.writePoint(rec->fInfo.fOffset);
+        buffer.writeBool(rec->fInfo.fPostTranslate);
+        buffer.writePaint(rec->fPaint);
+        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;
+        info.fFlagsMask = buffer.readInt();
+        info.fPaintBits = buffer.readInt();
+        info.fColorMode = (SkXfermode::Mode)buffer.readInt();
+        buffer.readPoint(&info.fOffset);
+        info.fPostTranslate = buffer.readBool();
+        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;
+        int n = 0;
+        while (rec) {
+            rec = rec->fNext;
+            n += 1;
+        }
+        SkASSERT(count == n);
+    }
+#endif
+}
diff --git a/src/effects/SkLayerRasterizer.cpp b/src/effects/SkLayerRasterizer.cpp
new file mode 100644
index 0000000..7365e12
--- /dev/null
+++ b/src/effects/SkLayerRasterizer.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 "SkLayerRasterizer.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>
+
+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();
+
+    SkNEW_PLACEMENT_ARGS(&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,
+                                SkPaint::kFill_Style)) {
+            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;
+}
+
+SkLayerRasterizer::SkLayerRasterizer(SkFlattenableReadBuffer& buffer)
+    : SkRasterizer(buffer), fLayers(sizeof(SkLayerRasterizer_Rec)) {
+    int count = buffer.readInt();
+
+    for (int i = 0; i < count; i++) {
+        SkLayerRasterizer_Rec* rec = (SkLayerRasterizer_Rec*)fLayers.push_back();
+
+        SkNEW_PLACEMENT(&rec->fPaint, SkPaint);
+        buffer.readPaint(&rec->fPaint);
+        buffer.readPoint(&rec->fOffset);
+    }
+}
+
+void SkLayerRasterizer::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.writeInt(fLayers.count());
+
+    SkDeque::F2BIter                iter(fLayers);
+    const SkLayerRasterizer_Rec*    rec;
+
+    while ((rec = (const SkLayerRasterizer_Rec*)iter.next()) != NULL) {
+        buffer.writePaint(rec->fPaint);
+        buffer.writePoint(rec->fOffset);
+    }
+}
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
new file mode 100644
index 0000000..ddb3193
--- /dev/null
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -0,0 +1,1486 @@
+/*
+ * 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 "GrProgramStageFactory.h"
+#include "effects/GrSingleTextureEffect.h"
+#include "gl/GrGLProgramStage.h"
+#include "gl/GrGLTexture.h"
+#include "GrCustomStage.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));
+}
+
+void setUniformPoint3FlipY(const GrGLUniformManager& uman,
+                           UniformHandle uni,
+                           const SkPoint3& point,
+                           int height) {
+    setUniformPoint3(uman, uni, SkPoint3(point.fX, height-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 asNewCustomStage(GrCustomStage** stage, 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 asNewCustomStage(GrCustomStage** stage, 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();
+
+    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+
+    const SkLight* light() const { return fLight; }
+    SkScalar surfaceScale() const { return fSurfaceScale; }
+private:
+    typedef GrSingleTextureEffect INHERITED;
+    const SkLight* fLight;
+    SkScalar fSurfaceScale;
+};
+
+class GrDiffuseLightingEffect : public GrLightingEffect {
+public:
+    GrDiffuseLightingEffect(GrTexture* texture,
+                            const SkLight* light,
+                            SkScalar surfaceScale,
+                            SkScalar kd);
+
+    static const char* Name() { return "DiffuseLighting"; }
+
+    typedef GrGLDiffuseLightingEffect GLProgramStage;
+
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+    SkScalar kd() const { return fKD; }
+private:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+    typedef GrLightingEffect INHERITED;
+    SkScalar fKD;
+};
+
+class GrSpecularLightingEffect : public GrLightingEffect {
+public:
+    GrSpecularLightingEffect(GrTexture* texture,
+                             const SkLight* light,
+                             SkScalar surfaceScale,
+                             SkScalar ks,
+                             SkScalar shininess);
+
+    static const char* Name() { return "SpecularLighting"; }
+
+    typedef GrGLSpecularLightingEffect GLProgramStage;
+
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+    SkScalar ks() const { return fKS; }
+    SkScalar shininess() const { return fShininess; }
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+    typedef GrLightingEffect INHERITED;
+    SkScalar fKS;
+    SkScalar fShininess;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLLight {
+public:
+    virtual ~GrGLLight() {}
+    virtual void setupVariables(GrGLShaderBuilder* builder);
+    virtual void emitVS(SkString* out) const {}
+    virtual void emitFuncs(GrGLShaderBuilder* builder) {}
+    virtual void emitSurfaceToLight(const GrGLShaderBuilder*,
+                                    SkString* out,
+                                    const char* z) const = 0;
+    virtual void emitLightColor(GrGLShaderBuilder*,
+                                const char *surfaceToLight) const;
+    virtual void setData(const GrGLUniformManager&, const GrRenderTarget* rt, const SkLight* light) const;
+
+private:
+    typedef SkRefCnt INHERITED;
+
+protected:
+    UniformHandle fColorUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLDistantLight : public GrGLLight {
+public:
+    virtual ~GrGLDistantLight() {}
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrRenderTarget* rt, const SkLight* light) const SK_OVERRIDE;
+    virtual void emitSurfaceToLight(const GrGLShaderBuilder*,
+                                    SkString* out,
+                                    const char* z) const SK_OVERRIDE;
+private:
+    typedef GrGLLight INHERITED;
+    UniformHandle fDirectionUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLPointLight : public GrGLLight {
+public:
+    virtual ~GrGLPointLight() {}
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrRenderTarget* rt, const SkLight* light) const SK_OVERRIDE;
+    virtual void emitVS(SkString* out) const SK_OVERRIDE;
+    virtual void emitSurfaceToLight(const GrGLShaderBuilder*,
+                                    SkString* out,
+                                    const char* z) const SK_OVERRIDE;
+private:
+    typedef GrGLLight INHERITED;
+    SkPoint3 fLocation;
+    UniformHandle fLocationUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLSpotLight : public GrGLLight {
+public:
+    virtual ~GrGLSpotLight() {}
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrRenderTarget* rt, const SkLight* light) const SK_OVERRIDE;
+    virtual void emitVS(SkString* out) const SK_OVERRIDE;
+    virtual void emitFuncs(GrGLShaderBuilder* builder);
+    virtual void emitSurfaceToLight(const GrGLShaderBuilder* builder,
+                                    SkString* out,
+                                    const char* z) const SK_OVERRIDE;
+    virtual void emitLightColor(GrGLShaderBuilder*,
+                                const char *surfaceToLight) const 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::asNewCustomStage(GrCustomStage** stage,
+                                                    GrTexture* texture) const {
+#if SK_SUPPORT_GPU
+    if (stage) {
+        SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
+        *stage = SkNEW_ARGS(GrDiffuseLightingEffect, (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::asNewCustomStage(GrCustomStage** stage,
+                                                     GrTexture* texture) const {
+#if SK_SUPPORT_GPU
+    if (stage) {
+        SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
+        *stage = SkNEW_ARGS(GrSpecularLightingEffect, (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 GrGLProgramStage {
+public:
+    GrGLLightingEffect(const GrProgramStageFactory& factory,
+                       const GrCustomStage& stage);
+    virtual ~GrGLLightingEffect();
+
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE;
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void emitLightFunc(GrGLShaderBuilder*, SkString* funcName) = 0;
+
+    static inline StageKey GenKey(const GrCustomStage& s, const GrGLCaps& caps);
+
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
+private:
+    typedef GrGLProgramStage INHERITED;
+
+    UniformHandle   fImageIncrementUni;
+    UniformHandle   fSurfaceScaleUni;
+    GrGLLight*      fLight;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLDiffuseLightingEffect  : public GrGLLightingEffect {
+public:
+    GrGLDiffuseLightingEffect(const GrProgramStageFactory& factory,
+                              const GrCustomStage& stage);
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void emitLightFunc(GrGLShaderBuilder*, SkString* funcName) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
+private:
+    typedef GrGLLightingEffect INHERITED;
+
+    UniformHandle   fKDUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLSpecularLightingEffect  : public GrGLLightingEffect {
+public:
+    GrGLSpecularLightingEffect(const GrProgramStageFactory& factory,
+                               const GrCustomStage& stage);
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void emitLightFunc(GrGLShaderBuilder*, SkString* funcName) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
+private:
+    typedef GrGLLightingEffect INHERITED;
+
+    UniformHandle   fKSUni;
+    UniformHandle   fShininessUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrLightingEffect::GrLightingEffect(GrTexture* texture, const SkLight* light, SkScalar surfaceScale)
+    : GrSingleTextureEffect(texture)
+    , fLight(light)
+    , fSurfaceScale(surfaceScale) {
+    fLight->ref();
+}
+
+GrLightingEffect::~GrLightingEffect() {
+    fLight->unref();
+}
+
+bool GrLightingEffect::isEqual(const GrCustomStage& sBase) const {
+    const GrLightingEffect& s =
+        static_cast<const GrLightingEffect&>(sBase);
+    return INHERITED::isEqual(sBase) &&
+           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 GrProgramStageFactory& GrDiffuseLightingEffect::getFactory() const {
+    return GrTProgramStageFactory<GrDiffuseLightingEffect>::getInstance();
+}
+
+bool GrDiffuseLightingEffect::isEqual(const GrCustomStage& sBase) const {
+    const GrDiffuseLightingEffect& s =
+        static_cast<const GrDiffuseLightingEffect&>(sBase);
+    return INHERITED::isEqual(sBase) &&
+            this->kd() == s.kd();
+}
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrDiffuseLightingEffect);
+
+GrCustomStage* GrDiffuseLightingEffect::TestCreate(SkRandom* random,
+                                                   GrContext* context,
+                                                   GrTexture* textures[]) {
+    SkScalar surfaceScale = random->nextSScalar1();
+    SkScalar kd = random->nextUScalar1();
+    SkAutoTUnref<SkLight> light(create_random_light(random));
+    return SkNEW_ARGS(GrDiffuseLightingEffect, (textures[GrCustomStageUnitTest::kAlphaTextureIdx],
+                                                light, surfaceScale, kd));
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLLightingEffect::GrGLLightingEffect(const GrProgramStageFactory& factory,
+                                       const GrCustomStage& stage)
+    : GrGLProgramStage(factory)
+    , fImageIncrementUni(kInvalidUniformHandle)
+    , fSurfaceScaleUni(kInvalidUniformHandle) {
+    const GrLightingEffect& m = static_cast<const GrLightingEffect&>(stage);
+    fLight = m.light()->createGLLight();
+}
+
+GrGLLightingEffect::~GrGLLightingEffect() {
+    delete fLight;
+}
+
+void GrGLLightingEffect::setupVariables(GrGLShaderBuilder* builder) {
+    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                              kVec2f_GrSLType,
+                                             "ImageIncrement");
+    fSurfaceScaleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                           kFloat_GrSLType,
+                                           "SurfaceScale");
+    fLight->setupVariables(builder);
+}
+
+void GrGLLightingEffect::emitVS(GrGLShaderBuilder* builder,
+                                const char* vertexCoords) {
+    fLight->emitVS(&builder->fVSCode);
+}
+
+void GrGLLightingEffect::emitFS(GrGLShaderBuilder* builder,
+                                const char* outputColor,
+                                const char* inputColor,
+                                const TextureSamplerArray& samplers) {
+    SkString* code = &builder->fFSCode;
+    fLight->emitFuncs(builder);
+    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", builder->defaultTexCoordsName());
+    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);
+}
+
+GrGLProgramStage::StageKey GrGLLightingEffect::GenKey(const GrCustomStage& s,
+                                                      const GrGLCaps& caps) {
+    return static_cast<const GrLightingEffect&>(s).light()->type();
+}
+
+void GrGLLightingEffect::setData(const GrGLUniformManager& uman,
+                                 const GrCustomStage& data,
+                                 const GrRenderTarget* rt,
+                                 int stageNum) {
+    const GrLightingEffect& effect =
+        static_cast<const GrLightingEffect&>(data);
+    GrGLTexture* texture = static_cast<GrGLTexture*>(data.texture(0));
+    float ySign = texture->orientation() == GrGLTexture::kTopDown_Orientation ? -1.0f : 1.0f;
+    uman.set2f(fImageIncrementUni, 1.0f / texture->width(), ySign / texture->height());
+    uman.set1f(fSurfaceScaleUni, effect.surfaceScale());
+    fLight->setData(uman, rt, effect.light());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLDiffuseLightingEffect::GrGLDiffuseLightingEffect(const GrProgramStageFactory& factory,
+                                            const GrCustomStage& stage)
+    : INHERITED(factory, stage)
+    , fKDUni(kInvalidUniformHandle) {
+}
+
+void GrGLDiffuseLightingEffect::setupVariables(GrGLShaderBuilder* builder) {
+    INHERITED::setupVariables(builder);
+    fKDUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, kFloat_GrSLType, "KD");
+}
+
+void GrGLDiffuseLightingEffect::emitLightFunc(GrGLShaderBuilder* builder, SkString* funcName) {
+    const char* kd = builder->getUniformCStr(fKDUni);
+    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 GrCustomStage& data,
+                                        const GrRenderTarget* rt,
+                                        int stageNum) {
+    INHERITED::setData(uman, data, rt, stageNum);
+    const GrDiffuseLightingEffect& effect =
+        static_cast<const GrDiffuseLightingEffect&>(data);
+    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 GrProgramStageFactory& GrSpecularLightingEffect::getFactory() const {
+    return GrTProgramStageFactory<GrSpecularLightingEffect>::getInstance();
+}
+
+bool GrSpecularLightingEffect::isEqual(const GrCustomStage& sBase) const {
+    const GrSpecularLightingEffect& s =
+        static_cast<const GrSpecularLightingEffect&>(sBase);
+    return INHERITED::isEqual(sBase) &&
+           this->ks() == s.ks() &&
+           this->shininess() == s.shininess();
+}
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrSpecularLightingEffect);
+
+GrCustomStage* GrSpecularLightingEffect::TestCreate(SkRandom* random,
+                                                    GrContext* context,
+                                                    GrTexture* textures[]) {
+    SkScalar surfaceScale = random->nextSScalar1();
+    SkScalar ks = random->nextUScalar1();
+    SkScalar shininess = random->nextUScalar1();
+    SkAutoTUnref<SkLight> light(create_random_light(random));
+    return SkNEW_ARGS(GrSpecularLightingEffect, (textures[GrCustomStageUnitTest::kAlphaTextureIdx],
+                                                 light, surfaceScale, ks, shininess));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLSpecularLightingEffect::GrGLSpecularLightingEffect(const GrProgramStageFactory& factory,
+                                            const GrCustomStage& stage)
+    : GrGLLightingEffect(factory, stage)
+    , fKSUni(kInvalidUniformHandle)
+    , fShininessUni(kInvalidUniformHandle) {
+}
+
+void GrGLSpecularLightingEffect::setupVariables(GrGLShaderBuilder* builder) {
+    INHERITED::setupVariables(builder);
+    fKSUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                 kFloat_GrSLType, "KS");
+    fShininessUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                        kFloat_GrSLType, "Shininess");
+}
+
+void GrGLSpecularLightingEffect::emitLightFunc(GrGLShaderBuilder* builder, SkString* funcName) {
+    const char* ks = builder->getUniformCStr(fKSUni);
+    const char* shininess = builder->getUniformCStr(fShininessUni);
+
+    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("\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 GrGLSpecularLightingEffect::setData(const GrGLUniformManager& uman,
+                                         const GrCustomStage& data,
+                                         const GrRenderTarget* rt,
+                                         int stageNum) {
+    INHERITED::setData(uman, data, rt, stageNum);
+    const GrSpecularLightingEffect& effect = static_cast<const GrSpecularLightingEffect&>(data);
+    uman.set1f(fKSUni, effect.ks());
+    uman.set1f(fShininessUni, effect.shininess());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLLight::emitLightColor(GrGLShaderBuilder* builder,
+                               const char *surfaceToLight) const {
+    const char* color = builder->getUniformCStr(fColorUni);
+    builder->fFSCode.append(color);
+}
+
+void GrGLLight::setupVariables(GrGLShaderBuilder* builder) {
+    fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                    kVec3f_GrSLType, "LightColor");
+}
+
+void GrGLLight::setData(const GrGLUniformManager& uman,
+                        const GrRenderTarget* rt,
+                        const SkLight* light) const {
+    setUniformPoint3(uman, fColorUni, light->color() * SkScalarInvert(SkIntToScalar(255)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLDistantLight::setupVariables(GrGLShaderBuilder* builder) {
+    INHERITED::setupVariables(builder);
+    fDirectionUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, kVec3f_GrSLType,
+                                        "LightDirection");
+}
+
+void GrGLDistantLight::setData(const GrGLUniformManager& uman,
+                               const GrRenderTarget* rt,
+                               const SkLight* light) const {
+    INHERITED::setData(uman, rt, light);
+    SkASSERT(light->type() == SkLight::kDistant_LightType);
+    const SkDistantLight* distantLight = static_cast<const SkDistantLight*>(light);
+    setUniformNormal3(uman, fDirectionUni, distantLight->direction());
+}
+
+void GrGLDistantLight::emitSurfaceToLight(const GrGLShaderBuilder* builder,
+                                          SkString* out,
+                                          const char* z) const {
+    const char* dir = builder->getUniformCStr(fDirectionUni);
+    out->append(dir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLPointLight::setupVariables(GrGLShaderBuilder* builder) {
+    INHERITED::setupVariables(builder);
+    fLocationUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, kVec3f_GrSLType,
+                                       "LightLocation");
+}
+
+void GrGLPointLight::setData(const GrGLUniformManager& uman,
+                             const GrRenderTarget* rt,
+                             const SkLight* light) const {
+    INHERITED::setData(uman, rt, light);
+    SkASSERT(light->type() == SkLight::kPoint_LightType);
+    const SkPointLight* pointLight = static_cast<const SkPointLight*>(light);
+    setUniformPoint3FlipY(uman, fLocationUni, pointLight->location(), rt->height());
+}
+
+void GrGLPointLight::emitVS(SkString* out) const {
+}
+
+void GrGLPointLight::emitSurfaceToLight(const GrGLShaderBuilder* builder,
+                                        SkString* out,
+                                        const char* z) const {
+    const char* loc = builder->getUniformCStr(fLocationUni);
+    out->appendf("normalize(%s - vec3(gl_FragCoord.xy, %s))", loc, z);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLSpotLight::setupVariables(GrGLShaderBuilder* builder) {
+    INHERITED::setupVariables(builder);
+    fLocationUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                       kVec3f_GrSLType, "LightLocation");
+    fExponentUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                       kFloat_GrSLType, "Exponent");
+    fCosInnerConeAngleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                kFloat_GrSLType, "CosInnerConeAngle");
+    fCosOuterConeAngleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                kFloat_GrSLType, "CosOuterConeAngle");
+    fConeScaleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                        kFloat_GrSLType, "ConeScale");
+    fSUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                              kVec3f_GrSLType, "S");
+}
+
+void GrGLSpotLight::setData(const GrGLUniformManager& uman,
+                            const GrRenderTarget* rt,
+                            const SkLight* light) const {
+    INHERITED::setData(uman, rt, light);
+    SkASSERT(light->type() == SkLight::kSpot_LightType);
+    const SkSpotLight* spotLight = static_cast<const SkSpotLight *>(light);
+    setUniformPoint3FlipY(uman, fLocationUni, spotLight->location(), rt->height());
+    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::emitVS(SkString* out) const {
+}
+
+void GrGLSpotLight::emitFuncs(GrGLShaderBuilder* builder) {
+    const char* exponent = builder->getUniformCStr(fExponentUni);
+    const char* cosInner = builder->getUniformCStr(fCosInnerConeAngleUni);
+    const char* cosOuter = builder->getUniformCStr(fCosOuterConeAngleUni);
+    const char* coneScale = builder->getUniformCStr(fConeScaleUni);
+    const char* s = builder->getUniformCStr(fSUni);
+    const char* color = builder->getUniformCStr(fColorUni);
+    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);
+}
+
+void GrGLSpotLight::emitSurfaceToLight(const GrGLShaderBuilder* builder,
+                                       SkString* out,
+                                       const char* z) const {
+    const char* location= builder->getUniformCStr(fLocationUni);
+    out->appendf("normalize(%s - vec3(gl_FragCoord.xy, %s))", location, z);
+}
+
+void GrGLSpotLight::emitLightColor(GrGLShaderBuilder* builder,
+                                   const char *surfaceToLight) const {
+    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..9871fc0
--- /dev/null
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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"
+
+#include <algorithm>
+
+////////////////////////////////////////////////////////////////////////////////
+#if SK_SUPPORT_GPU
+#include "effects/GrSingleTextureEffect.h"
+#include "gl/GrGLProgramStage.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrProgramStageFactory.h"
+
+class GrGLMagnifierEffect;
+
+class GrMagnifierEffect : public GrSingleTextureEffect {
+
+public:
+    GrMagnifierEffect(GrTexture* texture,
+                      float xOffset,
+                      float yOffset,
+                      float xZoom,
+                      float yZoom,
+                      float xInset,
+                      float yInset)
+        : GrSingleTextureEffect(texture)
+        , fXOffset(xOffset)
+        , fYOffset(yOffset)
+        , fXZoom(xZoom)
+        , fYZoom(yZoom)
+        , fXInset(xInset)
+        , fYInset(yInset) {}
+
+    virtual ~GrMagnifierEffect() {};
+
+    static const char* Name() { return "Magnifier"; }
+
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+    virtual bool isEqual(const GrCustomStage&) 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 GLProgramStage;
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_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 GrGLProgramStage {
+public:
+    GrGLMagnifierEffect(const GrProgramStageFactory& factory,
+                        const GrCustomStage& stage);
+
+    virtual void setupVariables(GrGLShaderBuilder* state) SK_OVERRIDE;
+    virtual void emitVS(GrGLShaderBuilder* state,
+                        const char* vertexCoords) SK_OVERRIDE;
+    virtual void emitFS(GrGLShaderBuilder* state,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void setData(const GrGLUniformManager& uman,
+                         const GrCustomStage& data,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
+    static inline StageKey GenKey(const GrCustomStage&, const GrGLCaps&);
+
+private:
+
+    UniformHandle  fOffsetVar;
+    UniformHandle  fZoomVar;
+    UniformHandle  fInsetVar;
+
+    typedef GrGLProgramStage INHERITED;
+};
+
+GrGLMagnifierEffect::GrGLMagnifierEffect(const GrProgramStageFactory& factory,
+                                         const GrCustomStage& stage)
+    : GrGLProgramStage(factory)
+    , fOffsetVar(GrGLUniformManager::kInvalidUniformHandle)
+    , fZoomVar(GrGLUniformManager::kInvalidUniformHandle)
+    , fInsetVar(GrGLUniformManager::kInvalidUniformHandle) {
+}
+
+void GrGLMagnifierEffect::setupVariables(GrGLShaderBuilder* state) {
+    fOffsetVar = state->addUniform(
+        GrGLShaderBuilder::kFragment_ShaderType |
+        GrGLShaderBuilder::kVertex_ShaderType,
+        kVec2f_GrSLType, "uOffset");
+    fZoomVar = state->addUniform(
+        GrGLShaderBuilder::kFragment_ShaderType |
+        GrGLShaderBuilder::kVertex_ShaderType,
+        kVec2f_GrSLType, "uZoom");
+    fInsetVar = state->addUniform(
+        GrGLShaderBuilder::kFragment_ShaderType |
+        GrGLShaderBuilder::kVertex_ShaderType,
+        kVec2f_GrSLType, "uInset");
+}
+
+void GrGLMagnifierEffect::emitVS(GrGLShaderBuilder* state,
+                                 const char* vertexCoords) {
+}
+
+void GrGLMagnifierEffect::emitFS(GrGLShaderBuilder* state,
+                                 const char* outputColor,
+                                 const char* inputColor,
+                                 const TextureSamplerArray& samplers) {
+    SkString* code = &state->fFSCode;
+
+    code->appendf("\t\tvec2 coord = %s;\n", state->defaultTexCoordsName());
+    code->appendf("\t\tvec2 zoom_coord = %s + %s / %s;\n",
+                  state->getUniformCStr(fOffsetVar),
+                  state->defaultTexCoordsName(),
+                  state->getUniformCStr(fZoomVar));
+
+    code->appendf("\t\tvec2 delta = min(coord, vec2(1.0, 1.0) - coord);\n");
+
+    code->appendf(
+        "\t\tdelta = delta / %s;\n", state->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 = ");
+    state->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 GrCustomStage& data,
+                                  const GrRenderTarget*,
+                                  int stageNum) {
+    const GrMagnifierEffect& zoom =
+        static_cast<const GrMagnifierEffect&>(data);
+
+    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());
+}
+
+GrGLProgramStage::StageKey GrGLMagnifierEffect::GenKey(const GrCustomStage& s,
+                                                       const GrGLCaps& caps) {
+    return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrMagnifierEffect);
+
+GrCustomStage* 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));
+    GrSamplerState sampler;
+    GrCustomStage* stage;
+    filter->asNewCustomStage(&stage, textures[0]);
+    GrAssert(NULL != stage);
+    return stage;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const GrProgramStageFactory& GrMagnifierEffect::getFactory() const {
+    return GrTProgramStageFactory<GrMagnifierEffect>::getInstance();
+}
+
+bool GrMagnifierEffect::isEqual(const GrCustomStage& sBase) const {
+     const GrMagnifierEffect& s =
+        static_cast<const GrMagnifierEffect&>(sBase);
+    return (this->fXOffset == s.fXOffset &&
+            this->fYOffset == s.fYOffset &&
+            this->fXZoom == s.fXZoom &&
+            this->fYZoom == s.fYZoom &&
+            this->fXInset == s.fXInset &&
+            this->fYInset == s.fYInset);
+}
+
+#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::asNewCustomStage(GrCustomStage** stage,
+                                              GrTexture* texture) const {
+#if SK_SUPPORT_GPU
+    if (stage) {
+      *stage =
+          SkNEW_ARGS(GrMagnifierEffect, (texture,
+                                         fSrcRect.x() / texture->width(),
+                                         fSrcRect.y() / texture->height(),
+                                         texture->width() / fSrcRect.width(),
+                                         texture->height() / fSrcRect.height(),
+                                         fInset / texture->width(),
+                                         fInset / texture->height()));
+    }
+    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..234a74a
--- /dev/null
+++ b/src/effects/SkMatrixConvolutionImageFilter.cpp
@@ -0,0 +1,545 @@
+/*
+ * 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/GrGLProgramStage.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);
+    uint32_t readSize = buffer.readScalarArray(fKernel);
+    SkASSERT(readSize == size);
+    fGain = buffer.readScalar();
+    fBias = buffer.readScalar();
+    fTarget.fX = buffer.readScalar();
+    fTarget.fY = buffer.readScalar();
+    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.writeScalar(fTarget.fX);
+    buffer.writeScalar(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;
+    GrMatrixConvolutionEffect(GrTexture*,
+                              const SkISize& kernelSize,
+                              const SkScalar* kernel,
+                              SkScalar gain,
+                              SkScalar bias,
+                              const SkIPoint& target,
+                              TileMode tileMode,
+                              bool convolveAlpha);
+    virtual ~GrMatrixConvolutionEffect();
+
+    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 GLProgramStage;
+
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+
+private:
+    SkISize  fKernelSize;
+    float   *fKernel;
+    float    fGain;
+    float    fBias;
+    float    fTarget[2];
+    TileMode fTileMode;
+    bool     fConvolveAlpha;
+
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+class GrGLMatrixConvolutionEffect : public GrGLProgramStage {
+public:
+    GrGLMatrixConvolutionEffect(const GrProgramStageFactory& factory,
+                                const GrCustomStage& stage);
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void emitVS(GrGLShaderBuilder* state,
+                        const char* vertexCoords) SK_OVERRIDE {}
+    virtual void emitFS(GrGLShaderBuilder* state,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline StageKey GenKey(const GrCustomStage& s, const GrGLCaps& caps);
+
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) 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;
+};
+
+GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrProgramStageFactory& factory,
+                                           const GrCustomStage& stage)
+    : GrGLProgramStage(factory)
+    , fKernelUni(GrGLUniformManager::kInvalidUniformHandle)
+    , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle)
+    , fTargetUni(GrGLUniformManager::kInvalidUniformHandle)
+    , fGainUni(GrGLUniformManager::kInvalidUniformHandle)
+    , fBiasUni(GrGLUniformManager::kInvalidUniformHandle) {
+    const GrMatrixConvolutionEffect& m = static_cast<const GrMatrixConvolutionEffect&>(stage);
+    fKernelSize = m.kernelSize();
+    fTileMode = m.tileMode();
+    fConvolveAlpha = m.convolveAlpha();
+}
+
+void GrGLMatrixConvolutionEffect::setupVariables(GrGLShaderBuilder* builder) {
+    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");
+}
+
+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::emitFS(GrGLShaderBuilder* builder,
+                                  const char* outputColor,
+                                  const char* inputColor,
+                                  const TextureSamplerArray& samplers) {
+    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",
+                  builder->defaultTexCoordsName(), 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], builder->defaultTexCoordsName(), 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);
+}
+
+};
+
+GrGLProgramStage::StageKey GrGLMatrixConvolutionEffect::GenKey(const GrCustomStage& s,
+                                                        const GrGLCaps& caps) {
+    const GrMatrixConvolutionEffect& m = static_cast<const GrMatrixConvolutionEffect&>(s);
+    StageKey key = encodeXY(m.kernelSize().width(), m.kernelSize().height());
+    key |= m.tileMode() << 7;
+    key |= m.convolveAlpha() ? 1 << 9 : 0;
+    return key;
+}
+
+void GrGLMatrixConvolutionEffect::setData(const GrGLUniformManager& uman,
+                                   const GrCustomStage& data,
+                                   const GrRenderTarget*,
+                                   int stageNum) {
+    const GrMatrixConvolutionEffect& effect =
+        static_cast<const GrMatrixConvolutionEffect&>(data);
+    GrGLTexture& texture =
+        *static_cast<GrGLTexture*>(data.texture(0));
+    // the code we generated was for a specific kernel size
+    GrAssert(effect.kernelSize() == fKernelSize);
+    GrAssert(effect.tileMode() == fTileMode);
+    float imageIncrement[2];
+    imageIncrement[0] = 1.0f / texture.width();
+    imageIncrement[1] = 1.0f / 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());
+}
+
+GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
+                                                     const SkISize& kernelSize,
+                                                     const SkScalar* kernel,
+                                                     SkScalar gain,
+                                                     SkScalar bias,
+                                                     const SkIPoint& target,
+                                                     TileMode tileMode,
+                                                     bool convolveAlpha)
+  : INHERITED(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] = target.x();
+    fTarget[1] = target.y();
+}
+
+GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
+    delete[] fKernel;
+}
+
+const GrProgramStageFactory& GrMatrixConvolutionEffect::getFactory() const {
+    return GrTProgramStageFactory<GrMatrixConvolutionEffect>::getInstance();
+}
+
+bool GrMatrixConvolutionEffect::isEqual(const GrCustomStage& sBase) const {
+    const GrMatrixConvolutionEffect& s =
+        static_cast<const GrMatrixConvolutionEffect&>(sBase);
+    return INHERITED::isEqual(sBase) &&
+           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_CUSTOM_STAGE_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
+
+GrCustomStage* GrMatrixConvolutionEffect::TestCreate(SkRandom* random,
+                                                     GrContext* context,
+                                                     GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrCustomStageUnitTest::kSkiaPMTextureIdx :
+                                      GrCustomStageUnitTest::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 SkNEW_ARGS(GrMatrixConvolutionEffect, (textures[texIdx],
+                                                  kernelSize,
+                                                  kernel,
+                                                  gain,
+                                                  bias,
+                                                  target,
+                                                  tileMode,
+                                                  convolveAlpha));
+
+}
+
+bool SkMatrixConvolutionImageFilter::asNewCustomStage(GrCustomStage** stage,
+                                                      GrTexture* texture) const {
+    bool ok = fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE;
+    if (ok && stage) {
+        *stage = SkNEW_ARGS(GrMatrixConvolutionEffect, (texture,
+                                                        fKernelSize,
+                                                        fKernel,
+                                                        fGain,
+                                                        fBias,
+                                                        fTarget,
+                                                        fTileMode,
+                                                        fConvolveAlpha));
+    }
+    return ok;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
new file mode 100644
index 0000000..9bddb9b
--- /dev/null
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -0,0 +1,494 @@
+/*
+ * 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 "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkRect.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrTexture.h"
+#include "GrGpu.h"
+#include "gl/GrGLProgramStage.h"
+#include "effects/Gr1DKernelEffect.h"
+#endif
+
+SkMorphologyImageFilter::SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer) {
+    fRadius.fWidth = buffer.readInt();
+    fRadius.fHeight = buffer.readInt();
+}
+
+SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input)
+    : INHERITED(input), fRadius(SkISize::Make(radiusX, radiusY)) {
+}
+
+
+void SkMorphologyImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeInt(fRadius.fWidth);
+    buffer.writeInt(fRadius.fHeight);
+}
+
+static void erode(const SkPMColor* src, SkPMColor* dst,
+                  int radius, int width, int height,
+                  int srcStrideX, int srcStrideY,
+                  int dstStrideX, int dstStrideY)
+{
+    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;
+        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)
+{
+    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;
+        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* 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;
+    }
+
+    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* 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;
+    }
+
+    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;
+}
+
+#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,
+    };
+
+    GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
+    virtual ~GrMorphologyEffect();
+
+    MorphologyType type() const { return fType; }
+
+    static const char* Name() { return "Morphology"; }
+
+    typedef GrGLMorphologyEffect GLProgramStage;
+
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+
+protected:
+
+    MorphologyType fType;
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+
+    typedef Gr1DKernelEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLMorphologyEffect  : public GrGLProgramStage {
+public:
+    GrGLMorphologyEffect (const GrProgramStageFactory& factory,
+                          const GrCustomStage& stage);
+
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void emitVS(GrGLShaderBuilder* state,
+                        const char* vertexCoords) SK_OVERRIDE {};
+    virtual void emitFS(GrGLShaderBuilder* state,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline StageKey GenKey(const GrCustomStage& s, const GrGLCaps& caps);
+
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
+private:
+    int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
+
+    int                                 fRadius;
+    GrMorphologyEffect::MorphologyType  fType;
+    GrGLUniformManager::UniformHandle   fImageIncrementUni;
+
+    typedef GrGLProgramStage INHERITED;
+};
+
+GrGLMorphologyEffect::GrGLMorphologyEffect(const GrProgramStageFactory& factory,
+                                           const GrCustomStage& stage)
+    : GrGLProgramStage(factory)
+    , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle) {
+    const GrMorphologyEffect& m = static_cast<const GrMorphologyEffect&>(stage);
+    fRadius = m.radius();
+    fType = m.type();
+}
+
+void GrGLMorphologyEffect::setupVariables(GrGLShaderBuilder* builder) {
+    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                             kVec2f_GrSLType, "ImageIncrement");
+}
+
+void GrGLMorphologyEffect::emitFS(GrGLShaderBuilder* builder,
+                                  const char* outputColor,
+                                  const char* inputColor,
+                                  const TextureSamplerArray& samplers) {
+    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",
+                   builder->defaultTexCoordsName(), 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);
+}
+
+GrGLProgramStage::StageKey GrGLMorphologyEffect::GenKey(const GrCustomStage& s,
+                                                        const GrGLCaps& caps) {
+    const GrMorphologyEffect& m = static_cast<const GrMorphologyEffect&>(s);
+    StageKey key = static_cast<StageKey>(m.radius());
+    key |= (m.type() << 8);
+    return key;
+}
+
+void GrGLMorphologyEffect::setData(const GrGLUniformManager& uman,
+                                   const GrCustomStage& data,
+                                   const GrRenderTarget*,
+                                   int stageNum) {
+    const Gr1DKernelEffect& kern =
+        static_cast<const Gr1DKernelEffect&>(data);
+    GrGLTexture& texture =
+        *static_cast<GrGLTexture*>(data.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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
+                                       Direction direction,
+                                       int radius,
+                                       MorphologyType type)
+    : Gr1DKernelEffect(texture, direction, radius)
+    , fType(type) {
+}
+
+GrMorphologyEffect::~GrMorphologyEffect() {
+}
+
+const GrProgramStageFactory& GrMorphologyEffect::getFactory() const {
+    return GrTProgramStageFactory<GrMorphologyEffect>::getInstance();
+}
+
+bool GrMorphologyEffect::isEqual(const GrCustomStage& sBase) const {
+    const GrMorphologyEffect& s =
+        static_cast<const GrMorphologyEffect&>(sBase);
+    return (INHERITED::isEqual(sBase) &&
+            this->radius() == s.radius() &&
+            this->direction() == s.direction() &&
+            this->type() == s.type());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrMorphologyEffect);
+
+GrCustomStage* GrMorphologyEffect::TestCreate(SkRandom* random,
+                                              GrContext* context,
+                                              GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrCustomStageUnitTest::kSkiaPMTextureIdx :
+                                      GrCustomStageUnitTest::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 SkNEW_ARGS(GrMorphologyEffect, (textures[texIdx], dir, radius, type));
+}
+
+namespace {
+
+void apply_morphology_pass(GrContext* context,
+                           GrTexture* texture,
+                           const SkRect& rect,
+                           int radius,
+                           GrMorphologyEffect::MorphologyType morphType,
+                           Gr1DKernelEffect::Direction direction) {
+    GrMatrix sampleM;
+    sampleM.setIDiv(texture->width(), texture->height());
+    GrPaint paint;
+    paint.colorSampler(0)->setCustomStage(SkNEW_ARGS(GrMorphologyEffect, (texture, direction, radius, morphType)), sampleM)->unref();
+    context->drawRect(paint, rect);
+}
+
+GrTexture* apply_morphology(GrTexture* srcTexture,
+                            const GrRect& 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 = SkScalarCeilToInt(rect.width());
+    desc.fHeight = SkScalarCeilToInt(rect.height());
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    if (radius.fWidth > 0) {
+        GrAutoScratchTexture ast(context, desc);
+        GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
+        apply_morphology_pass(context, srcTexture, rect, radius.fWidth,
+                              morphType, Gr1DKernelEffect::kX_Direction);
+        SkIRect clearRect = SkIRect::MakeXYWH(
+                    SkScalarFloorToInt(rect.fLeft),
+                    SkScalarFloorToInt(rect.fBottom),
+                    SkScalarFloorToInt(rect.width()),
+                    radius.fHeight);
+        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;
+}
+
+};
+
+GrTexture* SkDilateImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) {
+    SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(proxy, src, rect));
+    return apply_morphology(input, rect, GrMorphologyEffect::kDilate_MorphologyType, radius());
+}
+
+GrTexture* SkErodeImageFilter::onFilterImageGPU(Proxy* proxy, GrTexture* src, const SkRect& rect) {
+    SkAutoTUnref<GrTexture> input(this->getInputResultAsTexture(proxy, src, rect));
+    return apply_morphology(input, rect, GrMorphologyEffect::kErode_MorphologyType, radius());
+}
+
+#endif
diff --git a/src/effects/SkPaintFlagsDrawFilter.cpp b/src/effects/SkPaintFlagsDrawFilter.cpp
new file mode 100644
index 0000000..1fe4402
--- /dev/null
+++ b/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/src/effects/SkPixelXorXfermode.cpp b/src/effects/SkPixelXorXfermode.cpp
new file mode 100644
index 0000000..cf454da
--- /dev/null
+++ b/src/effects/SkPixelXorXfermode.cpp
@@ -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.
+ */
+
+
+#include "SkPixelXorXfermode.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.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) const {
+    this->INHERITED::flatten(wb);
+    wb.writeColor(fOpColor);
+}
+
+SkPixelXorXfermode::SkPixelXorXfermode(SkFlattenableReadBuffer& rb)
+        : INHERITED(rb) {
+    fOpColor = rb.readColor();
+}
diff --git a/src/effects/SkPorterDuff.cpp b/src/effects/SkPorterDuff.cpp
new file mode 100644
index 0000000..8acb345
--- /dev/null
+++ b/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/SkRectShape.cpp b/src/effects/SkRectShape.cpp
new file mode 100644
index 0000000..52c3b37
--- /dev/null
+++ b/src/effects/SkRectShape.cpp
@@ -0,0 +1,87 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkRectShape.h"
+#include "SkCanvas.h"
+#include "SkFlattenableBuffers.h"
+
+SkPaintShape::SkPaintShape() {
+    fPaint.setAntiAlias(true);
+}
+
+SkRectShape::SkRectShape() {
+    fBounds.setEmpty();
+    fRadii.set(0, 0);
+}
+
+void SkRectShape::setRect(const SkRect& bounds) {
+    fBounds = bounds;
+    fRadii.set(0, 0);
+}
+
+void SkRectShape::setOval(const SkRect& bounds) {
+    fBounds = bounds;
+    fRadii.set(-SK_Scalar1, -SK_Scalar1);
+}
+
+void SkRectShape::setCircle(SkScalar cx, SkScalar cy, SkScalar radius) {
+    fBounds.set(cx - radius, cy - radius, cx + radius, cy + radius);
+    fRadii.set(-SK_Scalar1, -SK_Scalar1);
+}
+
+void SkRectShape::setRRect(const SkRect& bounds, SkScalar rx, SkScalar ry) {
+    if (rx < 0) {
+        rx = 0;
+    }
+    if (ry < 0) {
+        ry = 0;
+    }
+
+    fBounds = bounds;
+    fRadii.set(rx, ry);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkRectShape::onDraw(SkCanvas* canvas) {
+    const SkPaint& paint = this->paint();
+
+    if (fRadii.fWidth < 0) {
+        canvas->drawOval(fBounds, paint);
+    } else if (fRadii.isZero()) {
+        canvas->drawRect(fBounds, paint);
+    } else {
+        canvas->drawRoundRect(fBounds, fRadii.fWidth, fRadii.fHeight, paint);
+    }
+}
+
+void SkRectShape::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.writeRect(fBounds);
+    buffer.writeScalar(fRadii.fWidth);
+    buffer.writeScalar(fRadii.fHeight);
+}
+
+SkRectShape::SkRectShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    buffer.readRect(&fBounds);
+    fRadii.fWidth = buffer.readScalar();
+    fRadii.fHeight = buffer.readScalar();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkPaintShape::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePaint(fPaint);
+}
+
+SkPaintShape::SkPaintShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    buffer.readPaint(&fPaint);
+}
diff --git a/src/effects/SkSingleInputImageFilter.cpp b/src/effects/SkSingleInputImageFilter.cpp
new file mode 100644
index 0000000..2019e25
--- /dev/null
+++ b/src/effects/SkSingleInputImageFilter.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 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"
+#if SK_SUPPORT_GPU
+#include "GrTexture.h"
+#include "SkGr.h"
+#include "SkGrPixelRef.h"
+#endif
+
+SkSingleInputImageFilter::SkSingleInputImageFilter(SkImageFilter* input) : INHERITED(1, 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);
+}
+
+#if SK_SUPPORT_GPU
+// FIXME:  generalize and move to base class
+GrTexture* SkSingleInputImageFilter::getInputResultAsTexture(Proxy* proxy,
+                                                             GrTexture* src,
+                                                             const SkRect& rect) {
+    GrTexture* resultTex = NULL;
+    SkImageFilter* input = getInput(0);
+    if (!input) {
+        resultTex = src;
+    } else if (input->canFilterImageGPU()) {
+        // onFilterImageGPU() already refs the result, so just return it here.
+        return input->onFilterImageGPU(proxy, src, rect);
+    } else {
+        SkBitmap srcBitmap, result;
+        srcBitmap.setConfig(SkBitmap::kARGB_8888_Config, src->width(), src->height());
+        srcBitmap.setPixelRef(new SkGrPixelRef(src))->unref();
+        SkIPoint offset;
+        if (input->filterImage(proxy, srcBitmap, SkMatrix(), &result, &offset)) {
+            if (result.getTexture()) {
+                resultTex = (GrTexture*) result.getTexture();
+            } else {
+                resultTex = GrLockCachedBitmapTexture(src->getContext(), result, NULL);
+                SkSafeRef(resultTex);
+                GrUnlockCachedBitmapTexture(resultTex);
+                return resultTex;
+            }
+        } else {
+            resultTex = src;
+        }
+    }
+    SkSafeRef(resultTex);
+    return resultTex;
+}
+#endif
diff --git a/src/effects/SkStippleMaskFilter.cpp b/src/effects/SkStippleMaskFilter.cpp
new file mode 100644
index 0000000..434528e
--- /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) {
+
+    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
new file mode 100644
index 0000000..fcfb444
--- /dev/null
+++ b/src/effects/SkTableColorFilter.cpp
@@ -0,0 +1,237 @@
+
+#include "SkBitmap.h"
+#include "SkTableColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.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 ~SkTable_ColorFilter() {
+        SkDELETE(fBitmap);
+    }
+
+    virtual bool asComponentTable(SkBitmap* table) SK_OVERRIDE;
+
+    virtual void filterSpan(const SkPMColor src[], int count,
+                            SkPMColor dst[]) SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTable_ColorFilter)
+
+protected:
+    SkTable_ColorFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+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]);
+    }
+}
+
+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) const {
+    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.writeByteArray(storage, size);
+}
+
+SkTable_ColorFilter::SkTable_ColorFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    fBitmap = NULL;
+
+    uint8_t storage[5*256];
+
+    fFlags = buffer.readInt();
+
+    size_t size = buffer.getArrayCount();
+    SkASSERT(size <= sizeof(storage));
+    buffer.readByteArray(storage);
+
+    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 = SkNEW(SkBitmap);
+            fBitmap->setConfig(SkBitmap::kA8_Config, 256, 4, 256);
+            fBitmap->allocPixels();
+            uint8_t* bitmapPixels = fBitmap->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;
+            }
+        }
+        *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));
+}
+
+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
new file mode 100644
index 0000000..d1fe134
--- /dev/null
+++ b/src/effects/SkTableMaskFilter.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 "SkTableMaskFilter.h"
+#include "SkFlattenableBuffers.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) const {
+    this->INHERITED::flatten(wb);
+    wb.writeByteArray(fTable, 256);
+}
+
+SkTableMaskFilter::SkTableMaskFilter(SkFlattenableReadBuffer& rb)
+        : INHERITED(rb) {
+    SkASSERT(256 == rb.getArrayCount());
+    rb.readByteArray(fTable);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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/src/effects/SkTestImageFilters.cpp b/src/effects/SkTestImageFilters.cpp
new file mode 100755
index 0000000..a672c33
--- /dev/null
+++ b/src/effects/SkTestImageFilters.cpp
@@ -0,0 +1,315 @@
+
+#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) {
+        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) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fOffset);
+}
+
+SkOffsetImageFilter::SkOffsetImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    buffer.readPoint(&fOffset);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkComposeImageFilter::~SkComposeImageFilter() {
+}
+
+bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
+                                         const SkBitmap& src,
+                                         const SkMatrix& ctm,
+                                         SkBitmap* result,
+                                         SkIPoint* loc) {
+    SkImageFilter* outer = getInput(0);
+    SkImageFilter* inner = getInput(1);
+
+    if (!outer && !inner) {
+        return false;
+    }
+
+    if (!outer || !inner) {
+        return (outer ? outer : inner)->filterImage(proxy, src, ctm, result, loc);
+    }
+
+    SkBitmap tmp;
+    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) {
+    SkImageFilter* outer = getInput(0);
+    SkImageFilter* inner = getInput(1);
+
+    if (!outer && !inner) {
+        return false;
+    }
+
+    if (!outer || !inner) {
+        return (outer ? outer : inner)->filterBounds(src, ctm, dst);
+    }
+
+    SkIRect tmp;
+    return inner->filterBounds(src, ctm, &tmp) &&
+           outer->filterBounds(tmp, ctm, dst);
+}
+
+SkComposeImageFilter::SkComposeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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(2, first, second) {
+    if (SkXfermode::kSrcOver_Mode != mode) {
+        SkXfermode::Mode modes[] = { mode, mode };
+        this->initModes(modes);
+    } else {
+        fModes = NULL;
+    }
+}
+
+SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* const 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();
+
+    SkDevice* dst = proxy->createDevice(bounds.width(), bounds.height());
+    if (NULL == dst) {
+        return false;
+    }
+    OwnDeviceCanvas 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;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.writeScalar(fScale);
+}
+
+SkDownSampleImageFilter::SkDownSampleImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    fScale = buffer.readScalar();
+}
diff --git a/src/effects/SkTransparentShader.cpp b/src/effects/SkTransparentShader.cpp
new file mode 100644
index 0000000..28b075f
--- /dev/null
+++ b/src/effects/SkTransparentShader.cpp
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright 2006 The Android 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);
+    }
+}
diff --git a/src/effects/gradients/SkBitmapCache.cpp b/src/effects/gradients/SkBitmapCache.cpp
new file mode 100644
index 0000000..91f67ec
--- /dev/null
+++ b/src/effects/gradients/SkBitmapCache.cpp
@@ -0,0 +1,154 @@
+
+/*
+ * 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..ea9cf91
--- /dev/null
+++ b/src/effects/gradients/SkBitmapCache.h
@@ -0,0 +1,50 @@
+
+/*
+ * 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..c4bf8b1
--- /dev/null
+++ b/src/effects/gradients/SkClampRange.cpp
@@ -0,0 +1,166 @@
+
+/*
+ * Copyright 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..68a27e9
--- /dev/null
+++ b/src/effects/gradients/SkClampRange.h
@@ -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.
+ */
+
+
+#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..cc7e13c
--- /dev/null
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -0,0 +1,797 @@
+
+/*
+ * Copyright 2006 The Android 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)) {
+        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;
+}
+
+/** We duplicate the last value in each half of the cache so that
+    interpolation doesn't have to special-case being at the last point.
+*/
+static void complete_16bit_cache(uint16_t* cache, int stride) {
+    cache[stride - 1] = cache[stride - 2];
+    cache[2 * stride - 1] = cache[2 * stride - 2];
+}
+
+const uint16_t* SkGradientShaderBase::getCache16() const {
+    if (fCache16 == NULL) {
+        // double the count for dither entries
+        const int entryCount = kCache16Count * 2;
+        const size_t allocSize = sizeof(uint16_t) * entryCount;
+
+        if (fCache16Storage == NULL) { // set the storage and our working ptr
+            fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
+        }
+        fCache16 = fCache16Storage;
+        if (fColorCount == 2) {
+            Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1],
+                            kGradient16Length);
+        } else {
+            Rec* rec = fRecs;
+            int prevIndex = 0;
+            for (int i = 1; i < fColorCount; i++) {
+                int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
+                SkASSERT(nextIndex < kCache16Count);
+
+                if (nextIndex > prevIndex)
+                    Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
+                prevIndex = nextIndex;
+            }
+            // one extra space left over at the end for complete_16bit_cache()
+            SkASSERT(prevIndex == kGradient16Length - 1);
+        }
+
+        if (fMapper) {
+            fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
+            uint16_t* linear = fCache16;         // just computed linear data
+            uint16_t* mapped = fCache16Storage;  // storage for mapped data
+            SkUnitMapper* map = fMapper;
+            for (int i = 0; i < kGradient16Length; i++) {
+                int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
+                mapped[i] = linear[index];
+                mapped[i + kCache16Count] = linear[index + kCache16Count];
+            }
+            sk_free(fCache16);
+            fCache16 = fCache16Storage;
+        }
+        complete_16bit_cache(fCache16, kCache16Count);
+    }
+    return fCache16;
+}
+
+/** We duplicate the last value in each half of the cache so that
+    interpolation doesn't have to special-case being at the last point.
+*/
+static void complete_32bit_cache(SkPMColor* cache, int stride) {
+    cache[stride - 1] = cache[stride - 2];
+    cache[2 * stride - 1] = cache[2 * stride - 2];
+}
+
+const SkPMColor* 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],
+                            kGradient32Length, fCacheAlpha);
+        } else {
+            Rec* rec = fRecs;
+            int prevIndex = 0;
+            for (int i = 1; i < fColorCount; i++) {
+                int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
+                SkASSERT(nextIndex < kGradient32Length);
+
+                if (nextIndex > prevIndex)
+                    Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
+                                    fOrigColors[i],
+                                    nextIndex - prevIndex + 1, fCacheAlpha);
+                prevIndex = nextIndex;
+            }
+            SkASSERT(prevIndex == kGradient32Length - 1);
+        }
+
+        if (fMapper) {
+            SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
+                                                 (NULL, allocSize, NULL));
+            SkPMColor* linear = fCache32;           // just computed linear data
+            SkPMColor* mapped = (SkPMColor*)newPR->getAddr();    // storage for mapped data
+            SkUnitMapper* map = fMapper;
+            for (int i = 0; i < kGradient32Length; i++) {
+                int index = map->mapUnit16((i << 8) | i) >> 8;
+                mapped[i] = linear[index];
+                mapped[i + kCache32Count] = linear[index + kCache32Count];
+            }
+            fCache32PixelRef->unref();
+            fCache32PixelRef = newPR;
+            fCache32 = (SkPMColor*)newPR->getAddr();
+        }
+        complete_32bit_cache(fCache32, kCache32Count);
+    }
+    return fCache32;
+}
+
+/*
+ *  Because our caller might rebuild the same (logically the same) gradient
+ *  over and over, we'd like to return exactly the same "bitmap" if possible,
+ *  allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
+ *  To do that, we maintain a private cache of built-bitmaps, based on our
+ *  colors and positions. Note: we don't try to flatten the fMapper, so if one
+ *  is present, we skip the cache for now.
+ */
+void 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, kGradient32Length, 1);
+        bitmap->setPixelRef(fCache32PixelRef);
+        return;
+    }
+
+    // build our key: [numColors + colors[] + {positions[]} ]
+    int count = 1 + fColorCount;
+    if (fColorCount > 2) {
+        count += fColorCount - 1;    // fRecs[].fPos
+    }
+
+    SkAutoSTMalloc<16, int32_t> storage(count);
+    int32_t* buffer = storage.get();
+
+    *buffer++ = fColorCount;
+    memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
+    buffer += fColorCount;
+    if (fColorCount > 2) {
+        for (int i = 1; i < fColorCount; i++) {
+            *buffer++ = fRecs[i].fPos;
+        }
+    }
+    SkASSERT(buffer - storage.get() == count);
+
+    ///////////////////////////////////
+
+    SK_DECLARE_STATIC_MUTEX(gMutex);
+    static SkBitmapCache* gCache;
+    // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
+    static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
+    SkAutoMutexAcquire ama(gMutex);
+
+    if (NULL == gCache) {
+        gCache = 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();
+        // Only expose the linear section of the cache; don't let the caller
+        // know about the padding at the end to make interpolation faster.
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
+        bitmap->setPixelRef(fCache32PixelRef);
+
+        gCache->add(storage.get(), size, *bitmap);
+    }
+}
+
+void 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;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#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"
+
+GrGLGradientStage::GrGLGradientStage(const GrProgramStageFactory& factory)
+    : INHERITED(factory)
+    , fCachedYCoord(GR_ScalarMax)
+    , fFSYUni(GrGLUniformManager::kInvalidUniformHandle) { }
+
+GrGLGradientStage::~GrGLGradientStage() { }
+
+void GrGLGradientStage::setupVariables(GrGLShaderBuilder* builder) {
+    fFSYUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                  kFloat_GrSLType, "GradientYCoordFS");
+}
+
+void GrGLGradientStage::setData(const GrGLUniformManager& uman,
+                                const GrCustomStage& stage,
+                                const GrRenderTarget*,
+                                int stageNum) {
+    GrScalar yCoord = static_cast<const GrGradientEffect&>(stage).getYCoord();
+    if (yCoord != fCachedYCoord) {
+        uman.set1f(fFSYUni, yCoord);
+        fCachedYCoord = yCoord;
+    }
+}
+
+void GrGLGradientStage::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());
+    GrGLSLMulVarBy4f(code, 1, outputColor, inputColor);
+    code->appendf("\t%s = ", outputColor);
+    builder->appendTextureLookupAndModulate(code, inputColor, sampler, "coord");
+    code->append(";\n");
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GrGradientEffect::GrGradientEffect(GrContext* ctx,
+                                   const SkGradientShaderBase& shader,
+                                   SkShader::TileMode tileMode)
+    : INHERITED(1) {
+    // TODO: check for simple cases where we don't need a texture:
+    //GradientInfo info;
+    //shader.asAGradient(&info);
+    //if (info.fColorCount == 2) { ...
+
+    SkBitmap bitmap;
+    shader.getGradientTableBitmap(&bitmap);
+
+    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) + GR_ScalarHalf *
+                  fAtlas->getVerticalScaleFactor();
+        fTextureAccess.reset(fAtlas->getTexture(), params);
+    } else {
+        GrTexture* texture = GrLockCachedBitmapTexture(ctx, bitmap, &params);
+        fTextureAccess.reset(texture, params);
+        fYCoord = GR_ScalarHalf;
+
+        // Unlock immediately, this is not great, but we don't have a way of
+        // knowing when else to unlock it currently, so it may get purged from
+        // the cache, but it'll still be ref'd until it's no longer being used.
+        GrUnlockCachedBitmapTexture(texture);
+    }
+}
+
+GrGradientEffect::~GrGradientEffect() {
+    if (this->useAtlas()) {
+        fAtlas->unlockRow(fRow);
+    }
+}
+
+const GrTextureAccess& GrGradientEffect::textureAccess(int index) const {
+    GrAssert(0 == index);
+    return fTextureAccess;
+}
+
+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;
+    }
+
+    GrScalar 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..61b9c35
--- /dev/null
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -0,0 +1,305 @@
+
+/*
+ * Copyright 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 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 SkFixed clamp_tileproc(SkFixed x) {
+    return SkClampMax(x, 0xFFFF);
+}
+
+// Repeat
+
+static 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();
+
+    // overrides
+    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
+    virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
+    virtual bool isOpaque() const SK_OVERRIDE;
+
+    void getGradientTableBitmap(SkBitmap*) const;
+
+    enum {
+        /// Seems like enough for visual accuracy. TODO: if pos[] deserves
+        /// it, use a larger cache.
+        kCache16Bits    = 8,
+        kGradient16Length = (1 << kCache16Bits),
+        /// Each cache gets 1 extra entry at the end so we don't have to
+        /// test for end-of-cache in lerps. This is also the value used
+        /// to stride *writes* into the dither cache; it must not be zero.
+        /// Total space for a cache is 2x kCache16Count entries: one
+        /// regular cache, one for dithering.
+        kCache16Count   = kGradient16Length + 1,
+        kCache16Shift   = 16 - kCache16Bits,
+        kSqrt16Shift    = 8 - kCache16Bits,
+
+        /// Seems like enough for visual accuracy. TODO: if pos[] deserves
+        /// it, use a larger cache.
+        kCache32Bits    = 8,
+        kGradient32Length = (1 << kCache32Bits),
+        /// Each cache gets 1 extra entry at the end so we don't have to
+        /// test for end-of-cache in lerps. This is also the value used
+        /// to stride *writes* into the dither cache; it must not be zero.
+        /// Total space for a cache is 2x kCache32Count entries: one
+        /// regular cache, one for dithering.
+        kCache32Count   = kGradient32Length + 1,
+        kCache32Shift   = 16 - kCache32Bits,
+        kSqrt32Shift    = 8 - kCache32Bits,
+
+        /// This value is used to *read* the dither cache; it may be 0
+        /// if dithering is disabled.
+#ifdef USE_DITHER_32BIT_GRADIENT
+        kDitherStride32 = kCache32Count,
+#else
+        kDitherStride32 = 0,
+#endif
+        kDitherStride16 = kCache16Count,
+        kLerpRemainderMask32 = (1 << (16 - kCache32Bits)) - 1
+    };
+
+
+protected:
+    SkGradientShaderBase(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "gl/GrGLProgramStage.h"
+
+class GrSamplerState;
+class GrProgramStageFactory;
+
+/*
+ * 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.
+ *
+ * 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 GrCustomStage {
+public:
+
+    GrGradientEffect(GrContext* ctx,
+                     const SkGradientShaderBase& shader,
+                     SkShader::TileMode tileMode);
+
+    virtual ~GrGradientEffect();
+
+    virtual const GrTextureAccess& textureAccess(int index) const SK_OVERRIDE;
+
+    bool useAtlas() const { return SkToBool(-1 != fRow); }
+    GrScalar getYCoord() const { return fYCoord; };
+
+    virtual bool isEqual(const GrCustomStage& stage) const SK_OVERRIDE {
+        const GrGradientEffect& s = static_cast<const GrGradientEffect&>(stage);
+        return INHERITED::isEqual(stage) && this->useAtlas() == s.useAtlas() &&
+               fYCoord == s.getYCoord();
+    }
+
+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);
+
+private:
+    GrTextureAccess fTextureAccess;
+    GrScalar fYCoord;
+    GrTextureStripAtlas* fAtlas;
+    int fRow;
+
+    typedef GrCustomStage INHERITED;
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Base class for GL gradient custom stages
+class GrGLGradientStage : public GrGLProgramStage {
+public:
+
+    GrGLGradientStage(const GrProgramStageFactory& factory);
+    virtual ~GrGLGradientStage();
+
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
+    // 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
+    void emitColorLookup(GrGLShaderBuilder* builder,
+                         const char* gradientTValue,
+                         const char* outputColor,
+                         const char* inputColor,
+                         const GrGLShaderBuilder::TextureSampler&);
+
+private:
+
+    GrScalar fCachedYCoord;
+    GrGLUniformManager::UniformHandle fFSYUni;
+
+    typedef GrGLProgramStage INHERITED;
+};
+
+#endif
+
+#endif
+
diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp
new file mode 100644
index 0000000..fff2180
--- /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 ^= SkGradientShaderBase::kDitherStride32; \
+    } while (0)
+
+namespace {
+
+typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
+                                SkPMColor* dstC, const SkPMColor* cache,
+                                int toggle, int count);
+
+// This function is deprecated, and will be replaced by
+// shadeSpan_linear_vertical_lerp() once Chrome has been weaned off of it.
+void shadeSpan_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
+                               SkPMColor* SK_RESTRICT dstC,
+                               const SkPMColor* SK_RESTRICT cache,
+                               int toggle, int count) {
+    // We're a vertical gradient, so no change in a span.
+    // If colors change sharply across the gradient, dithering is
+    // insufficient (it subsamples the color space) and we need to lerp.
+    unsigned fullIndex = proc(fx);
+    unsigned fi = fullIndex >> (16 - SkGradientShaderBase::kCache32Bits);
+    sk_memset32_dither(dstC,
+            cache[toggle + fi],
+            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + fi],
+            count);
+}
+
+// Linear interpolation (lerp) is unnecessary if there are no sharp
+// discontinuities in the gradient - which must be true if there are
+// only 2 colors - but it's cheap.
+void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
+                                    SkPMColor* SK_RESTRICT dstC,
+                                    const SkPMColor* SK_RESTRICT cache,
+                                    int toggle, int count) {
+    // We're a vertical gradient, so no change in a span.
+    // If colors change sharply across the gradient, dithering is
+    // insufficient (it subsamples the color space) and we need to lerp.
+    unsigned fullIndex = proc(fx);
+    unsigned fi = fullIndex >> (16 - SkGradientShaderBase::kCache32Bits);
+    unsigned remainder = fullIndex & SkGradientShaderBase::kLerpRemainderMask32;
+    SkPMColor lerp =
+        SkFastFourByteInterp(
+            cache[toggle + fi + 1],
+            cache[toggle + fi], remainder);
+    SkPMColor dlerp =
+        SkFastFourByteInterp(
+            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + fi + 1],
+            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + fi], remainder);
+    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::kGradient32Length);
+
+    if ((count = range.fCount0) > 0) {
+        sk_memset32_dither(dstC,
+            cache[toggle + range.fV0],
+            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + range.fV0],
+            count);
+        dstC += count;
+    }
+    if ((count = range.fCount1) > 0) {
+        int unroll = count >> 3;
+        fx = range.fFx1;
+        for (int i = 0; i < unroll; i++) {
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+        }
+        if ((count &= 7) > 0) {
+            do {
+                NO_CHECK_ITER;
+            } while (--count != 0);
+        }
+    }
+    if ((count = range.fCount2) > 0) {
+        sk_memset32_dither(dstC,
+            cache[toggle + range.fV1],
+            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + range.fV1],
+            count);
+    }
+}
+
+void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
+                             SkPMColor* SK_RESTRICT dstC,
+                             const SkPMColor* SK_RESTRICT cache,
+                             int toggle, int count) {
+    do {
+        unsigned fi = mirror_8bits(fx >> 8);
+        SkASSERT(fi <= 0xFF);
+        fx += dx;
+        *dstC++ = cache[toggle + fi];
+        toggle ^= SkGradientShaderBase::kDitherStride32;
+    } while (--count != 0);
+}
+
+void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
+        SkPMColor* SK_RESTRICT dstC,
+        const SkPMColor* SK_RESTRICT cache,
+        int toggle, int count) {
+    do {
+        unsigned fi = repeat_8bits(fx >> 8);
+        SkASSERT(fi <= 0xFF);
+        fx += dx;
+        *dstC++ = cache[toggle + fi];
+        toggle ^= SkGradientShaderBase::kDitherStride32;
+    } 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 = ((x ^ y) & 1) * kDitherStride32;
+#else
+    int toggle = 0;
+#endif
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed dxStorage[1];
+            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
+            dx = dxStorage[0];
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = SkScalarToFixed(fDstToIndex.getScaleX());
+        }
+
+        LinearShadeProc shadeProc = shadeSpan_linear_repeat;
+        if (SkFixedNearlyZero(dx)) {
+#ifdef SK_SIMPLE_TWOCOLOR_VERTICAL_GRADIENTS
+            if (fColorCount > 2) {
+                shadeProc = shadeSpan_linear_vertical_lerp;
+            } else {
+                shadeProc = shadeSpan_linear_vertical;
+            }
+#else
+            shadeProc = shadeSpan_linear_vertical_lerp;
+#endif
+        } else if (SkShader::kClamp_TileMode == fTileMode) {
+            shadeProc = shadeSpan_linear_clamp;
+        } else if (SkShader::kMirror_TileMode == fTileMode) {
+            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 ^= SkGradientShaderBase::kDitherStride32;
+            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 ^= SkGradientShaderBase::kDitherStride16;            \
+    } while (0)
+
+namespace {
+
+typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
+                                  uint16_t* dstC, const uint16_t* cache,
+                                  int toggle, int count);
+
+void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
+                                 uint16_t* SK_RESTRICT dstC,
+                                 const uint16_t* SK_RESTRICT cache,
+                                 int toggle, int count) {
+    // we're a vertical gradient, so no change in a span
+    unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift;
+    SkASSERT(fi < SkGradientShaderBase::kCache16Count);
+    dither_memset16(dstC, cache[toggle + fi],
+        cache[(toggle ^ SkGradientShaderBase::kDitherStride16) + fi], count);
+
+}
+
+void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
+                              uint16_t* SK_RESTRICT dstC,
+                              const uint16_t* SK_RESTRICT cache,
+                              int toggle, int count) {
+    SkClampRange range;
+    range.init(fx, dx, count, 0, SkGradientShaderBase::kGradient16Length);
+
+    if ((count = range.fCount0) > 0) {
+        dither_memset16(dstC,
+            cache[toggle + range.fV0],
+            cache[(toggle ^ SkGradientShaderBase::kDitherStride16) + range.fV0],
+            count);
+        dstC += count;
+    }
+    if ((count = range.fCount1) > 0) {
+        int unroll = count >> 3;
+        fx = range.fFx1;
+        for (int i = 0; i < unroll; i++) {
+            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
+            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
+            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
+            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
+        }
+        if ((count &= 7) > 0) {
+            do {
+                NO_CHECK_ITER_16;
+            } while (--count != 0);
+        }
+    }
+    if ((count = range.fCount2) > 0) {
+        dither_memset16(dstC,
+            cache[toggle + range.fV1],
+            cache[(toggle ^ SkGradientShaderBase::kDitherStride16) + range.fV1],
+            count);
+    }
+}
+
+void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
+                               uint16_t* SK_RESTRICT dstC,
+                               const uint16_t* SK_RESTRICT cache,
+                               int toggle, int count) {
+    do {
+        unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift,
+                                        SkGradientShaderBase::kCache16Bits);
+        SkASSERT(fi < SkGradientShaderBase::kCache16Count);
+        fx += dx;
+        *dstC++ = cache[toggle + fi];
+        toggle ^= SkGradientShaderBase::kDitherStride16;
+    } while (--count != 0);
+}
+
+void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
+                               uint16_t* SK_RESTRICT dstC,
+                               const uint16_t* SK_RESTRICT cache,
+                               int toggle, int count) {
+    do {
+        unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift,
+                                  SkGradientShaderBase::kCache16Bits);
+        SkASSERT(fi < SkGradientShaderBase::kCache16Count);
+        fx += dx;
+        *dstC++ = cache[toggle + fi];
+        toggle ^= SkGradientShaderBase::kDitherStride16;
+    } 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 = ((x ^ y) & 1) * kDitherStride16;
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed dxStorage[1];
+            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
+            dx = dxStorage[0];
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = SkScalarToFixed(fDstToIndex.getScaleX());
+        }
+
+        LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
+        if (SkFixedNearlyZero(dx)) {
+            shadeProc = shadeSpan16_linear_vertical;
+        } else if (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 ^= SkGradientShaderBase::kDitherStride16;
+
+            dstX += SK_Scalar1;
+        } while (--count != 0);
+    }
+}
+
+#if SK_SUPPORT_GPU
+
+/////////////////////////////////////////////////////////////////////
+
+class GrGLLinearGradient : public GrGLGradientStage {
+public:
+
+    GrGLLinearGradient(const GrProgramStageFactory& factory,
+                       const GrCustomStage&)
+                       : INHERITED (factory) { }
+
+    virtual ~GrGLLinearGradient() { }
+
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE { }
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+    static StageKey GenKey(const GrCustomStage& s, const GrGLCaps& caps) { return 0; }
+
+private:
+
+    typedef GrGLGradientStage INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrLinearGradient : public GrGradientEffect {
+public:
+
+    GrLinearGradient(GrContext* ctx, const SkLinearGradient& shader, SkShader::TileMode tm)
+        : INHERITED(ctx, shader, tm) { }
+    virtual ~GrLinearGradient() { }
+
+    static const char* Name() { return "Linear Gradient"; }
+    const GrProgramStageFactory& getFactory() const SK_OVERRIDE {
+        return GrTProgramStageFactory<GrLinearGradient>::getInstance();
+    }
+
+    typedef GrGLLinearGradient GLProgramStage;
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrLinearGradient);
+
+GrCustomStage* 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));
+    GrSamplerState sampler;
+    shader->asNewCustomStage(context, &sampler);
+    GrAssert(NULL != sampler.getCustomStage());
+    // const_cast and ref is a hack! Will remove when asNewCustomStage returns GrCustomStage*
+    sampler.getCustomStage()->ref();
+    return const_cast<GrCustomStage*>(sampler.getCustomStage());
+}
+
+/////////////////////////////////////////////////////////////////////
+
+void GrGLLinearGradient::emitFS(GrGLShaderBuilder* builder,
+                                const char* outputColor,
+                                const char* inputColor,
+                                const TextureSamplerArray& samplers) {
+    SkString t;
+    t.printf("%s.x", builder->defaultTexCoordsName());
+    this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+bool SkLinearGradient::asNewCustomStage(GrContext* context, GrSamplerState* sampler) const {
+    SkASSERT(NULL != context && NULL != sampler);
+
+    SkAutoTUnref<GrCustomStage> stage(SkNEW_ARGS(GrLinearGradient, (context, *this, fTileMode)));
+
+    SkMatrix matrix;
+    if (this->getLocalMatrix(&matrix)) {
+        if (!matrix.invert(&matrix)) {
+            return false;
+        }
+        matrix.postConcat(fPtsToUnit);
+        sampler->setCustomStage(stage, matrix);
+    } else {
+        sampler->setCustomStage(stage, fPtsToUnit);
+    }
+    
+    return true;
+}
+
+#else
+
+bool SkLinearGradient::asNewCustomStage(GrContext*, GrSamplerState*) const {
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return false;
+}
+
+#endif
diff --git a/src/effects/gradients/SkLinearGradient.h b/src/effects/gradients/SkLinearGradient.h
new file mode 100644
index 0000000..fe60543
--- /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 bool asNewCustomStage(GrContext* context, GrSamplerState* sampler) const SK_OVERRIDE;
+
+    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..0156acd
--- /dev/null
+++ b/src/effects/gradients/SkRadialGradient.cpp
@@ -0,0 +1,586 @@
+
+/*
+ * Copyright 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 ^= SkGradientShaderBase::kDitherStride16;
+        } while (--count != 0);
+    } else {
+        do {
+            unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
+            unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
+            fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
+            fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
+            fx += dx;
+            fy += dy;
+            *dstC++ = cache[toggle +
+                            (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
+            toggle ^= SkGradientShaderBase::kDitherStride16;
+        } while (--count != 0);
+    }
+}
+
+void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
+        int toggle, int count) {
+    do {
+#ifdef SK_SCALAR_IS_FLOAT
+        float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
+        SkFixed dist = SkFloatToFixed(fdist);
+#else
+        SkFixed magnitudeSquared = SkFixedSquare(sfx) +
+            SkFixedSquare(sfy);
+        if (magnitudeSquared < 0) // Overflow.
+            magnitudeSquared = SK_FixedMax;
+        SkFixed dist = SkFixedSqrt(magnitudeSquared);
+#endif
+        unsigned fi = mirror_tileproc(dist);
+        SkASSERT(fi <= 0xFFFF);
+        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
+        toggle ^= SkGradientShaderBase::kDitherStride16;
+        sfx += sdx;
+        sfy += sdy;
+    } while (--count != 0);
+}
+
+void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
+        int toggle, int count) {
+    SkFixed fx = SkScalarToFixed(sfx);
+    SkFixed dx = SkScalarToFixed(sdx);
+    SkFixed fy = SkScalarToFixed(sfy);
+    SkFixed dy = SkScalarToFixed(sdy);
+    do {
+        SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
+        unsigned fi = repeat_tileproc(dist);
+        SkASSERT(fi <= 0xFFFF);
+        fx += dx;
+        fy += dy;
+        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
+        toggle ^= SkGradientShaderBase::kDitherStride16;
+    } 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 = ((x ^ y) & 1) * kDitherStride16;
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+        SkScalar sdx = fDstToIndex.getScaleX();
+        SkScalar sdy = fDstToIndex.getSkewY();
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed storage[2];
+            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
+                                           &storage[0], &storage[1]);
+            sdx = SkFixedToScalar(storage[0]);
+            sdy = SkFixedToScalar(storage[1]);
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+        }
+
+        RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
+        if (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 ^= kDitherStride16;
+
+            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(kGradient32Length),
+                         SkIntToScalar(kGradient32Length));
+        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 ^= SkGradientShaderBase::kDitherStride32; \
+    fx += dx; \
+    fy += dy;
+
+typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        SkPMColor* dstC, const SkPMColor* cache,
+        int count, int toggle);
+
+// On Linux, this is faster with SkPMColor[] params than SkPMColor* SK_RESTRICT
+void shadeSpan_radial_clamp(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
+        int count, int toggle) {
+    // Floating point seems to be slower than fixed point,
+    // even when we have float hardware.
+    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
+    SkFixed fx = SkScalarToFixed(sfx) >> 1;
+    SkFixed dx = SkScalarToFixed(sdx) >> 1;
+    SkFixed fy = SkScalarToFixed(sfy) >> 1;
+    SkFixed dy = SkScalarToFixed(sdy) >> 1;
+    if ((count > 4) && radial_completely_pinned(fx, dx, fy, dy)) {
+        unsigned fi = SkGradientShaderBase::kGradient32Length;
+        sk_memset32_dither(dstC,
+            cache[toggle + fi],
+            cache[(toggle ^ SkGradientShaderBase::kDitherStride32) + fi],
+            count);
+    } else if ((count > 4) &&
+               no_need_for_radial_pin(fx, dx, fy, dy, count)) {
+        unsigned fi;
+        // 4x unroll appears to be no faster than 2x unroll on Linux
+        while (count > 1) {
+            UNPINNED_RADIAL_STEP;
+            UNPINNED_RADIAL_STEP;
+            count -= 2;
+        }
+        if (count) {
+            UNPINNED_RADIAL_STEP;
+        }
+    }
+    else  {
+        // Specializing for dy == 0 gains us 25% on Skia benchmarks
+        if (dy == 0) {
+            unsigned yy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
+            yy *= yy;
+            do {
+                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
+                unsigned fi = (xx * xx + yy) >> (14 + 16 - kSQRT_TABLE_BITS);
+                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
+                *dstC++ = cache[toggle + (sqrt_table[fi] >>
+                    SkGradientShaderBase::kSqrt32Shift)];
+                toggle ^= SkGradientShaderBase::kDitherStride32;
+                fx += dx;
+            } while (--count != 0);
+        } else {
+            do {
+                unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
+                unsigned fi = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
+                fi = (xx * xx + fi * fi) >> (14 + 16 - kSQRT_TABLE_BITS);
+                fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
+                *dstC++ = cache[toggle + (sqrt_table[fi] >>
+                    SkGradientShaderBase::kSqrt32Shift)];
+                toggle ^= SkGradientShaderBase::kDitherStride32;
+                fx += dx;
+                fy += dy;
+            } while (--count != 0);
+        }
+    }
+}
+
+// Unrolling this loop doesn't seem to help (when float); we're stalling to
+// get the results of the sqrt (?), and don't have enough extra registers to
+// have many in flight.
+void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
+        int count, int toggle) {
+    do {
+#ifdef SK_SCALAR_IS_FLOAT
+        float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
+        SkFixed dist = SkFloatToFixed(fdist);
+#else
+        SkFixed magnitudeSquared = SkFixedSquare(sfx) +
+            SkFixedSquare(sfy);
+        if (magnitudeSquared < 0) // Overflow.
+            magnitudeSquared = SK_FixedMax;
+        SkFixed dist = SkFixedSqrt(magnitudeSquared);
+#endif
+        unsigned fi = mirror_tileproc(dist);
+        SkASSERT(fi <= 0xFFFF);
+        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
+        toggle ^= SkGradientShaderBase::kDitherStride32;
+        sfx += sdx;
+        sfy += sdy;
+    } while (--count != 0);
+}
+
+void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
+        int count, int toggle) {
+    SkFixed fx = SkScalarToFixed(sfx);
+    SkFixed dx = SkScalarToFixed(sdx);
+    SkFixed fy = SkScalarToFixed(sfy);
+    SkFixed dy = SkScalarToFixed(sdy);
+    do {
+        SkFixed magnitudeSquared = SkFixedSquare(fx) +
+            SkFixedSquare(fy);
+        if (magnitudeSquared < 0) // Overflow.
+            magnitudeSquared = SK_FixedMax;
+        SkFixed dist = SkFixedSqrt(magnitudeSquared);
+        unsigned fi = repeat_tileproc(dist);
+        SkASSERT(fi <= 0xFFFF);
+        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
+        toggle ^= SkGradientShaderBase::kDitherStride32;
+        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 = ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride32;
+#else
+    int toggle = 0;
+#endif
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkScalar sdx = fDstToIndex.getScaleX();
+        SkScalar sdy = fDstToIndex.getSkewY();
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed storage[2];
+            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
+                                           &storage[0], &storage[1]);
+            sdx = SkFixedToScalar(storage[0]);
+            sdy = SkFixedToScalar(storage[1]);
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+        }
+
+        RadialShadeProc shadeProc = shadeSpan_radial_repeat;
+        if (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
+
+class GrGLRadialGradient : public GrGLGradientStage {
+public:
+
+    GrGLRadialGradient(const GrProgramStageFactory& factory,
+                       const GrCustomStage&) : INHERITED (factory) { }
+    virtual ~GrGLRadialGradient() { }
+
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE { }
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+
+    static StageKey GenKey(const GrCustomStage& s, const GrGLCaps& caps) { return 0; }
+
+private:
+
+    typedef GrGLGradientStage INHERITED;
+
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrRadialGradient : public GrGradientEffect {
+public:
+
+    GrRadialGradient(GrContext* ctx, const SkRadialGradient& shader, SkShader::TileMode tm)
+        : INHERITED(ctx, shader, tm) {
+    }
+
+    virtual ~GrRadialGradient() { }
+
+    static const char* Name() { return "Radial Gradient"; }
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE {
+        return GrTProgramStageFactory<GrRadialGradient>::getInstance();
+    }
+
+    typedef GrGLRadialGradient GLProgramStage;
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrRadialGradient);
+
+GrCustomStage* 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));
+    GrSamplerState sampler;
+    shader->asNewCustomStage(context, &sampler);
+    GrAssert(NULL != sampler.getCustomStage());
+    // const_cast and ref is a hack! Will remove when asNewCustomStage returns GrCustomStage*
+    sampler.getCustomStage()->ref();
+    return const_cast<GrCustomStage*>(sampler.getCustomStage());
+}
+
+/////////////////////////////////////////////////////////////////////
+
+void GrGLRadialGradient::emitFS(GrGLShaderBuilder* builder,
+                                const char* outputColor,
+                                const char* inputColor,
+                                const TextureSamplerArray& samplers) {
+    SkString t;
+    t.printf("length(%s.xy)", builder->defaultTexCoordsName());
+    this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+bool SkRadialGradient::asNewCustomStage(GrContext* context, GrSamplerState* sampler) const {
+    SkASSERT(NULL != context && NULL != sampler);
+    SkAutoTUnref<GrCustomStage> stage(SkNEW_ARGS(GrRadialGradient, (context, *this, fTileMode)));
+
+    SkMatrix matrix;
+    if (this->getLocalMatrix(&matrix)) {
+        if (!matrix.invert(&matrix)) {
+            return false;
+        }
+        matrix.postConcat(fPtsToUnit);
+        sampler->setCustomStage(stage, matrix);
+    } else {
+        sampler->setCustomStage(stage, fPtsToUnit);
+    }
+
+    return true;
+}
+
+#else
+
+bool SkRadialGradient::asNewCustomStage(GrContext*, GrSamplerState*) const {
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return false;
+}
+
+#endif
diff --git a/src/effects/gradients/SkRadialGradient.h b/src/effects/gradients/SkRadialGradient.h
new file mode 100644
index 0000000..fc17520
--- /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 bool asNewCustomStage(GrContext* context, GrSamplerState* sampler) const SK_OVERRIDE;
+
+    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..9fb5176
--- /dev/null
+++ b/src/effects/gradients/SkSweepGradient.cpp
@@ -0,0 +1,493 @@
+
+/*
+ * Copyright 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 = ((x ^ y) & 1) * kDitherStride16;
+    SkPoint             srcPt;
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkScalar dx, fx = srcPt.fX;
+        SkScalar dy, fy = srcPt.fY;
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed storage[2];
+            (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
+                                      &storage[0], &storage[1]);
+            dx = SkFixedToScalar(storage[0]);
+            dy = SkFixedToScalar(storage[1]);
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = matrix.getScaleX();
+            dy = matrix.getSkewY();
+        }
+
+        for (; count > 0; --count) {
+            int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
+            *dstC++ = cache[toggle + index];
+            toggle ^= kDitherStride16;
+            fx += dx;
+            fy += dy;
+        }
+    } else {  // perspective case
+        for (int stop = x + count; x < stop; x++) {
+            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+            int index = SkATan2_255(srcPt.fY, srcPt.fX);
+            index >>= (8 - kCache16Bits);
+            *dstC++ = cache[toggle + index];
+            toggle ^= kDitherStride16;
+        }
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+class GrGLSweepGradient : public GrGLGradientStage {
+public:
+
+    GrGLSweepGradient(const GrProgramStageFactory& factory,
+                      const GrCustomStage&) : INHERITED (factory) { }
+    virtual ~GrGLSweepGradient() { }
+
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE { }
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+
+    static StageKey GenKey(const GrCustomStage& s, const GrGLCaps& caps) { return 0; }
+
+private:
+
+    typedef GrGLGradientStage INHERITED;
+
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrSweepGradient : public GrGradientEffect {
+public:
+
+    GrSweepGradient(GrContext* ctx,
+                    const SkSweepGradient& shader)
+    : INHERITED(ctx, shader, SkShader::kClamp_TileMode) { }
+    virtual ~GrSweepGradient() { }
+
+    static const char* Name() { return "Sweep Gradient"; }
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE {
+        return GrTProgramStageFactory<GrSweepGradient>::getInstance();
+    }
+
+    typedef GrGLSweepGradient GLProgramStage;
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrSweepGradient);
+
+GrCustomStage* 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));
+    GrSamplerState sampler;
+    shader->asNewCustomStage(context, &sampler);
+    GrAssert(NULL != sampler.getCustomStage());
+    // const_cast and ref is a hack! Will remove when asNewCustomStage returns GrCustomStage*
+    sampler.getCustomStage()->ref();
+    return const_cast<GrCustomStage*>(sampler.getCustomStage());
+}
+
+/////////////////////////////////////////////////////////////////////
+
+void GrGLSweepGradient::emitFS(GrGLShaderBuilder* builder,
+                              const char* outputColor,
+                              const char* inputColor,
+                              const TextureSamplerArray& samplers) {
+    SkString t;
+    t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5",
+        builder->defaultTexCoordsName(), builder->defaultTexCoordsName());
+    this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+bool SkSweepGradient::asNewCustomStage(GrContext* context, GrSamplerState* sampler) const {
+    SkAutoTUnref<GrCustomStage> stage(SkNEW_ARGS(GrSweepGradient, (context, *this)));
+
+
+    SkMatrix matrix;
+    if (this->getLocalMatrix(&matrix)) {
+        if (!matrix.invert(&matrix)) {
+            return false;
+        }
+        matrix.postConcat(fPtsToUnit);
+        sampler->setCustomStage(stage, matrix);
+    } else {
+        sampler->setCustomStage(stage, fPtsToUnit);
+    }
+    
+    return true;
+}
+
+#else
+
+bool SkSweepGradient::asNewCustomStage(GrContext*, GrSamplerState*) const {
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return false;
+}
+
+#endif
diff --git a/src/effects/gradients/SkSweepGradient.h b/src/effects/gradients/SkSweepGradient.h
new file mode 100644
index 0000000..8e42be0
--- /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 bool asNewCustomStage(GrContext* context, GrSamplerState* sampler) const SK_OVERRIDE;
+
+    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..19b1228
--- /dev/null
+++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp
@@ -0,0 +1,714 @@
+
+/*
+ * Copyright 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
+
+// For brevity
+typedef GrGLUniformManager::UniformHandle UniformHandle;
+static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
+
+class GrGLConical2Gradient : public GrGLGradientStage {
+public:
+
+    GrGLConical2Gradient(const GrProgramStageFactory& factory,
+                         const GrCustomStage&);
+    virtual ~GrGLConical2Gradient() { }
+
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE;
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
+    static StageKey GenKey(const GrCustomStage& s, const GrGLCaps& caps);
+
+protected:
+
+    UniformHandle           fVSParamUni;
+    UniformHandle           fFSParamUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    bool fIsDegenerate;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    GrScalar fCachedCenter;
+    GrScalar fCachedRadius;
+    GrScalar fCachedDiffRadius;
+
+    // @}
+
+private:
+
+    typedef GrGLGradientStage INHERITED;
+
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrConical2Gradient : public GrGradientEffect {
+public:
+
+    GrConical2Gradient(GrContext* ctx,
+                       const SkTwoPointConicalGradient& shader,
+                       SkShader::TileMode tm)
+        : INHERITED(ctx, shader, tm)
+        , fCenterX1(shader.getCenterX1())
+        , fRadius0(shader.getStartRadius())
+        , fDiffRadius(shader.getDiffRadius()) { }
+
+    virtual ~GrConical2Gradient() { }
+
+    static const char* Name() { return "Two-Point Conical Gradient"; }
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE {
+        return GrTProgramStageFactory<GrConical2Gradient>::getInstance();
+    }
+    virtual bool isEqual(const GrCustomStage& sBase) const SK_OVERRIDE {
+        const GrConical2Gradient& s = static_cast<const GrConical2Gradient&>(sBase);
+        return (INHERITED::isEqual(sBase) &&
+                this->fCenterX1 == s.fCenterX1 &&
+                this->fRadius0 == s.fRadius0 &&
+                this->fDiffRadius == s.fDiffRadius);
+    }
+
+    // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
+    bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
+    GrScalar center() const { return fCenterX1; }
+    GrScalar diffRadius() const { return fDiffRadius; }
+    GrScalar radius() const { return fRadius0; }
+
+    typedef GrGLConical2Gradient GLProgramStage;
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+
+    // @{
+    // Cache of values - these can change arbitrarily, EXCEPT
+    // we shouldn't change between degenerate and non-degenerate?!
+
+    GrScalar fCenterX1;
+    GrScalar fRadius0;
+    GrScalar fDiffRadius;
+
+    // @}
+
+    typedef GrGradientEffect INHERITED;
+};
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrConical2Gradient);
+
+GrCustomStage* GrConical2Gradient::TestCreate(SkRandom* random,
+                                              GrContext* context,
+                                              GrTexture**) {
+    SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
+    SkScalar radius1 = random->nextUScalar1();
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center1.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));
+    GrSamplerState sampler;
+    shader->asNewCustomStage(context, &sampler);
+    GrAssert(NULL != sampler.getCustomStage());
+    // const_cast and ref is a hack! Will remove when asNewCustomStage returns GrCustomStage*
+    sampler.getCustomStage()->ref();
+    return const_cast<GrCustomStage*>(sampler.getCustomStage());
+}
+
+
+/////////////////////////////////////////////////////////////////////
+
+GrGLConical2Gradient::GrGLConical2Gradient(
+        const GrProgramStageFactory& factory,
+        const GrCustomStage& baseData)
+    : INHERITED(factory)
+    , fVSParamUni(kInvalidUniformHandle)
+    , fFSParamUni(kInvalidUniformHandle)
+    , fVSVaryingName(NULL)
+    , fFSVaryingName(NULL)
+    , fCachedCenter(GR_ScalarMax)
+    , fCachedRadius(-GR_ScalarMax)
+    , fCachedDiffRadius(-GR_ScalarMax) {
+
+    const GrConical2Gradient& data =
+        static_cast<const GrConical2Gradient&>(baseData);
+    fIsDegenerate = data.isDegenerate();
+}
+
+void GrGLConical2Gradient::setupVariables(GrGLShaderBuilder* builder) {
+    INHERITED::setupVariables(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 (!builder->defaultTextureMatrixIsPerspective()) {
+        builder->addVarying(kFloat_GrSLType, "Conical2BCoeff",
+                            &fVSVaryingName, &fFSVaryingName);
+    }
+}
+
+void GrGLConical2Gradient::emitVS(GrGLShaderBuilder* builder,
+                                  const char* vertexCoords) {
+    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 (!builder->defaultTextureMatrixIsPerspective()) {
+        // 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(),
+                      vertexCoords, p3.c_str(), p5.c_str());
+    }
+}
+
+void GrGLConical2Gradient::emitFS(GrGLShaderBuilder* builder,
+                                  const char* outputColor,
+                                  const char* inputColor,
+                                  const TextureSamplerArray& samplers) {
+    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 (!builder->defaultTextureMatrixIsPerspective()) {
+        bVar = fFSVaryingName;
+    } else {
+        bVar = "b";
+        code->appendf("\tfloat %s = -2.0 * (%s * %s.x + %s * %s);\n",
+                      bVar.c_str(), p2.c_str(), builder->defaultTexCoordsName(),
+                      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(),
+                  builder->defaultTexCoordsName(), builder->defaultTexCoordsName(),
+                  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 GrCustomStage& baseData,
+                                   const GrRenderTarget* target,
+                                   int stageNum) {
+    INHERITED::setData(uman, baseData, target, stageNum);
+    const GrConical2Gradient& data =
+        static_cast<const GrConical2Gradient&>(baseData);
+    GrAssert(data.isDegenerate() == fIsDegenerate);
+    GrScalar centerX1 = data.center();
+    GrScalar radius0 = data.radius();
+    GrScalar diffRadius = data.diffRadius();
+
+    if (fCachedCenter != centerX1 ||
+        fCachedRadius != radius0 ||
+        fCachedDiffRadius != diffRadius) {
+
+        GrScalar a = GrMul(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] = {
+            GrScalarToFloat(a * 4),
+            1.f / (GrScalarToFloat(a)),
+            GrScalarToFloat(centerX1),
+            GrScalarToFloat(radius0),
+            GrScalarToFloat(SkScalarMul(radius0, radius0)),
+            GrScalarToFloat(diffRadius)
+        };
+
+        uman.set1fv(fVSParamUni, 0, 6, values);
+        uman.set1fv(fFSParamUni, 0, 6, values);
+        fCachedCenter = centerX1;
+        fCachedRadius = radius0;
+        fCachedDiffRadius = diffRadius;
+    }
+}
+
+GrCustomStage::StageKey GrGLConical2Gradient::GenKey(const GrCustomStage& s, const GrGLCaps& caps) {
+    return (static_cast<const GrConical2Gradient&>(s).isDegenerate());
+}
+
+/////////////////////////////////////////////////////////////////////
+
+bool SkTwoPointConicalGradient::asNewCustomStage(GrContext* context,
+                                                 GrSamplerState* sampler) const {
+    SkASSERT(NULL != context && NULL != sampler);
+
+    SkMatrix matrix;
+    SkPoint diff = fCenter2 - fCenter1;
+    SkScalar diffLen = diff.length();
+    if (0 != diffLen) {
+        SkScalar invDiffLen = SkScalarInvert(diffLen);
+        matrix.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
+                         SkScalarMul(invDiffLen, diff.fX));
+    } else {
+        matrix.reset();
+    }
+    matrix.preTranslate(-fCenter1.fX, -fCenter1.fY);
+
+    SkMatrix localM;
+    if (this->getLocalMatrix(&localM)) {
+        if (!localM.invert(&localM)) {
+            return false;
+        }
+        matrix.preConcat(localM);
+    }
+
+    sampler->setCustomStage(SkNEW_ARGS(GrConical2Gradient, (context, *this, fTileMode)), matrix)->unref();
+
+    return true;
+}
+
+#else
+
+bool SkTwoPointConicalGradient::asNewCustomStage(GrContext*, GrSamplerState*) const {
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return false;
+}
+
+#endif
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.h b/src/effects/gradients/SkTwoPointConicalGradient.h
new file mode 100644
index 0000000..4054491
--- /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 bool asNewCustomStage(GrContext* context, GrSamplerState* sampler) const SK_OVERRIDE;
+
+    SkScalar getCenterX1() const { return SkPoint::Distance(fCenter1, fCenter2); }
+    SkScalar getStartRadius() const { return fRadius1; }
+    SkScalar getDiffRadius() const { return fRadius2 - fRadius1; }
+
+    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..2715511
--- /dev/null
+++ b/src/effects/gradients/SkTwoPointRadialGradient.cpp
@@ -0,0 +1,686 @@
+
+/*
+ * Copyright 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){
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    // For now, we might have divided by zero, so detect that
+    if (0 == fDiffRadius) {
+        return false;
+    }
+
+    // we don't have a span16 proc
+    fFlags &= ~kHasSpan16_Flag;
+    return true;
+}
+
+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
+
+// For brevity
+typedef GrGLUniformManager::UniformHandle UniformHandle;
+static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
+
+class GrGLRadial2Gradient : public GrGLGradientStage {
+
+public:
+
+    GrGLRadial2Gradient(const GrProgramStageFactory& factory,
+                        const GrCustomStage&);
+    virtual ~GrGLRadial2Gradient() { }
+
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE;
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
+    static StageKey GenKey(const GrCustomStage& s, const GrGLCaps& caps);
+
+protected:
+
+    UniformHandle   fVSParamUni;
+    UniformHandle   fFSParamUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    bool fIsDegenerate;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    GrScalar fCachedCenter;
+    GrScalar fCachedRadius;
+    bool     fCachedPosRoot;
+
+    // @}
+
+private:
+
+    typedef GrGLGradientStage INHERITED;
+
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrRadial2Gradient : public GrGradientEffect {
+public:
+
+    GrRadial2Gradient(GrContext* ctx, const SkTwoPointRadialGradient& shader, SkShader::TileMode tm)
+        : INHERITED(ctx, shader, tm)
+        , fCenterX1(shader.getCenterX1())
+        , fRadius0(shader.getStartRadius())
+        , fPosRoot(shader.getDiffRadius() < 0) { }
+    virtual ~GrRadial2Gradient() { }
+
+    static const char* Name() { return "Two-Point Radial Gradient"; }
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE {
+        return GrTProgramStageFactory<GrRadial2Gradient>::getInstance();
+    }
+    virtual bool isEqual(const GrCustomStage& sBase) const SK_OVERRIDE {
+        const GrRadial2Gradient& s = static_cast<const GrRadial2Gradient&>(sBase);
+        return (INHERITED::isEqual(sBase) &&
+                this->fCenterX1 == s.fCenterX1 &&
+                this->fRadius0 == s.fRadius0 &&
+                this->fPosRoot == s.fPosRoot);
+    }
+
+    // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
+    bool isDegenerate() const { return GR_Scalar1 == fCenterX1; }
+    GrScalar center() const { return fCenterX1; }
+    GrScalar radius() const { return fRadius0; }
+    bool isPosRoot() const { return SkToBool(fPosRoot); }
+
+    typedef GrGLRadial2Gradient GLProgramStage;
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+
+    // @{
+    // Cache of values - these can change arbitrarily, EXCEPT
+    // we shouldn't change between degenerate and non-degenerate?!
+
+    GrScalar fCenterX1;
+    GrScalar fRadius0;
+    SkBool8  fPosRoot;
+
+    // @}
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrRadial2Gradient);
+
+GrCustomStage* GrRadial2Gradient::TestCreate(SkRandom* random,
+                                             GrContext* context,
+                                             GrTexture**) {
+    SkPoint center1 = {random->nextUScalar1(), random->nextUScalar1()};
+    SkScalar radius1 = random->nextUScalar1();
+    SkPoint center2;
+    SkScalar radius2;
+    do {
+        center1.set(random->nextUScalar1(), random->nextUScalar1());
+        radius2 = random->nextUScalar1 ();
+        // There is a bug in two point radial gradients with idenitical 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));
+    GrSamplerState sampler;
+    shader->asNewCustomStage(context, &sampler);
+    GrAssert(NULL != sampler.getCustomStage());
+    // const_cast and ref is a hack! Will remove when asNewCustomStage returns GrCustomStage*
+    sampler.getCustomStage()->ref();
+    return const_cast<GrCustomStage*>(sampler.getCustomStage());
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GrGLRadial2Gradient::GrGLRadial2Gradient(
+        const GrProgramStageFactory& factory,
+        const GrCustomStage& baseData)
+    : INHERITED(factory)
+    , fVSParamUni(kInvalidUniformHandle)
+    , fFSParamUni(kInvalidUniformHandle)
+    , fVSVaryingName(NULL)
+    , fFSVaryingName(NULL)
+    , fCachedCenter(GR_ScalarMax)
+    , fCachedRadius(-GR_ScalarMax)
+    , fCachedPosRoot(0) {
+
+    const GrRadial2Gradient& data =
+        static_cast<const GrRadial2Gradient&>(baseData);
+    fIsDegenerate = data.isDegenerate();
+}
+
+void GrGLRadial2Gradient::setupVariables(GrGLShaderBuilder* builder) {
+    INHERITED::setupVariables(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, "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 (!builder->defaultTextureMatrixIsPerspective()) {
+        builder->addVarying(kFloat_GrSLType, "Radial2BCoeff",
+                          &fVSVaryingName, &fFSVaryingName);
+    }
+}
+
+void GrGLRadial2Gradient::emitVS(GrGLShaderBuilder* builder,
+                                 const char* vertexCoords) {
+    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 (!builder->defaultTextureMatrixIsPerspective()) {
+        // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
+        code->appendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
+                      fVSVaryingName, p2.c_str(),
+                      vertexCoords, p3.c_str());
+    }
+}
+
+void GrGLRadial2Gradient::emitFS(GrGLShaderBuilder* builder,
+                                 const char* outputColor,
+                                 const char* inputColor,
+                                 const TextureSamplerArray& samplers) {
+    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 (!builder->defaultTextureMatrixIsPerspective()) {
+        bVar = fFSVaryingName;
+    } else {
+        bVar = "b";
+        //bVar.appendS32(stageNum);
+        code->appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
+                      bVar.c_str(), p2.c_str(),
+                      builder->defaultTexCoordsName(), p3.c_str());
+    }
+
+    // c = (x^2)+(y^2) - params[4]
+    code->appendf("\tfloat %s = dot(%s, %s) - %s;\n",
+                  cName.c_str(),
+                  builder->defaultTexCoordsName(),
+                  builder->defaultTexCoordsName(),
+                  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 GrCustomStage& baseData,
+                                  const GrRenderTarget* target,
+                                  int stageNum) {
+    INHERITED::setData(uman, baseData, target, stageNum);
+    const GrRadial2Gradient& data =
+        static_cast<const GrRadial2Gradient&>(baseData);
+    GrAssert(data.isDegenerate() == fIsDegenerate);
+    GrScalar centerX1 = data.center();
+    GrScalar radius0 = data.radius();
+    if (fCachedCenter != centerX1 ||
+        fCachedRadius != radius0 ||
+        fCachedPosRoot != data.isPosRoot()) {
+
+        GrScalar a = GrMul(centerX1, centerX1) - GR_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] = {
+            GrScalarToFloat(a),
+            1 / (2.f * GrScalarToFloat(a)),
+            GrScalarToFloat(centerX1),
+            GrScalarToFloat(radius0),
+            GrScalarToFloat(GrMul(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();
+    }
+}
+
+GrCustomStage::StageKey GrGLRadial2Gradient::GenKey(const GrCustomStage& s, const GrGLCaps& caps) {
+    return (static_cast<const GrRadial2Gradient&>(s).isDegenerate());
+}
+
+/////////////////////////////////////////////////////////////////////
+
+bool SkTwoPointRadialGradient::asNewCustomStage(GrContext* context,
+                                                GrSamplerState* sampler) const {
+    SkASSERT(NULL != context && NULL != sampler);
+    SkScalar diffLen = fDiff.length();
+    SkMatrix matrix;
+    if (0 != diffLen) {
+        SkScalar invDiffLen = SkScalarInvert(diffLen);
+        matrix.setSinCos(-SkScalarMul(invDiffLen, fDiff.fY),
+                         SkScalarMul(invDiffLen, fDiff.fX));
+    } else {
+        matrix.reset();
+    }
+    
+    matrix.preConcat(fPtsToUnit);
+
+    SkMatrix localM;
+    if (this->getLocalMatrix(&localM)) {
+        if (!localM.invert(&localM)) {
+            return false;
+        }
+        matrix.preConcat(localM);
+    }
+
+    sampler->setCustomStage(SkNEW_ARGS(GrRadial2Gradient, (context, *this, fTileMode)), matrix)->unref();
+    return true;
+}
+
+#else
+
+bool SkTwoPointRadialGradient::asNewCustomStage(GrContext*, GrSamplerState*) const {
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return false;
+}
+
+#endif
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.h b/src/effects/gradients/SkTwoPointRadialGradient.h
new file mode 100644
index 0000000..adbb602
--- /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 bool asNewCustomStage(GrContext* context, GrSamplerState* sampler) 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_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
new file mode 100644
index 0000000..8d9e1c5
--- /dev/null
+++ b/src/gpu/FlingState.cpp
@@ -0,0 +1,127 @@
+
+/*
+ * 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 "FlingState.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 x is zero
+static SkScalar SkScalarSign(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 = SkScalarSign(unit->fY);
+    } else if (SkScalarAbs(unit->fY) < TOLERANCE) {
+        unit->fX = SkScalarSign(unit->fX);
+        unit->fY = 0;
+    }
+}
+
+void FlingState::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 FlingState::evaluateMatrix(SkMatrix* matrix) {
+    if (!fActive) {
+        return false;
+    }
+
+    const float t =  getseconds() - fTime0;
+    const float MIN_SPEED = 2;
+    const float K0 = 5.0;
+    const float K1 = 0.02;
+    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 = sk_float_round2int(tx);
+        ty = sk_float_round2int(ty);
+    }
+    matrix->setTranslate(tx, ty);
+//    printf("---- evaluate (%g %g)\n", tx, ty);
+
+    return true;
+}
+
+////////////////////////////////////////
+
+GrAnimateFloat::GrAnimateFloat() : fTime0(0) {}
+
+void GrAnimateFloat::start(float v0, float v1, float duration) {
+    fValue0 = v0;
+    fValue1 = v1;
+    fDuration = duration;
+    if (duration > 0) {
+        fTime0 = SkTime::GetMSecs();
+        if (!fTime0) {
+            fTime0 = 1;  // time0 is our sentinel
+        }
+    } else {
+        fTime0 = 0;
+    }
+}
+
+float GrAnimateFloat::evaluate() {
+    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
new file mode 100644
index 0000000..a525968
--- /dev/null
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -0,0 +1,511 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrAAConvexPathRenderer.h"
+
+#include "GrContext.h"
+#include "GrDrawState.h"
+#include "GrPathUtils.h"
+#include "SkString.h"
+#include "SkTrace.h"
+
+
+GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
+}
+
+namespace {
+
+struct Segment {
+    enum {
+        // 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
+    GrVec fNorms[2];
+    // is the corner where the previous segment meets this segment
+    // sharp. If so, fMid is a normalized bisector facing outward.
+    GrVec fMid;
+
+    int countPoints() {
+        GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
+        return fType + 1;
+    }
+    const SkPoint& endPt() const {
+        GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
+        return fPts[fType];
+    };
+    const SkPoint& endNorm() const {
+        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 = {0, 0};
+    int count = segments.count();
+    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
+        // from the origin.
+        p0 = segments[0].endPt();
+        SkPoint pi;
+        SkPoint pj;
+        // 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;
+        for (int i = 1; i < count - 1; ++i) {
+            pi = pj;
+            const SkPoint pj = segments[i + 1].endPt() - p0;
+
+            GrScalar t = GrMul(pi.fX, pj.fY) - GrMul(pj.fX, pi.fY);
+            area += t;
+            center.fX += (pi.fX + pj.fX) * t;
+            center.fY += (pi.fY + pj.fY) * t;
+
+        }
+    }
+    // If the poly has no area then we instead return the average of
+    // its points.
+    if (SkScalarNearlyZero(area)) {
+        SkPoint avg;
+        avg.set(0, 0);
+        for (int i = 0; i < count; ++i) {
+            const SkPoint& pt = segments[i].endPt();
+            avg.fX += pt.fX;
+            avg.fY += pt.fY;
+        }
+        SkScalar denom = SK_Scalar1 / count;
+        avg.scale(denom);
+        *c = avg;
+    } else {
+        area *= 3;
+        area = GrScalarDiv(GR_Scalar1, area);
+        center.fX = GrScalarMul(center.fX, area);
+        center.fY = GrScalarMul(center.fY, area);
+        // undo the translate of p0 to the origin.
+        *c = center + p0;
+    }
+    GrAssert(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
+}
+
+void compute_vectors(SegmentArray* segments,
+                     SkPoint* fanPt,
+                     SkPath::Direction dir,
+                     int* vCount,
+                     int* iCount) {
+    center_of_mass(*segments, fanPt);
+    int count = segments->count();
+
+    // Make the normals point towards the outside
+    GrPoint::Side normSide;
+    if (dir == SkPath::kCCW_Direction) {
+        normSide = GrPoint::kRight_Side;
+    } else {
+        normSide = GrPoint::kLeft_Side;
+    }
+
+    *vCount = 0;
+    *iCount = 0;
+    // compute normals at all points
+    for (int a = 0; a < count; ++a) {
+        const Segment& sega = (*segments)[a];
+        int b = (a + 1) % count;
+        Segment& segb = (*segments)[b];
+
+        const GrPoint* prevPt = &sega.endPt();
+        int n = segb.countPoints();
+        for (int p = 0; p < n; ++p) {
+            segb.fNorms[p] = segb.fPts[p] - *prevPt;
+            segb.fNorms[p].normalize();
+            segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
+            prevPt = &segb.fPts[p];
+        }
+        if (Segment::kLine == segb.fType) {
+            *vCount += 5;
+            *iCount += 9;
+        } else {
+            *vCount += 6;
+            *iCount += 12;
+        }
+    }
+
+    // compute mid-vectors where segments meet. TODO: Detect shallow corners
+    // and leave out the wedges and close gaps by stitching segments together.
+    for (int a = 0; a < count; ++a) {
+        const Segment& sega = (*segments)[a];
+        int b = (a + 1) % count;
+        Segment& segb = (*segments)[b];
+        segb.fMid = segb.fNorms[0] + sega.endNorm();
+        segb.fMid.normalize();
+        // corner wedges
+        *vCount += 4;
+        *iCount += 6;
+    }
+}
+
+struct DegenerateTestData {
+    DegenerateTestData() { fStage = kInitial; }
+    bool isDegenerate() const { return kNonDegenerate != fStage; }
+    enum {
+        kInitial,
+        kPoint,
+        kLine,
+        kNonDegenerate
+    }           fStage;
+    GrPoint     fFirstPoint;
+    GrVec       fLineNormal;
+    GrScalar    fLineC;
+};
+
+void update_degenerate_test(DegenerateTestData* data, const GrPoint& pt) {
+    static const SkScalar TOL = (SK_Scalar1 / 16);
+    static const SkScalar TOL_SQD = SkScalarMul(TOL, TOL);
+
+    switch (data->fStage) {
+        case DegenerateTestData::kInitial:
+            data->fFirstPoint = pt;
+            data->fStage = DegenerateTestData::kPoint;
+            break;
+        case DegenerateTestData::kPoint:
+            if (pt.distanceToSqd(data->fFirstPoint) > TOL_SQD) {
+                data->fLineNormal = pt - data->fFirstPoint;
+                data->fLineNormal.normalize();
+                data->fLineNormal.setOrthog(data->fLineNormal);
+                data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
+                data->fStage = DegenerateTestData::kLine;
+            }
+            break;
+        case DegenerateTestData::kLine:
+            if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > TOL) {
+                data->fStage = DegenerateTestData::kNonDegenerate;
+            }
+        case DegenerateTestData::kNonDegenerate:
+            break;
+        default:
+            GrCrash("Unexpected degenerate test stage.");
+    }
+}
+
+inline bool get_direction(const SkPath& path, const GrMatrix& m, SkPath::Direction* dir) {
+    if (!path.cheapComputeDirection(dir)) {
+        return false;
+    }
+    // check whether m reverses the orientation
+    GrAssert(!m.hasPerspective());
+    GrScalar det2x2 = GrMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
+                      GrMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
+    if (det2x2 < 0) {
+        GR_STATIC_ASSERT(0 == SkPath::kCW_Direction || 1 == SkPath::kCW_Direction);
+        GR_STATIC_ASSERT(0 == SkPath::kCCW_Direction || 1 == SkPath::kCCW_Direction);
+        *dir = static_cast<SkPath::Direction>(*dir ^ 0x1);
+    }
+    return true;
+}
+
+bool get_segments(const SkPath& path,
+                  const GrMatrix& 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
+    // 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
+    // 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;
+                segments->back().fPts[0] = pts[1];
+                break;
+            }
+            case kQuadratic_PathCmd:
+                m.mapPoints(pts + 1, 2);
+                update_degenerate_test(&degenerateData, pts[1]);
+                update_degenerate_test(&degenerateData, pts[2]);
+                segments->push_back();
+                segments->back().fType = Segment::kQuad;
+                segments->back().fPts[0] = pts[1];
+                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, true, dir, &quads);
+                int count = quads.count();
+                for (int q = 0; q < count; q += 3) {
+                    segments->push_back();
+                    segments->back().fType = Segment::kQuad;
+                    segments->back().fPts[0] = quads[q + 1];
+                    segments->back().fPts[1] = quads[q + 2];
+                }
+                break;
+            };
+            case kEnd_PathCmd:
+                if (degenerateData.isDegenerate()) {
+                    return false;
+                } else {
+                    compute_vectors(segments, fanPt, dir, vCount, iCount);
+                    return true;
+                }
+            default:
+                break;
+        }
+    }
+}
+
+struct QuadVertex {
+    GrPoint  fPos;
+    GrPoint  fUV;
+    GrScalar fD0;
+    GrScalar fD1;
+};
+
+void create_vertices(const SegmentArray&  segments,
+                     const SkPoint& fanPt,
+                     QuadVertex*    verts,
+                     uint16_t*      idxs) {
+    int v = 0;
+    int i = 0;
+
+    int count = segments.count();
+    for (int a = 0; a < count; ++a) {
+        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();
+        verts[v + 2].fPos = verts[v + 0].fPos + segb.fMid;
+        verts[v + 3].fPos = verts[v + 0].fPos + segb.fNorms[0];
+        verts[v + 0].fUV.set(0,0);
+        verts[v + 1].fUV.set(0,-SK_Scalar1);
+        verts[v + 2].fUV.set(0,-SK_Scalar1);
+        verts[v + 3].fUV.set(0,-SK_Scalar1);
+        verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
+        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;
+
+        if (Segment::kLine == segb.fType) {
+            verts[v + 0].fPos = fanPt;
+            verts[v + 1].fPos = sega.endPt();
+            verts[v + 2].fPos = segb.fPts[0];
+
+            verts[v + 3].fPos = verts[v + 1].fPos + segb.fNorms[0];
+            verts[v + 4].fPos = verts[v + 2].fPos + segb.fNorms[0];
+
+            // 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,
+                                                        verts[v + 2].fPos);
+            verts[v + 0].fUV.set(0, dist);
+            verts[v + 1].fUV.set(0, 0);
+            verts[v + 2].fUV.set(0, 0);
+            verts[v + 3].fUV.set(0, -SK_Scalar1);
+            verts[v + 4].fUV.set(0, -SK_Scalar1);
+
+            verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
+            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;
+            verts[v + 4].fD0 = verts[v + 4].fD1 = -SK_Scalar1;
+
+            idxs[i + 0] = v + 0;
+            idxs[i + 1] = v + 2;
+            idxs[i + 2] = v + 1;
+
+            idxs[i + 3] = v + 3;
+            idxs[i + 4] = v + 1;
+            idxs[i + 5] = v + 2;
+
+            idxs[i + 6] = v + 4;
+            idxs[i + 7] = v + 3;
+            idxs[i + 8] = v + 2;
+
+            v += 5;
+            i += 9;
+        } else {
+            GrPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
+
+            GrVec midVec = segb.fNorms[0] + segb.fNorms[1];
+            midVec.normalize();
+
+            verts[v + 0].fPos = fanPt;
+            verts[v + 1].fPos = qpts[0];
+            verts[v + 2].fPos = qpts[2];
+            verts[v + 3].fPos = qpts[0] + segb.fNorms[0];
+            verts[v + 4].fPos = qpts[2] + segb.fNorms[1];
+            verts[v + 5].fPos = qpts[1] + midVec;
+
+            GrScalar 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;
+
+            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;
+
+            GrPathUtils::QuadUVMatrix toUV(qpts);
+            toUV.apply<6, sizeof(QuadVertex), sizeof(GrPoint)>(verts + v);
+
+            idxs[i + 0] = v + 3;
+            idxs[i + 1] = v + 1;
+            idxs[i + 2] = v + 2;
+            idxs[i + 3] = v + 4;
+            idxs[i + 4] = v + 3;
+            idxs[i + 5] = v + 2;
+
+            idxs[i + 6] = v + 5;
+            idxs[i + 7] = v + 3;
+            idxs[i + 8] = v + 4;
+
+            idxs[i +  9] = v + 0;
+            idxs[i + 10] = v + 2;
+            idxs[i + 11] = v + 1;
+
+            v += 6;
+            i += 12;
+        }
+    }
+}
+
+}
+
+bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
+                                         GrPathFill fill,
+                                         const GrDrawTarget* target,
+                                         bool antiAlias) const {
+    if (!target->getCaps().shaderDerivativeSupport() || !antiAlias ||
+        kHairLine_GrPathFill == fill || GrIsFillInverted(fill) ||
+        !path.isConvex()) {
+        return false;
+    }  else {
+        return true;
+    }
+}
+
+bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath,
+                                        GrPathFill fill,
+                                        GrDrawTarget* target,
+                                        bool antiAlias) {
+
+    const SkPath* path = &origPath;
+    if (path->isEmpty()) {
+        return true;
+    }
+    GrDrawState* drawState = target->drawState();
+
+    GrDrawState::AutoDeviceCoordDraw adcd(drawState);
+    if (!adcd.succeeded()) {
+        return false;
+    }
+    const GrMatrix* vm = &adcd.getOriginalMatrix();
+
+    GrVertexLayout layout = 0;
+    layout |= GrDrawTarget::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 = &GrMatrix::I();
+    }
+
+    QuadVertex *verts;
+    uint16_t* idxs;
+
+    int vCount;
+    int iCount;
+    enum {
+        kPreallocSegmentCnt = 512 / sizeof(Segment),
+    };
+    SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
+    SkPoint fanPt;
+
+    if (!get_segments(*path, *vm, &segments, &fanPt, &vCount, &iCount)) {
+        return false;
+    }
+
+    GrDrawTarget::AutoReleaseGeometry arg(target, layout, vCount, iCount);
+    if (!arg.succeeded()) {
+        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_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
new file mode 100644
index 0000000..7a2fd38
--- /dev/null
+++ b/src/gpu/GrAAConvexPathRenderer.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.
+ */
+
+#include "GrPathRenderer.h"
+
+
+class GrAAConvexPathRenderer : public GrPathRenderer {
+public:
+    GrAAConvexPathRenderer();
+
+    virtual bool canDrawPath(const SkPath& path,
+                             GrPathFill fill,
+                             const GrDrawTarget* target,
+                             bool antiAlias) const SK_OVERRIDE;
+
+protected:
+    virtual bool onDrawPath(const SkPath& path,
+                            GrPathFill fill,
+                            GrDrawTarget* target,
+                            bool antiAlias) SK_OVERRIDE;
+};
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
new file mode 100644
index 0000000..06d8e71
--- /dev/null
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -0,0 +1,635 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrAAHairLinePathRenderer.h"
+
+#include "GrContext.h"
+#include "GrDrawState.h"
+#include "GrGpu.h"
+#include "GrIndexBuffer.h"
+#include "GrPathUtils.h"
+#include "SkGeometry.h"
+#include "SkTemplates.h"
+
+namespace {
+// quadratics are rendered as 5-sided polys in order to bound the
+// AA stroke around the center-curve. See comments in push_quad_index_buffer and
+// bloat_quad.
+static const int kVertsPerQuad = 5;
+static const int kIdxsPerQuad = 9;
+
+static const int kVertsPerLineSeg = 4;
+static const int kIdxsPerLineSeg = 6;
+
+static const int kNumQuadsInIdxBuffer = 256;
+static const size_t kQuadIdxSBufize = kIdxsPerQuad *
+                                      sizeof(uint16_t) *
+                                      kNumQuadsInIdxBuffer;
+
+bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) {
+    uint16_t* data = (uint16_t*) qIdxBuffer->lock();
+    bool tempData = NULL == data;
+    if (tempData) {
+        data = SkNEW_ARRAY(uint16_t, kNumQuadsInIdxBuffer * kIdxsPerQuad);
+    }
+    for (int i = 0; i < kNumQuadsInIdxBuffer; ++i) {
+
+        // Each quadratic is rendered as a five sided polygon. This poly bounds
+        // the quadratic's bounding triangle but has been expanded so that the
+        // 1-pixel wide area around the curve is inside the poly.
+        // If a,b,c are the original control points then the poly a0,b0,c0,c1,a1
+        // that is rendered would look like this:
+        //              b0
+        //              b
+        //
+        //     a0              c0
+        //      a            c
+        //       a1       c1
+        // 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
+        data[1 + baseIdx] = baseVert + 1; // a1
+        data[2 + baseIdx] = baseVert + 2; // b0
+        data[3 + baseIdx] = baseVert + 2; // b0
+        data[4 + baseIdx] = baseVert + 4; // c1
+        data[5 + baseIdx] = baseVert + 3; // c0
+        data[6 + baseIdx] = baseVert + 1; // a1
+        data[7 + baseIdx] = baseVert + 4; // c1
+        data[8 + baseIdx] = baseVert + 2; // b0
+    }
+    if (tempData) {
+        bool ret = qIdxBuffer->updateData(data, kQuadIdxSBufize);
+        delete[] data;
+        return ret;
+    } else {
+        qIdxBuffer->unlock();
+        return true;
+    }
+}
+}
+
+GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) {
+    const GrIndexBuffer* lIdxBuffer = context->getQuadIndexBuffer();
+    if (NULL == lIdxBuffer) {
+        return NULL;
+    }
+    GrGpu* gpu = context->getGpu();
+    GrIndexBuffer* qIdxBuf = gpu->createIndexBuffer(kQuadIdxSBufize, false);
+    SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf);
+    if (NULL == qIdxBuf ||
+        !push_quad_index_data(qIdxBuf)) {
+        return NULL;
+    }
+    return SkNEW_ARGS(GrAAHairLinePathRenderer,
+                      (context, lIdxBuffer, qIdxBuf));
+}
+
+GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
+                                        const GrContext* context,
+                                        const GrIndexBuffer* linesIndexBuffer,
+                                        const GrIndexBuffer* quadsIndexBuffer) {
+    fLinesIndexBuffer = linesIndexBuffer;
+    linesIndexBuffer->ref();
+    fQuadsIndexBuffer = quadsIndexBuffer;
+    quadsIndexBuffer->ref();
+}
+
+GrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() {
+    fLinesIndexBuffer->unref();
+    fQuadsIndexBuffer->unref();
+}
+
+namespace {
+
+typedef SkTArray<SkPoint, true> PtArray;
+#define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
+typedef SkTArray<int, true> IntArray;
+
+// Takes 178th time of logf on Z600 / VC2010
+int get_float_exp(float x) {
+    GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
+#if GR_DEBUG
+    static bool tested;
+    if (!tested) {
+        tested = true;
+        GrAssert(get_float_exp(0.25f) == -2);
+        GrAssert(get_float_exp(0.3f) == -2);
+        GrAssert(get_float_exp(0.5f) == -1);
+        GrAssert(get_float_exp(1.f) == 0);
+        GrAssert(get_float_exp(2.f) == 1);
+        GrAssert(get_float_exp(2.5f) == 1);
+        GrAssert(get_float_exp(8.f) == 3);
+        GrAssert(get_float_exp(100.f) == 6);
+        GrAssert(get_float_exp(1000.f) == 9);
+        GrAssert(get_float_exp(1024.f) == 10);
+        GrAssert(get_float_exp(3000000.f) == 21);
+    }
+#endif
+    const int* iptr = (const int*)&x;
+    return (((*iptr) & 0x7f800000) >> 23) - 127;
+}
+
+// we subdivide the quads to avoid huge overfill
+// if it returns -1 then should be drawn as lines
+int num_quad_subdivs(const SkPoint p[3]) {
+    static const SkScalar gDegenerateToLineTol = SK_Scalar1;
+    static const SkScalar gDegenerateToLineTolSqd =
+        SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
+
+    if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd ||
+        p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) {
+        return -1;
+    }
+
+    GrScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
+    if (dsqd < gDegenerateToLineTolSqd) {
+        return -1;
+    }
+
+    if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) {
+        return -1;
+    }
+
+    static const int kMaxSub = 4;
+    // tolerance of triangle height in pixels
+    // tuned on windows  Quadro FX 380 / Z600
+    // trade off of fill vs cpu time on verts
+    // maybe different when do this using gpu (geo or tess shaders)
+    static const SkScalar gSubdivTol = 175 * SK_Scalar1;
+
+    if (dsqd <= SkScalarMul(gSubdivTol, gSubdivTol)) {
+        return 0;
+    } else {
+        // subdividing the quad reduces d by 4. so we want x = log4(d/tol)
+        // = log4(d*d/tol*tol)/2
+        // = log2(d*d/tol*tol)
+
+#ifdef SK_SCALAR_IS_FLOAT
+        // +1 since we're ignoring the mantissa contribution.
+        int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1;
+        log = GrMin(GrMax(0, log), kMaxSub);
+        return log;
+#else
+        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);
+#endif
+    }
+}
+
+/**
+ * Generates the lines and quads to be rendered. Lines are always recorded in
+ * device space. We will do a device space bloat to account for the 1pixel
+ * thickness.
+ * Quads are recorded in device space unless m contains
+ * perspective, then in they are in src space. We do this because we will
+ * subdivide large quads to reduce over-fill. This subdivision has to be
+ * performed before applying the perspective matrix.
+ */
+int generate_lines_and_quads(const SkPath& path,
+                             const SkMatrix& m,
+                             const GrIRect& devClipBounds,
+                             PtArray* lines,
+                             PtArray* quads,
+                             IntArray* quadSubdivCnts) {
+    SkPath::Iter iter(path, false);
+
+    int totalQuadCount = 0;
+    GrRect bounds;
+    GrIRect ibounds;
+
+    bool persp = m.hasPerspective();
+
+    for (;;) {
+        GrPoint pts[4];
+        GrPoint devPts[4];
+        GrPathCmd cmd = (GrPathCmd)iter.next(pts);
+        switch (cmd) {
+            case kMove_PathCmd:
+                break;
+            case kLine_PathCmd:
+                m.mapPoints(devPts, pts, 2);
+                bounds.setBounds(devPts, 2);
+                bounds.outset(SK_Scalar1, SK_Scalar1);
+                bounds.roundOut(&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:
+                m.mapPoints(devPts, pts, 3);
+                bounds.setBounds(devPts, 3);
+                bounds.outset(SK_Scalar1, SK_Scalar1);
+                bounds.roundOut(&ibounds);
+                if (SkIRect::Intersects(devClipBounds, ibounds)) {
+                    int subdiv = num_quad_subdivs(devPts);
+                    GrAssert(subdiv >= -1);
+                    if (-1 == subdiv) {
+                        SkPoint* pts = lines->push_back_n(4);
+                        pts[0] = devPts[0];
+                        pts[1] = devPts[1];
+                        pts[2] = devPts[1];
+                        pts[3] = devPts[2];
+                    } else {
+                        // when in perspective keep quads in src space
+                        SkPoint* qPts = persp ? pts : devPts;
+                        SkPoint* pts = quads->push_back_n(3);
+                        pts[0] = qPts[0];
+                        pts[1] = qPts[1];
+                        pts[2] = qPts[2];
+                        quadSubdivCnts->push_back() = subdiv;
+                        totalQuadCount += 1 << subdiv;
+                    }
+                }
+                break;
+            case kCubic_PathCmd:
+                m.mapPoints(devPts, pts, 4);
+                bounds.setBounds(devPts, 4);
+                bounds.outset(SK_Scalar1, SK_Scalar1);
+                bounds.roundOut(&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 =
+                            GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
+                                                             path.getBounds());
+                        GrPathUtils::convertCubicToQuads(pts, tolScale, false, kDummyDir, &q);
+                    } else {
+                        GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, false, kDummyDir, &q);
+                    }
+                    for (int i = 0; i < q.count(); i += 3) {
+                        SkPoint* qInDevSpace;
+                        // bounds has to be calculated in device space, but q is
+                        // in src space when there is perspective.
+                        if (persp) {
+                            m.mapPoints(devPts, &q[i], 3);
+                            bounds.setBounds(devPts, 3);
+                            qInDevSpace = devPts;
+                        } else {
+                            bounds.setBounds(&q[i], 3);
+                            qInDevSpace = &q[i];
+                        }
+                        bounds.outset(SK_Scalar1, SK_Scalar1);
+                        bounds.roundOut(&ibounds);
+                        if (SkIRect::Intersects(devClipBounds, ibounds)) {
+                            int subdiv = num_quad_subdivs(qInDevSpace);
+                            GrAssert(subdiv >= -1);
+                            if (-1 == subdiv) {
+                                SkPoint* pts = lines->push_back_n(4);
+                                // lines should always be in device coords
+                                pts[0] = qInDevSpace[0];
+                                pts[1] = qInDevSpace[1];
+                                pts[2] = qInDevSpace[1];
+                                pts[3] = qInDevSpace[2];
+                            } else {
+                                SkPoint* pts = quads->push_back_n(3);
+                                // q is already in src space when there is no
+                                // perspective and dev coords otherwise.
+                                pts[0] = q[0 + i];
+                                pts[1] = q[1 + i];
+                                pts[2] = q[2 + i];
+                                quadSubdivCnts->push_back() = subdiv;
+                                totalQuadCount += 1 << subdiv;
+                            }
+                        }
+                    }
+                }
+                break;
+            case kClose_PathCmd:
+                break;
+            case kEnd_PathCmd:
+                return totalQuadCount;
+        }
+    }
+}
+
+struct Vertex {
+    GrPoint fPos;
+    union {
+        struct {
+            GrScalar fA;
+            GrScalar fB;
+            GrScalar fC;
+        } fLine;
+        GrVec   fQuadCoord;
+        struct {
+            GrScalar fBogus[4];
+        };
+    };
+};
+GR_STATIC_ASSERT(sizeof(Vertex) == 3 * sizeof(GrPoint));
+
+void intersect_lines(const SkPoint& ptA, const SkVector& normA,
+                     const SkPoint& ptB, const SkVector& normB,
+                     SkPoint* result) {
+
+    SkScalar lineAW = -normA.dot(ptA);
+    SkScalar lineBW = -normB.dot(ptB);
+
+    SkScalar wInv = SkScalarMul(normA.fX, normB.fY) -
+                    SkScalarMul(normA.fY, normB.fX);
+    wInv = SkScalarInvert(wInv);
+
+    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]) {
+    GrAssert(!toDevice == !toSrc);
+    // original quad is specified by tri a,b,c
+    SkPoint a = qpts[0];
+    SkPoint b = qpts[1];
+    SkPoint c = qpts[2];
+
+    // this should be in the src space, not dev coords, when we have perspective
+    GrPathUtils::QuadUVMatrix DevToUV(qpts);
+
+    if (toDevice) {
+        toDevice->mapPoints(&a, 1);
+        toDevice->mapPoints(&b, 1);
+        toDevice->mapPoints(&c, 1);
+    }
+    // make a new poly where we replace a and c by a 1-pixel wide edges orthog
+    // to edges ab and bc:
+    //
+    //   before       |        after
+    //                |              b0
+    //         b      |
+    //                |
+    //                |     a0            c0
+    // a         c    |        a1       c1
+    //
+    // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c,
+    // respectively.
+    Vertex& a0 = verts[0];
+    Vertex& a1 = verts[1];
+    Vertex& b0 = verts[2];
+    Vertex& c0 = verts[3];
+    Vertex& c1 = verts[4];
+
+    SkVector ab = b;
+    ab -= a;
+    SkVector ac = c;
+    ac -= a;
+    SkVector cb = b;
+    cb -= c;
+
+    // We should have already handled degenerates
+    GrAssert(ab.length() > 0 && cb.length() > 0);
+
+    ab.normalize();
+    SkVector abN;
+    abN.setOrthog(ab, SkVector::kLeft_Side);
+    if (abN.dot(ac) > 0) {
+        abN.negate();
+    }
+
+    cb.normalize();
+    SkVector cbN;
+    cbN.setOrthog(cb, SkVector::kLeft_Side);
+    if (cbN.dot(ac) < 0) {
+        cbN.negate();
+    }
+
+    a0.fPos = a;
+    a0.fPos += abN;
+    a1.fPos = a;
+    a1.fPos -= abN;
+
+    c0.fPos = c;
+    c0.fPos += cbN;
+    c1.fPos = c;
+    c1.fPos -= cbN;
+
+    intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos);
+
+    if (toSrc) {
+        toSrc->mapPointsWithStride(&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,
+               Vertex** vert) {
+    GrAssert(subdiv >= 0);
+    if (subdiv) {
+        SkPoint newP[5];
+        SkChopQuadAtHalf(p, newP);
+        add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert);
+        add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert);
+    } else {
+        bloat_quad(p, toDevice, toSrc, *vert);
+        *vert += kVertsPerQuad;
+    }
+}
+
+void add_line(const SkPoint p[2],
+              int rtHeight,
+              const SkMatrix* toSrc,
+              Vertex** vert) {
+    const SkPoint& a = p[0];
+    const SkPoint& b = p[1];
+
+    SkVector orthVec = b;
+    orthVec -= a;
+
+    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));
+        for (int i = 0; i < kVertsPerLineSeg; ++i) {
+            (*vert)[i].fPos = (i < 2) ? a : b;
+            if (0 == i || 3 == i) {
+                (*vert)[i].fPos -= orthVec;
+            } else {
+                (*vert)[i].fPos += orthVec;
+            }
+            (*vert)[i].fLine.fA = normal.fX;
+            (*vert)[i].fLine.fB = normal.fY;
+            (*vert)[i].fLine.fC = lineC;
+        }
+        if (NULL != toSrc) {
+            toSrc->mapPointsWithStride(&(*vert)->fPos,
+                                       sizeof(Vertex),
+                                       kVertsPerLineSeg);
+        }
+    } else {
+        // just make it degenerate and likely offscreen
+        (*vert)[0].fPos.set(SK_ScalarMax, SK_ScalarMax);
+        (*vert)[1].fPos.set(SK_ScalarMax, SK_ScalarMax);
+        (*vert)[2].fPos.set(SK_ScalarMax, SK_ScalarMax);
+        (*vert)[3].fPos.set(SK_ScalarMax, SK_ScalarMax);
+    }
+
+    *vert += kVertsPerLineSeg;
+}
+
+}
+
+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 devClipBounds;
+    target->getClip()->getConservativeBounds(drawState.getRenderTarget(),
+                                             &devClipBounds);
+
+    GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
+    GrMatrix viewM = drawState.getViewMatrix();
+
+    PREALLOC_PTARRAY(128) lines;
+    PREALLOC_PTARRAY(128) quads;
+    IntArray qSubdivs;
+    *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));
+
+    if (!arg->set(target, layout, vertCnt, 0)) {
+        return false;
+    }
+
+    Vertex* verts = reinterpret_cast<Vertex*>(arg->vertices());
+
+    const GrMatrix* toDevice = NULL;
+    const GrMatrix* toSrc = NULL;
+    GrMatrix ivm;
+
+    if (viewM.hasPerspective()) {
+        if (viewM.invert(&ivm)) {
+            toDevice = &viewM;
+            toSrc = &ivm;
+        }
+    }
+
+    for (int i = 0; i < *lineCnt; ++i) {
+        add_line(&lines[2*i], rtHeight, toSrc, &verts);
+    }
+
+    int unsubdivQuadCnt = quads.count() / 3;
+    for (int i = 0; i < unsubdivQuadCnt; ++i) {
+        GrAssert(qSubdivs[i] >= 0);
+        add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts);
+    }
+
+    return true;
+}
+
+bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path,
+                                           GrPathFill fill,
+                                           const GrDrawTarget* target,
+                                           bool antiAlias) const {
+    if (fill != kHairLine_GrPathFill || !antiAlias) {
+        return false;
+    }
+
+    static const uint32_t gReqDerivMask = SkPath::kCubic_SegmentMask |
+                                          SkPath::kQuad_SegmentMask;
+    if (!target->getCaps().shaderDerivativeSupport() &&
+        (gReqDerivMask & path.getSegmentMasks())) {
+        return false;
+    }
+    return true;
+}
+
+bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
+                                          GrPathFill fill,
+                                          GrDrawTarget* target,
+                                          bool antiAlias) {
+
+    int lineCnt;
+    int quadCnt;
+    GrDrawTarget::AutoReleaseGeometry arg;
+    if (!this->createGeom(path,
+                          target,
+                          &lineCnt,
+                          &quadCnt,
+                          &arg)) {
+        return false;
+    }
+
+    GrDrawState::AutoDeviceCoordDraw adcd;
+    GrDrawState* drawState = target->drawState();
+    // createGeom transforms the geometry to device space when the matrix does not have
+    // perspective.
+    if (!drawState->getViewMatrix().hasPerspective()) {
+        adcd.set(drawState);
+        if (!adcd.succeeded()) {
+            return false;
+        }
+    }
+
+    // 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);
+        target->drawIndexed(kTriangles_GrPrimitiveType,
+                            kVertsPerLineSeg*lines,    // startV
+                            0,                         // startI
+                            kVertsPerLineSeg*n,        // vCount
+                            kIdxsPerLineSeg*n);        // iCount
+        lines += n;
+    }
+
+    target->setIndexSourceToBuffer(fQuadsIndexBuffer);
+    int quads = 0;
+    drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType);
+    while (quads < quadCnt) {
+        int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer);
+        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
new file mode 100644
index 0000000..9129a89
--- /dev/null
+++ b/src/gpu/GrAAHairLinePathRenderer.h
@@ -0,0 +1,51 @@
+
+/*
+ * 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 GrAAHairLinePathRenderer_DEFINED
+#define GrAAHairLinePathRenderer_DEFINED
+
+#include "GrPathRenderer.h"
+
+class GrAAHairLinePathRenderer : public GrPathRenderer {
+public:
+    virtual ~GrAAHairLinePathRenderer();
+
+    static GrPathRenderer* Create(GrContext* context);
+
+    virtual bool canDrawPath(const SkPath& path,
+                            GrPathFill fill,
+                            const GrDrawTarget* target,
+                            bool antiAlias) const SK_OVERRIDE;
+
+protected:
+    virtual bool onDrawPath(const SkPath& path,
+                            GrPathFill fill,
+                            GrDrawTarget* target,
+                            bool antiAlias) SK_OVERRIDE;
+
+private:
+
+    GrAAHairLinePathRenderer(const GrContext* context,
+                             const GrIndexBuffer* fLinesIndexBuffer,
+                             const GrIndexBuffer* fQuadsIndexBuffer);
+
+    bool createGeom(const SkPath& path,
+                    GrDrawTarget* target,
+                    int* lineCnt,
+                    int* quadCnt,
+                    GrDrawTarget::AutoReleaseGeometry* arg);
+
+    const GrIndexBuffer*        fLinesIndexBuffer;
+    const GrIndexBuffer*        fQuadsIndexBuffer;
+
+    typedef GrPathRenderer INHERITED;
+};
+
+
+#endif
+
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
new file mode 100644
index 0000000..36f37ab
--- /dev/null
+++ b/src/gpu/GrAARectRenderer.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 "GrAARectRenderer.h"
+#include "GrRefCnt.h"
+#include "GrGpu.h"
+
+SK_DEFINE_INST_COUNT(GrAARectRenderer)
+
+namespace {
+
+static GrVertexLayout aa_rect_layout(const GrDrawTarget* target,
+                                     bool useCoverage) {
+    GrVertexLayout layout = 0;
+    if (useCoverage) {
+        layout |= GrDrawTarget::kCoverage_VertexLayoutBit;
+    } else {
+        layout |= GrDrawTarget::kColor_VertexLayoutBit;
+    }
+    return layout;
+}
+
+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);
+}
+
+};
+
+void GrAARectRenderer::reset() {
+    GrSafeSetNull(fAAFillRectIndexBuffer);
+    GrSafeSetNull(fAAStrokeRectIndexBuffer);
+}
+
+const uint16_t GrAARectRenderer::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 GrAARectRenderer::aaFillRectIndexCount() {
+    return GR_ARRAY_COUNT(gFillAARectIdx);
+}
+
+GrIndexBuffer* GrAARectRenderer::aaFillRectIndexBuffer(GrGpu* gpu) {
+    if (NULL == fAAFillRectIndexBuffer) {
+        fAAFillRectIndexBuffer = gpu->createIndexBuffer(sizeof(gFillAARectIdx),
+                                                         false);
+        if (NULL != fAAFillRectIndexBuffer) {
+#if GR_DEBUG
+            bool updated =
+#endif
+            fAAFillRectIndexBuffer->updateData(gFillAARectIdx,
+                                               sizeof(gFillAARectIdx));
+            GR_DEBUGASSERT(updated);
+        }
+    }
+    return fAAFillRectIndexBuffer;
+}
+
+const uint16_t GrAARectRenderer::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(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(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);
+
+    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_GrPrimitiveType, 0,
+                        0, 8, this->aaFillRectIndexCount());
+}
+
+void GrAARectRenderer::strokeAARect(GrGpu* gpu,
+                                    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);
+        this->fillAARect(gpu, 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(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);
+
+    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);
+
+    // 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
new file mode 100644
index 0000000..24f1017
--- /dev/null
+++ b/src/gpu/GrAddPathRenderers_default.cpp
@@ -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.
+ */
+
+
+#include "GrStencilAndCoverPathRenderer.h"
+#include "GrAAHairLinePathRenderer.h"
+#include "GrAAConvexPathRenderer.h"
+#include "GrSoftwarePathRenderer.h"
+
+void GrPathRenderer::AddPathRenderers(GrContext* ctx,
+                                      GrPathRendererChain::UsageFlags flags,
+                                      GrPathRendererChain* chain) {
+    if (GrPathRenderer* pr = GrStencilAndCoverPathRenderer::Create(ctx)) {
+        chain->addPathRenderer(pr)->unref();
+    }
+    if (!(GrPathRendererChain::kNonAAOnly_UsageFlag & flags)) {
+
+        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
new file mode 100644
index 0000000..02da710
--- /dev/null
+++ b/src/gpu/GrAddPathRenderers_none.cpp
@@ -0,0 +1,15 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrPathRenderer.h"
+
+
+void GrPathRenderer::AddPathRenderers(GrContext*,
+                                      GrPathRendererChain::UsageFlags,
+                                      GrPathRendererChain*) {}
diff --git a/src/gpu/GrAllocPool.cpp b/src/gpu/GrAllocPool.cpp
new file mode 100644
index 0000000..39f8350
--- /dev/null
+++ b/src/gpu/GrAllocPool.cpp
@@ -0,0 +1,120 @@
+
+/*
+ * 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 "GrAllocPool.h"
+
+#define GrAllocPool_MIN_BLOCK_SIZE      ((size_t)128)
+
+struct GrAllocPool::Block {
+    Block*  fNext;
+    char*   fPtr;
+    size_t  fBytesFree;
+    size_t  fBytesTotal;
+
+    static Block* Create(size_t size, Block* next) {
+        GrAssert(size >= GrAllocPool_MIN_BLOCK_SIZE);
+
+        Block* block = (Block*)GrMalloc(sizeof(Block) + size);
+        block->fNext = next;
+        block->fPtr = (char*)block + sizeof(Block);
+        block->fBytesFree = size;
+        block->fBytesTotal = size;
+        return block;
+    }
+
+    bool canAlloc(size_t bytes) const {
+        return bytes <= fBytesFree;
+    }
+
+    void* alloc(size_t bytes) {
+        GrAssert(bytes <= fBytesFree);
+        fBytesFree -= bytes;
+        void* ptr = fPtr;
+        fPtr += bytes;
+        return ptr;
+    }
+
+    size_t release(size_t bytes) {
+        GrAssert(bytes > 0);
+        size_t free = GrMin(bytes, fBytesTotal - fBytesFree);
+        fBytesFree += free;
+        fPtr -= free;
+        return bytes - free;
+    }
+
+    bool empty() const { return fBytesTotal == fBytesFree; }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrAllocPool::GrAllocPool(size_t blockSize) {
+    fBlock = NULL;
+    fMinBlockSize = GrMax(blockSize, GrAllocPool_MIN_BLOCK_SIZE);
+    GR_DEBUGCODE(fBlocksAllocated = 0;)
+}
+
+GrAllocPool::~GrAllocPool() {
+    this->reset();
+}
+
+void GrAllocPool::reset() {
+    this->validate();
+
+    Block* block = fBlock;
+    while (block) {
+        Block* next = block->fNext;
+        GrFree(block);
+        block = next;
+    }
+    fBlock = NULL;
+    GR_DEBUGCODE(fBlocksAllocated = 0;)
+}
+
+void* GrAllocPool::alloc(size_t size) {
+    this->validate();
+
+    if (!fBlock || !fBlock->canAlloc(size)) {
+        size_t blockSize = GrMax(fMinBlockSize, size);
+        fBlock = Block::Create(blockSize, fBlock);
+        GR_DEBUGCODE(fBlocksAllocated += 1;)
+    }
+    return fBlock->alloc(size);
+}
+
+void GrAllocPool::release(size_t bytes) {
+    this->validate();
+
+    while (bytes && NULL != fBlock) {
+        bytes = fBlock->release(bytes);
+        if (fBlock->empty()) {
+            Block* next = fBlock->fNext;
+            GrFree(fBlock);
+            fBlock = next;
+            GR_DEBUGCODE(fBlocksAllocated -= 1;)
+        }
+    }
+}
+
+
+#if GR_DEBUG
+
+void GrAllocPool::validate() const {
+    Block* block = fBlock;
+    int count = 0;
+    while (block) {
+        count += 1;
+        block = block->fNext;
+    }
+    GrAssert(fBlocksAllocated == count);
+}
+
+#endif
+
+
diff --git a/src/gpu/GrAllocPool.h b/src/gpu/GrAllocPool.h
new file mode 100644
index 0000000..3489cb9
--- /dev/null
+++ b/src/gpu/GrAllocPool.h
@@ -0,0 +1,64 @@
+
+/*
+ * 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 GrAllocPool_DEFINED
+#define GrAllocPool_DEFINED
+
+#include "GrNoncopyable.h"
+
+class GrAllocPool : GrNoncopyable {
+public:
+    GrAllocPool(size_t blockSize = 0);
+    ~GrAllocPool();
+
+    /**
+     *  Frees all blocks that have been allocated with alloc().
+     */
+    void reset();
+
+    /**
+     *  Returns a block of memory bytes size big. This address must not be
+     *  passed to realloc/free/delete or any other function that assumes the
+     *  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.
+     */
+    void release(size_t bytes);
+
+private:
+    struct Block;
+
+    Block*  fBlock;
+    size_t  fMinBlockSize;
+
+#if GR_DEBUG
+    int fBlocksAllocated;
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+};
+
+template <typename T> class GrTAllocPool {
+public:
+    GrTAllocPool(int count) : fPool(count * sizeof(T)) {}
+
+    void reset() { fPool.reset(); }
+    T* alloc() { return (T*)fPool.alloc(sizeof(T)); }
+
+private:
+    GrAllocPool fPool;
+};
+
+#endif
+
diff --git a/src/gpu/GrAllocator.h b/src/gpu/GrAllocator.h
new file mode 100755
index 0000000..dc4c3de
--- /dev/null
+++ b/src/gpu/GrAllocator.h
@@ -0,0 +1,250 @@
+
+/*
+ * 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 GrAllocator_DEFINED
+#define GrAllocator_DEFINED
+
+#include "GrNoncopyable.h"
+#include "GrConfig.h"
+#include "SkTArray.h"
+
+class GrAllocator : GrNoncopyable {
+public:
+    ~GrAllocator() {
+        reset();
+    }
+
+    /**
+     * Create an allocator
+     *
+     * @param   itemSize        the size of each item to allocate
+     * @param   itemsPerBlock   the number of items to allocate at once
+     * @param   initialBlock    optional memory to use for the first block.
+     *                          Must be at least itemSize*itemsPerBlock sized.
+     *                          Caller is responsible for freeing this memory.
+     */
+    GrAllocator(size_t itemSize, int itemsPerBlock, void* initialBlock) :
+            fItemSize(itemSize),
+            fItemsPerBlock(itemsPerBlock),
+            fOwnFirstBlock(NULL == initialBlock),
+            fCount(0) {
+        GrAssert(itemsPerBlock > 0);
+        fBlockSize = fItemSize * fItemsPerBlock;
+        fBlocks.push_back() = initialBlock;
+        GR_DEBUGCODE(if (!fOwnFirstBlock) {*((char*)initialBlock+fBlockSize-1)='a';} );
+    }
+
+    /**
+     * Adds an item and returns pointer to it.
+     *
+     * @return pointer to the added item.
+     */
+    void* push_back() {
+        int indexInBlock = fCount % fItemsPerBlock;
+        // we always have at least one block
+        if (0 == indexInBlock) {
+            if (0 != fCount) {
+                fBlocks.push_back() = GrMalloc(fBlockSize);
+            } else if (fOwnFirstBlock) {
+                fBlocks[0] = GrMalloc(fBlockSize);
+            }
+        }
+        void* ret = (char*)fBlocks[fCount/fItemsPerBlock] +
+                    fItemSize * indexInBlock;
+        ++fCount;
+        return ret;
+    }
+
+    /**
+     * removes all added items
+     */
+    void reset() {
+        int blockCount = GrMax((unsigned)1,
+                               GrUIDivRoundUp(fCount, fItemsPerBlock));
+        for (int i = 1; i < blockCount; ++i) {
+            GrFree(fBlocks[i]);
+        }
+        if (fOwnFirstBlock) {
+            GrFree(fBlocks[0]);
+            fBlocks[0] = NULL;
+        }
+        fBlocks.pop_back_n(blockCount-1);
+        fCount = 0;
+    }
+
+    /**
+     * count of items
+     */
+    int count() const {
+        return fCount;
+    }
+
+    /**
+     * is the count 0
+     */
+    bool empty() const { return fCount == 0; }
+
+    /**
+     * access last item, only call if count() != 0
+     */
+    void* back() {
+        GrAssert(fCount);
+        return (*this)[fCount-1];
+    }
+
+    /**
+     * access last item, only call if count() != 0
+     */
+    const void* back() const {
+        GrAssert(fCount);
+        return (*this)[fCount-1];
+    }
+
+    /**
+     * access item by index.
+     */
+    void* operator[] (int i) {
+        GrAssert(i >= 0 && i < fCount);
+        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] +
+               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;
+    int                                     fItemsPerBlock;
+    bool                                    fOwnFirstBlock;
+    int                                     fCount;
+
+    typedef GrNoncopyable INHERITED;
+};
+
+template <typename T>
+class GrTAllocator : GrNoncopyable {
+
+public:
+    virtual ~GrTAllocator() { this->reset(); };
+
+    /**
+     * Create an allocator
+     *
+     * @param   itemsPerBlock   the number of items to allocate at once
+     * @param   initialBlock    optional memory to use for the first block.
+     *                          Must be at least size(T)*itemsPerBlock sized.
+     *                          Caller is responsible for freeing this memory.
+     */
+    explicit GrTAllocator(int itemsPerBlock)
+        : fAllocator(sizeof(T), itemsPerBlock, NULL) {}
+
+    /**
+     * Adds an item and returns it.
+     *
+     * @return the added item.
+     */
+    T& push_back() {
+        void* item = fAllocator.push_back();
+        GrAssert(NULL != item);
+        SkNEW_PLACEMENT(item, T);
+        return *(T*)item;
+    }
+
+    T& push_back(const T& t) {
+        void* item = fAllocator.push_back();
+        GrAssert(NULL != item);
+        SkNEW_PLACEMENT_ARGS(item, T, (t));
+        return *(T*)item;
+    }
+
+    /**
+     * removes all added items
+     */
+    void reset() {
+        int c = fAllocator.count();
+        for (int i = 0; i < c; ++i) {
+            ((T*)fAllocator[i])->~T();
+        }
+        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
+     */
+    T& back() {
+        return *(T*)fAllocator.back();
+    }
+
+    /**
+     * access last item, only call if count() != 0
+     */
+    const T& back() const {
+        return *(const T*)fAllocator.back();
+    }
+
+    /**
+     * access item by index.
+     */
+    T& operator[] (int i) {
+        return *(T*)(fAllocator[i]);
+    }
+
+    /**
+     * access item by index.
+     */
+    const T& operator[] (int i) const {
+        return *(const T*)(fAllocator[i]);
+    }
+
+protected:
+    GrTAllocator(int itemsPerBlock, void* initialBlock)
+        : fAllocator(sizeof(T), itemsPerBlock, initialBlock) {
+    }
+
+private:
+    GrAllocator fAllocator;
+    typedef GrNoncopyable INHERITED;
+};
+
+template <int N, typename T> class GrSTAllocator : public GrTAllocator<T> {
+private:
+    typedef GrTAllocator<T> INHERITED;
+
+public:
+    GrSTAllocator() : INHERITED(N, fStorage.get()) {
+    }
+
+private:
+    SkAlignedSTStorage<N, T> fStorage;
+};
+
+#endif
diff --git a/src/gpu/GrAtlas.cpp b/src/gpu/GrAtlas.cpp
new file mode 100644
index 0000000..491f6cf
--- /dev/null
+++ b/src/gpu/GrAtlas.cpp
@@ -0,0 +1,207 @@
+
+/*
+ * 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 "GrAtlas.h"
+#include "GrContext.h"
+#include "GrGpu.h"
+#include "GrRectanizer.h"
+#include "GrPlotMgr.h"
+
+#if 0
+#define GR_PLOT_WIDTH   8
+#define GR_PLOT_HEIGHT  4
+#define GR_ATLAS_WIDTH  256
+#define GR_ATLAS_HEIGHT 256
+
+#define GR_ATLAS_TEXTURE_WIDTH  (GR_PLOT_WIDTH * GR_ATLAS_WIDTH)
+#define GR_ATLAS_TEXTURE_HEIGHT (GR_PLOT_HEIGHT * GR_ATLAS_HEIGHT)
+
+#else
+
+#define GR_ATLAS_TEXTURE_WIDTH  1024
+#define GR_ATLAS_TEXTURE_HEIGHT 2048
+
+#define GR_ATLAS_WIDTH  341
+#define GR_ATLAS_HEIGHT 341
+
+#define GR_PLOT_WIDTH   (GR_ATLAS_TEXTURE_WIDTH / GR_ATLAS_WIDTH)
+#define GR_PLOT_HEIGHT  (GR_ATLAS_TEXTURE_HEIGHT / GR_ATLAS_HEIGHT)
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define BORDER      1
+
+#if GR_DEBUG
+    static int gCounter;
+#endif
+
+GrAtlas::GrAtlas(GrAtlasMgr* mgr, int plotX, int plotY, GrMaskFormat format) {
+    fAtlasMgr = mgr;    // just a pointer, not an owner
+    fNext = NULL;
+    fTexture = mgr->getTexture(format); // we're not an owner, just a pointer
+    fPlot.set(plotX, plotY);
+
+    fRects = GrRectanizer::Factory(GR_ATLAS_WIDTH - BORDER,
+                                   GR_ATLAS_HEIGHT - BORDER);
+
+    fMaskFormat = format;
+
+#if GR_DEBUG
+//    GrPrintf(" GrAtlas %p [%d %d] %d\n", this, plotX, plotY, gCounter);
+    gCounter += 1;
+#endif
+}
+
+GrAtlas::~GrAtlas() {
+    fAtlasMgr->freePlot(fPlot.fX, fPlot.fY);
+
+    delete fRects;
+
+#if GR_DEBUG
+    --gCounter;
+//    GrPrintf("~GrAtlas %p [%d %d] %d\n", this, fPlot.fX, fPlot.fY, gCounter);
+#endif
+}
+
+static void adjustForPlot(GrIPoint16* loc, const GrIPoint16& plot) {
+    loc->fX += plot.fX * GR_ATLAS_WIDTH;
+    loc->fY += plot.fY * GR_ATLAS_HEIGHT;
+}
+
+static uint8_t* zerofill(uint8_t* ptr, int count) {
+    while (--count >= 0) {
+        *ptr++ = 0;
+    }
+    return ptr;
+}
+
+bool GrAtlas::addSubImage(int width, int height, const void* image,
+                          GrIPoint16* loc) {
+    if (!fRects->addRect(width + BORDER, height + BORDER, loc)) {
+        return false;
+    }
+
+    SkAutoSMalloc<1024> storage;
+    int dstW = width + 2*BORDER;
+    int dstH = height + 2*BORDER;
+    if (BORDER) {
+        const int bpp = GrMaskFormatBytesPerPixel(fMaskFormat);
+        const size_t dstRB = dstW * bpp;
+        uint8_t* dst = (uint8_t*)storage.reset(dstH * dstRB);
+        Gr_bzero(dst, dstRB);                // zero top row
+        dst += dstRB;
+        for (int y = 0; y < height; y++) {
+            dst = zerofill(dst, bpp);   // zero left edge
+            memcpy(dst, image, width * bpp);
+            dst += width * bpp;
+            dst = zerofill(dst, bpp);   // zero right edge
+            image = (const void*)((const char*)image + width * bpp);
+        }
+        Gr_bzero(dst, dstRB);                // zero bottom row
+        image = storage.get();
+    }
+    adjustForPlot(loc, fPlot);
+    GrContext* context = fTexture->getContext();
+    // 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;
+    loc->fY += BORDER;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrAtlasMgr::GrAtlasMgr(GrGpu* gpu) {
+    fGpu = gpu;
+    gpu->ref();
+    Gr_bzero(fTexture, sizeof(fTexture));
+    fPlotMgr = SkNEW_ARGS(GrPlotMgr, (GR_PLOT_WIDTH, GR_PLOT_HEIGHT));
+}
+
+GrAtlasMgr::~GrAtlasMgr() {
+    for (size_t i = 0; i < GR_ARRAY_COUNT(fTexture); i++) {
+        GrSafeUnref(fTexture[i]);
+    }
+    delete fPlotMgr;
+    fGpu->unref();
+}
+
+static GrPixelConfig maskformat2pixelconfig(GrMaskFormat format) {
+    switch (format) {
+        case kA8_GrMaskFormat:
+            return kAlpha_8_GrPixelConfig;
+        case kA565_GrMaskFormat:
+            return kRGB_565_GrPixelConfig;
+        case kA888_GrMaskFormat:
+            return kSkia8888_PM_GrPixelConfig;
+        default:
+            GrAssert(!"unknown maskformat");
+    }
+    return kUnknown_GrPixelConfig;
+}
+
+GrAtlas* GrAtlasMgr::addToAtlas(GrAtlas* atlas,
+                                int width, int height, const void* image,
+                                GrMaskFormat format,
+                                GrIPoint16* loc) {
+    GrAssert(NULL == atlas || atlas->getMaskFormat() == format);
+
+    if (atlas && atlas->addSubImage(width, height, image, loc)) {
+        return atlas;
+    }
+
+    // If the above fails, then either we have no starting atlas, or the current
+    // one is full. Either way we need to allocate a new atlas
+
+    GrIPoint16 plot;
+    if (!fPlotMgr->newPlot(&plot)) {
+        return NULL;
+    }
+
+    GrAssert(0 == kA8_GrMaskFormat);
+    GrAssert(1 == kA565_GrMaskFormat);
+    if (NULL == fTexture[format]) {
+        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 = SkNEW_ARGS(GrAtlas, (this, plot.fX, plot.fY, format));
+    if (!newAtlas->addSubImage(width, height, image, loc)) {
+        delete newAtlas;
+        return NULL;
+    }
+
+    newAtlas->fNext = atlas;
+    return newAtlas;
+}
+
+void GrAtlasMgr::freePlot(int x, int y) {
+    GrAssert(fPlotMgr->isBusy(x, y));
+    fPlotMgr->freePlot(x, y);
+}
+
+
diff --git a/src/gpu/GrAtlas.h b/src/gpu/GrAtlas.h
new file mode 100644
index 0000000..f0114e3
--- /dev/null
+++ b/src/gpu/GrAtlas.h
@@ -0,0 +1,84 @@
+
+/*
+ * 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 GrAtlas_DEFINED
+#define GrAtlas_DEFINED
+
+#include "GrPoint.h"
+#include "GrTexture.h"
+#include "GrTDArray.h"
+
+class GrGpu;
+class GrRectanizer;
+class GrAtlasMgr;
+
+class GrAtlas {
+public:
+    GrAtlas(GrAtlasMgr*, int plotX, int plotY, GrMaskFormat);
+
+    int getPlotX() const { return fPlot.fX; }
+    int getPlotY() const { return fPlot.fY; }
+    GrMaskFormat getMaskFormat() const { return fMaskFormat; }
+
+    GrTexture* texture() const { return fTexture; }
+
+    bool addSubImage(int width, int height, const void*, GrIPoint16*);
+
+    static void FreeLList(GrAtlas* atlas) {
+        while (atlas) {
+            GrAtlas* next = atlas->fNext;
+            delete atlas;
+            atlas = next;
+        }
+    }
+
+    // testing
+    GrAtlas* nextAtlas() const { return fNext; }
+
+private:
+    ~GrAtlas(); // does not try to delete the fNext field
+
+    GrAtlas*        fNext;
+    GrTexture*      fTexture;
+    GrRectanizer*   fRects;
+    GrAtlasMgr*     fAtlasMgr;
+    GrIPoint16      fPlot;
+    GrMaskFormat    fMaskFormat;
+
+    friend class GrAtlasMgr;
+};
+
+class GrPlotMgr;
+
+class GrAtlasMgr {
+public:
+    GrAtlasMgr(GrGpu*);
+    ~GrAtlasMgr();
+
+    GrAtlas* addToAtlas(GrAtlas*, int width, int height, const void*,
+                        GrMaskFormat, GrIPoint16*);
+
+    GrTexture* getTexture(GrMaskFormat format) const {
+        GrAssert((unsigned)format < kCount_GrMaskFormats);
+        return fTexture[format];
+    }
+
+    // to be called by ~GrAtlas()
+    void freePlot(int x, int y);
+
+private:
+    GrGpu*      fGpu;
+    GrTexture*  fTexture[kCount_GrMaskFormats];
+    GrPlotMgr*  fPlotMgr;
+};
+
+#endif
+
+
diff --git a/src/gpu/GrBinHashKey.h b/src/gpu/GrBinHashKey.h
new file mode 100644
index 0000000..d2194e9
--- /dev/null
+++ b/src/gpu/GrBinHashKey.h
@@ -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.
+ */
+
+
+#ifndef GrBinHashKey_DEFINED
+#define GrBinHashKey_DEFINED
+
+#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).
+ *
+ *  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 GrTBinHashKey {
+public:
+    GrTBinHashKey() {
+        this->reset();
+    }
+
+    GrTBinHashKey(const GrTBinHashKey<Entry, KeySize>& other) {
+        *this = other;
+    }
+
+    GrTBinHashKey<Entry, KeySize>& operator=(const GrTBinHashKey<Entry, KeySize>& other) {
+        memcpy(this, &other, sizeof(*this));
+        return *this;
+    }
+
+    ~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);
+
+        uint32_t hash = 0;
+        size_t len = KeySize;
+        while (len >= 4) {
+            hash += *data++;
+            hash += (fHash << 10);
+            hash ^= (hash >> 6);
+            len -= 4;
+        }
+        hash += (fHash << 3);
+        hash ^= (fHash >> 11);
+        hash += (fHash << 15);
+#if GR_DEBUG
+        fIsValid = true;
+#endif
+        fHash = hash;
+    }
+
+    int compare(const GrTBinHashKey<Entry, KeySize>& key) const {
+        GrAssert(fIsValid && key.fIsValid);
+        return memcmp(fData, key.fData, KeySize);
+    }
+
+    static bool EQ(const Entry& entry, const GrTBinHashKey<Entry, KeySize>& key) {
+        GrAssert(key.fIsValid);
+        return 0 == entry.compare(key);
+    }
+
+    static bool LT(const Entry& entry, const GrTBinHashKey<Entry, KeySize>& key) {
+        GrAssert(key.fIsValid);
+        return entry.compare(key) < 0;
+    }
+
+    uint32_t getHash() const {
+        GrAssert(fIsValid);
+        return fHash;
+    }
+
+private:
+    uint32_t            fHash;
+    uint8_t             fData[KeySize];  // Buffer for key storage
+
+#if GR_DEBUG
+public:
+    bool                fIsValid;
+#endif
+};
+
+#endif
diff --git a/src/gpu/GrBufferAllocPool.cpp b/src/gpu/GrBufferAllocPool.cpp
new file mode 100644
index 0000000..66b74e4
--- /dev/null
+++ b/src/gpu/GrBufferAllocPool.cpp
@@ -0,0 +1,477 @@
+
+/*
+ * 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 "GrBufferAllocPool.h"
+#include "GrTypes.h"
+#include "GrVertexBuffer.h"
+#include "GrIndexBuffer.h"
+#include "GrGpu.h"
+
+#if GR_DEBUG
+    #define VALIDATE validate
+#else
+    static void VALIDATE(bool x = false) {}
+#endif
+
+// page size
+#define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 12)
+
+GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu,
+                                     BufferType bufferType,
+                                     bool frequentResetHint,
+                                     size_t blockSize,
+                                     int preallocBufferCnt) :
+        fBlocks(GrMax(8, 2*preallocBufferCnt)) {
+
+    GrAssert(NULL != gpu);
+    fGpu = gpu;
+    fGpu->ref();
+    fGpuIsReffed = true;
+
+    fBufferType = bufferType;
+    fFrequentResetHint = frequentResetHint;
+    fBufferPtr = NULL;
+    fMinBlockSize = GrMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
+
+    fBytesInUse = 0;
+
+    fPreallocBuffersInUse = 0;
+    fPreallocBufferStartIdx = 0;
+    for (int i = 0; i < preallocBufferCnt; ++i) {
+        GrGeometryBuffer* buffer = this->createBuffer(fMinBlockSize);
+        if (NULL != buffer) {
+            *fPreallocBuffers.append() = buffer;
+        }
+    }
+}
+
+GrBufferAllocPool::~GrBufferAllocPool() {
+    VALIDATE();
+    if (fBlocks.count()) {
+        GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
+        if (buffer->isLocked()) {
+            buffer->unlock();
+        }
+    }
+    while (!fBlocks.empty()) {
+        destroyBlock();
+    }
+    fPreallocBuffers.unrefAll();
+    releaseGpuRef();
+}
+
+void GrBufferAllocPool::releaseGpuRef() {
+    if (fGpuIsReffed) {
+        fGpu->unref();
+        fGpuIsReffed = false;
+    }
+}
+
+void GrBufferAllocPool::reset() {
+    VALIDATE();
+    fBytesInUse = 0;
+    if (fBlocks.count()) {
+        GrGeometryBuffer* buffer = fBlocks.back().fBuffer;
+        if (buffer->isLocked()) {
+            buffer->unlock();
+        }
+    }
+    // fPreallocBuffersInUse will be decremented down to zero in the while loop
+    int preallocBuffersInUse = fPreallocBuffersInUse;
+    while (!fBlocks.empty()) {
+        this->destroyBlock();
+    }
+    if (fPreallocBuffers.count()) {
+        // must set this after above loop.
+        fPreallocBufferStartIdx = (fPreallocBufferStartIdx +
+                                   preallocBuffersInUse) %
+                                  fPreallocBuffers.count();
+    }
+    // we may have created a large cpu mirror of a large VB. Reset the size
+    // to match our pre-allocated VBs.
+    fCpuData.reset(fMinBlockSize);
+    GrAssert(0 == fPreallocBuffersInUse);
+    VALIDATE();
+}
+
+void GrBufferAllocPool::unlock() {
+    VALIDATE();
+
+    if (NULL != fBufferPtr) {
+        BufferBlock& block = fBlocks.back();
+        if (block.fBuffer->isLocked()) {
+            block.fBuffer->unlock();
+        } else {
+            size_t flushSize = block.fBuffer->sizeInBytes() - block.fBytesFree;
+            flushCpuData(fBlocks.back().fBuffer, flushSize);
+        }
+        fBufferPtr = NULL;
+    }
+    VALIDATE();
+}
+
+#if GR_DEBUG
+void GrBufferAllocPool::validate(bool unusedBlockAllowed) const {
+    if (NULL != fBufferPtr) {
+        GrAssert(!fBlocks.empty());
+        if (fBlocks.back().fBuffer->isLocked()) {
+            GrGeometryBuffer* buf = fBlocks.back().fBuffer;
+            GrAssert(buf->lockPtr() == fBufferPtr);
+        } else {
+            GrAssert(fCpuData.get() == fBufferPtr);
+        }
+    } else {
+        GrAssert(fBlocks.empty() || !fBlocks.back().fBuffer->isLocked());
+    }
+    size_t bytesInUse = 0;
+    for (int i = 0; i < fBlocks.count() - 1; ++i) {
+        GrAssert(!fBlocks[i].fBuffer->isLocked());
+    }
+    for (int i = 0; i < fBlocks.count(); ++i) {
+        size_t bytes = fBlocks[i].fBuffer->sizeInBytes() - fBlocks[i].fBytesFree;
+        bytesInUse += bytes;
+        GrAssert(bytes || unusedBlockAllowed);
+    }
+
+    GrAssert(bytesInUse == fBytesInUse);
+    if (unusedBlockAllowed) {
+        GrAssert((fBytesInUse && !fBlocks.empty()) ||
+                 (!fBytesInUse && (fBlocks.count() < 2)));
+    } else {
+        GrAssert((0 == fBytesInUse) == fBlocks.empty());
+    }
+}
+#endif
+
+void* GrBufferAllocPool::makeSpace(size_t size,
+                                   size_t alignment,
+                                   const GrGeometryBuffer** buffer,
+                                   size_t* offset) {
+    VALIDATE();
+
+    GrAssert(NULL != buffer);
+    GrAssert(NULL != offset);
+
+    if (NULL != fBufferPtr) {
+        BufferBlock& back = fBlocks.back();
+        size_t usedBytes = back.fBuffer->sizeInBytes() - back.fBytesFree;
+        size_t pad = GrSizeAlignUpPad(usedBytes,
+                                      alignment);
+        if ((size + pad) <= back.fBytesFree) {
+            usedBytes += pad;
+            *offset = usedBytes;
+            *buffer = back.fBuffer;
+            back.fBytesFree -= size + pad;
+            fBytesInUse += size + pad;
+            VALIDATE();
+            return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
+        }
+    }
+
+    // We could honor the space request using by a partial update of the current
+    // VB (if there is room). But we don't currently use draw calls to GL that
+    // allow the driver to know that previously issued draws won't read from
+    // the part of the buffer we update. Also, the GL buffer implementation
+    // 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;
+    }
+    GrAssert(NULL != fBufferPtr);
+
+    *offset = 0;
+    BufferBlock& back = fBlocks.back();
+    *buffer = back.fBuffer;
+    back.fBytesFree -= size;
+    fBytesInUse += size;
+    VALIDATE();
+    return fBufferPtr;
+}
+
+int GrBufferAllocPool::currentBufferItems(size_t itemSize) const {
+    VALIDATE();
+    if (NULL != fBufferPtr) {
+        const BufferBlock& back = fBlocks.back();
+        size_t usedBytes = back.fBuffer->sizeInBytes() - back.fBytesFree;
+        size_t pad = GrSizeAlignUpPad(usedBytes, itemSize);
+        return (back.fBytesFree - pad) / itemSize;
+    } else if (fPreallocBuffersInUse < fPreallocBuffers.count()) {
+        return fMinBlockSize / itemSize;
+    }
+    return 0;
+}
+
+int GrBufferAllocPool::preallocatedBuffersRemaining() const {
+    return fPreallocBuffers.count() - fPreallocBuffersInUse;
+}
+
+int GrBufferAllocPool::preallocatedBufferCount() const {
+    return fPreallocBuffers.count();
+}
+
+void GrBufferAllocPool::putBack(size_t bytes) {
+    VALIDATE();
+
+    // if the putBack unwinds all the preallocated buffers then we will
+    // advance the starting index. As blocks are destroyed fPreallocBuffersInUse
+    // will be decremented. I will reach zero if all blocks using preallocated
+    // buffers are released.
+    int preallocBuffersInUse = fPreallocBuffersInUse;
+
+    while (bytes) {
+        // caller shouldnt try to put back more than they've taken
+        GrAssert(!fBlocks.empty());
+        BufferBlock& block = fBlocks.back();
+        size_t bytesUsed = block.fBuffer->sizeInBytes() - block.fBytesFree;
+        if (bytes >= bytesUsed) {
+            bytes -= bytesUsed;
+            fBytesInUse -= bytesUsed;
+            // if we locked a vb to satisfy the make space and we're releasing
+            // beyond it, then unlock it.
+            if (block.fBuffer->isLocked()) {
+                block.fBuffer->unlock();
+            }
+            this->destroyBlock();
+        } else {
+            block.fBytesFree += bytes;
+            fBytesInUse -= bytes;
+            bytes = 0;
+            break;
+        }
+    }
+    if (!fPreallocBuffersInUse && fPreallocBuffers.count()) {
+            fPreallocBufferStartIdx = (fPreallocBufferStartIdx +
+                                       preallocBuffersInUse) %
+                                      fPreallocBuffers.count();
+    }
+    VALIDATE();
+}
+
+bool GrBufferAllocPool::createBlock(size_t requestSize) {
+
+    size_t size = GrMax(requestSize, fMinBlockSize);
+    GrAssert(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
+
+    VALIDATE();
+
+    BufferBlock& block = fBlocks.push_back();
+
+    if (size == fMinBlockSize &&
+        fPreallocBuffersInUse < fPreallocBuffers.count()) {
+
+        uint32_t nextBuffer = (fPreallocBuffersInUse +
+                               fPreallocBufferStartIdx) %
+                              fPreallocBuffers.count();
+        block.fBuffer = fPreallocBuffers[nextBuffer];
+        block.fBuffer->ref();
+        ++fPreallocBuffersInUse;
+    } else {
+        block.fBuffer = this->createBuffer(size);
+        if (NULL == block.fBuffer) {
+            fBlocks.pop_back();
+            return false;
+        }
+    }
+
+    block.fBytesFree = size;
+    if (NULL != fBufferPtr) {
+        GrAssert(fBlocks.count() > 1);
+        BufferBlock& prev = fBlocks.fromBack(1);
+        if (prev.fBuffer->isLocked()) {
+            prev.fBuffer->unlock();
+        } else {
+            flushCpuData(prev.fBuffer,
+                         prev.fBuffer->sizeInBytes() - prev.fBytesFree);
+        }
+        fBufferPtr = NULL;
+    }
+
+    GrAssert(NULL == fBufferPtr);
+
+    if (fGpu->getCaps().bufferLockSupport() &&
+        size > GR_GEOM_BUFFER_LOCK_THRESHOLD &&
+        (!fFrequentResetHint || requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD)) {
+        fBufferPtr = block.fBuffer->lock();
+    }
+
+    if (NULL == fBufferPtr) {
+        fBufferPtr = fCpuData.reset(size);
+    }
+
+    VALIDATE(true);
+
+    return true;
+}
+
+void GrBufferAllocPool::destroyBlock() {
+    GrAssert(!fBlocks.empty());
+
+    BufferBlock& block = fBlocks.back();
+    if (fPreallocBuffersInUse > 0) {
+        uint32_t prevPreallocBuffer = (fPreallocBuffersInUse +
+                                       fPreallocBufferStartIdx +
+                                       (fPreallocBuffers.count() - 1)) %
+                                      fPreallocBuffers.count();
+        if (block.fBuffer == fPreallocBuffers[prevPreallocBuffer]) {
+            --fPreallocBuffersInUse;
+        }
+    }
+    GrAssert(!block.fBuffer->isLocked());
+    block.fBuffer->unref();
+    fBlocks.pop_back();
+    fBufferPtr = NULL;
+}
+
+void GrBufferAllocPool::flushCpuData(GrGeometryBuffer* buffer,
+                                     size_t flushSize) {
+    GrAssert(NULL != buffer);
+    GrAssert(!buffer->isLocked());
+    GrAssert(fCpuData.get() == fBufferPtr);
+    GrAssert(flushSize <= buffer->sizeInBytes());
+    VALIDATE(true);
+
+    if (fGpu->getCaps().bufferLockSupport() &&
+        flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) {
+        void* data = buffer->lock();
+        if (NULL != data) {
+            memcpy(data, fBufferPtr, flushSize);
+            buffer->unlock();
+            return;
+        }
+    }
+    buffer->updateData(fBufferPtr, flushSize);
+    VALIDATE(true);
+}
+
+GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) {
+    if (kIndex_BufferType == fBufferType) {
+        return fGpu->createIndexBuffer(size, true);
+    } else {
+        GrAssert(kVertex_BufferType == fBufferType);
+        return fGpu->createVertexBuffer(size, true);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu,
+                                                 bool frequentResetHint,
+                                                 size_t bufferSize,
+                                                 int preallocBufferCnt)
+: GrBufferAllocPool(gpu,
+                    kVertex_BufferType,
+                    frequentResetHint,
+                    bufferSize,
+                    preallocBufferCnt) {
+}
+
+void* GrVertexBufferAllocPool::makeSpace(GrVertexLayout layout,
+                                         int vertexCount,
+                                         const GrVertexBuffer** buffer,
+                                         int* startVertex) {
+
+    GrAssert(vertexCount >= 0);
+    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,
+                                     &geomBuffer,
+                                     &offset);
+
+    *buffer = (const GrVertexBuffer*) geomBuffer;
+    GrAssert(0 == offset % vSize);
+    *startVertex = offset / vSize;
+    return ptr;
+}
+
+bool GrVertexBufferAllocPool::appendVertices(GrVertexLayout layout,
+                                             int vertexCount,
+                                             const void* vertices,
+                                             const GrVertexBuffer** buffer,
+                                             int* startVertex) {
+    void* space = makeSpace(layout, vertexCount, buffer, startVertex);
+    if (NULL != space) {
+        memcpy(space,
+               vertices,
+               GrDrawTarget::VertexSize(layout) * vertexCount);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const {
+    return INHERITED::preallocatedBufferSize() /
+            GrDrawTarget::VertexSize(layout);
+}
+
+int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const {
+    return currentBufferItems(GrDrawTarget::VertexSize(layout));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu,
+                                               bool frequentResetHint,
+                                               size_t bufferSize,
+                                               int preallocBufferCnt)
+: GrBufferAllocPool(gpu,
+                    kIndex_BufferType,
+                    frequentResetHint,
+                    bufferSize,
+                    preallocBufferCnt) {
+}
+
+void* GrIndexBufferAllocPool::makeSpace(int indexCount,
+                                        const GrIndexBuffer** buffer,
+                                        int* startIndex) {
+
+    GrAssert(indexCount >= 0);
+    GrAssert(NULL != buffer);
+    GrAssert(NULL != startIndex);
+
+    size_t offset = 0; // assign to suppress warning
+    const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
+    void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t),
+                                     sizeof(uint16_t),
+                                     &geomBuffer,
+                                     &offset);
+
+    *buffer = (const GrIndexBuffer*) geomBuffer;
+    GrAssert(0 == offset % sizeof(uint16_t));
+    *startIndex = offset / sizeof(uint16_t);
+    return ptr;
+}
+
+bool GrIndexBufferAllocPool::appendIndices(int indexCount,
+                                           const void* indices,
+                                           const GrIndexBuffer** buffer,
+                                           int* startIndex) {
+    void* space = makeSpace(indexCount, buffer, startIndex);
+    if (NULL != space) {
+        memcpy(space, indices, sizeof(uint16_t) * indexCount);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+int GrIndexBufferAllocPool::preallocatedBufferIndices() const {
+    return INHERITED::preallocatedBufferSize() / sizeof(uint16_t);
+}
+
+int GrIndexBufferAllocPool::currentBufferIndices() const {
+    return currentBufferItems(sizeof(uint16_t));
+}
diff --git a/src/gpu/GrBufferAllocPool.h b/src/gpu/GrBufferAllocPool.h
new file mode 100644
index 0000000..7ed4b0c
--- /dev/null
+++ b/src/gpu/GrBufferAllocPool.h
@@ -0,0 +1,352 @@
+
+/*
+ * 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 GrBufferAllocPool_DEFINED
+#define GrBufferAllocPool_DEFINED
+
+#include "GrNoncopyable.h"
+#include "GrTDArray.h"
+
+#include "SkTArray.h"
+
+class GrGeometryBuffer;
+class GrGpu;
+
+/**
+ * A pool of geometry buffers tied to a GrGpu.
+ *
+ * The pool allows a client to make space for geometry and then put back excess
+ * space if it over allocated. When a client is ready to draw from the pool
+ * it calls unlock on the pool ensure buffers are ready for drawing. The pool
+ * can be reset after drawing is completed to recycle space.
+ *
+ * At creation time a minimum per-buffer size can be specified. Additionally,
+ * a number of buffers to preallocate can be specified. These will
+ * be allocated at the min size and kept around until the pool is destroyed.
+ */
+class GrBufferAllocPool : GrNoncopyable {
+
+public:
+    /**
+     * Ensures all buffers are unlocked and have all data written to them.
+     * Call before drawing using buffers from the pool.
+     */
+    void unlock();
+
+    /**
+     *  Invalidates all the data in the pool, unrefs non-preallocated buffers.
+     */
+    void reset();
+
+    /**
+     * Gets the number of preallocated buffers that are yet to be used.
+     */
+    int preallocatedBuffersRemaining() const;
+
+    /**
+     * gets the number of preallocated buffers
+     */
+    int preallocatedBufferCount() const;
+
+    /**
+     * Frees data from makeSpaces in LIFO order.
+     */
+    void putBack(size_t bytes);
+
+    /**
+     * Gets the GrGpu that this pool is associated with.
+     */
+    GrGpu* getGpu() { return fGpu; }
+
+protected:
+    /**
+     * Used to determine what type of buffers to create. We could make the
+     * createBuffer a virtual except that we want to use it in the cons for
+     * pre-allocated buffers.
+     */
+    enum BufferType {
+        kVertex_BufferType,
+        kIndex_BufferType,
+    };
+
+    /**
+     * Constructor
+     *
+     * @param gpu                   The GrGpu used to create the buffers.
+     * @param bufferType            The type of buffers to create.
+     * @param frequentResetHint     A hint that indicates that the pool
+     *                              should expect frequent unlock() calls
+     *                              (as opposed to many makeSpace / acquires
+     *                              between resets).
+     * @param bufferSize            The minimum size of created buffers.
+     *                              This value will be clamped to some
+     *                              reasonable minimum.
+     * @param preallocBufferCnt     The pool will allocate this number of
+     *                              buffers at bufferSize and keep them until it
+     *                              is destroyed.
+     */
+     GrBufferAllocPool(GrGpu* gpu,
+                       BufferType bufferType,
+                       bool frequentResetHint,
+                       size_t   bufferSize = 0,
+                       int preallocBufferCnt = 0);
+
+    virtual ~GrBufferAllocPool();
+
+    /**
+     * Gets the size of the preallocated buffers.
+     *
+     * @return the size of preallocated buffers.
+     */
+    size_t preallocatedBufferSize() const {
+        return fPreallocBuffers.count() ? fMinBlockSize : 0;
+    }
+
+    /**
+     * Returns a block of memory to hold data. A buffer designated to hold the
+     * data is given to the caller. The buffer may or may not be locked. The
+     * returned ptr remains valid until any of the following:
+     *      *makeSpace is called again.
+     *      *unlock is called.
+     *      *reset is called.
+     *      *this object is destroyed.
+     *
+     * Once unlock on the pool is called the data is guaranteed to be in the
+     * buffer at the offset indicated by offset. Until that time it may be
+     * in temporary storage and/or the buffer may be locked.
+     *
+     * @param size         the amount of data to make space for
+     * @param alignment    alignment constraint from start of buffer
+     * @param buffer       returns the buffer that will hold the data.
+     * @param offset       returns the offset into buffer of the data.
+     * @return pointer to where the client should write the data.
+     */
+    void* makeSpace(size_t size,
+                    size_t alignment,
+                    const GrGeometryBuffer** buffer,
+                    size_t* offset);
+
+    /**
+     * Gets the number of items of a size that can be added to the current
+     * buffer without spilling to another buffer. If the pool has been reset, or
+     * the previous makeSpace completely exhausted a buffer then the returned
+     * size will be the size of the next available preallocated buffer, or zero
+     * if no preallocated buffer remains available. It is assumed that items
+     * should be itemSize-aligned from the start of a buffer.
+     *
+     * @return the number of items that would fit in the current buffer.
+     */
+    int currentBufferItems(size_t itemSize) const;
+
+    GrGeometryBuffer* createBuffer(size_t size);
+
+private:
+
+    // The GrGpu must be able to clear the ref of pools it creates as members
+    friend class GrGpu;
+    void releaseGpuRef();
+
+    struct BufferBlock {
+        size_t              fBytesFree;
+        GrGeometryBuffer*   fBuffer;
+    };
+
+    bool createBlock(size_t requestSize);
+    void destroyBlock();
+    void flushCpuData(GrGeometryBuffer* buffer, size_t flushSize);
+#if GR_DEBUG
+    void validate(bool unusedBlockAllowed = false) const;
+#endif
+
+    size_t                          fBytesInUse;
+
+    GrGpu*                          fGpu;
+    bool                            fGpuIsReffed;
+    bool                            fFrequentResetHint;
+    GrTDArray<GrGeometryBuffer*>    fPreallocBuffers;
+    size_t                          fMinBlockSize;
+    BufferType                      fBufferType;
+
+    SkTArray<BufferBlock>           fBlocks;
+    int                             fPreallocBuffersInUse;
+    // We attempt to cycle through the preallocated buffers rather than
+    // always starting from the first.
+    int                             fPreallocBufferStartIdx;
+    SkAutoMalloc                    fCpuData;
+    void*                           fBufferPtr;
+};
+
+class GrVertexBuffer;
+
+/**
+ * A GrBufferAllocPool of vertex buffers
+ */
+class GrVertexBufferAllocPool : public GrBufferAllocPool {
+public:
+    /**
+     * Constructor
+     *
+     * @param gpu                   The GrGpu used to create the vertex buffers.
+     * @param frequentResetHint     A hint that indicates that the pool
+     *                              should expect frequent unlock() calls
+     *                              (as opposed to many makeSpace / acquires
+     *                              between resets).
+     * @param bufferSize            The minimum size of created VBs This value
+     *                              will be clamped to some reasonable minimum.
+     * @param preallocBufferCnt     The pool will allocate this number of VBs at
+     *                              bufferSize and keep them until it is
+     *                              destroyed.
+     */
+    GrVertexBufferAllocPool(GrGpu* gpu,
+                            bool frequentResetHint,
+                            size_t bufferSize = 0,
+                            int preallocBufferCnt = 0);
+
+    /**
+     * Returns a block of memory to hold vertices. A buffer designated to hold
+     * the vertices given to the caller. The buffer may or may not be locked.
+     * The returned ptr remains valid until any of the following:
+     *      *makeSpace is called again.
+     *      *unlock is called.
+     *      *reset is called.
+     *      *this object is destroyed.
+     *
+     * Once unlock on the pool is called the vertices are guaranteed to be in
+     * 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 vertexCount  number of vertices to allocate space for
+     * @param buffer       returns the vertex buffer that will hold the
+     *                     vertices.
+     * @param startVertex  returns the offset into buffer of the first vertex.
+     *                     In units of the size of a vertex from layout param.
+     * @return pointer to first vertex.
+     */
+    void* makeSpace(GrVertexLayout layout,
+                    int vertexCount,
+                    const GrVertexBuffer** buffer,
+                    int* startVertex);
+
+    /**
+     * Shortcut to make space and then write verts into the made space.
+     */
+    bool appendVertices(GrVertexLayout layout,
+                        int vertexCount,
+                        const void* vertices,
+                        const GrVertexBuffer** buffer,
+                        int* startVertex);
+
+    /**
+     * Gets the number of vertices that can be added to the current VB without
+     * spilling to another VB. If the pool has been reset, or the previous
+     * makeSpace completely exhausted a VB then the returned number of vertices
+     * would fit in the next available preallocated buffer. If any makeSpace
+     * would force a new VB to be created the return value will be zero.
+     *
+     * @param   the format of vertices to compute space for.
+     * @return the number of vertices that would fit in the current buffer.
+     */
+    int currentBufferVertices(GrVertexLayout layout) 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.
+     *
+     * @return number of vertices that fit in one of the preallocated vertex
+     *         buffers.
+     */
+    int preallocatedBufferVertices(GrVertexLayout layout) const;
+
+private:
+    typedef GrBufferAllocPool INHERITED;
+};
+
+class GrIndexBuffer;
+
+/**
+ * A GrBufferAllocPool of index buffers
+ */
+class GrIndexBufferAllocPool : public GrBufferAllocPool {
+public:
+    /**
+     * Constructor
+     *
+     * @param gpu                   The GrGpu used to create the index buffers.
+     * @param frequentResetHint     A hint that indicates that the pool
+     *                              should expect frequent unlock() calls
+     *                              (as opposed to many makeSpace / acquires
+     *                              between resets).
+     * @param bufferSize            The minimum size of created IBs This value
+     *                              will be clamped to some reasonable minimum.
+     * @param preallocBufferCnt     The pool will allocate this number of VBs at
+     *                              bufferSize and keep them until it is
+     *                              destroyed.
+     */
+    GrIndexBufferAllocPool(GrGpu* gpu,
+                           bool frequentResetHint,
+                           size_t bufferSize = 0,
+                           int preallocBufferCnt = 0);
+
+    /**
+     * Returns a block of memory to hold indices. A buffer designated to hold
+     * the indices is given to the caller. The buffer may or may not be locked.
+     * The returned ptr remains valid until any of the following:
+     *      *makeSpace is called again.
+     *      *unlock is called.
+     *      *reset is called.
+     *      *this object is destroyed.
+     *
+     * Once unlock on the pool is called the indices are guaranteed to be in the
+     * buffer at the offset indicated by startIndex. Until that time they may be
+     * in temporary storage and/or the buffer may be locked.
+     *
+     * @param indexCount   number of indices to allocate space for
+     * @param buffer       returns the index buffer that will hold the indices.
+     * @param startIndex   returns the offset into buffer of the first index.
+     * @return pointer to first index.
+     */
+    void* makeSpace(int indexCount,
+                    const GrIndexBuffer** buffer,
+                    int* startIndex);
+
+    /**
+     * Shortcut to make space and then write indices into the made space.
+     */
+    bool appendIndices(int indexCount,
+                       const void* indices,
+                       const GrIndexBuffer** buffer,
+                       int* startIndex);
+
+    /**
+     * Gets the number of indices that can be added to the current IB without
+     * spilling to another IB. If the pool has been reset, or the previous
+     * makeSpace completely exhausted a IB then the returned number of indices
+     * would fit in the next available preallocated buffer. If any makeSpace
+     * would force a new IB to be created the return value will be zero.
+     */
+    int currentBufferIndices() const;
+
+    /**
+     * Gets the number of indices that can fit in a preallocated index buffer.
+     * Zero if no preallocated buffers.
+     *
+     * @return number of indices that fit in one of the preallocated index
+     *         buffers.
+     */
+    int preallocatedBufferIndices() const;
+
+private:
+    typedef GrBufferAllocPool INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrCacheID.cpp b/src/gpu/GrCacheID.cpp
new file mode 100644
index 0000000..4c6dd49
--- /dev/null
+++ b/src/gpu/GrCacheID.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrCacheID.h"
+#include "SkThread.h"       // for sk_atomic_inc
+
+uint8_t GrCacheID::GetNextDomain() {
+    // 0 reserved for kUnrestricted_ResourceDomain
+    static int32_t gNextDomain = 1;
+
+    int32_t domain = sk_atomic_inc(&gNextDomain);
+    if (domain >= 256) {
+        GrCrash("Too many Cache Domains");
+    }
+
+    return (uint8_t) domain;
+}
+
+uint8_t GrCacheID::GetNextResourceType() {
+    // 0 reserved for kInvalid_ResourceType
+    static int32_t gNextResourceType = 1;
+
+    int32_t type = sk_atomic_inc(&gNextResourceType);
+    if (type >= 256) {
+        GrCrash("Too many Cache Resource Types");
+    }
+
+    return (uint8_t) type;
+}
+
+void GrCacheID::toRaw(uint32_t v[4]) {
+    GrAssert(4*sizeof(uint32_t) == sizeof(GrCacheID));
+
+    v[0] = (uint32_t) (fPublicID & 0xffffffffUL);
+    v[1] = (uint32_t) ((fPublicID >> 32) & 0xffffffffUL);
+    v[2] = fResourceSpecific32;
+    v[3] = fDomain << 24 |
+           fResourceType << 16 |
+           fResourceSpecific16;
+}
diff --git a/src/gpu/GrClipData.cpp b/src/gpu/GrClipData.cpp
new file mode 100644
index 0000000..7c84c18
--- /dev/null
+++ b/src/gpu/GrClipData.cpp
@@ -0,0 +1,35 @@
+
+/*
+ * 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..b7cfb65
--- /dev/null
+++ b/src/gpu/GrClipMaskCache.cpp
@@ -0,0 +1,22 @@
+
+/*
+ * Copyright 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..6722d70
--- /dev/null
+++ b/src/gpu/GrClipMaskCache.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright 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(const SkClipStack& clip, const GrIRect& devBounds) {
+
+        if (fStack.empty()) {
+            GrAssert(false);
+            return false;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        if (back->fLastMask.texture() &&
+            back->fLastBound == devBounds &&
+            clip == back->fLastClip) {
+            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();
+        }
+    }
+
+    void getLastClip(SkClipStack* clip) const {
+
+        if (fStack.empty()) {
+            GrAssert(false);
+            clip->reset();
+            return;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        *clip = back->fLastClip;
+    }
+
+    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(const SkClipStack& clip,
+                     const GrTextureDesc& desc,
+                     const GrIRect& bound) {
+
+        if (fStack.empty()) {
+            GrAssert(false);
+            return;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        back->acquireMask(fContext, clip, 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();
+        }
+    }
+
+protected:
+private:
+    struct GrClipStackFrame {
+
+        GrClipStackFrame() {
+            reset();
+        }
+
+        void acquireMask(GrContext* context,
+                         const SkClipStack& clip,
+                         const GrTextureDesc& desc,
+                         const GrIRect& bound) {
+
+            fLastClip = clip;
+
+            fLastMask.set(context, desc);
+
+            fLastBound = bound;
+        }
+
+        void reset () {
+            fLastClip.reset();
+
+            GrTextureDesc desc;
+
+            fLastMask.set(NULL, desc);
+            fLastBound.setEmpty();
+        }
+
+        SkClipStack             fLastClip;
+        // The mask's width & height values are used in setupDrawStateAAClip to
+        // correctly scale the uvs for geometry drawn with this mask
+        GrAutoScratchTexture    fLastMask;
+        // fLastBound stores the bounding box of the clip mask in canvas
+        // space. The left and top fields are used to offset the uvs for
+        // geometry drawn with this mask (in setupDrawStateAAClip)
+        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..6104f66
--- /dev/null
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -0,0 +1,1221 @@
+
+/*
+ * Copyright 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 "GrGpu.h"
+#include "GrRenderTarget.h"
+#include "GrStencilBuffer.h"
+#include "GrPathRenderer.h"
+#include "GrPaint.h"
+#include "SkRasterClip.h"
+#include "GrAAConvexPathRenderer.h"
+#include "GrAAHairLinePathRenderer.h"
+#include "GrSWMaskHelper.h"
+#include "GrCacheID.h"
+
+GR_DEFINE_RESOURCE_CACHE_DOMAIN(GrClipMaskManager, GetAlphaMaskDomain)
+
+#define GR_AA_CLIP 1
+#define GR_SW_CLIP 1
+
+////////////////////////////////////////////////////////////////////////////////
+namespace {
+// set up the draw state to enable the aa clipping mask. Besides setting up the
+// sampler 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;
+
+    GrMatrix mat;
+    mat.setIDiv(result->width(), result->height());
+    mat.preTranslate(SkIntToScalar(-devBound.fLeft),
+                     SkIntToScalar(-devBound.fTop));
+    mat.preConcat(drawState->getViewMatrix());
+
+    drawState->sampler(kMaskStage)->reset();
+    drawState->createTextureEffect(kMaskStage, result, mat);
+}
+
+bool path_needs_SW_renderer(GrContext* context,
+                            GrGpu* gpu,
+                            const SkPath& path,
+                            GrPathFill fill,
+                            bool doAA) {
+    // last (false) parameter disallows use of the SW path renderer
+    return NULL == context->getPathRenderer(path, fill, gpu, doAA, false);
+}
+
+GrPathFill get_path_fill(const SkPath& path) {
+    switch (path.getFillType()) {
+        case SkPath::kWinding_FillType:
+            return kWinding_GrPathFill;
+        case SkPath::kEvenOdd_FillType:
+            return  kEvenOdd_GrPathFill;
+        case SkPath::kInverseWinding_FillType:
+            return kInverseWinding_GrPathFill;
+        case SkPath::kInverseEvenOdd_FillType:
+            return kInverseEvenOdd_GrPathFill;
+        default:
+            GrCrash("Unsupported path fill in clip.");
+            return kWinding_GrPathFill; // suppress warning
+    }
+}
+
+/**
+ * Does any individual clip in 'clipIn' use anti-aliasing?
+ */
+bool requires_AA(const SkClipStack& clipIn) {
+
+    SkClipStack::Iter iter;
+    iter.reset(clipIn, SkClipStack::Iter::kBottom_IterStart);
+
+    const SkClipStack::Iter::Clip* clip = NULL;
+    for (clip = iter.skipToTopmost(SkRegion::kReplace_Op);
+         NULL != clip;
+         clip = iter.next()) {
+
+        if (clip->fDoAA) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+}
+
+/*
+ * 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 SkClipStack& clipIn) {
+
+    // 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.
+    bool useSW = false;
+
+    SkClipStack::Iter iter(clipIn, SkClipStack::Iter::kBottom_IterStart);
+    const SkClipStack::Iter::Clip* clip = NULL;
+
+    for (clip = iter.skipToTopmost(SkRegion::kReplace_Op);
+         NULL != clip;
+         clip = iter.next()) {
+
+        if (SkRegion::kReplace_Op == clip->fOp) {
+            // Everything before a replace op can be ignored so start
+            // afresh w.r.t. determining if any element uses the SW path
+            useSW = false;
+        }
+
+        // rects can always be drawn directly w/o using the software path
+        // so only paths need to be checked
+        if (NULL != clip->fPath &&
+            path_needs_SW_renderer(this->getContext(), fGpu,
+                                   *clip->fPath,
+                                   get_path_fill(*clip->fPath),
+                                   clip->fDoAA)) {
+            useSW = true;
+        }
+    }
+
+    return useSW;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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;
+
+    GrDrawState* drawState = fGpu->drawState();
+    if (!drawState->isClipState() || clipDataIn->fClipStack->isWideOpen()) {
+        fGpu->disableScissor();
+        this->setGpuStencil();
+        return true;
+    }
+
+    GrRenderTarget* rt = drawState->getRenderTarget();
+    // GrDrawTarget should have filtered this for us
+    GrAssert(NULL != rt);
+
+    GrIRect devClipBounds;
+    bool isIntersectionOfRects = false;
+
+    clipDataIn->getConservativeBounds(rt, &devClipBounds,
+                                      &isIntersectionOfRects);
+    if (devClipBounds.isEmpty()) {
+        return false;
+    }
+
+#if GR_SW_CLIP
+    bool requiresAA = requires_AA(*clipDataIn->fClipStack);
+
+    // If MSAA is enabled we can do everything in the stencil buffer.
+    // Otherwise check if we should just create the entire clip mask
+    // in software (this will only happen if the clip mask is anti-aliased
+    // and too complex for the gpu to handle in its entirety)
+    if (0 == rt->numSamples() &&
+        requiresAA &&
+        this->useSWOnlyPath(*clipDataIn->fClipStack)) {
+        // The clip geometry is complex enough that it will be more
+        // efficient to create it entirely in software
+        GrTexture* result = NULL;
+        GrIRect devBound;
+        if (this->createSoftwareClipMask(*clipDataIn, &result, &devBound)) {
+            setup_drawstate_aaclip(fGpu, result, devBound);
+            fGpu->disableScissor();
+            this->setGpuStencil();
+            return true;
+        }
+
+        // if SW clip mask creation fails fall through to the other
+        // two possible methods (bottoming out at stencil clipping)
+    }
+#endif // GR_SW_CLIP
+
+#if GR_AA_CLIP
+    // If MSAA is enabled use the (faster) stencil path for AA clipping
+    // otherwise the alpha clip mask is our only option
+    if (0 == rt->numSamples() && requiresAA) {
+        // Since we are going to create a destination texture of the correct
+        // size for the mask (rather than being bound by the size of the
+        // render target) we aren't going to use scissoring like the stencil
+        // path does (see scissorSettings below)
+        GrTexture* result = NULL;
+        GrIRect devBound;
+        if (this->createAlphaClipMask(*clipDataIn, &result, &devBound)) {
+            setup_drawstate_aaclip(fGpu, result, devBound);
+            fGpu->disableScissor();
+            this->setGpuStencil();
+            return true;
+        }
+
+        // if alpha clip mask creation fails fall through to the stencil
+        // buffer method
+    }
+#endif // GR_AA_CLIP
+
+    // Either a hard (stencil buffer) clip was explicitly requested or
+    // an antialiased clip couldn't be created. In either case, free up
+    // the texture in the antialiased 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 (isIntersectionOfRects) {
+        fGpu->enableScissor(devClipBounds);
+        this->setGpuStencil();
+        return true;
+    }
+
+    // use the stencil clip if we can't represent the clip as a rectangle.
+    bool useStencil = !clipDataIn->fClipStack->isWideOpen() &&
+                      !devClipBounds.isEmpty();
+
+    if (useStencil) {
+        this->createStencilClipMask(*clipDataIn, devClipBounds);
+    }
+    // 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.
+    fGpu->enableScissor(devClipBounds);
+    this->setGpuStencil();
+    return true;
+}
+
+#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 {
+/**
+ * Does "canvContainer" contain "devContainee"? If either is empty then
+ * no containment is possible. "canvContainer" is in canvas coordinates while
+ * "devContainee" is in device coordiates. "origin" provides the mapping between
+ * the two.
+ */
+bool contains(const SkRect& canvContainer,
+              const SkIRect& devContainee,
+              const SkIPoint& origin) {
+    return  !devContainee.isEmpty() && !canvContainer.isEmpty() &&
+            canvContainer.fLeft <= SkIntToScalar(devContainee.fLeft+origin.fX) &&
+            canvContainer.fTop <= SkIntToScalar(devContainee.fTop+origin.fY) &&
+            canvContainer.fRight >= SkIntToScalar(devContainee.fRight+origin.fX) &&
+            canvContainer.fBottom >= SkIntToScalar(devContainee.fBottom+origin.fY);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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.
+const SkClipStack::Iter::Clip* process_initial_clip_elements(
+                                  SkClipStack::Iter* iter,
+                                  const GrIRect& devBounds,
+                                  bool* clearToInside,
+                                  SkRegion::Op* firstOp,
+                                  const GrClipData& clipData) {
+
+    GrAssert(NULL != iter && NULL != clearToInside && NULL != firstOp);
+
+    // 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.
+    bool done = false;
+    *clearToInside = true;
+
+    const SkClipStack::Iter::Clip* clip = NULL;
+
+    for (clip = iter->skipToTopmost(SkRegion::kReplace_Op);
+         NULL != clip && !done;
+         clip = iter->next()) {
+        switch (clip->fOp) {
+            case SkRegion::kReplace_Op:
+                // replace ignores everything previous
+                *firstOp = SkRegion::kReplace_Op;
+                *clearToInside = false;
+                done = true;
+                break;
+            case SkRegion::kIntersect_Op:
+                // if this element contains the entire bounds then we
+                // can skip it.
+                if (NULL != clip->fRect &&
+                    contains(*clip->fRect, devBounds, clipData.fOrigin)) {
+                    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) {
+                    *firstOp = SkRegion::kReplace_Op;
+                    *clearToInside = false;
+                    done = true;
+                }
+                break;
+                // we can skip a leading union.
+            case SkRegion::kUnion_Op:
+                // if everything is initially outside then union is
+                // same as replace. Otherwise, every pixel is still
+                // clearToInside
+                if (!*clearToInside) {
+                    *firstOp = SkRegion::kReplace_Op;
+                    done = true;
+                }
+                break;
+            case SkRegion::kXOR_Op:
+                // xor is same as difference or replace both of which
+                // can be 1-pass instead of 2 for xor.
+                if (*clearToInside) {
+                    *firstOp = SkRegion::kDifference_Op;
+                } else {
+                    *firstOp = SkRegion::kReplace_Op;
+                }
+                done = true;
+                break;
+            case SkRegion::kDifference_Op:
+                // if all pixels are clearToInside then we have to process the
+                // difference, otherwise it has no effect and all pixels
+                // remain outside.
+                if (*clearToInside) {
+                    *firstOp = SkRegion::kDifference_Op;
+                    done = true;
+                }
+                break;
+            case SkRegion::kReverseDifference_Op:
+                // if all pixels are clearToInside then reverse difference
+                // produces empty set. Otherise it is same as replace
+                if (*clearToInside) {
+                    *clearToInside = false;
+                } else {
+                    *firstOp = SkRegion::kReplace_Op;
+                    done = true;
+                }
+                break;
+            default:
+                GrCrash("Unknown set op.");
+        }
+
+        if (done) {
+            // we need to break out here (rather than letting the test in
+            // the loop do it) since backing up the iterator is very expensive
+            break;
+        }
+    }
+    return clip;
+}
+
+}
+
+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 draw_path_in_software(GrContext* context,
+                           GrGpu* gpu,
+                           const SkPath& path,
+                           GrPathFill fill,
+                           bool doAA,
+                           const GrIRect& resultBounds) {
+
+    SkAutoTUnref<GrTexture> texture(
+                GrSWMaskHelper::DrawPathMaskToTexture(context, path,
+                                                      resultBounds, fill,
+                                                      doAA, NULL));
+    if (NULL == texture) {
+        return false;
+    }
+
+    // The ClipMaskManager accumulates the clip mask in the UL corner
+    GrIRect rect = GrIRect::MakeWH(resultBounds.width(), resultBounds.height());
+
+    GrSWMaskHelper::DrawToTargetWithPathMask(texture, gpu, rect);
+
+    GrAssert(!GrIsFillInverted(fill));
+    return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+bool draw_path(GrContext* context,
+               GrGpu* gpu,
+               const SkPath& path,
+               GrPathFill fill,
+               bool doAA,
+               const GrIRect& resultBounds) {
+
+    GrPathRenderer* pr = context->getPathRenderer(path, fill, gpu, doAA, false);
+    if (NULL == pr) {
+        return draw_path_in_software(context, gpu, path, fill, doAA, resultBounds);
+    }
+
+    pr->drawPath(path, fill, gpu, doAA);
+    return true;
+}
+
+// 'rect' enters in device coordinates and leaves in canvas coordinates
+void device_to_canvas(SkRect* rect, const SkIPoint& origin) {
+    GrAssert(NULL != rect);
+
+    rect->fLeft   += SkIntToScalar(origin.fX);
+    rect->fTop    += SkIntToScalar(origin.fY);
+    rect->fRight  += SkIntToScalar(origin.fX);
+    rect->fBottom += SkIntToScalar(origin.fY);
+}
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+bool GrClipMaskManager::drawClipShape(GrTexture* target,
+                                      const SkClipStack::Iter::Clip* clip,
+                                      const GrIRect& resultBounds) {
+    GrDrawState* drawState = fGpu->drawState();
+    GrAssert(NULL != drawState);
+
+    drawState->setRenderTarget(target->asRenderTarget());
+
+    if (NULL != clip->fRect) {
+        if (clip->fDoAA) {
+            getContext()->getAARectRenderer()->fillAARect(fGpu, fGpu,
+                                                          *clip->fRect,
+                                                          true);
+        } else {
+            fGpu->drawSimpleRect(*clip->fRect, NULL);
+        }
+    } else if (NULL != clip->fPath) {
+        return draw_path(this->getContext(), fGpu,
+                         *clip->fPath,
+                         get_path_fill(*clip->fPath),
+                         clip->fDoAA,
+                         resultBounds);
+    }
+    return true;
+}
+
+void GrClipMaskManager::drawTexture(GrTexture* target,
+                                    GrTexture* texture) {
+    GrDrawState* drawState = fGpu->drawState();
+    GrAssert(NULL != drawState);
+
+    // no AA here since it is encoded in the texture
+    drawState->setRenderTarget(target->asRenderTarget());
+
+    GrMatrix sampleM;
+    sampleM.setIDiv(texture->width(), texture->height());
+
+    drawState->createTextureEffect(0, texture, sampleM);
+
+    GrRect rect = GrRect::MakeWH(SkIntToScalar(target->width()),
+                                 SkIntToScalar(target->height()));
+
+    fGpu->drawSimpleRect(rect, NULL);
+
+    drawState->disableStage(0);
+}
+
+// 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(const GrIRect& bounds,
+                                GrAutoScratchTexture* temp) {
+    if (NULL != temp->texture()) {
+        // we've already allocated the temp texture
+        return;
+    }
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
+    desc.fWidth = bounds.width();
+    desc.fHeight = bounds.height();
+    desc.fConfig = kAlpha_8_GrPixelConfig;
+
+    temp->set(this->getContext(), desc);
+}
+
+
+void GrClipMaskManager::setupCache(const SkClipStack& clipIn,
+                                   const GrIRect& bounds) {
+    // 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|kNoStencil_GrTextureFlagBit;
+    desc.fWidth = bounds.width();
+    desc.fHeight = bounds.height();
+    desc.fConfig = kAlpha_8_GrPixelConfig;
+
+    fAACache.acquireMask(clipIn, desc, bounds);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Shared preamble between gpu and SW-only AA clip mask creation paths.
+// Handles caching, determination of clip mask bound & allocation (if needed)
+// of the result texture
+// Returns true if there is no more work to be done (i.e., we got a cache hit)
+bool GrClipMaskManager::clipMaskPreamble(const GrClipData& clipDataIn,
+                                         GrTexture** result,
+                                         GrIRect* devResultBounds) {
+    GrDrawState* origDrawState = fGpu->drawState();
+    GrAssert(origDrawState->isClipState());
+
+    GrRenderTarget* rt = origDrawState->getRenderTarget();
+    GrAssert(NULL != rt);
+
+    // unlike the stencil path the alpha path is not bound to the size of the
+    // render target - determine the minimum size required for the mask
+    // Note: intBounds is in device (as opposed to canvas) coordinates
+    clipDataIn.getConservativeBounds(rt, devResultBounds);
+
+    // need to outset a pixel since the standard bounding box computation
+    // path doesn't leave any room for antialiasing (esp. w.r.t. rects)
+    devResultBounds->outset(1, 1);
+
+    // TODO: make sure we don't outset if bounds are still 0,0 @ min
+
+    if (fAACache.canReuse(*clipDataIn.fClipStack, *devResultBounds)) {
+        *result = fAACache.getLastMask();
+        fAACache.getLastBound(devResultBounds);
+        return true;
+    }
+
+    this->setupCache(*clipDataIn.fClipStack, *devResultBounds);
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Create a 8-bit clip mask in alpha
+bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn,
+                                            GrTexture** result,
+                                            GrIRect *devResultBounds) {
+    GrAssert(NULL != devResultBounds);
+    GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
+
+    if (this->clipMaskPreamble(clipDataIn, result, devResultBounds)) {
+        fCurrClipMaskType = kAlpha_ClipMaskType;
+        return true;
+    }
+
+    // Note: 'resultBounds' is in device (as opposed to canvas) coordinates
+
+    GrTexture* accum = fAACache.getLastMask();
+    if (NULL == accum) {
+        fAACache.reset();
+        return false;
+    }
+
+    GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
+    GrDrawState* drawState = fGpu->drawState();
+
+    GrDrawTarget::AutoGeometryPush agp(fGpu);
+
+    if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft ||
+        0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) {
+        // if we were able to trim down the size of the mask we need to
+        // offset the paths & rects that will be used to compute it
+        drawState->viewMatrix()->setTranslate(
+                SkIntToScalar(-devResultBounds->fLeft-clipDataIn.fOrigin.fX),
+                SkIntToScalar(-devResultBounds->fTop-clipDataIn.fOrigin.fY));
+    }
+
+    bool clearToInside;
+    SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning
+
+    SkClipStack::Iter iter(*clipDataIn.fClipStack,
+                           SkClipStack::Iter::kBottom_IterStart);
+    const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter,
+                                                              *devResultBounds,
+                                                              &clearToInside,
+                                                              &firstOp,
+                                                              clipDataIn);
+
+    fGpu->clear(NULL,
+                clearToInside ? 0xffffffff : 0x00000000,
+                accum->asRenderTarget());
+
+    GrAutoScratchTexture temp;
+    bool first = true;
+    // walk through each clip element and perform its set op
+    for ( ; NULL != clip; clip = iter.next()) {
+
+        SkRegion::Op op = clip->fOp;
+        if (first) {
+            first = false;
+            op = firstOp;
+        }
+
+        if (SkRegion::kReplace_Op == op) {
+            // clear the accumulator and draw the new object directly into it
+            fGpu->clear(NULL, 0x00000000, accum->asRenderTarget());
+
+            setup_boolean_blendcoeffs(drawState, op);
+            this->drawClipShape(accum, clip, *devResultBounds);
+
+        } else if (SkRegion::kReverseDifference_Op == op ||
+                   SkRegion::kIntersect_Op == op) {
+            // there is no point in intersecting a screen filling rectangle.
+            if (SkRegion::kIntersect_Op == op && NULL != clip->fRect &&
+                contains(*clip->fRect, *devResultBounds, clipDataIn.fOrigin)) {
+                continue;
+            }
+
+            getTemp(*devResultBounds, &temp);
+            if (NULL == temp.texture()) {
+                fAACache.reset();
+                return false;
+            }
+
+            // clear the temp target & draw into it
+            fGpu->clear(NULL, 0x00000000, temp.texture()->asRenderTarget());
+
+            setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
+            this->drawClipShape(temp.texture(), clip, *devResultBounds);
+
+            // TODO: rather than adding these two translations here
+            // compute the bounding box needed to render the texture
+            // into temp
+            if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft ||
+                0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) {
+                // In order for the merge of the temp clip into the accumulator
+                // to work we need to disable the translation
+                drawState->viewMatrix()->reset();
+            }
+
+            // Now draw into the accumulator using the real operation
+            // and the temp buffer as a texture
+            setup_boolean_blendcoeffs(drawState, op);
+            this->drawTexture(accum, temp.texture());
+
+            if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft ||
+                0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) {
+                drawState->viewMatrix()->setTranslate(
+                  SkIntToScalar(-devResultBounds->fLeft-clipDataIn.fOrigin.fX),
+                  SkIntToScalar(-devResultBounds->fTop-clipDataIn.fOrigin.fY));
+            }
+
+        } else {
+            // all the remaining ops can just be directly draw into
+            // the accumulation buffer
+            setup_boolean_blendcoeffs(drawState, op);
+            this->drawClipShape(accum, clip, *devResultBounds);
+        }
+    }
+
+    *result = accum;
+    fCurrClipMaskType = kAlpha_ClipMaskType;
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
+// (as opposed to canvas) coordinates
+bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn,
+                                              const GrIRect& devClipBounds) {
+
+    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;
+    }
+
+    if (stencilBuffer->mustRenderClip(clipDataIn, rt->width(), rt->height())) {
+
+        stencilBuffer->setLastClip(clipDataIn, 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 GrClipData* oldClipData = fGpu->getClip();
+
+        // The origin of 'newClipData' is (0, 0) so it is okay to place
+        // a device-coordinate bound in 'newClipStack'
+        SkClipStack newClipStack(devClipBounds);
+        GrClipData newClipData;
+        newClipData.fClipStack = &newClipStack;
+
+        fGpu->setClip(&newClipData);
+
+        GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
+        drawState = fGpu->drawState();
+        drawState->setRenderTarget(rt);
+        GrDrawTarget::AutoGeometryPush agp(fGpu);
+
+        if (0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) {
+            // Add the saveLayer's offset to the view matrix rather than
+            // offset each individual draw
+            drawState->viewMatrix()->setTranslate(
+                           SkIntToScalar(-clipDataIn.fOrigin.fX),
+                           SkIntToScalar(-clipDataIn.fOrigin.fY));
+        }
+
+#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));
+
+        GrIRect devRTRect = GrIRect::MakeWH(rt->width(), rt->height());
+
+        bool clearToInside;
+        SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning
+
+        SkClipStack::Iter iter(*oldClipData->fClipStack,
+                               SkClipStack::Iter::kBottom_IterStart);
+        const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter,
+                                                  devRTRect,
+                                                  &clearToInside,
+                                                  &firstOp,
+                                                  clipDataIn);
+
+        fGpu->clearStencilClip(devClipBounds, clearToInside);
+        bool first = true;
+
+        // walk through each clip element and perform its set op
+        // with the existing clip.
+        for ( ; NULL != clip; clip = iter.next()) {
+            GrPathFill fill;
+            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, clip->fDoAA);
+            }
+
+            // 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?
+            bool canRenderDirectToStencil = false;
+
+            SkRegion::Op op = clip->fOp;
+            if (first) {
+                first = false;
+                op = firstOp;
+            }
+
+            GrPathRenderer* pr = NULL;
+            const SkPath* clipPath = NULL;
+            if (NULL != clip->fRect) {
+                canRenderDirectToStencil = true;
+                fill = kEvenOdd_GrPathFill;
+                fillInverted = false;
+                // there is no point in intersecting a screen filling
+                // rectangle.
+                if (SkRegion::kIntersect_Op == op &&
+                    contains(*clip->fRect, devRTRect, oldClipData->fOrigin)) {
+                    continue;
+                }
+            } else {
+                GrAssert(NULL != clip->fPath);
+                fill = get_path_fill(*clip->fPath);
+                fillInverted = GrIsFillInverted(fill);
+                fill = GrNonInvertedFill(fill);
+                clipPath = clip->fPath;
+                pr = this->getContext()->getPathRenderer(*clipPath, fill, fGpu, false, true);
+                if (NULL == pr) {
+                    fGpu->setClip(oldClipData);
+                    return false;
+                }
+                canRenderDirectToStencil =
+                    !pr->requiresStencilPass(*clipPath, fill, fGpu);
+            }
+
+            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 (NULL != clip->fRect) {
+                    *drawState->stencil() = gDrawToStencil;
+                    fGpu->drawSimpleRect(*clip->fRect, NULL);
+                } else {
+                    if (canRenderDirectToStencil) {
+                        *drawState->stencil() = gDrawToStencil;
+                        pr->drawPath(*clipPath, fill, fGpu, false);
+                    } else {
+                        pr->drawPathToStencil(*clipPath, fill, 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 (NULL != clip->fRect) {
+                        SET_RANDOM_COLOR
+                        fGpu->drawSimpleRect(*clip->fRect, NULL);
+                    } else {
+                        SET_RANDOM_COLOR
+                        pr->drawPath(*clipPath, fill, fGpu, false);
+                    }
+                } else {
+                    SET_RANDOM_COLOR
+                    // 'devClipBounds' is already in device coordinates so the
+                    // translation in the view matrix is inappropriate.
+                    // Convert it to canvas space so the drawn rect will
+                    // be in the correct location
+                    GrRect canvClipBounds;
+                    canvClipBounds.set(devClipBounds);
+                    device_to_canvas(&canvClipBounds, clipDataIn.fOrigin);
+                    fGpu->drawSimpleRect(canvClipBounds, NULL);
+                }
+            }
+        }
+        // restore clip
+        fGpu->setClip(oldClipData);
+    }
+    // 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();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+GrPathFill invert_fill(GrPathFill fill) {
+    static const GrPathFill gInvertedFillTable[] = {
+        kInverseWinding_GrPathFill, // kWinding_GrPathFill
+        kInverseEvenOdd_GrPathFill, // kEvenOdd_GrPathFill
+        kWinding_GrPathFill,        // kInverseWinding_GrPathFill
+        kEvenOdd_GrPathFill,        // kInverseEvenOdd_GrPathFill
+        kHairLine_GrPathFill,       // kHairLine_GrPathFill
+    };
+    GR_STATIC_ASSERT(0 == kWinding_GrPathFill);
+    GR_STATIC_ASSERT(1 == kEvenOdd_GrPathFill);
+    GR_STATIC_ASSERT(2 == kInverseWinding_GrPathFill);
+    GR_STATIC_ASSERT(3 == kInverseEvenOdd_GrPathFill);
+    GR_STATIC_ASSERT(4 == kHairLine_GrPathFill);
+    GR_STATIC_ASSERT(5 == kGrPathFillCount);
+    return gInvertedFillTable[fill];
+}
+
+}
+
+bool GrClipMaskManager::createSoftwareClipMask(const GrClipData& clipDataIn,
+                                               GrTexture** result,
+                                               GrIRect* devResultBounds) {
+    GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
+
+    if (this->clipMaskPreamble(clipDataIn, result, devResultBounds)) {
+        return true;
+    }
+
+    GrTexture* accum = fAACache.getLastMask();
+    if (NULL == accum) {
+        fAACache.reset();
+        return false;
+    }
+
+    GrSWMaskHelper helper(this->getContext());
+
+    GrMatrix matrix;
+    matrix.setTranslate(SkIntToScalar(-clipDataIn.fOrigin.fX),
+                        SkIntToScalar(-clipDataIn.fOrigin.fY));
+    helper.init(*devResultBounds, &matrix);
+
+    bool clearToInside;
+    SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning
+
+    SkClipStack::Iter iter(*clipDataIn.fClipStack,
+                           SkClipStack::Iter::kBottom_IterStart);
+    const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter,
+                                              *devResultBounds,
+                                              &clearToInside,
+                                              &firstOp,
+                                              clipDataIn);
+
+    helper.clear(clearToInside ? 0xFF : 0x00);
+
+    bool first = true;
+    for ( ; NULL != clip; clip = iter.next()) {
+
+        SkRegion::Op op = clip->fOp;
+        if (first) {
+            first = false;
+            op = firstOp;
+        }
+
+        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;
+                temp.set(*devResultBounds);
+                temp.offset(SkIntToScalar(clipDataIn.fOrigin.fX),
+                            SkIntToScalar(clipDataIn.fOrigin.fX));
+
+                // invert the entire scene
+                helper.draw(temp, SkRegion::kXOR_Op, false, 0xFF);
+            }
+
+            if (NULL != clip->fRect) {
+
+                // convert the rect to a path so we can invert the fill
+                SkPath temp;
+                temp.addRect(*clip->fRect);
+
+                helper.draw(temp, SkRegion::kReplace_Op,
+                            kInverseEvenOdd_GrPathFill, clip->fDoAA,
+                            0x00);
+            } else if (NULL != clip->fPath) {
+                helper.draw(*clip->fPath,
+                            SkRegion::kReplace_Op,
+                            invert_fill(get_path_fill(*clip->fPath)),
+                            clip->fDoAA,
+                            0x00);
+            }
+
+            continue;
+        }
+
+        // The other ops (union, xor, diff) only affect pixels inside
+        // the geometry so they can just be drawn normally
+        if (NULL != clip->fRect) {
+
+            helper.draw(*clip->fRect,
+                        op,
+                        clip->fDoAA, 0xFF);
+
+        } else if (NULL != clip->fPath) {
+            helper.draw(*clip->fPath,
+                        op,
+                        get_path_fill(*clip->fPath),
+                        clip->fDoAA, 0xFF);
+        }
+    }
+
+    // Because we are using the scratch texture cache, "accum" may be
+    // larger than expected and have some cruft in the areas we aren't using.
+    // Clear it out.
+    fGpu->clear(NULL, 0x00000000, accum->asRenderTarget());
+
+    helper.toTexture(accum, clearToInside ? 0xFF : 0x00);
+
+    *result = accum;
+
+    fCurrClipMaskType = kAlpha_ClipMaskType;
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void GrClipMaskManager::releaseResources() {
+    fAACache.releaseResources();
+}
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
new file mode 100644
index 0000000..d23a525
--- /dev/null
+++ b/src/gpu/GrClipMaskManager.h
@@ -0,0 +1,157 @@
+
+/*
+ * Copyright 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 "GrStencil.h"
+#include "GrTexture.h"
+
+#include "SkClipStack.h"
+#include "SkDeque.h"
+#include "SkPath.h"
+#include "SkRefCnt.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:
+    GR_DECLARE_RESOURCE_CACHE_DOMAIN(GetAlphaMaskDomain)
+
+    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
+
+    bool createStencilClipMask(const GrClipData& clipDataIn,
+                               const GrIRect& devClipBounds);
+    bool createAlphaClipMask(const GrClipData& clipDataIn,
+                             GrTexture** result,
+                             GrIRect *devResultBounds);
+    bool createSoftwareClipMask(const GrClipData& clipDataIn,
+                                GrTexture** result,
+                                GrIRect *devResultBounds);
+    bool clipMaskPreamble(const GrClipData& clipDataIn,
+                          GrTexture** result,
+                          GrIRect *devResultBounds);
+
+    bool useSWOnlyPath(const SkClipStack& clipIn);
+
+    bool drawClipShape(GrTexture* target,
+                       const SkClipStack::Iter::Clip* clip,
+                       const GrIRect& resultBounds);
+
+    void drawTexture(GrTexture* target,
+                     GrTexture* texture);
+
+    void getTemp(const GrIRect& bounds, 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
new file mode 100644
index 0000000..8b7d2d0
--- /dev/null
+++ b/src/gpu/GrContext.cpp
@@ -0,0 +1,1901 @@
+
+/*
+ * Copyright 2011 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 "effects/GrConvolutionEffect.h"
+#include "effects/GrSingleTextureEffect.h"
+#include "effects/GrConfigConversionEffect.h"
+
+#include "GrBufferAllocPool.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 "SkTLazy.h"
+#include "SkTLS.h"
+#include "SkTrace.h"
+
+SK_DEFINE_INST_COUNT(GrContext)
+SK_DEFINE_INST_COUNT(GrDrawState)
+
+// 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
+
+#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 MAX_TEXTURE_CACHE_COUNT = 256;
+static const size_t MAX_TEXTURE_CACHE_BYTES = 16 * 1024 * 1024;
+
+static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 15;
+static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4;
+
+static const size_t DRAW_BUFFER_IBPOOL_BUFFER_SIZE = 1 << 11;
+static const int DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS = 4;
+
+#define ASSERT_OWNED_RESOURCE(R) GrAssert(!(R) || (R)->getContext() == this)
+
+GrContext* GrContext::Create(GrEngine engine,
+                             GrPlatform3DContext context3D) {
+    GrContext* ctx = NULL;
+    GrGpu* fGpu = GrGpu::Create(engine, context3D);
+    if (NULL != 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;
+
+    fAARectRenderer->unref();
+
+    fGpu->unref();
+    GrSafeUnref(fPathRendererChain);
+    GrSafeUnref(fSoftwarePathRenderer);
+    fDrawState->unref();
+
+    --THREAD_INSTANCE_COUNT;
+}
+
+void GrContext::contextLost() {
+    contextDestroyed();
+    this->setupDrawBuffer();
+}
+
+void GrContext::contextDestroyed() {
+    // abandon first to so destructors
+    // don't try to free the resources in the API.
+    fGpu->abandonResources();
+
+    // a path renderer may be holding onto resources that
+    // are now unusable
+    GrSafeSetNull(fPathRendererChain);
+    GrSafeSetNull(fSoftwarePathRenderer);
+
+    delete fDrawBuffer;
+    fDrawBuffer = NULL;
+
+    delete fDrawBufferVBAllocPool;
+    fDrawBufferVBAllocPool = NULL;
+
+    delete fDrawBufferIBAllocPool;
+    fDrawBufferIBAllocPool = NULL;
+
+    fAARectRenderer->reset();
+
+    fTextureCache->purgeAllUnlocked();
+    fFontCache->freeAll();
+    fGpu->markContextDirty();
+}
+
+void GrContext::resetContext() {
+    fGpu->markContextDirty();
+}
+
+void GrContext::freeGpuResources() {
+    this->flush();
+
+    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 {
+  return fTextureCache->getCachedResourceBytes();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+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);
+    GrMatrix sampleM;
+    sampleM.setIDiv(texture->width(), texture->height());
+    SkAutoTUnref<GrConvolutionEffect> conv(SkNEW_ARGS(GrConvolutionEffect,
+                                                      (texture, direction, radius,
+                                                       sigma)));
+    drawState->sampler(0)->setCustomStage(conv, sampleM);
+    target->drawSimpleRect(rect, NULL);
+}
+
+}
+
+
+GrTexture* GrContext::findTexture(const GrCacheKey& key) {
+    return static_cast<GrTexture*>(fTextureCache->find(key.key()));
+}
+
+GrTexture* GrContext::findTexture(const GrTextureDesc& desc,
+                                  const GrCacheData& cacheData,
+                                  const GrTextureParams* params) {
+    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheData, false);
+    GrResource* resource = fTextureCache->find(resourceKey);
+    return static_cast<GrTexture*>(resource);
+}
+
+bool GrContext::isTextureInCache(const GrTextureDesc& desc,
+                                 const GrCacheData& cacheData,
+                                 const GrTextureParams* params) const {
+    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheData, false);
+    return fTextureCache->hasKey(resourceKey);
+}
+
+void GrContext::addStencilBuffer(GrStencilBuffer* sb) {
+    ASSERT_OWNED_RESOURCE(sb);
+
+    GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(sb->width(),
+                                                            sb->height(),
+                                                            sb->numSamples());
+    fTextureCache->create(resourceKey, sb);
+}
+
+GrStencilBuffer* GrContext::findStencilBuffer(int width, int height,
+                                              int sampleCnt) {
+    GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(width,
+                                                            height,
+                                                            sampleCnt);
+    GrResource* resource = fTextureCache->find(resourceKey);
+    return static_cast<GrStencilBuffer*>(resource);
+}
+
+static void stretchImage(void* dst,
+                         int dstW,
+                         int dstH,
+                         void* src,
+                         int srcW,
+                         int srcH,
+                         int bpp) {
+    GrFixed dx = (srcW << 16) / dstW;
+    GrFixed dy = (srcH << 16) / dstH;
+
+    GrFixed y = dy >> 1;
+
+    int dstXLimit = dstW*bpp;
+    for (int j = 0; j < dstH; ++j) {
+        GrFixed x = dx >> 1;
+        void* srcRow = (uint8_t*)src + (y>>16)*srcW*bpp;
+        void* dstRow = (uint8_t*)dst + j*dstW*bpp;
+        for (int i = 0; i < dstXLimit; i += bpp) {
+            memcpy((uint8_t*) dstRow + i,
+                   (uint8_t*) srcRow + (x>>16)*bpp,
+                   bpp);
+            x += dx;
+        }
+        y += dy;
+    }
+}
+
+// The desired texture is NPOT and tiled but that isn't supported by
+// the current hardware. Resize the texture to be a POT
+GrTexture* GrContext::createResizedTexture(const GrTextureDesc& desc,
+                                           const GrCacheData& cacheData,
+                                           void* srcData,
+                                           size_t rowBytes,
+                                           bool needsFiltering) {
+    GrTexture* clampedTexture = this->findTexture(desc, cacheData, NULL);
+    if (NULL == clampedTexture) {
+        clampedTexture = this->createTexture(NULL, desc, cacheData, srcData, rowBytes);
+
+        GrAssert(NULL != clampedTexture);
+        if (NULL == clampedTexture) {
+            return NULL;
+        }
+    }
+
+    clampedTexture->ref();
+
+    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, GrMatrix::I(), params);
+
+        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_GrPrimitiveType,
+                                    0, 4);
+        }
+        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);
+    }
+
+    clampedTexture->unref();
+    return texture;
+}
+
+GrTexture* GrContext::createTexture(
+        const GrTextureParams* params,
+        const GrTextureDesc& desc,
+        const GrCacheData& cacheData,
+        void* srcData,
+        size_t rowBytes) {
+    SK_TRACE_EVENT0("GrContext::createAndLockTexture");
+
+#if GR_DUMP_TEXTURE_UPLOAD
+    GrPrintf("GrContext::createAndLockTexture [%d %d]\n", desc.fWidth, desc.fHeight);
+#endif
+
+    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheData, false);
+
+    SkAutoTUnref<GrTexture> texture;
+    if (GrTexture::NeedsResizing(resourceKey)) {
+        texture.reset(this->createResizedTexture(desc, cacheData,
+                                             srcData, rowBytes,
+                                             GrTexture::NeedsFiltering(resourceKey)));
+    } else {
+        texture.reset(fGpu->createTexture(desc, srcData, rowBytes));
+    }
+
+    if (NULL != texture) {
+        fTextureCache->create(resourceKey, texture);
+    }
+
+    return texture;
+}
+
+GrTexture* GrContext::lockScratchTexture(const GrTextureDesc& inDesc,
+                                         ScratchTexMatch match) {
+    GrTextureDesc desc = inDesc;
+    GrCacheData cacheData(GrCacheData::kScratch_CacheID);
+
+    GrAssert((desc.fFlags & kRenderTarget_GrTextureFlagBit) ||
+             !(desc.fFlags & kNoStencil_GrTextureFlagBit));
+
+    if (kExact_ScratchTexMatch != match) {
+        // 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));
+    }
+
+    GrResource* resource = NULL;
+    int origWidth = desc.fWidth;
+    int origHeight = desc.fHeight;
+    bool doubledW = false;
+    bool doubledH = false;
+
+    do {
+        GrResourceKey key = GrTexture::ComputeKey(fGpu, NULL, desc, cacheData, true);
+        resource = fTextureCache->find(key);
+        // if we miss, relax the fit of the flags...
+        // then try doubling width... then height.
+        if (NULL != resource || kExact_ScratchTexMatch == match) {
+            break;
+        }
+        // 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;
+            desc.fWidth *= 2;
+            doubledW = true;
+        } else if (!doubledH) {
+            desc.fFlags = inDesc.fFlags;
+            desc.fWidth = origWidth;
+            desc.fHeight *= 2;
+            doubledH = true;
+        } else {
+            break;
+        }
+
+    } while (true);
+
+    if (NULL == resource) {
+        desc.fFlags = inDesc.fFlags;
+        desc.fWidth = origWidth;
+        desc.fHeight = origHeight;
+        SkAutoTUnref<GrTexture> texture(fGpu->createTexture(desc, NULL, 0));
+        if (NULL != texture) {
+            GrResourceKey key = GrTexture::ComputeKey(fGpu, NULL,
+                                                      texture->desc(),
+                                                      cacheData,
+                                                      true);
+            fTextureCache->create(key, texture);
+            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 make it exclusive to hide it from future searches.
+    if (NULL != resource) {
+        fTextureCache->makeExclusive(resource->getCacheEntry());
+    }
+
+    return static_cast<GrTexture*>(resource);
+}
+
+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 (GrTexture::IsScratchTexture(texture->getCacheEntry()->key())) {
+        fTextureCache->makeNonExclusive(texture->getCacheEntry());
+    }
+
+    this->purgeCache();
+}
+
+void GrContext::purgeCache() {
+    if (NULL != fTextureCache) {
+        fTextureCache->purgeAsNeeded();
+    }
+}
+
+GrTexture* GrContext::createUncachedTexture(const GrTextureDesc& descIn,
+                                            void* srcData,
+                                            size_t rowBytes) {
+    GrTextureDesc descCopy = descIn;
+    return fGpu->createTexture(descCopy, srcData, rowBytes);
+}
+
+void GrContext::getTextureCacheLimits(int* maxTextures,
+                                      size_t* maxTextureBytes) const {
+    fTextureCache->getLimits(maxTextures, maxTextureBytes);
+}
+
+void GrContext::setTextureCacheLimits(int maxTextures, size_t maxTextureBytes) {
+    fTextureCache->setLimits(maxTextures, maxTextureBytes);
+}
+
+int GrContext::getMaxTextureSize() const {
+    return fGpu->getCaps().maxTextureSize();
+}
+
+int GrContext::getMaxRenderTargetSize() const {
+    return fGpu->getCaps().maxRenderTargetSize();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrTexture* GrContext::createPlatformTexture(const GrPlatformTextureDesc& desc) {
+    return fGpu->createPlatformTexture(desc);
+}
+
+GrRenderTarget* GrContext::createPlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) {
+    return fGpu->createPlatformRenderTarget(desc);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool GrContext::supportsIndex8PixelConfig(const GrTextureParams* params,
+                                          int width, int height) const {
+    const GrDrawTarget::Caps& caps = fGpu->getCaps();
+    if (!caps.eightBitPaletteSupport()) {
+        return false;
+    }
+
+    bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
+
+    if (!isPow2) {
+        bool tiled = NULL != params && params->isTiled();
+        if (tiled && !caps.npotTextureTileSupport()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+const GrClipData* GrContext::getClip() const {
+    return fGpu->getClip();
+}
+
+void GrContext::setClip(const GrClipData* clipData) {
+    fGpu->setClip(clipData);
+
+    fDrawState->setState(GrDrawState::kClip_StateBit,
+                         clipData->fClipStack && !clipData->fClipStack->isWideOpen());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+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& 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;
+    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 (!fDrawState->getViewInverse(&inverse)) {
+            GrPrintf("Could not invert matrix\n");
+            return;
+        }
+        inverse.mapRect(&r);
+    } else {
+        if (!am.setIdentity(this, paint.writable())) {
+            GrPrintf("Could not invert matrix\n");
+            return;
+        }
+    }
+    // by definition this fills the entire clip, no need for AA
+    if (paint->isAntiAlias()) {
+        paint.writable()->setAntiAlias(false);
+    }
+    this->drawRect(*paint, r);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+inline bool disable_coverage_aa_for_blend(GrDrawTarget* target) {
+    return DISABLE_COVERAGE_AA_FOR_BLEND && !target->canApplyCoverage();
+}
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+/*  create a triangle strip that strokes the specified triangle. There are 8
+ unique vertices, but we repreat the last 2 to close up. Alternatively we
+ could use an indices array, and then only send 8 verts, but not sure that
+ would be faster.
+ */
+static void setStrokeRectStrip(GrPoint verts[10], GrRect rect,
+                               GrScalar width) {
+    const GrScalar rad = GrScalarHalf(width);
+    rect.sort();
+
+    verts[0].set(rect.fLeft + rad, rect.fTop + rad);
+    verts[1].set(rect.fLeft - rad, rect.fTop - rad);
+    verts[2].set(rect.fRight - rad, rect.fTop + rad);
+    verts[3].set(rect.fRight + rad, rect.fTop - rad);
+    verts[4].set(rect.fRight - rad, rect.fBottom - rad);
+    verts[5].set(rect.fRight + rad, rect.fBottom + rad);
+    verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
+    verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
+    verts[8] = verts[0];
+    verts[9] = verts[1];
+}
+
+/**
+ * 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);
+}
+
+static bool apply_aa_to_rect(GrDrawTarget* target,
+                             const GrRect& rect,
+                             GrScalar width,
+                             const GrMatrix* matrix,
+                             GrMatrix* 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
+    // integer coords.
+
+    // we are keeping around the "tweak the alpha" trick because
+    // it is our only hope for the fixed-pipe implementation.
+    // In a shader implementation we can give a separate coverage input
+    // TODO: remove this ugliness when we drop the fixed-pipe impl
+    *useVertexCoverage = false;
+    if (!target->canTweakAlphaForCoverage()) {
+        if (disable_coverage_aa_for_blend(target)) {
+#if GR_DEBUG
+            //GrPrintf("Turning off AA to correctly apply blend.\n");
+#endif
+            return false;
+        } else {
+            *useVertexCoverage = true;
+        }
+    }
+    const GrDrawState& drawState = target->getDrawState();
+    if (drawState.getRenderTarget()->isMultisampled()) {
+        return false;
+    }
+
+    if (0 == width && target->willUseHWAALines()) {
+        return false;
+    }
+
+    if (!drawState.getViewMatrix().preservesAxisAlignment()) {
+        return false;
+    }
+
+    if (NULL != matrix &&
+        !matrix->preservesAxisAlignment()) {
+        return false;
+    }
+
+    *combinedMatrix = drawState.getViewMatrix();
+    if (NULL != matrix) {
+        combinedMatrix->preConcat(*matrix);
+        GrAssert(combinedMatrix->preservesAxisAlignment());
+    }
+
+    combinedMatrix->mapRect(devRect, rect);
+    devRect->sort();
+
+    if (width < 0) {
+        return !isIRect(*devRect);
+    } else {
+        return true;
+    }
+}
+
+void GrContext::drawRect(const GrPaint& paint,
+                         const GrRect& rect,
+                         GrScalar width,
+                         const GrMatrix* matrix) {
+    SK_TRACE_EVENT0("GrContext::drawRect");
+
+    GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
+    GrDrawState::AutoStageDisable atr(fDrawState);
+
+    GrRect devRect = rect;
+    GrMatrix combinedMatrix;
+    bool useVertexCoverage;
+    bool needAA = paint.isAntiAlias() &&
+                  !this->getRenderTarget()->isMultisampled();
+    bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix,
+                                           &combinedMatrix, &devRect,
+                                           &useVertexCoverage);
+
+    if (doAA) {
+        GrDrawState::AutoDeviceCoordDraw adcd(target->drawState());
+        if (!adcd.succeeded()) {
+            return;
+        }
+        if (width >= 0) {
+            GrVec strokeSize;;
+            if (width > 0) {
+                strokeSize.set(width, width);
+                combinedMatrix.mapVectors(&strokeSize, 1);
+                strokeSize.setAbs(strokeSize);
+            } else {
+                strokeSize.set(GR_Scalar1, GR_Scalar1);
+            }
+            fAARectRenderer->strokeAARect(this->getGpu(), target, devRect,
+                                         strokeSize, useVertexCoverage);
+        } else {
+            fAARectRenderer->fillAARect(this->getGpu(), target,
+                                       devRect, useVertexCoverage);
+        }
+        return;
+    }
+
+    if (width >= 0) {
+        // TODO: consider making static vertex buffers for these cases.
+        // Hairline could be done by just adding closing vertex to
+        // unitSquareVertexBuffer()
+
+        static const int worstCaseVertCount = 10;
+        GrDrawTarget::AutoReleaseGeometry geo(target, 0, worstCaseVertCount, 0);
+
+        if (!geo.succeeded()) {
+            GrPrintf("Failed to get space for vertices!\n");
+            return;
+        }
+
+        GrPrimitiveType primType;
+        int vertCount;
+        GrPoint* vertex = geo.positions();
+
+        if (width > 0) {
+            vertCount = 10;
+            primType = kTriangleStrip_GrPrimitiveType;
+            setStrokeRectStrip(vertex, rect, width);
+        } else {
+            // hairline
+            vertCount = 5;
+            primType = kLineStrip_GrPrimitiveType;
+            vertex[0].set(rect.fLeft, rect.fTop);
+            vertex[1].set(rect.fRight, rect.fTop);
+            vertex[2].set(rect.fRight, rect.fBottom);
+            vertex[3].set(rect.fLeft, rect.fBottom);
+            vertex[4].set(rect.fLeft, rect.fTop);
+        }
+
+        GrDrawState::AutoViewMatrixRestore avmr;
+        if (NULL != matrix) {
+            GrDrawState* drawState = target->drawState();
+            avmr.set(drawState, *matrix);
+        }
+
+        target->drawNonIndexed(primType, 0, vertCount);
+    } else {
+#if GR_STATIC_RECT_VB
+            const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer();
+            if (NULL == sqVB) {
+                GrPrintf("Failed to create static rect vb.\n");
+                return;
+            }
+            target->setVertexSourceToBuffer(0, sqVB);
+            GrDrawState* drawState = target->drawState();
+            GrMatrix m;
+            m.setAll(rect.width(),    0,             rect.fLeft,
+                        0,            rect.height(), rect.fTop,
+                        0,            0,             GrMatrix::I()[8]);
+
+            if (NULL != matrix) {
+                m.postConcat(*matrix);
+            }
+            GrDrawState::AutoViewMatrixRestore avmr(drawState, m);
+
+            target->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
+#else
+            target->drawSimpleRect(rect, matrix);
+#endif
+    }
+}
+
+void GrContext::drawRectToRect(const GrPaint& paint,
+                               const GrRect& dstRect,
+                               const GrRect& srcRect,
+                               const GrMatrix* dstMatrix,
+                               const GrMatrix* srcMatrix) {
+    SK_TRACE_EVENT0("GrContext::drawRectToRect");
+
+    // srcRect refers to paint's first color stage
+    if (!paint.isColorStageEnabled(0)) {
+        drawRect(paint, dstRect, -1, dstMatrix);
+        return;
+    }
+
+    GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
+
+#if GR_STATIC_RECT_VB
+    GrDrawState::AutoStageDisable atr(fDrawState);
+    GrDrawState* drawState = target->drawState();
+
+    GrMatrix m;
+
+    m.setAll(dstRect.width(), 0,                dstRect.fLeft,
+             0,               dstRect.height(), dstRect.fTop,
+             0,               0,                GrMatrix::I()[8]);
+    if (NULL != dstMatrix) {
+        m.postConcat(*dstMatrix);
+    }
+
+    // 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]);
+    if (NULL != srcMatrix) {
+        m.postConcat(*srcMatrix);
+    }
+    drawState->sampler(GrPaint::kFirstColorStage)->preConcatMatrix(m);
+
+    const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer();
+    if (NULL == sqVB) {
+        GrPrintf("Failed to create static rect vb.\n");
+        return;
+    }
+    target->setVertexSourceToBuffer(0, sqVB);
+    target->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
+#else
+    GrDrawState::AutoStageDisable atr(fDrawState);
+
+    const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
+    const GrMatrix* srcMatrices[GrDrawState::kNumStages] = {NULL};
+    srcRects[0] = &srcRect;
+    srcMatrices[0] = srcMatrix;
+
+    target->drawRect(dstRect, dstMatrix, srcRects, srcMatrices);
+#endif
+}
+
+void GrContext::drawVertices(const GrPaint& paint,
+                             GrPrimitiveType primitiveType,
+                             int vertexCount,
+                             const GrPoint positions[],
+                             const GrPoint texCoords[],
+                             const GrColor colors[],
+                             const uint16_t indices[],
+                             int indexCount) {
+    SK_TRACE_EVENT0("GrContext::drawVertices");
+
+    GrDrawTarget::AutoReleaseGeometry geo;
+
+    GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
+    GrDrawState::AutoStageDisable atr(fDrawState);
+
+    GrVertexLayout layout = 0;
+    if (NULL != texCoords) {
+        layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(0, 0);
+    }
+    if (NULL != colors) {
+        layout |= GrDrawTarget::kColor_VertexLayoutBit;
+    }
+    int vertexSize = GrDrawTarget::VertexSize(layout);
+
+    if (sizeof(GrPoint) != vertexSize) {
+        if (!geo.set(target, layout, vertexCount, 0)) {
+            GrPrintf("Failed to get space for vertices!\n");
+            return;
+        }
+        int texOffsets[GrDrawState::kMaxTexCoords];
+        int colorOffset;
+        GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
+                                                texOffsets,
+                                                &colorOffset,
+                                                NULL,
+                                                NULL);
+        void* curVertex = geo.vertices();
+
+        for (int i = 0; i < vertexCount; ++i) {
+            *((GrPoint*)curVertex) = positions[i];
+
+            if (texOffsets[0] > 0) {
+                *(GrPoint*)((intptr_t)curVertex + texOffsets[0]) = texCoords[i];
+            }
+            if (colorOffset > 0) {
+                *(GrColor*)((intptr_t)curVertex + colorOffset) = colors[i];
+            }
+            curVertex = (void*)((intptr_t)curVertex + vertexSize);
+        }
+    } else {
+        target->setVertexSourceToArray(layout, positions, vertexCount);
+    }
+
+    // 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) {
+        target->setIndexSourceToArray(indices, indexCount);
+        target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
+    } else {
+        target->drawNonIndexed(primitiveType, 0, vertexCount);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+namespace {
+
+struct CircleVertex {
+    GrPoint fPos;
+    GrPoint fCenter;
+    GrScalar fOuterRadius;
+    GrScalar fInnerRadius;
+};
+
+/* Returns true if will map a circle to another circle. This can be true
+ * if the matrix only includes square-scale, rotation, translation.
+ */
+inline bool isSimilarityTransformation(const SkMatrix& matrix,
+                                       SkScalar tol = SK_ScalarNearlyZero) {
+    if (matrix.isIdentity() || matrix.getType() == SkMatrix::kTranslate_Mask) {
+        return true;
+    }
+    if (matrix.hasPerspective()) {
+        return false;
+    }
+
+    SkScalar mx = matrix.get(SkMatrix::kMScaleX);
+    SkScalar sx = matrix.get(SkMatrix::kMSkewX);
+    SkScalar my = matrix.get(SkMatrix::kMScaleY);
+    SkScalar sy = matrix.get(SkMatrix::kMSkewY);
+
+    if (mx == 0 && sx == 0 && my == 0 && sy == 0) {
+        return false;
+    }
+
+    // it has scales or skews, but it could also be rotation, check it out.
+    SkVector vec[2];
+    vec[0].set(mx, sx);
+    vec[1].set(sy, my);
+
+    return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
+           SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
+                SkScalarSquare(tol));
+}
+
+}
+
+// TODO: strokeWidth can't be larger than zero right now.
+// It will be fixed when drawPath() can handle strokes.
+void GrContext::drawOval(const GrPaint& paint,
+                         const GrRect& rect,
+                         SkScalar strokeWidth) {
+    GrAssert(strokeWidth <= 0);
+    if (!isSimilarityTransformation(this->getMatrix()) ||
+        !paint.isAntiAlias() ||
+        rect.height() != rect.width()) {
+        SkPath path;
+        path.addOval(rect);
+        GrPathFill fill = (strokeWidth == 0) ?
+                           kHairLine_GrPathFill : kWinding_GrPathFill;
+        this->internalDrawPath(paint, path, fill);
+        return;
+    }
+
+    GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
+
+    GrDrawState* drawState = target->drawState();
+    GrDrawState::AutoStageDisable atr(fDrawState);
+    const GrMatrix vm = drawState->getViewMatrix();
+
+    const GrRenderTarget* rt = drawState->getRenderTarget();
+    if (NULL == rt) {
+        return;
+    }
+
+    GrDrawState::AutoDeviceCoordDraw adcd(drawState);
+    if (!adcd.succeeded()) {
+        return;
+    }
+
+    GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
+    GrAssert(sizeof(CircleVertex) == GrDrawTarget::VertexSize(layout));
+
+    GrPoint center = GrPoint::Make(rect.centerX(), rect.centerY());
+    GrScalar radius = SkScalarHalf(rect.width());
+
+    vm.mapPoints(&center, 1);
+    radius = vm.mapRadius(radius);
+
+    GrScalar outerRadius = radius;
+    GrScalar innerRadius = 0;
+    SkScalar halfWidth = 0;
+    if (strokeWidth == 0) {
+        halfWidth = SkScalarHalf(SK_Scalar1);
+
+        outerRadius += halfWidth;
+        innerRadius = SkMaxScalar(0, radius - halfWidth);
+    }
+
+    GrDrawTarget::AutoReleaseGeometry geo(target, layout, 4, 0);
+    if (!geo.succeeded()) {
+        GrPrintf("Failed to get space for vertices!\n");
+        return;
+    }
+
+    CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
+
+    // The fragment shader will extend the radius out half a pixel
+    // to antialias. Expand the drawn rect here so all the pixels
+    // will be captured.
+    SkScalar L = center.fX - outerRadius - SkFloatToScalar(0.5f);
+    SkScalar R = center.fX + outerRadius + SkFloatToScalar(0.5f);
+    SkScalar T = center.fY - outerRadius - SkFloatToScalar(0.5f);
+    SkScalar B = center.fY + outerRadius + SkFloatToScalar(0.5f);
+
+    verts[0].fPos = SkPoint::Make(L, T);
+    verts[1].fPos = SkPoint::Make(R, T);
+    verts[2].fPos = SkPoint::Make(L, B);
+    verts[3].fPos = SkPoint::Make(R, B);
+
+    for (int i = 0; i < 4; ++i) {
+        // this goes to fragment shader, it should be in y-points-up space.
+        verts[i].fCenter = SkPoint::Make(center.fX, rt->height() - center.fY);
+
+        verts[i].fOuterRadius = outerRadius;
+        verts[i].fInnerRadius = innerRadius;
+    }
+
+    drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType);
+    target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4);
+}
+
+void GrContext::drawPath(const GrPaint& paint, const SkPath& path, GrPathFill fill) {
+
+    if (path.isEmpty()) {
+       if (GrIsFillInverted(fill)) {
+           this->drawPaint(paint);
+       }
+       return;
+    }
+
+    SkRect ovalRect;
+    if (!GrIsFillInverted(fill) && path.isOval(&ovalRect)) {
+        SkScalar width = (fill == kHairLine_GrPathFill) ? 0 : -SK_Scalar1;
+        this->drawOval(paint, ovalRect, width);
+        return;
+    }
+
+    this->internalDrawPath(paint, path, fill);
+}
+
+void GrContext::internalDrawPath(const GrPaint& paint, const SkPath& path, GrPathFill fill) {
+
+    // 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
+    // aa. If we have some future driver-mojo path AA that can do the right
+    // thing WRT to the blend then we'll need some query on the PR.
+    if (disable_coverage_aa_for_blend(target)) {
+#if GR_DEBUG
+        //GrPrintf("Turning off AA to correctly apply blend.\n");
+#endif
+        prAA = false;
+    }
+
+    GrPathRenderer* pr = this->getPathRenderer(path, fill, target, prAA, true);
+    if (NULL == pr) {
+#if GR_DEBUG
+        GrPrintf("Unable to find path renderer compatible with path.\n");
+#endif
+        return;
+    }
+
+    pr->drawPath(path, fill, target, prAA);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrContext::flush(int flagsBitfield) {
+    if (kDiscard_FlushBit & flagsBitfield) {
+        fDrawBuffer->reset();
+    } else {
+        this->flushDrawBuffer();
+    }
+    if (kForceCurrentRenderTarget_FlushBit & flagsBitfield) {
+        fGpu->forceRenderTargetFlush();
+    }
+}
+
+void GrContext::flushDrawBuffer() {
+    if (fDrawBuffer) {
+        // 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;
+    }
+}
+
+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();
+    }
+
+    fGpu->writeTexturePixels(texture, left, top, width, height,
+                             config, buffer, rowBytes);
+}
+
+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->readRenderTargetPixels(target,
+                                            left, top, width, height,
+                                            config, buffer, rowBytes,
+                                            flags);
+    } else {
+        return false;
+    }
+}
+
+#include "SkConfig8888.h"
+
+namespace {
+/**
+ * Converts a GrPixelConfig to a SkCanvas::Config8888. Only byte-per-channel
+ * formats are representable as Config8888 and so the function returns false
+ * if the GrPixelConfig has no equivalent Config8888.
+ */
+bool grconfig_to_config8888(GrPixelConfig config,
+                            bool unpremul,
+                            SkCanvas::Config8888* config8888) {
+    switch (config) {
+        case kRGBA_8888_GrPixelConfig:
+            if (unpremul) {
+                *config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
+            } else {
+                *config8888 = SkCanvas::kRGBA_Premul_Config8888;
+            }
+            return true;
+        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::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 = fDrawState->getRenderTarget();
+        if (NULL == target) {
+            return false;
+        }
+    }
+
+    if (!(kDontFlush_PixelOpsFlag & flags)) {
+        this->flush();
+    }
+
+    // Determine which conversions have to be applied: flipY, swapRAnd, and/or unpremul.
+
+    // 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 swapRAndB = fGpu->preferredReadPixelsConfig(config) == GrPixelConfigSwapRAndB(config);
+
+    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;
+    }
+
+    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 &&
+            target->width() == width &&
+            target->height() == height &&
+            fGpu->fullReadPixelsIsFasterThanPartial()) {
+            match = kExact_ScratchTexMatch;
+        }
+        ast.set(this, desc, match);
+        GrTexture* texture = ast.texture();
+        if (texture) {
+            SkAutoTUnref<GrCustomStage> stage;
+            if (unpremul) {
+                stage.reset(this->createPMToUPMEffect(src, swapRAndB));
+            }
+            // 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 != stage || flipY || swapRAndB) {
+                if (NULL == stage) {
+                    stage.reset(GrConfigConversionEffect::Create(src, swapRAndB));
+                    GrAssert(NULL != stage);
+                } else {
+                    unpremul = false; // we will handle the UPM conversion in the draw
+                }
+                swapRAndB = false; // we will handle the swap in the draw.
+
+                GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
+                GrDrawState* drawState = fGpu->drawState();
+                drawState->setRenderTarget(texture->asRenderTarget());
+                GrMatrix matrix;
+                if (flipY) {
+                    matrix.setTranslate(SK_Scalar1 * left,
+                                        SK_Scalar1 * (top + height));
+                    matrix.set(GrMatrix::kMScaleY, -GR_Scalar1);
+                    flipY = false; // the y flip will be handled in the draw
+                } else {
+                    matrix.setTranslate(SK_Scalar1 *left, SK_Scalar1 *top);
+                }
+                matrix.postIDiv(src->width(), src->height());
+                drawState->sampler(0)->setCustomStage(stage, matrix);
+                GrRect rect = GrRect::MakeWH(GrIntToScalar(width), GrIntToScalar(height));
+                fGpu->drawSimpleRect(rect, NULL);
+                // we want to read back from the scratch's origin
+                left = 0;
+                top = 0;
+                target = texture->asRenderTarget();
+            }
+        }
+    }
+    if (!fGpu->readPixels(target,
+                          left, top, width, height,
+                          readConfig, buffer, rowBytes, readUpsideDown)) {
+        return false;
+    }
+    // Perform any conversions we weren't able to perfom 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) {
+    GrAssert(target);
+    ASSERT_OWNED_RESOURCE(target);
+    // In the future we may track whether there are any pending draws to this
+    // target. We don't today so we always perform a flush. We don't promise
+    // this to our clients, though.
+    this->flush();
+    fGpu->resolveRenderTarget(target);
+}
+
+void GrContext::copyTexture(GrTexture* src, GrRenderTarget* dst) {
+    if (NULL == src || NULL == dst) {
+        return;
+    }
+    ASSERT_OWNED_RESOURCE(src);
+
+    // Writes pending to the source texture are not tracked, so a flush
+    // is required to ensure that the copy captures the most recent contents
+    // of the source texture. See similar behaviour in
+    // GrContext::resolveRenderTarget.
+    this->flush();
+
+    GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
+    GrDrawState* drawState = fGpu->drawState();
+    drawState->setRenderTarget(dst);
+    GrMatrix sampleM;
+    sampleM.setIDiv(src->width(), src->height());
+    drawState->createTextureEffect(0, src, sampleM);
+    SkRect rect = SkRect::MakeXYWH(0, 0,
+                                   SK_Scalar1 * src->width(),
+                                   SK_Scalar1 * src->height());
+    fGpu->drawSimpleRect(rect, NULL);
+}
+
+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 = 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).
+
+    // 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() && !(kUnpremul_PixelOpsFlag & flags)) {
+        this->writeTexturePixels(target->asTexture(),
+                                 left, top, width, height,
+                                 config, buffer, rowBytes, flags);
+        return;
+    }
+#endif
+    SkAutoTUnref<GrCustomStage> stage;
+    bool swapRAndB = (fGpu->preferredReadPixelsConfig(config) == GrPixelConfigSwapRAndB(config));
+
+    GrPixelConfig textureConfig;
+    if (swapRAndB) {
+        textureConfig = GrPixelConfigSwapRAndB(config);
+    } else {
+        textureConfig = config;
+    }
+
+    GrTextureDesc desc;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fConfig = textureConfig;
+    GrAutoScratchTexture ast(this, desc);
+    GrTexture* texture = ast.texture();
+    if (NULL == texture) {
+        return;
+    }
+    // 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;
+        }
+        stage.reset(this->createUPMToPMEffect(texture, swapRAndB));
+        if (NULL == stage) {
+            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 == stage) {
+        stage.reset(GrConfigConversionEffect::Create(texture, swapRAndB));
+        GrAssert(NULL != stage);
+    }
+
+    this->writeTexturePixels(texture,
+                             0, 0, width, height,
+                             textureConfig, buffer, rowBytes,
+                             flags & ~kUnpremul_PixelOpsFlag);
+
+    GrDrawTarget::AutoStateRestore  asr(fGpu, GrDrawTarget::kReset_ASRInit);
+    GrDrawState* drawState = fGpu->drawState();
+
+    GrMatrix matrix;
+    matrix.setTranslate(GrIntToScalar(left), GrIntToScalar(top));
+    drawState->setViewMatrix(matrix);
+    drawState->setRenderTarget(target);
+
+    matrix.setIDiv(texture->width(), texture->height());
+    drawState->sampler(0)->setCustomStage(stage, matrix);
+
+    fGpu->drawSimpleRect(GrRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)), NULL);
+}
+////////////////////////////////////////////////////////////////////////////////
+
+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");
+        }
+#endif
+    }
+    if (kYes_BufferedDraw == buffered) {
+        fDrawBuffer->setClip(fGpu->getClip());
+        fLastDrawWasBuffered = kYes_BufferedDraw;
+        return fDrawBuffer;
+    } else {
+        GrAssert(kNo_BufferedDraw == buffered);
+        return fGpu;
+    }
+}
+
+/*
+ * 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,
+                                           GrPathFill fill,
+                                           const GrDrawTarget* target,
+                                           bool antiAlias,
+                                           bool allowSW) {
+    if (NULL == fPathRendererChain) {
+        fPathRendererChain =
+            SkNEW_ARGS(GrPathRendererChain,
+                       (this, GrPathRendererChain::kNone_UsageFlag));
+    }
+
+    GrPathRenderer* pr = fPathRendererChain->getPathRenderer(path, fill,
+                                                             target,
+                                                             antiAlias);
+
+    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);
+    fDrawState->setRenderTarget(target);
+}
+
+GrRenderTarget* GrContext::getRenderTarget() {
+    return fDrawState->getRenderTarget();
+}
+
+const GrRenderTarget* GrContext::getRenderTarget() const {
+    return fDrawState->getRenderTarget();
+}
+
+bool GrContext::isConfigRenderable(GrPixelConfig config) const {
+    return fGpu->isConfigRenderable(config);
+}
+
+const GrMatrix& GrContext::getMatrix() const {
+    return fDrawState->getViewMatrix();
+}
+
+void GrContext::setMatrix(const GrMatrix& m) {
+    fDrawState->setViewMatrix(m);
+}
+
+void GrContext::setIdentityMatrix() {
+    fDrawState->viewMatrix()->reset();
+}
+
+void GrContext::concatMatrix(const GrMatrix& m) const {
+    fDrawState->preConcatViewMatrix(m);
+}
+
+static inline intptr_t setOrClear(intptr_t bits, int shift, intptr_t pred) {
+    intptr_t mask = 1 << shift;
+    if (pred) {
+        bits |= mask;
+    } else {
+        bits &= ~mask;
+    }
+    return bits;
+}
+
+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 = SkNEW_ARGS(GrResourceCache,
+                               (MAX_TEXTURE_CACHE_COUNT,
+                                MAX_TEXTURE_CACHE_BYTES));
+    fFontCache = SkNEW_ARGS(GrFontCache, (fGpu));
+
+    fLastDrawWasBuffered = kNo_BufferedDraw;
+
+    fDrawBuffer = NULL;
+    fDrawBufferVBAllocPool = NULL;
+    fDrawBufferIBAllocPool = NULL;
+
+    fAARectRenderer = SkNEW(GrAARectRenderer);
+
+    fDidTestPMConversions = false;
+
+    this->setupDrawBuffer();
+}
+
+void GrContext::setupDrawBuffer() {
+
+    GrAssert(NULL == fDrawBuffer);
+    GrAssert(NULL == fDrawBufferVBAllocPool);
+    GrAssert(NULL == fDrawBufferIBAllocPool);
+
+    fDrawBufferVBAllocPool =
+        SkNEW_ARGS(GrVertexBufferAllocPool, (fGpu, false,
+                                    DRAW_BUFFER_VBPOOL_BUFFER_SIZE,
+                                    DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS));
+    fDrawBufferIBAllocPool =
+        SkNEW_ARGS(GrIndexBufferAllocPool, (fGpu, false,
+                                   DRAW_BUFFER_IBPOOL_BUFFER_SIZE,
+                                   DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS));
+
+    fDrawBuffer = SkNEW_ARGS(GrInOrderDrawBuffer, (fGpu,
+                                          fDrawBufferVBAllocPool,
+                                          fDrawBufferIBAllocPool));
+
+    fDrawBuffer->setQuadIndexBuffer(this->getQuadIndexBuffer());
+    if (fDrawBuffer) {
+        fDrawBuffer->setAutoFlushTarget(fGpu);
+        fDrawBuffer->setDrawState(fDrawState);
+    }
+}
+
+GrDrawTarget* GrContext::getTextTarget(const GrPaint& paint) {
+    return prepareToDraw(&paint, DEFAULT_BUFFERING);
+}
+
+const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
+    return fGpu->getQuadIndexBuffer();
+}
+
+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;
+}
+}
+
+GrCustomStage* GrContext::createPMToUPMEffect(GrTexture* texture, bool swapRAndB) {
+    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);
+    } else {
+        return NULL;
+    }
+}
+
+GrCustomStage* GrContext::createUPMToPMEffect(GrTexture* texture, bool swapRAndB) {
+    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);
+    } 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);
+
+    GrPaint paint;
+    paint.reset();
+
+    for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
+        GrMatrix 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.colorSampler(0)->setCustomStage(SkNEW_ARGS(GrSingleTextureEffect,
+                                                         (srcTexture, true)), matrix)->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);
+        GrMatrix matrix;
+        // FIXME:  This should be mitchell, not bilinear.
+        matrix.setIDiv(srcTexture->width(), srcTexture->height());
+        this->setRenderTarget(dstTexture->asRenderTarget());
+        paint.colorSampler(0)->setCustomStage(SkNEW_ARGS(GrSingleTextureEffect,
+                                                         (srcTexture, true)),
+                                              matrix)->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/GrCustomStage.cpp b/src/gpu/GrCustomStage.cpp
new file mode 100644
index 0000000..0000278
--- /dev/null
+++ b/src/gpu/GrCustomStage.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 "GrContext.h"
+#include "GrCustomStage.h"
+#include "GrMemoryPool.h"
+#include "SkTLS.h"
+
+SK_DEFINE_INST_COUNT(GrCustomStage)
+
+#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+SkTArray<GrCustomStageTestFactory*, true>* GrCustomStageTestFactory::GetFactories() {
+    static SkTArray<GrCustomStageTestFactory*, true> gFactories;
+    return &gFactories;
+}
+#endif
+
+class GrCustomStage_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 GrProgramStageFactory::fCurrStageClassID =
+                                    GrProgramStageFactory::kIllegalStageClassID;
+
+GrCustomStage::GrCustomStage(int numTextures)
+    : fNumTextures(numTextures) {
+}
+
+GrCustomStage::~GrCustomStage() {
+
+}
+
+bool GrCustomStage::isOpaque(bool inputTextureIsOpaque) const {
+    return false;
+}
+
+bool GrCustomStage::isEqual(const GrCustomStage& s) const {
+    if (this->numTextures() != s.numTextures()) {
+        return false;
+    }
+    for (int i = 0; i < this->numTextures(); ++i) {
+        if (this->textureAccess(i) != s.textureAccess(i)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+const GrTextureAccess& GrCustomStage::textureAccess(int index) const {
+    GrCrash("We shouldn't be calling this function on the base class.");
+    static GrTextureAccess kDummy;
+    return kDummy;
+}
+
+void * GrCustomStage::operator new(size_t size) {
+    return GrCustomStage_Globals::GetTLS()->allocate(size);
+}
+
+void GrCustomStage::operator delete(void* target) {
+    GrCustomStage_Globals::GetTLS()->release(target);
+}
diff --git a/src/gpu/GrDefaultPathRenderer.cpp b/src/gpu/GrDefaultPathRenderer.cpp
new file mode 100644
index 0000000..4f725af
--- /dev/null
+++ b/src/gpu/GrDefaultPathRenderer.cpp
@@ -0,0 +1,518 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrDefaultPathRenderer.h"
+
+#include "GrContext.h"
+#include "GrDrawState.h"
+#include "GrPathUtils.h"
+#include "SkString.h"
+#include "SkTrace.h"
+
+
+GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
+                                             bool stencilWrapOpsSupport)
+    : fSeparateStencil(separateStencilSupport)
+    , fStencilWrapOps(stencilWrapOpsSupport) {
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Stencil rules for paths
+
+////// Even/Odd
+
+GR_STATIC_CONST_SAME_STENCIL(gEOStencilPass,
+    kInvert_StencilOp,
+    kKeep_StencilOp,
+    kAlwaysIfInClip_StencilFunc,
+    0xffff,
+    0xffff,
+    0xffff);
+
+// ok not to check clip b/c stencil pass only wrote inside clip
+GR_STATIC_CONST_SAME_STENCIL(gEOColorPass,
+    kZero_StencilOp,
+    kZero_StencilOp,
+    kNotEqual_StencilFunc,
+    0xffff,
+    0x0000,
+    0xffff);
+
+// have to check clip b/c outside clip will always be zero.
+GR_STATIC_CONST_SAME_STENCIL(gInvEOColorPass,
+    kZero_StencilOp,
+    kZero_StencilOp,
+    kEqualIfInClip_StencilFunc,
+    0xffff,
+    0x0000,
+    0xffff);
+
+////// Winding
+
+// when we have separate stencil we increment front faces / decrement back faces
+// when we don't have wrap incr and decr we use the stencil test to simulate
+// them.
+
+GR_STATIC_CONST_STENCIL(gWindStencilSeparateWithWrap,
+    kIncWrap_StencilOp,             kDecWrap_StencilOp,
+    kKeep_StencilOp,                kKeep_StencilOp,
+    kAlwaysIfInClip_StencilFunc,    kAlwaysIfInClip_StencilFunc,
+    0xffff,                         0xffff,
+    0xffff,                         0xffff,
+    0xffff,                         0xffff);
+
+// if inc'ing the max value, invert to make 0
+// if dec'ing zero invert to make all ones.
+// we can't avoid touching the stencil on both passing and
+// failing, so we can't resctrict ourselves to the clip.
+GR_STATIC_CONST_STENCIL(gWindStencilSeparateNoWrap,
+    kInvert_StencilOp,              kInvert_StencilOp,
+    kIncClamp_StencilOp,            kDecClamp_StencilOp,
+    kEqual_StencilFunc,             kEqual_StencilFunc,
+    0xffff,                         0xffff,
+    0xffff,                         0x0000,
+    0xffff,                         0xffff);
+
+// When there are no separate faces we do two passes to setup the winding rule
+// stencil. First we draw the front faces and inc, then we draw the back faces
+// and dec. These are same as the above two split into the incrementing and
+// decrementing passes.
+GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapInc,
+    kIncWrap_StencilOp,
+    kKeep_StencilOp,
+    kAlwaysIfInClip_StencilFunc,
+    0xffff,
+    0xffff,
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilWithWrapDec,
+    kDecWrap_StencilOp,
+    kKeep_StencilOp,
+    kAlwaysIfInClip_StencilFunc,
+    0xffff,
+    0xffff,
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapInc,
+    kInvert_StencilOp,
+    kIncClamp_StencilOp,
+    kEqual_StencilFunc,
+    0xffff,
+    0xffff,
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gWindSingleStencilNoWrapDec,
+    kInvert_StencilOp,
+    kDecClamp_StencilOp,
+    kEqual_StencilFunc,
+    0xffff,
+    0x0000,
+    0xffff);
+
+// Color passes are the same whether we use the two-sided stencil or two passes
+
+GR_STATIC_CONST_SAME_STENCIL(gWindColorPass,
+    kZero_StencilOp,
+    kZero_StencilOp,
+    kNonZeroIfInClip_StencilFunc,
+    0xffff,
+    0x0000,
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gInvWindColorPass,
+    kZero_StencilOp,
+    kZero_StencilOp,
+    kEqualIfInClip_StencilFunc,
+    0xffff,
+    0x0000,
+    0xffff);
+
+////// Normal render to stencil
+
+// Sometimes the default path renderer can draw a path directly to the stencil
+// buffer without having to first resolve the interior / exterior.
+GR_STATIC_CONST_SAME_STENCIL(gDirectToStencil,
+    kZero_StencilOp,
+    kIncClamp_StencilOp,
+    kAlwaysIfInClip_StencilFunc,
+    0xffff,
+    0x0000,
+    0xffff);
+
+////////////////////////////////////////////////////////////////////////////////
+// Helpers for drawPath
+
+#define STENCIL_OFF     0   // Always disable stencil (even when needed)
+
+static inline bool single_pass_path(const SkPath& path, GrPathFill fill) {
+#if STENCIL_OFF
+    return true;
+#else
+    if (kEvenOdd_GrPathFill == fill || kWinding_GrPathFill == fill) {
+        return path.isConvex();
+    }
+    return false;
+#endif
+}
+
+bool GrDefaultPathRenderer::requiresStencilPass(const SkPath& path,
+                                                GrPathFill fill,
+                                                const GrDrawTarget* target) const {
+    return !single_pass_path(path, fill);
+}
+
+static inline void append_countour_edge_indices(GrPathFill fillType,
+                                                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_GrPathFill != fillType) {
+        *((*indices)++) = fanCenterIdx;
+    }
+    *((*indices)++) = edgeV0Idx;
+    *((*indices)++) = edgeV0Idx + 1;
+}
+
+bool GrDefaultPathRenderer::createGeom(const SkPath& path,
+                                       GrPathFill fill,
+                                       GrScalar srcSpaceTol,
+                                       GrDrawTarget* target,
+                                       GrPrimitiveType* primType,
+                                       int* vertexCnt,
+                                       int* indexCnt,
+                                       GrDrawTarget::AutoReleaseGeometry* arg) {
+    {
+    SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom");
+
+    GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol);
+    int contourCnt;
+    int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt,
+                                                  srcSpaceTol);
+
+    if (maxPts <= 0) {
+        return false;
+    }
+    if (maxPts > ((int)SK_MaxU16 + 1)) {
+        GrPrintf("Path not rendered, too many verts (%d)\n", maxPts);
+        return false;
+    }
+
+    GrVertexLayout layout = 0;
+    bool indexed = contourCnt > 1;
+
+    int maxIdxs = 0;
+    if (kHairLine_GrPathFill == fill) {
+        if (indexed) {
+            maxIdxs = 2 * maxPts;
+            *primType = kLines_GrPrimitiveType;
+        } else {
+            *primType = kLineStrip_GrPrimitiveType;
+        }
+    } else {
+        if (indexed) {
+            maxIdxs = 3 * maxPts;
+            *primType = kTriangles_GrPrimitiveType;
+        } else {
+            *primType = kTriangleFan_GrPrimitiveType;
+        }
+    }
+
+
+    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;
+
+    GrPoint pts[4];
+
+    bool first = true;
+    int subpath = 0;
+
+    SkPath::Iter iter(path, false);
+
+    for (;;) {
+        GrPathCmd cmd = (GrPathCmd)iter.next(pts);
+        switch (cmd) {
+            case kMove_PathCmd:
+                if (!first) {
+                    uint16_t currIdx = (uint16_t) (vert - base);
+                    subpathIdxStart = currIdx;
+                    ++subpath;
+                }
+                *vert = pts[0];
+                vert++;
+                break;
+            case kLine_PathCmd:
+                if (indexed) {
+                    uint16_t prevIdx = (uint16_t)(vert - base) - 1;
+                    append_countour_edge_indices(fill, subpathIdxStart,
+                                                 prevIdx, &idx);
+                }
+                *(vert++) = pts[1];
+                break;
+            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)
+                    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,
+                                                     firstQPtIdx + i, &idx);
+                    }
+                }
+                break;
+            }
+            case kCubic_PathCmd: {
+                // first pt of cubic is the pt we ended on in previous step
+                uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1;
+                uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
+                                pts[0], pts[1], pts[2], pts[3],
+                                srcSpaceTolSqd, &vert,
+                                GrPathUtils::cubicPointCount(pts, srcSpaceTol));
+                if (indexed) {
+                    for (uint16_t i = 0; i < numPts; ++i) {
+                        append_countour_edge_indices(fill, subpathIdxStart,
+                                                     firstCPtIdx + i, &idx);
+                    }
+                }
+                break;
+            }
+            case kClose_PathCmd:
+                break;
+            case kEnd_PathCmd:
+             // uint16_t currIdx = (uint16_t) (vert - base);
+                goto FINISHED;
+        }
+        first = false;
+    }
+FINISHED:
+    GrAssert((vert - base) <= maxPts);
+    GrAssert((idx - idxBase) <= maxIdxs);
+
+    *vertexCnt = vert - base;
+    *indexCnt = idx - idxBase;
+
+    }
+    return true;
+}
+
+bool GrDefaultPathRenderer::internalDrawPath(const SkPath& path,
+                                             GrPathFill fill,
+                                             GrDrawTarget* target,
+                                             bool stencilOnly) {
+
+    GrMatrix viewM = target->getDrawState().getViewMatrix();
+    GrScalar tol = GR_Scalar1;
+    tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds());
+
+    int vertexCnt;
+    int indexCnt;
+    GrPrimitiveType primType;
+    GrDrawTarget::AutoReleaseGeometry arg;
+    if (!this->createGeom(path,
+                          fill,
+                          tol,
+                          target,
+                          &primType,
+                          &vertexCnt,
+                          &indexCnt,
+                          &arg)) {
+        return false;
+    }
+
+    GrAssert(NULL != 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());
+
+    int                         passCount = 0;
+    const GrStencilSettings*    passes[3];
+    GrDrawState::DrawFace       drawFace[3];
+    bool                        reverse = false;
+    bool                        lastPassIsBounds;
+
+    if (kHairLine_GrPathFill == fill) {
+        passCount = 1;
+        if (stencilOnly) {
+            passes[0] = &gDirectToStencil;
+        } else {
+            passes[0] = NULL;
+        }
+        lastPassIsBounds = false;
+        drawFace[0] = GrDrawState::kBoth_DrawFace;
+    } else {
+        if (single_pass_path(path, fill)) {
+            passCount = 1;
+            if (stencilOnly) {
+                passes[0] = &gDirectToStencil;
+            } else {
+                passes[0] = NULL;
+            }
+            drawFace[0] = GrDrawState::kBoth_DrawFace;
+            lastPassIsBounds = false;
+        } else {
+            switch (fill) {
+                case kInverseEvenOdd_GrPathFill:
+                    reverse = true;
+                    // fallthrough
+                case kEvenOdd_GrPathFill:
+                    passes[0] = &gEOStencilPass;
+                    if (stencilOnly) {
+                        passCount = 1;
+                        lastPassIsBounds = false;
+                    } else {
+                        passCount = 2;
+                        lastPassIsBounds = true;
+                        if (reverse) {
+                            passes[1] = &gInvEOColorPass;
+                        } else {
+                            passes[1] = &gEOColorPass;
+                        }
+                    }
+                    drawFace[0] = drawFace[1] = GrDrawState::kBoth_DrawFace;
+                    break;
+
+                case kInverseWinding_GrPathFill:
+                    reverse = true;
+                    // fallthrough
+                case kWinding_GrPathFill:
+                    if (fSeparateStencil) {
+                        if (fStencilWrapOps) {
+                            passes[0] = &gWindStencilSeparateWithWrap;
+                        } else {
+                            passes[0] = &gWindStencilSeparateNoWrap;
+                        }
+                        passCount = 2;
+                        drawFace[0] = GrDrawState::kBoth_DrawFace;
+                    } else {
+                        if (fStencilWrapOps) {
+                            passes[0] = &gWindSingleStencilWithWrapInc;
+                            passes[1] = &gWindSingleStencilWithWrapDec;
+                        } else {
+                            passes[0] = &gWindSingleStencilNoWrapInc;
+                            passes[1] = &gWindSingleStencilNoWrapDec;
+                        }
+                        // which is cw and which is ccw is arbitrary.
+                        drawFace[0] = GrDrawState::kCW_DrawFace;
+                        drawFace[1] = GrDrawState::kCCW_DrawFace;
+                        passCount = 3;
+                    }
+                    if (stencilOnly) {
+                        lastPassIsBounds = false;
+                        --passCount;
+                    } else {
+                        lastPassIsBounds = true;
+                        drawFace[passCount-1] = GrDrawState::kBoth_DrawFace;
+                        if (reverse) {
+                            passes[passCount-1] = &gInvWindColorPass;
+                        } else {
+                            passes[passCount-1] = &gWindColorPass;
+                        }
+                    }
+                    break;
+                default:
+                    GrAssert(!"Unknown path fFill!");
+                    return false;
+            }
+        }
+    }
+
+    {
+    for (int p = 0; p < passCount; ++p) {
+        drawState->setDrawFace(drawFace[p]);
+        if (NULL != passes[p]) {
+            *drawState->stencil() = *passes[p];
+        }
+
+        if (lastPassIsBounds && (p == passCount-1)) {
+            if (!colorWritesWereDisabled) {
+                drawState->disableState(GrDrawState::kNoColorWrites_StateBit);
+            }
+            GrRect bounds;
+            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;
+                // mapRect through persp matrix may not be correct
+                if (!drawState->getViewMatrix().hasPerspective() &&
+                    drawState->getViewInverse(&vmi)) {
+                    vmi.mapRect(&bounds);
+                } else {
+                    const GrMatrix& vm = drawState->getViewMatrix();
+                    if (!drawState->preConcatSamplerMatricesWithInverse(vm)) {
+                        GrPrintf("Could not invert matrix.\n");
+                        return false;
+                    }
+                    drawState->viewMatrix()->reset();
+                }
+            } else {
+                bounds = path.getBounds();
+            }
+            GrDrawTarget::AutoGeometryPush agp(target);
+            target->drawSimpleRect(bounds, NULL);
+        } else {
+            if (passCount > 1) {
+                drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
+            }
+            if (indexCnt) {
+                target->drawIndexed(primType, 0, 0,
+                                    vertexCnt, indexCnt);
+            } else {
+                target->drawNonIndexed(primType, 0, vertexCnt);
+            }
+        }
+    }
+    }
+    return true;
+}
+
+bool GrDefaultPathRenderer::canDrawPath(const SkPath& path,
+                                        GrPathFill fill,
+                                        const GrDrawTarget* target,
+                                        bool antiAlias) const {
+    // this class can draw any path with any fill but doesn't do any
+    // anti-aliasing.
+    return !antiAlias;
+}
+
+bool GrDefaultPathRenderer::onDrawPath(const SkPath& path,
+                                       GrPathFill fill,
+                                       GrDrawTarget* target,
+                                       bool antiAlias) {
+    return this->internalDrawPath(path,
+                                  fill,
+                                  target,
+                                  false);
+}
+
+void GrDefaultPathRenderer::drawPathToStencil(const SkPath& path,
+                                              GrPathFill fill,
+                                              GrDrawTarget* target) {
+    GrAssert(kInverseEvenOdd_GrPathFill != fill);
+    GrAssert(kInverseWinding_GrPathFill != fill);
+    this->internalDrawPath(path, fill, target, true);
+}
diff --git a/src/gpu/GrDefaultPathRenderer.h b/src/gpu/GrDefaultPathRenderer.h
new file mode 100644
index 0000000..e17f9cf
--- /dev/null
+++ b/src/gpu/GrDefaultPathRenderer.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 GrDefaultPathRenderer_DEFINED
+#define GrDefaultPathRenderer_DEFINED
+
+#include "GrPathRenderer.h"
+#include "SkTemplates.h"
+
+/**
+ *  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);
+
+
+    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;
+
+private:
+
+    virtual bool onDrawPath(const SkPath& path,
+                            GrPathFill fill,
+                            GrDrawTarget* target,
+                            bool antiAlias) SK_OVERRIDE;
+
+    bool internalDrawPath(const SkPath& path,
+                          GrPathFill fill,
+                          GrDrawTarget* target,
+                          bool stencilOnly);
+
+    bool createGeom(const SkPath& path,
+                    GrPathFill fill,
+                    GrScalar srcSpaceTol,
+                    GrDrawTarget* target,
+                    GrPrimitiveType* primType,
+                    int* vertexCnt,
+                    int* indexCnt,
+                    GrDrawTarget::AutoReleaseGeometry* arg);
+
+    bool    fSeparateStencil;
+    bool    fStencilWrapOps;
+
+    typedef GrPathRenderer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
new file mode 100644
index 0000000..cc11f6c
--- /dev/null
+++ b/src/gpu/GrDrawState.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 "GrDrawState.h"
+
+#include "GrPaint.h"
+
+void GrDrawState::setFromPaint(const GrPaint& paint) {
+    for (int i = 0; i < GrPaint::kMaxColorStages; ++i) {
+        int s = i + GrPaint::kFirstColorStage;
+        if (paint.isColorStageEnabled(i)) {
+            *this->sampler(s) = paint.getColorSampler(i);
+        }
+    }
+
+    this->setFirstCoverageStage(GrPaint::kFirstCoverageStage);
+
+    for (int i = 0; i < GrPaint::kMaxCoverageStages; ++i) {
+        int s = i + GrPaint::kFirstCoverageStage;
+        if (paint.isCoverageStageEnabled(i)) {
+            *this->sampler(s) = paint.getCoverageSampler(i);
+        }
+    }
+
+    // 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());
+
+    if (paint.isColorMatrixEnabled()) {
+        this->enableState(GrDrawState::kColorMatrix_StateBit);
+        this->setColorMatrix(paint.getColorMatrix());
+    } else {
+        this->disableState(GrDrawState::kColorMatrix_StateBit);
+    }
+    this->setBlendFunc(paint.getSrcBlendCoeff(), paint.getDstBlendCoeff());
+    this->setColorFilter(paint.getColorFilterColor(), paint.getColorFilterMode());
+    this->setCoverage(paint.getCoverage());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrDrawState::AutoViewMatrixRestore::restore() {
+    if (NULL != fDrawState) {
+        fDrawState->setViewMatrix(fViewMatrix);
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            if (fRestoreMask & (1 << s)) {
+                fDrawState->sampler(s)->setMatrixDeprecated(fSamplerMatrices[s]);
+            }
+        }
+    }
+    fDrawState = NULL;
+}
+
+void GrDrawState::AutoViewMatrixRestore::set(GrDrawState* drawState,
+                                             const GrMatrix& 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);
+            fSamplerMatrices[s] = drawState->sampler(s)->getMatrix();
+            drawState->sampler(s)->preConcatMatrix(preconcatMatrix);
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrDrawState::AutoDeviceCoordDraw::restore() {
+    if (NULL != fDrawState) {
+        fDrawState->setViewMatrix(fViewMatrix);
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            if (fRestoreMask & (1 << s)) {
+                fDrawState->sampler(s)->setMatrixDeprecated(fSamplerMatrices[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;
+    GrMatrix 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);
+            GrSamplerState* sampler = drawState->sampler(s);
+            fSamplerMatrices[s] = sampler->getMatrix();
+            sampler->preConcatMatrix(invVM);
+        }
+    }
+    drawState->viewMatrix()->reset();
+    return true;
+}
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
new file mode 100644
index 0000000..f3d5e37
--- /dev/null
+++ b/src/gpu/GrDrawState.h
@@ -0,0 +1,951 @@
+/*
+ * 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 GrDrawState_DEFINED
+#define GrDrawState_DEFINED
+
+#include "GrColor.h"
+#include "GrMatrix.h"
+#include "GrNoncopyable.h"
+#include "GrRefCnt.h"
+#include "GrSamplerState.h"
+#include "GrStencil.h"
+#include "GrTexture.h"
+#include "GrRenderTarget.h"
+#include "effects/GrSingleTextureEffect.h"
+
+#include "SkXfermode.h"
+
+class GrPaint;
+
+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.
+     *
+     * Stages 0 through GrPaint::kTotalStages-1 are reserved for setting up
+     * the draw (i.e., textures and filter masks). 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 = 5,
+        kMaxTexCoords = kNumStages
+    };
+
+    GrDrawState()
+        : fRenderTarget(NULL) {
+
+        this->reset();
+    }
+
+    GrDrawState(const GrDrawState& state)
+        : fRenderTarget(NULL) {
+
+        *this = state;
+    }
+
+    virtual ~GrDrawState() {
+        this->disableStages();
+        GrSafeSetNull(fRenderTarget);
+    }
+
+    /**
+     * Resets to the default state.
+     * Sampler states *will* be modified: textures or CustomStage objects
+     * will be released.
+     */
+    void reset() {
+
+        this->disableStages();
+
+        fColor = 0xffffffff;
+        fViewMatrix.reset();
+        GrSafeSetNull(fRenderTarget);
+        fSrcBlend = kOne_GrBlendCoeff;
+        fDstBlend = kZero_GrBlendCoeff;
+        fBlendConstant = 0x0;
+        fFlagBits = 0x0;
+        fVertexEdgeType = kHairLine_EdgeType;
+        fStencilSettings.setDisabled();
+        fFirstCoverageStage = kNumStages;
+        fCoverage = 0xffffffff;
+        fColorFilterMode = SkXfermode::kDst_Mode;
+        fColorFilterColor = 0x0;
+        fDrawFace = kBoth_DrawFace;
+    }
+
+    /**
+     * 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 Color
+    ////
+
+    /**
+     *  Sets color for next draw to a premultiplied-alpha color.
+     *
+     *  @param color    the color to set.
+     */
+    void setColor(GrColor color) { fColor = color; }
+
+    GrColor getColor() const { return fColor; }
+
+    /**
+     *  Sets the color to be used for the next draw to be
+     *  (r,g,b,a) = (alpha, alpha, alpha, alpha).
+     *
+     *  @param alpha The alpha value to set as the color.
+     */
+    void setAlpha(uint8_t a) {
+        this->setColor((a << 24) | (a << 16) | (a << 8) | a);
+    }
+
+    /**
+     * Add a color filter that can be represented by a color and a mode. Applied
+     * after color-computing texture stages.
+     */
+    void setColorFilter(GrColor c, SkXfermode::Mode mode) {
+        fColorFilterColor = c;
+        fColorFilterMode = mode;
+    }
+
+    GrColor getColorFilterColor() const { return fColorFilterColor; }
+    SkXfermode::Mode getColorFilterMode() const { return fColorFilterMode; }
+
+    /**
+     * Constructor sets the color to be 'color' which is undone by the destructor.
+     */
+    class AutoColorRestore : public ::GrNoncopyable {
+    public:
+        AutoColorRestore(GrDrawState* drawState, GrColor color) {
+            fDrawState = drawState;
+            fOldColor = fDrawState->getColor();
+            fDrawState->setColor(color);
+        }
+        ~AutoColorRestore() {
+            fDrawState->setColor(fOldColor);
+        }
+    private:
+        GrDrawState*    fDrawState;
+        GrColor         fOldColor;
+    };
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Coverage
+    ////
+
+    /**
+     * 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);
+    }
+
+    /**
+     * Version of above that specifies 4 channel per-vertex color. The value
+     * should be premultiplied.
+     */
+    void setCoverage4(GrColor coverage) {
+        fCoverage = coverage;
+    }
+
+    GrColor getCoverage() const {
+        return fCoverage;
+    }
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Textures
+    ////
+
+    /**
+     * Creates a GrSingleTextureEffect.
+     */
+    void createTextureEffect(int stage, GrTexture* texture) {
+        GrAssert(!this->getSampler(stage).getCustomStage());
+        this->sampler(stage)->setCustomStage(
+            SkNEW_ARGS(GrSingleTextureEffect, (texture)))->unref();
+    }
+    void createTextureEffect(int stage, GrTexture* texture, const GrMatrix& matrix) {
+        GrAssert(!this->getSampler(stage).getCustomStage());
+        GrCustomStage* customStage = SkNEW_ARGS(GrSingleTextureEffect, (texture));
+        this->sampler(stage)->setCustomStage(customStage, matrix)->unref();
+    }
+    void createTextureEffect(int stage, GrTexture* texture,
+                             const GrMatrix& matrix,
+                             const GrTextureParams& params) {
+        GrAssert(!this->getSampler(stage).getCustomStage());
+        GrCustomStage* customStage = SkNEW_ARGS(GrSingleTextureEffect, (texture, params));
+        this->sampler(stage)->setCustomStage(customStage, matrix)->unref();
+    }
+
+
+    bool stagesDisabled() {
+        for (int i = 0; i < kNumStages; ++i) {
+            if (NULL != fSamplerStates[i].getCustomStage()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    void disableStage(int index) {
+        fSamplerStates[index].setCustomStage(NULL);
+    }
+
+    /**
+     * Release all the textures and custom stages 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;
+    };
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @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 of enabled stages with a matrix.
+     */
+    void preConcatSamplerMatrices(const GrMatrix& matrix) {
+        for (int i = 0; i < kNumStages; ++i) {
+            if (this->isStageEnabled(i)) {
+                fSamplerStates[i].preConcatMatrix(matrix);
+            }
+        }
+    }
+
+    /**
+     * Preconcats the matrix of all samplers in the mask with the inverse of a
+     * matrix. If the matrix inverse cannot be computed (and there is at least
+     * one enabled stage) then false is returned.
+     */
+    bool preConcatSamplerMatricesWithInverse(const GrMatrix& matrix) {
+        GrMatrix inv;
+        bool computed = false;
+        for (int i = 0; i < kNumStages; ++i) {
+            if (this->isStageEnabled(i)) {
+                if (!computed && !matrix.invert(&inv)) {
+                    return false;
+                } else {
+                    computed = true;
+                }
+                fSamplerStates[i].preConcatMatrix(inv);
+            }
+        }
+        return true;
+    }
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Coverage / Color Stages
+    ////
+
+    /**
+     * 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
+     * this method. Initially this is kNumStages (all stages
+     * are color-computing).
+     */
+    void setFirstCoverageStage(int firstCoverageStage) {
+        GrAssert((unsigned)firstCoverageStage <= kNumStages);
+        fFirstCoverageStage = firstCoverageStage;
+    }
+
+    /**
+     * Gets the index of the first coverage-computing stage.
+     */
+    int getFirstCoverageStage() const {
+        return fFirstCoverageStage;
+    }
+
+    ///@}
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Blending
+    ////
+
+    /**
+     * Sets the blending function coefficients.
+     *
+     * The blend function will be:
+     *    D' = sat(S*srcCoef + D*dstCoef)
+     *
+     *   where D is the existing destination color, S is the incoming source
+     *   color, and D' is the new destination color that will be written. sat()
+     *   is the saturation function.
+     *
+     * @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;
+    #if GR_DEBUG
+        switch (dstCoeff) {
+        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;
+        default:
+            break;
+        }
+        switch (srcCoeff) {
+        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;
+        default:
+            break;
+        }
+    #endif
+    }
+
+    GrBlendCoeff getSrcBlendCoeff() const { return fSrcBlend; }
+    GrBlendCoeff getDstBlendCoeff() const { return fDstBlend; }
+
+    void getDstBlendCoeff(GrBlendCoeff* srcBlendCoeff,
+                          GrBlendCoeff* dstBlendCoeff) const {
+        *srcBlendCoeff = fSrcBlend;
+        *dstBlendCoeff = fDstBlend;
+    }
+
+    /**
+     * Sets the blending function constant referenced by the following blending
+     * coefficients:
+     *      kConstC_GrBlendCoeff
+     *      kIConstC_GrBlendCoeff
+     *      kConstA_GrBlendCoeff
+     *      kIConstA_GrBlendCoeff
+     *
+     * @param constant the constant to set
+     */
+    void setBlendConstant(GrColor constant) { fBlendConstant = constant; }
+
+    /**
+     * Retrieves the last value set by setBlendConstant()
+     * @return the blending constant value
+     */
+    GrColor getBlendConstant() const { return fBlendConstant; }
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name View Matrix
+    ////
+
+    /**
+     * 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.)
+     */
+    void setViewMatrix(const GrMatrix& m) { fViewMatrix = m; }
+
+    /**
+     * Gets a writable pointer to the view matrix.
+     */
+    GrMatrix* viewMatrix() { return &fViewMatrix; }
+
+    /**
+     *  Multiplies the current view matrix by a matrix
+     *
+     *  After this call V' = V*m where V is the old view matrix,
+     *  m is the parameter to this function, and V' is the new view matrix.
+     *  (We consider positions to be column vectors so position vector p is
+     *  transformed by matrix X as p' = X*p.)
+     *
+     *  @param m the matrix used to modify the view matrix.
+     */
+    void preConcatViewMatrix(const GrMatrix& m) { fViewMatrix.preConcat(m); }
+
+    /**
+     *  Multiplies the current view matrix by a matrix
+     *
+     *  After this call V' = m*V where V is the old view matrix,
+     *  m is the parameter to this function, and V' is the new view matrix.
+     *  (We consider positions to be column vectors so position vector p is
+     *  transformed by matrix X as p' = X*p.)
+     *
+     *  @param m the matrix used to modify the view matrix.
+     */
+    void postConcatViewMatrix(const GrMatrix& m) { fViewMatrix.postConcat(m); }
+
+    /**
+     * Retrieves the current view matrix
+     * @return the current view matrix.
+     */
+    const GrMatrix& getViewMatrix() const { return fViewMatrix; }
+
+    /**
+     *  Retrieves the inverse of the current view matrix.
+     *
+     *  If the current view matrix is invertible, return true, and if matrix
+     *  is non-null, copy the inverse into it. If the current view matrix is
+     *  non-invertible, return false and ignore the matrix parameter.
+     *
+     * @param matrix if not null, will receive a copy of the current inverse.
+     */
+    bool getViewInverse(GrMatrix* matrix) const {
+        // TODO: determine whether we really need to leave matrix unmodified
+        // at call sites when inversion fails.
+        GrMatrix inverse;
+        if (fViewMatrix.invert(&inverse)) {
+            if (matrix) {
+                *matrix = inverse;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Preconcats the current view matrix and restores the previous view matrix in the destructor.
+     * Stage matrices are automatically adjusted to compensate.
+     */
+    class AutoViewMatrixRestore : public ::GrNoncopyable {
+    public:
+        AutoViewMatrixRestore() : fDrawState(NULL) {}
+
+        AutoViewMatrixRestore(GrDrawState* ds,
+                              const GrMatrix& preconcatMatrix,
+                              uint32_t explicitCoordStageMask = 0) {
+            fDrawState = NULL;
+            this->set(ds, preconcatMatrix, explicitCoordStageMask);
+        }
+
+        ~AutoViewMatrixRestore() { this->restore(); }
+
+        /**
+         * Can be called prior to destructor to restore the original matrix.
+         */
+        void restore();
+
+        void set(GrDrawState* drawState,
+                 const GrMatrix& preconcatMatrix,
+                 uint32_t explicitCoordStageMask = 0);
+
+        bool isSet() const { return NULL != fDrawState; }
+
+    private:
+        GrDrawState*       fDrawState;
+        GrMatrix           fViewMatrix;
+        GrMatrix           fSamplerMatrices[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 GrMatrix& getOriginalMatrix() const {
+            GrAssert(this->succeeded());
+            return fViewMatrix;
+        }
+
+        /**
+         * Can be called prior to destructor to restore the original matrix.
+         */
+        void restore();
+
+    private:
+        GrDrawState*       fDrawState;
+        GrMatrix           fViewMatrix;
+        GrMatrix           fSamplerMatrices[GrDrawState::kNumStages];
+        uint32_t           fRestoreMask;
+    };
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Render Target
+    ////
+
+    /**
+     * Sets the rendertarget used at the next drawing call
+     *
+     * @param target  The render target to set.
+     */
+    void setRenderTarget(GrRenderTarget* target) {
+        GrSafeAssign(fRenderTarget, target);
+    }
+
+    /**
+     * Retrieves the currently set rendertarget.
+     *
+     * @return    The currently set render target.
+     */
+    const GrRenderTarget* getRenderTarget() const { return fRenderTarget; }
+    GrRenderTarget* getRenderTarget() { return fRenderTarget; }
+
+    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->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;
+            }
+        }
+    private:
+        GrDrawState* fDrawState;
+        GrRenderTarget* fSavedTarget;
+    };
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Stencil
+    ////
+
+    /**
+     * Sets the stencil settings to use for the next draw.
+     * Changing the clip has the side-effect of possibly zeroing
+     * out the client settable stencil bits. So multipass algorithms
+     * using stencil should not change the clip between passes.
+     * @param settings  the stencil settings to use.
+     */
+    void setStencil(const GrStencilSettings& settings) {
+        fStencilSettings = settings;
+    }
+
+    /**
+     * Shortcut to disable stencil testing and ops.
+     */
+    void disableStencil() {
+        fStencilSettings.setDisabled();
+    }
+
+    const GrStencilSettings& getStencil() const { return 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; }
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+    // @name Edge AA
+    // 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.
+    //
+    ////
+
+    /**
+     * 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
+     * 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
+           components used). Coverage based on signed distance with negative
+           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,
+
+        kVertexEdgeTypeCnt
+    };
+
+    /**
+     * 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;
+    }
+
+    VertexEdgeType getVertexEdgeType() const { return fVertexEdgeType; }
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name State Flags
+    ////
+
+    /**
+     *  Flags that affect rendering. Controlled using enable/disableState(). All
+     *  default to disabled.
+     */
+    enum StateBits {
+        /**
+         * Perform dithering. TODO: Re-evaluate whether we need this bit
+         */
+        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.
+         */
+        kHWAntialias_StateBit   = 0x02,
+        /**
+         * Draws will respect the clip, otherwise the clip is ignored.
+         */
+        kClip_StateBit          = 0x04,
+        /**
+         * Disables writing to the color buffer. Useful when performing stencil
+         * operations.
+         */
+        kNoColorWrites_StateBit = 0x08,
+        /**
+         * Draws will apply the color matrix, otherwise the color matrix is
+         * ignored.
+         */
+        kColorMatrix_StateBit   = 0x10,
+
+        // Users of the class may add additional bits to the vector
+        kDummyStateBit,
+        kLastPublicStateBit = kDummyStateBit-1,
+    };
+
+    void resetStateFlags() {
+        fFlagBits = 0;
+    }
+
+    /**
+     * Enable render state settings.
+     *
+     * @param stateBits bitfield of StateBits specifying the states to enable
+     */
+    void enableState(uint32_t stateBits) {
+        fFlagBits |= stateBits;
+    }
+
+    /**
+     * Disable render state settings.
+     *
+     * @param stateBits bitfield of StateBits specifying the states to disable
+     */
+    void disableState(uint32_t stateBits) {
+        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);
+    }
+
+    bool isHWAntialiasState() const {
+        return 0 != (fFlagBits & kHWAntialias_StateBit);
+    }
+
+    bool isClipState() const {
+        return 0 != (fFlagBits & kClip_StateBit);
+    }
+
+    bool isColorWriteDisabled() const {
+        return 0 != (fFlagBits & kNoColorWrites_StateBit);
+    }
+
+    bool isStateFlagEnabled(uint32_t stateBit) const {
+        return 0 != (stateBit & fFlagBits);
+    }
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Face Culling
+    ////
+
+    enum DrawFace {
+        kInvalid_DrawFace = -1,
+
+        kBoth_DrawFace,
+        kCCW_DrawFace,
+        kCW_DrawFace,
+    };
+
+    /**
+     * Controls whether clockwise, counterclockwise, or both faces are drawn.
+     * @param face  the face(s) to draw.
+     */
+    void setDrawFace(DrawFace face) {
+        GrAssert(kInvalid_DrawFace != face);
+        fDrawFace = face;
+    }
+
+    /**
+     * Gets whether the target is drawing clockwise, counterclockwise,
+     * or both faces.
+     * @return the current draw face(s).
+     */
+    DrawFace getDrawFace() const { return fDrawFace; }
+
+    /// @}
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    bool isStageEnabled(int s) const {
+        GrAssert((unsigned)s < kNumStages);
+        return (NULL != fSamplerStates[s].getCustomStage());
+    }
+
+    // Most stages are usually not used, so conditionals here
+    // reduce the expected number of bytes touched by 50%.
+    bool operator ==(const GrDrawState& s) const {
+        if (fColor != s.fColor ||
+            !s.fViewMatrix.cheapEqualTo(fViewMatrix) ||
+            fRenderTarget != s.fRenderTarget ||
+            fSrcBlend != s.fSrcBlend ||
+            fDstBlend != s.fDstBlend ||
+            fBlendConstant != s.fBlendConstant ||
+            fFlagBits != s.fFlagBits ||
+            fVertexEdgeType != s.fVertexEdgeType ||
+            fStencilSettings != s.fStencilSettings ||
+            fFirstCoverageStage != s.fFirstCoverageStage ||
+            fCoverage != s.fCoverage ||
+            fColorFilterMode != s.fColorFilterMode ||
+            fColorFilterColor != s.fColorFilterColor ||
+            fDrawFace != s.fDrawFace) {
+            return false;
+        }
+
+        for (int i = 0; i < kNumStages; i++) {
+            bool enabled = this->isStageEnabled(i);
+            if (enabled != s.isStageEnabled(i)) {
+                return false;
+            }
+            if (enabled && this->fSamplerStates[i] != s.fSamplerStates[i]) {
+                return false;
+            }
+        }
+        if (kColorMatrix_StateBit & s.fFlagBits) {
+            if (memcmp(fColorMatrix,
+                        s.fColorMatrix,
+                        sizeof(fColorMatrix))) {
+                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) {
+        fColor = s.fColor;
+        fViewMatrix = s.fViewMatrix;
+        SkRefCnt_SafeAssign(fRenderTarget, s.fRenderTarget);
+        fSrcBlend = s.fSrcBlend;
+        fDstBlend = s.fDstBlend;
+        fBlendConstant = s.fBlendConstant;
+        fFlagBits = s.fFlagBits;
+        fVertexEdgeType = s.fVertexEdgeType;
+        fStencilSettings = s.fStencilSettings;
+        fFirstCoverageStage = s.fFirstCoverageStage;
+        fCoverage = s.fCoverage;
+        fColorFilterMode = s.fColorFilterMode;
+        fColorFilterColor = s.fColorFilterColor;
+        fDrawFace = s.fDrawFace;
+
+        for (int i = 0; i < kNumStages; i++) {
+            if (s.isStageEnabled(i)) {
+                this->fSamplerStates[i] = s.fSamplerStates[i];
+            }
+        }
+
+        if (kColorMatrix_StateBit & s.fFlagBits) {
+            memcpy(this->fColorMatrix, s.fColorMatrix, sizeof(fColorMatrix));
+        }
+
+        return *this;
+    }
+
+private:
+
+    // These fields are roughly sorted by decreasing likelihood of being different in op==
+    GrColor             fColor;
+    GrMatrix            fViewMatrix;
+    GrRenderTarget*     fRenderTarget;
+    GrBlendCoeff        fSrcBlend;
+    GrBlendCoeff        fDstBlend;
+    GrColor             fBlendConstant;
+    uint32_t            fFlagBits;
+    VertexEdgeType      fVertexEdgeType;
+    GrStencilSettings   fStencilSettings;
+    int                 fFirstCoverageStage;
+    GrColor             fCoverage;
+    SkXfermode::Mode    fColorFilterMode;
+    GrColor             fColorFilterColor;
+    DrawFace            fDrawFace;
+
+    // This field must be last; it will not be copied or compared
+    // if the corresponding fTexture[] is NULL.
+    GrSamplerState      fSamplerStates[kNumStages];
+    // only compared if the color matrix enable flag is set
+    float               fColorMatrix[20];       // 5 x 4 matrix
+
+    typedef GrRefCnt INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
new file mode 100644
index 0000000..3b27172
--- /dev/null
+++ b/src/gpu/GrDrawTarget.cpp
@@ -0,0 +1,1232 @@
+
+/*
+ * 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 "GrDrawTarget.h"
+#include "GrGpuVertex.h"
+#include "GrIndexBuffer.h"
+#include "GrRenderTarget.h"
+#include "GrTexture.h"
+#include "GrVertexBuffer.h"
+
+SK_DEFINE_INST_COUNT(GrDrawTarget)
+
+namespace {
+
+/**
+ * This function generates some masks that we like to have known at compile
+ * time. When the number of stages or tex coords is bumped or the way bits
+ * are defined in GrDrawTarget.h changes this function should be rerun to
+ * generate the new masks. (We attempted to force the compiler to generate the
+ * masks using recursive templates but always wound up with static initializers
+ * under gcc, even if they were just a series of immediate->memory moves.)
+ *
+ */
+void gen_mask_arrays(GrVertexLayout* stageTexCoordMasks,
+                     GrVertexLayout* texCoordMasks) {
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        stageTexCoordMasks[s] = 0;
+        for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+            stageTexCoordMasks[s] |= GrDrawTarget::StageTexCoordVertexLayoutBit(s, t);
+        }
+    }
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        texCoordMasks[t] = 0;
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            texCoordMasks[t] |= GrDrawTarget::StageTexCoordVertexLayoutBit(s, t);
+        }
+    }
+}
+
+/**
+ * Run this function to generate the code that declares the global masks.
+ */
+void gen_globals() {
+    GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
+    GrVertexLayout texCoordMasks[GrDrawState::kMaxTexCoords];
+    gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
+
+    GrPrintf("const GrVertexLayout gStageTexCoordMasks[] = {\n");
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        GrPrintf("    0x%x,\n", stageTexCoordMasks[s]);
+    }
+    GrPrintf("};\n");
+    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));\n\n");
+    GrPrintf("const GrVertexLayout gTexCoordMasks[] = {\n");
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        GrPrintf("    0x%x,\n", texCoordMasks[t]);
+    }
+    GrPrintf("};\n");
+    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));\n");
+}
+
+/* These values were generated by the above function */
+
+const GrVertexLayout gStageTexCoordMasks[] = {
+    0x108421,
+    0x210842,
+    0x421084,
+    0x842108,
+    0x1084210,
+};
+GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));
+
+const GrVertexLayout gTexCoordMasks[] = {
+    0x1f,
+    0x3e0,
+    0x7c00,
+    0xf8000,
+    0x1f00000,
+};
+GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));
+
+bool check_layout(GrVertexLayout layout) {
+    // can only have 1 or 0 bits set for each stage.
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        int stageBits = layout & gStageTexCoordMasks[s];
+        if (stageBits && !GrIsPow2(stageBits)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+int num_tex_coords(GrVertexLayout layout) {
+    int cnt = 0;
+    // figure out how many tex coordinates are present
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        if (gTexCoordMasks[t] & layout) {
+            ++cnt;
+        }
+    }
+    return cnt;
+}
+
+} //unnamed namespace
+
+size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    size_t vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                        sizeof(GrGpuTextVertex) :
+                        sizeof(GrPoint);
+
+    size_t size = vecSize; // position
+    size += num_tex_coords(vertexLayout) * vecSize;
+    if (vertexLayout & kColor_VertexLayoutBit) {
+        size += sizeof(GrColor);
+    }
+    if (vertexLayout & kCoverage_VertexLayoutBit) {
+        size += sizeof(GrColor);
+    }
+    if (vertexLayout & kEdge_VertexLayoutBit) {
+        size += 4 * sizeof(GrScalar);
+    }
+    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 GrDrawTarget::VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    if (!StageUsesTexCoords(vertexLayout, stage)) {
+        return 0;
+    }
+    int tcIdx = VertexTexCoordsForStage(stage, vertexLayout);
+    if (tcIdx >= 0) {
+
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        int offset = vecSize; // position
+        // figure out how many tex coordinates are present and precede this one.
+        for (int t = 0; t < tcIdx; ++t) {
+            if (gTexCoordMasks[t] & vertexLayout) {
+                offset += vecSize;
+            }
+        }
+        return offset;
+    }
+
+    return -1;
+}
+
+int GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    if (vertexLayout & kColor_VertexLayoutBit) {
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        return vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
+    }
+    return -1;
+}
+
+int GrDrawTarget::VertexCoverageOffset(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    if (vertexLayout & kCoverage_VertexLayoutBit) {
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+
+        int offset = vecSize * (num_tex_coords(vertexLayout) + 1);
+        if (vertexLayout & kColor_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
+        return offset;
+    }
+    return -1;
+}
+
+int GrDrawTarget::VertexEdgeOffset(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    // edge pts are after the pos, tex coords, and color
+    if (vertexLayout & kEdge_VertexLayoutBit) {
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        int offset = vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
+        if (vertexLayout & kColor_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
+        if (vertexLayout & kCoverage_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
+        return offset;
+    }
+    return -1;
+}
+
+int GrDrawTarget::VertexSizeAndOffsetsByIdx(
+        GrVertexLayout vertexLayout,
+        int texCoordOffsetsByIdx[GrDrawState::kMaxTexCoords],
+        int* colorOffset,
+        int* coverageOffset,
+        int* edgeOffset) {
+    GrAssert(check_layout(vertexLayout));
+
+    int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                                    sizeof(GrGpuTextVertex) :
+                                                    sizeof(GrPoint);
+    int size = vecSize; // position
+
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        if (gTexCoordMasks[t] & vertexLayout) {
+            if (NULL != texCoordOffsetsByIdx) {
+                texCoordOffsetsByIdx[t] = size;
+            }
+            size += vecSize;
+        } else {
+            if (NULL != texCoordOffsetsByIdx) {
+                texCoordOffsetsByIdx[t] = -1;
+            }
+        }
+    }
+    if (kColor_VertexLayoutBit & vertexLayout) {
+        if (NULL != colorOffset) {
+            *colorOffset = size;
+        }
+        size += sizeof(GrColor);
+    } 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(GrScalar);
+    } else {
+        if (NULL != edgeOffset) {
+            *edgeOffset = -1;
+        }
+    }
+    return size;
+}
+
+int GrDrawTarget::VertexSizeAndOffsetsByStage(
+        GrVertexLayout vertexLayout,
+        int texCoordOffsetsByStage[GrDrawState::kNumStages],
+        int* colorOffset,
+        int* coverageOffset,
+        int* edgeOffset) {
+    GrAssert(check_layout(vertexLayout));
+
+    int texCoordOffsetsByIdx[GrDrawState::kMaxTexCoords];
+    int size = VertexSizeAndOffsetsByIdx(vertexLayout,
+                                         (NULL == texCoordOffsetsByStage) ?
+                                               NULL :
+                                               texCoordOffsetsByIdx,
+                                         colorOffset,
+                                         coverageOffset,
+                                         edgeOffset);
+    if (NULL != texCoordOffsetsByStage) {
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            int tcIdx = VertexTexCoordsForStage(s, vertexLayout);
+            texCoordOffsetsByStage[s] =
+                tcIdx < 0 ? 0 : texCoordOffsetsByIdx[tcIdx];
+        }
+    }
+    return size;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+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 texCoordMasks[GrDrawState::kMaxTexCoords];
+    gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        GrAssert(stageTexCoordMasks[s] == gStageTexCoordMasks[s]);
+    }
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        GrAssert(texCoordMasks[t] == gTexCoordMasks[t]);
+    }
+
+    // not necessarily exhaustive
+    static bool run;
+    if (!run) {
+        run = true;
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+
+            GrVertexLayout stageMask = 0;
+            for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+                stageMask |= StageTexCoordVertexLayoutBit(s,t);
+            }
+            GrAssert(1 == GrDrawState::kMaxTexCoords ||
+                     !check_layout(stageMask));
+            GrAssert(gStageTexCoordMasks[s] == stageMask);
+            GrAssert(!check_layout(stageMask));
+        }
+        for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+            GrVertexLayout tcMask = 0;
+            GrAssert(!VertexUsesTexCoordIdx(t, 0));
+            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+                tcMask |= StageTexCoordVertexLayoutBit(s,t);
+                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
+                GrAssert(VertexUsesTexCoordIdx(t, tcMask));
+                GrAssert(2*sizeof(GrPoint) == VertexSize(tcMask));
+                GrAssert(t == VertexTexCoordsForStage(s, tcMask));
+                for (int s2 = s + 1; s2 < GrDrawState::kNumStages; ++s2) {
+                    GrAssert(-1 == VertexTexCoordsForStage(s2, tcMask));
+
+                #if GR_DEBUG
+                    GrVertexLayout posAsTex = tcMask;
+                #endif
+                    GrAssert(0 == VertexStageCoordOffset(s2, posAsTex));
+                    GrAssert(2*sizeof(GrPoint) == VertexSize(posAsTex));
+                    GrAssert(-1 == VertexTexCoordsForStage(s2, posAsTex));
+                    GrAssert(-1 == VertexEdgeOffset(posAsTex));
+                }
+                GrAssert(-1 == VertexEdgeOffset(tcMask));
+                GrAssert(-1 == VertexColorOffset(tcMask));
+                GrAssert(-1 == VertexCoverageOffset(tcMask));
+            #if GR_DEBUG
+                GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
+            #endif
+                GrAssert(-1 == VertexCoverageOffset(withColor));
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
+            #if GR_DEBUG
+                GrVertexLayout withEdge = tcMask | kEdge_VertexLayoutBit;
+            #endif
+                GrAssert(-1 == VertexColorOffset(withEdge));
+                GrAssert(2*sizeof(GrPoint) == VertexEdgeOffset(withEdge));
+                GrAssert(4*sizeof(GrPoint) == VertexSize(withEdge));
+            #if GR_DEBUG
+                GrVertexLayout withColorAndEdge = withColor | kEdge_VertexLayoutBit;
+            #endif
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColorAndEdge));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexEdgeOffset(withColorAndEdge));
+                GrAssert(4*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColorAndEdge));
+            #if GR_DEBUG
+                GrVertexLayout withCoverage = tcMask | kCoverage_VertexLayoutBit;
+            #endif
+                GrAssert(-1 == VertexColorOffset(withCoverage));
+                GrAssert(2*sizeof(GrPoint) == VertexCoverageOffset(withCoverage));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withCoverage));
+            #if GR_DEBUG
+                GrVertexLayout withCoverageAndColor = tcMask | kCoverage_VertexLayoutBit |
+                                                      kColor_VertexLayoutBit;
+            #endif
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withCoverageAndColor));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexCoverageOffset(withCoverageAndColor));
+                GrAssert(2*sizeof(GrPoint) + 2 * sizeof(GrColor) == VertexSize(withCoverageAndColor));
+            }
+            GrAssert(gTexCoordMasks[t] == tcMask);
+            GrAssert(check_layout(tcMask));
+
+            int stageOffsets[GrDrawState::kNumStages];
+            int colorOffset;
+            int edgeOffset;
+            int coverageOffset;
+            int size;
+            size = VertexSizeAndOffsetsByStage(tcMask,
+                                               stageOffsets, &colorOffset,
+                                               &coverageOffset, &edgeOffset);
+            GrAssert(2*sizeof(GrPoint) == size);
+            GrAssert(-1 == colorOffset);
+            GrAssert(-1 == coverageOffset);
+            GrAssert(-1 == edgeOffset);
+            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+                GrAssert(sizeof(GrPoint) == stageOffsets[s]);
+                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
+            }
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define DEBUG_INVAL_BUFFER 0xdeadcafe
+#define DEBUG_INVAL_START_IDX -1
+
+GrDrawTarget::GrDrawTarget() : fClip(NULL) {
+#if GR_DEBUG
+    VertexLayoutUnitTest();
+#endif
+    fDrawState = &fDefaultDrawState;
+    // We assume that fDrawState always owns a ref to the object it points at.
+    fDefaultDrawState.ref();
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.push_back();
+#if GR_DEBUG
+    geoSrc.fVertexCount = DEBUG_INVAL_START_IDX;
+    geoSrc.fVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER;
+    geoSrc.fIndexCount = DEBUG_INVAL_START_IDX;
+    geoSrc.fIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
+#endif
+    geoSrc.fVertexSrc = kNone_GeometrySrcType;
+    geoSrc.fIndexSrc  = kNone_GeometrySrcType;
+}
+
+GrDrawTarget::~GrDrawTarget() {
+    GrAssert(1 == fGeoSrcStateStack.count());
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    GrAssert(kNone_GeometrySrcType == geoSrc.fIndexSrc);
+    GrAssert(kNone_GeometrySrcType == geoSrc.fVertexSrc);
+    fDrawState->unref();
+}
+
+void GrDrawTarget::releaseGeometry() {
+    int popCnt = fGeoSrcStateStack.count() - 1;
+    while (popCnt) {
+        this->popGeometrySource();
+        --popCnt;
+    }
+    this->resetVertexSource();
+    this->resetIndexSource();
+}
+
+void GrDrawTarget::setClip(const GrClipData* clip) {
+    clipWillBeSet(clip);
+    fClip = clip;
+}
+
+const GrClipData* GrDrawTarget::getClip() const {
+    return fClip;
+}
+
+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,
+                                      int vertexCount,
+                                      void** vertices) {
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    bool acquired = false;
+    if (vertexCount > 0) {
+        GrAssert(NULL != vertices);
+        this->releasePreviousVertexSource();
+        geoSrc.fVertexSrc = kNone_GeometrySrcType;
+
+        acquired = this->onReserveVertexSpace(vertexLayout,
+                                              vertexCount,
+                                              vertices);
+    }
+    if (acquired) {
+        geoSrc.fVertexSrc = kReserved_GeometrySrcType;
+        geoSrc.fVertexCount = vertexCount;
+        geoSrc.fVertexLayout = vertexLayout;
+    } else if (NULL != vertices) {
+        *vertices = NULL;
+    }
+    return acquired;
+}
+
+bool GrDrawTarget::reserveIndexSpace(int indexCount,
+                                     void** indices) {
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    bool acquired = false;
+    if (indexCount > 0) {
+        GrAssert(NULL != indices);
+        this->releasePreviousIndexSource();
+        geoSrc.fIndexSrc = kNone_GeometrySrcType;
+
+        acquired = this->onReserveIndexSpace(indexCount, indices);
+    }
+    if (acquired) {
+        geoSrc.fIndexSrc = kReserved_GeometrySrcType;
+        geoSrc.fIndexCount = indexCount;
+    } else if (NULL != indices) {
+        *indices = NULL;
+    }
+    return acquired;
+
+}
+
+bool GrDrawTarget::StageUsesTexCoords(GrVertexLayout layout, int stage) {
+    return SkToBool(layout & gStageTexCoordMasks[stage]);
+}
+
+bool GrDrawTarget::reserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
+                                              int vertexCount,
+                                              int indexCount,
+                                              void** vertices,
+                                              void** indices) {
+    this->willReserveVertexAndIndexSpace(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(GrVertexLayout vertexLayout,
+                                 int32_t* vertexCount,
+                                 int32_t* indexCount) const {
+    if (NULL != vertexCount) {
+        *vertexCount = -1;
+    }
+    if (NULL != indexCount) {
+        *indexCount = -1;
+    }
+    return false;
+}
+
+void GrDrawTarget::releasePreviousVertexSource() {
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    switch (geoSrc.fVertexSrc) {
+        case kNone_GeometrySrcType:
+            break;
+        case kArray_GeometrySrcType:
+            this->releaseVertexArray();
+            break;
+        case kReserved_GeometrySrcType:
+            this->releaseReservedVertexSpace();
+            break;
+        case kBuffer_GeometrySrcType:
+            geoSrc.fVertexBuffer->unref();
+#if GR_DEBUG
+            geoSrc.fVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER;
+#endif
+            break;
+        default:
+            GrCrash("Unknown Vertex Source Type.");
+            break;
+    }
+}
+
+void GrDrawTarget::releasePreviousIndexSource() {
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    switch (geoSrc.fIndexSrc) {
+        case kNone_GeometrySrcType:   // these two don't require
+            break;
+        case kArray_GeometrySrcType:
+            this->releaseIndexArray();
+            break;
+        case kReserved_GeometrySrcType:
+            this->releaseReservedIndexSpace();
+            break;
+        case kBuffer_GeometrySrcType:
+            geoSrc.fIndexBuffer->unref();
+#if GR_DEBUG
+            geoSrc.fIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
+#endif
+            break;
+        default:
+            GrCrash("Unknown Index Source Type.");
+            break;
+    }
+}
+
+void GrDrawTarget::setVertexSourceToArray(GrVertexLayout vertexLayout,
+                                          const void* vertexArray,
+                                          int vertexCount) {
+    this->releasePreviousVertexSource();
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    geoSrc.fVertexSrc = kArray_GeometrySrcType;
+    geoSrc.fVertexLayout = vertexLayout;
+    geoSrc.fVertexCount = vertexCount;
+    this->onSetVertexSourceToArray(vertexArray, vertexCount);
+}
+
+void GrDrawTarget::setIndexSourceToArray(const void* indexArray,
+                                         int indexCount) {
+    this->releasePreviousIndexSource();
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    geoSrc.fIndexSrc = kArray_GeometrySrcType;
+    geoSrc.fIndexCount = indexCount;
+    this->onSetIndexSourceToArray(indexArray, indexCount);
+}
+
+void GrDrawTarget::setVertexSourceToBuffer(GrVertexLayout vertexLayout,
+                                           const GrVertexBuffer* buffer) {
+    this->releasePreviousVertexSource();
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    geoSrc.fVertexSrc    = kBuffer_GeometrySrcType;
+    geoSrc.fVertexBuffer = buffer;
+    buffer->ref();
+    geoSrc.fVertexLayout = vertexLayout;
+}
+
+void GrDrawTarget::setIndexSourceToBuffer(const GrIndexBuffer* buffer) {
+    this->releasePreviousIndexSource();
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    geoSrc.fIndexSrc     = kBuffer_GeometrySrcType;
+    geoSrc.fIndexBuffer  = buffer;
+    buffer->ref();
+}
+
+void GrDrawTarget::resetVertexSource() {
+    this->releasePreviousVertexSource();
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    geoSrc.fVertexSrc = kNone_GeometrySrcType;
+}
+
+void GrDrawTarget::resetIndexSource() {
+    this->releasePreviousIndexSource();
+    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    geoSrc.fIndexSrc = kNone_GeometrySrcType;
+}
+
+void GrDrawTarget::pushGeometrySource() {
+    this->geometrySourceWillPush();
+    GeometrySrcState& newState = fGeoSrcStateStack.push_back();
+    newState.fIndexSrc = kNone_GeometrySrcType;
+    newState.fVertexSrc = kNone_GeometrySrcType;
+#if GR_DEBUG
+    newState.fVertexCount  = ~0;
+    newState.fVertexBuffer = (GrVertexBuffer*)~0;
+    newState.fIndexCount   = ~0;
+    newState.fIndexBuffer = (GrIndexBuffer*)~0;
+#endif
+}
+
+void GrDrawTarget::popGeometrySource() {
+    // if popping last element then pops are unbalanced with pushes
+    GrAssert(fGeoSrcStateStack.count() > 1);
+
+    this->geometrySourceWillPop(fGeoSrcStateStack.fromBack(1));
+    this->releasePreviousVertexSource();
+    this->releasePreviousIndexSource();
+    fGeoSrcStateStack.pop_back();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+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;
+    int maxValidVertex;
+    switch (geoSrc.fVertexSrc) {
+        case kNone_GeometrySrcType:
+            GrCrash("Attempting to draw without vertex src.");
+        case kReserved_GeometrySrcType: // fallthrough
+        case kArray_GeometrySrcType:
+            maxValidVertex = geoSrc.fVertexCount;
+            break;
+        case kBuffer_GeometrySrcType:
+            maxValidVertex = geoSrc.fVertexBuffer->sizeInBytes() / VertexSize(geoSrc.fVertexLayout);
+            break;
+    }
+    if (maxVertex > maxValidVertex) {
+        GrCrash("Drawing outside valid vertex range.");
+    }
+    if (indexCount > 0) {
+        int maxIndex = startIndex + indexCount;
+        int maxValidIndex;
+        switch (geoSrc.fIndexSrc) {
+            case kNone_GeometrySrcType:
+                GrCrash("Attempting to draw indexed geom without index src.");
+            case kReserved_GeometrySrcType: // fallthrough
+            case kArray_GeometrySrcType:
+                maxValidIndex = geoSrc.fIndexCount;
+                break;
+            case kBuffer_GeometrySrcType:
+                maxValidIndex = geoSrc.fIndexBuffer->sizeInBytes() / sizeof(uint16_t);
+                break;
+        }
+        if (maxIndex > maxValidIndex) {
+            GrCrash("Index reads outside valid index range.");
+        }
+    }
+
+    GrAssert(NULL != drawState.getRenderTarget());
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        if (drawState.isStageEnabled(s)) {
+            const GrCustomStage* stage = drawState.getSampler(s).getCustomStage();
+            int numTextures = stage->numTextures();
+            for (int t = 0; t < numTextures; ++t) {
+                GrTexture* texture = stage->texture(t);
+                GrAssert(texture->asRenderTarget() != drawState.getRenderTarget());
+            }
+        }
+    }
+#endif
+    if (NULL == drawState.getRenderTarget()) {
+        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::drawNonIndexed(GrPrimitiveType type,
+                                  int startVertex,
+                                  int vertexCount) {
+    if (vertexCount > 0 &&
+        this->checkDraw(type, startVertex, -1, vertexCount, -1)) {
+        this->onDrawNonIndexed(type, startVertex, vertexCount);
+    }
+}
+
+void GrDrawTarget::stencilPath(const GrPath* path, GrPathFill fill) {
+    // TODO: extract portions of checkDraw that are relevant to path stenciling.
+    GrAssert(NULL != path);
+    GrAssert(fCaps.pathStencilingSupport());
+    GrAssert(kHairLine_GrPathFill != fill);
+    GrAssert(!GrIsFillInverted(fill));
+    this->onStencilPath(path, fill);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Some blend modes allow folding a partial coverage value into the color's
+// alpha channel, while others will blend incorrectly.
+bool GrDrawTarget::canTweakAlphaForCoverage() const {
+    /**
+     * The fractional coverage is f
+     * The src and dst coeffs are Cs and Cd
+     * The dst and src colors are S and D
+     * 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
+     * for Cd we find that only 1, ISA, and ISC produce the correct depth
+     * coeffecient in terms of S' and D.
+     */
+    GrBlendCoeff dstCoeff = this->getDrawState().getDstBlendCoeff();
+    return kOne_GrBlendCoeff == dstCoeff ||
+           kISA_GrBlendCoeff == dstCoeff ||
+           kISC_GrBlendCoeff == dstCoeff;
+}
+
+bool GrDrawTarget::srcAlphaWillBeOne(GrVertexLayout layout) const {
+    const GrDrawState& drawState = this->getDrawState();
+
+    // Check if per-vertex or constant color may have partial alpha
+    if ((layout & kColor_VertexLayoutBit) ||
+        0xff != GrColorUnpackA(drawState.getColor())) {
+        return false;
+    }
+    // Check if color filter could introduce an alpha
+    // (TODO: Consider being more aggressive with regards to detecting 0xff
+    // final alpha from color filter).
+    if (SkXfermode::kDst_Mode != drawState.getColorFilterMode()) {
+        return false;
+    }
+    // Check if a color stage could create a partial alpha
+    for (int s = 0; s < drawState.getFirstCoverageStage(); ++s) {
+        if (this->isStageEnabled(s)) {
+            const GrCustomStage* stage = drawState.getSampler(s).getCustomStage();
+            // FIXME: The param indicates whether the texture is opaque or not. However, the stage
+            // already controls its textures. It really needs to know whether the incoming color
+            // (from a uni, per-vertex colors, or previous stage) is opaque or not.
+            if (!stage->isOpaque(true)) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+namespace {
+GrVertexLayout default_blend_opts_vertex_layout() {
+    GrVertexLayout layout = 0;
+    return layout;
+}
+}
+
+GrDrawTarget::BlendOptFlags
+GrDrawTarget::getBlendOpts(bool forceCoverage,
+                           GrBlendCoeff* srcCoeff,
+                           GrBlendCoeff* dstCoeff) const {
+
+    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;
+    if (NULL == srcCoeff) {
+        srcCoeff = &bogusSrcCoeff;
+    }
+    *srcCoeff = drawState.getSrcBlendCoeff();
+
+    if (NULL == dstCoeff) {
+        dstCoeff = &bogusDstCoeff;
+    }
+    *dstCoeff = drawState.getDstBlendCoeff();
+
+    // We don't ever expect source coeffecients to reference the source
+    GrAssert(kSA_GrBlendCoeff != *srcCoeff &&
+             kISA_GrBlendCoeff != *srcCoeff &&
+             kSC_GrBlendCoeff != *srcCoeff &&
+             kISC_GrBlendCoeff != *srcCoeff);
+    // same for dst
+    GrAssert(kDA_GrBlendCoeff != *dstCoeff &&
+             kIDA_GrBlendCoeff != *dstCoeff &&
+             kDC_GrBlendCoeff != *dstCoeff &&
+             kIDC_GrBlendCoeff != *dstCoeff);
+
+    if (drawState.isColorWriteDisabled()) {
+        *srcCoeff = kZero_GrBlendCoeff;
+        *dstCoeff = kOne_GrBlendCoeff;
+    }
+
+    bool srcAIsOne = this->srcAlphaWillBeOne(layout);
+    bool dstCoeffIsOne = kOne_GrBlendCoeff == *dstCoeff ||
+                         (kSA_GrBlendCoeff == *dstCoeff && srcAIsOne);
+    bool dstCoeffIsZero = kZero_GrBlendCoeff == *dstCoeff ||
+                         (kISA_GrBlendCoeff == *dstCoeff && srcAIsOne);
+
+
+    // 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_GrBlendCoeff == *srcCoeff && dstCoeffIsOne) ||
+        (!(layout & kCoverage_VertexLayoutBit) &&
+         0 == drawState.getCoverage())) {
+        if (drawState.getStencil().doesWrite()) {
+            return kDisableBlend_BlendOptFlag |
+                   kEmitTransBlack_BlendOptFlag;
+        } else {
+            return kSkipDraw_BlendOptFlag;
+        }
+    }
+
+    // check for coverage due to constant coverage, per-vertex coverage,
+    // edge aa or coverage texture stage
+    bool hasCoverage = forceCoverage ||
+                       0xffffffff != drawState.getCoverage() ||
+                       (layout & kCoverage_VertexLayoutBit) ||
+                       (layout & kEdge_VertexLayoutBit);
+    for (int s = drawState.getFirstCoverageStage();
+         !hasCoverage && s < GrDrawState::kNumStages;
+         ++s) {
+        if (this->isStageEnabled(s)) {
+            hasCoverage = true;
+        }
+    }
+
+    // if we don't have coverage we can check whether the dst
+    // has to read at all. If not, we'll disable blending.
+    if (!hasCoverage) {
+        if (dstCoeffIsZero) {
+            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_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_GrBlendCoeff;
+                *dstCoeff = kZero_GrBlendCoeff;
+                return kDisableBlend_BlendOptFlag |
+                       kEmitTransBlack_BlendOptFlag;
+            }
+        }
+    } else {
+        // check whether coverage can be safely rolled into alpha
+        // of if we can skip color computation and just emit coverage
+        if (this->canTweakAlphaForCoverage()) {
+            return kCoverageAsAlpha_BlendOptFlag;
+        }
+        if (dstCoeffIsZero) {
+            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_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
+                // and set dst coeff to 1-Sa.
+                *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_GrBlendCoeff;
+            return  kCoverageAsAlpha_BlendOptFlag;
+        }
+    }
+    return kNone_BlendOpt;
+}
+
+bool GrDrawTarget::willUseHWAALines() const {
+    // there is a conflict between using smooth lines and our use of
+    // 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.hwAALineSupport() ||
+        !this->getDrawState().isHWAntialiasState()) {
+        return false;
+    }
+    BlendOptFlags opts = this->getBlendOpts();
+    return (kDisableBlend_BlendOptFlag & opts) &&
+           (kCoverageAsAlpha_BlendOptFlag & opts);
+}
+
+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().dualSourceBlendingSupport() ||
+           kNone_BlendOpt != this->getBlendOpts(true);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrDrawTarget::drawIndexedInstances(GrPrimitiveType type,
+                                        int instanceCount,
+                                        int verticesPerInstance,
+                                        int indicesPerInstance) {
+    if (!verticesPerInstance || !indicesPerInstance) {
+        return;
+    }
+
+    int instancesPerDraw = this->indexCountInCurrentSource() /
+                           indicesPerInstance;
+    if (!instancesPerDraw) {
+        return;
+    }
+
+    instancesPerDraw = GrMin(instanceCount, instancesPerDraw);
+    int startVertex = 0;
+    while (instanceCount) {
+        this->drawIndexed(type,
+                          startVertex,
+                          0,
+                          verticesPerInstance * instancesPerDraw,
+                          indicesPerInstance * instancesPerDraw);
+        startVertex += verticesPerInstance;
+        instanceCount -= instancesPerDraw;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrDrawTarget::drawRect(const GrRect& rect,
+                            const GrMatrix* matrix,
+                            const GrRect* srcRects[],
+                            const GrMatrix* srcMatrices[]) {
+    GrVertexLayout layout = GetRectVertexLayout(srcRects);
+
+    AutoReleaseGeometry geo(this, layout, 4, 0);
+    if (!geo.succeeded()) {
+        GrPrintf("Failed to get space for vertices!\n");
+        return;
+    }
+
+    SetRectVertices(rect, matrix, srcRects,
+                    srcMatrices, SK_ColorBLACK, layout, geo.vertices());
+
+    drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
+}
+
+GrVertexLayout GrDrawTarget::GetRectVertexLayout(const GrRect* srcRects[]) {
+    if (NULL == srcRects) {
+        return 0;
+    }
+
+    GrVertexLayout layout = 0;
+    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
+        int numTC = 0;
+        if (NULL != srcRects[i]) {
+            layout |= StageTexCoordVertexLayoutBit(i, numTC);
+            ++numTC;
+        }
+    }
+    return layout;
+}
+
+// This method fills int the four vertices for drawing 'rect'.
+//      matrix - is applied to each vertex
+//      srcRects - provide the uvs for each vertex
+//      srcMatrices - are applied to the corresponding 'srcRect'
+//      color - vertex color (replicated in each vertex)
+//      layout - specifies which uvs and/or color are present
+//      vertices - storage for the resulting vertices
+// Note: the color parameter will only be used when kColor_VertexLayoutBit
+// is present in 'layout'
+void GrDrawTarget::SetRectVertices(const GrRect& rect,
+                                   const GrMatrix* matrix,
+                                   const GrRect* srcRects[],
+                                   const GrMatrix* srcMatrices[],
+                                   GrColor color,
+                                   GrVertexLayout layout,
+                                   void* vertices) {
+#if GR_DEBUG
+    // check that the layout and srcRects agree
+    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
+        if (VertexTexCoordsForStage(i, layout) >= 0) {
+            GR_DEBUGASSERT(NULL != srcRects && NULL != srcRects[i]);
+        } else {
+            GR_DEBUGASSERT(NULL == srcRects || NULL == srcRects[i]);
+        }
+    }
+#endif
+
+    int stageOffsets[GrDrawState::kNumStages], colorOffset;
+    int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets,
+                                            &colorOffset, NULL, NULL);
+
+    GrTCast<GrPoint*>(vertices)->setRectFan(rect.fLeft, rect.fTop,
+                                            rect.fRight, rect.fBottom,
+                                            vsize);
+    if (NULL != matrix) {
+        matrix->mapPointsWithStride(GrTCast<GrPoint*>(vertices), vsize, 4);
+    }
+
+    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
+        if (stageOffsets[i] > 0) {
+            GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(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);
+            }
+        }
+    }
+
+    if (layout & kColor_VertexLayoutBit) {
+
+        GrColor* vertCol = GrTCast<GrColor*>(GrTCast<intptr_t>(vertices) + colorOffset);
+
+        for (int i = 0; i < 4; ++i) {
+            *vertCol = color;
+            vertCol = (GrColor*) ((intptr_t) vertCol + vsize);
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrDrawTarget::AutoStateRestore::AutoStateRestore() {
+    fDrawTarget = NULL;
+}
+
+GrDrawTarget::AutoStateRestore::AutoStateRestore(GrDrawTarget* target,
+                                                 ASRInit init) {
+    fDrawTarget = NULL;
+    this->set(target, init);
+}
+
+GrDrawTarget::AutoStateRestore::~AutoStateRestore() {
+    if (NULL != fDrawTarget) {
+        fDrawTarget->setDrawState(fSavedState);
+        fSavedState->unref();
+    }
+}
+
+void GrDrawTarget::AutoStateRestore::set(GrDrawTarget* target, ASRInit init) {
+    GrAssert(NULL == fDrawTarget);
+    fDrawTarget = target;
+    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);
+    }
+    target->setDrawState(fTempState.get());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrDrawTarget::AutoReleaseGeometry::AutoReleaseGeometry(
+                                         GrDrawTarget*  target,
+                                         GrVertexLayout vertexLayout,
+                                         int vertexCount,
+                                         int indexCount) {
+    fTarget = NULL;
+    this->set(target, vertexLayout, vertexCount, indexCount);
+}
+
+GrDrawTarget::AutoReleaseGeometry::AutoReleaseGeometry() {
+    fTarget = NULL;
+}
+
+GrDrawTarget::AutoReleaseGeometry::~AutoReleaseGeometry() {
+    this->reset();
+}
+
+bool GrDrawTarget::AutoReleaseGeometry::set(GrDrawTarget*  target,
+                                            GrVertexLayout vertexLayout,
+                                            int vertexCount,
+                                            int indexCount) {
+    this->reset();
+    fTarget = target;
+    bool success = true;
+    if (NULL != fTarget) {
+        fTarget = target;
+        success = target->reserveVertexAndIndexSpace(vertexLayout,
+                                                     vertexCount,
+                                                     indexCount,
+                                                     &fVertices,
+                                                     &fIndices);
+        if (!success) {
+            fTarget = NULL;
+            this->reset();
+        }
+    }
+    GrAssert(success == (NULL != fTarget));
+    return success;
+}
+
+void GrDrawTarget::AutoReleaseGeometry::reset() {
+    if (NULL != fTarget) {
+        if (NULL != fVertices) {
+            fTarget->resetVertexSource();
+        }
+        if (NULL != fIndices) {
+            fTarget->resetIndexSource();
+        }
+        fTarget = NULL;
+    }
+    fVertices = NULL;
+    fIndices = NULL;
+}
+
+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
new file mode 100644
index 0000000..134cccd
--- /dev/null
+++ b/src/gpu/GrDrawTarget.h
@@ -0,0 +1,1057 @@
+
+/*
+ * 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 GrDrawTarget_DEFINED
+#define GrDrawTarget_DEFINED
+
+#include "GrDrawState.h"
+#include "GrIndexBuffer.h"
+#include "GrMatrix.h"
+#include "GrRefCnt.h"
+#include "GrTemplates.h"
+
+#include "SkXfermode.h"
+#include "SkTLazy.h"
+#include "SkTArray.h"
+
+class GrClipData;
+class GrPath;
+class GrVertexBuffer;
+
+class GrDrawTarget : public GrRefCnt {
+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;
+        bool fStencilWrapOpsSupport     : 1;
+        bool fHWAALineSupport           : 1;
+        bool fShaderDerivativeSupport   : 1;
+        bool fGeometryShaderSupport     : 1;
+        bool fFSAASupport               : 1;
+        bool fDualSourceBlendingSupport : 1;
+        bool fBufferLockSupport         : 1;
+        bool fPathStencilingSupport     : 1;
+        int fMaxRenderTargetSize;
+        int fMaxTextureSize;
+    };
+
+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
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    GrDrawTarget();
+    virtual ~GrDrawTarget();
+
+    /**
+     * Gets the capabilities of the draw target.
+     */
+    const Caps& getCaps() const { return fCaps; }
+
+    /**
+     * Sets the current clip to the region specified by clip. All draws will be
+     * clipped against this clip if kClip_StateBit is enabled.
+     *
+     * Setting the clip may (or may not) zero out the client's stencil bits.
+     *
+     * @param description of the clipping region
+     */
+    void setClip(const GrClipData* clip);
+
+    /**
+     * Gets the current clip.
+     *
+     * @return the clip.
+     */
+    const GrClipData* getClip() const;
+
+    /**
+     * 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 setDrawState(GrDrawState*  drawState);
+
+    /**
+     * Read-only access to the GrDrawTarget's current draw state.
+     */
+    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
+     * blend modes it is safe to fold the coverage into constant or per-vertex
+     * color alpha value. For other blend modes they must be handled separately.
+     * Depending on features available in the underlying 3D API this may or may
+     * not be possible.
+     *
+     * 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. 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 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;
+
+    /**
+     * 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));
+    }
+
+    static bool StageUsesTexCoords(GrVertexLayout layout, int stage);
+
+private:
+    // non-stage bits start at this index.
+    static const int STAGE_BIT_CNT = GrDrawState::kNumStages *
+                                     GrDrawState::kMaxTexCoords;
+public:
+
+    /**
+     * Additional Bits that can be specified in GrVertexLayout.
+     */
+    enum VertexLayoutBits {
+        /* vertices have colors (GrColor) */
+        kColor_VertexLayoutBit              = 1 << (STAGE_BIT_CNT + 0),
+        /* vertices have coverage (GrColor)
+         */
+        kCoverage_VertexLayoutBit           = 1 << (STAGE_BIT_CNT + 1),
+        /* Use text vertices. (Pos and tex coords may be a different type for
+         * text [GrGpuTextVertex vs GrPoint].)
+         */
+        kTextFormat_VertexLayoutBit         = 1 << (STAGE_BIT_CNT + 2),
+
+        /* Each vertex specificies an edge. Distance to the edge is used to
+         * compute a coverage. See GrDrawState::setVertexEdgeType().
+         */
+        kEdge_VertexLayoutBit               = 1 << (STAGE_BIT_CNT + 3),
+        // for below assert
+        kDummyVertexLayoutBit,
+        kHighVertexLayoutBit = kDummyVertexLayoutBit - 1
+    };
+    // make sure we haven't exceeded the number of bits in GrVertexLayout.
+    GR_STATIC_ASSERT(kHighVertexLayoutBit < ((uint64_t)1 << 8*sizeof(GrVertexLayout)));
+
+    /**
+     * There are three types of "sources" of geometry (vertices and indices) for
+     * draw calls made on the target. When performing an indexed draw, the
+     * indices and vertices can use different source types. Once a source is
+     * specified it can be used for multiple draws. However, the time at which
+     * the geometry data is no longer editable depends on the source type.
+     *
+     * Sometimes it is necessary to perform a draw while upstack code has
+     * already specified geometry that it isn't finished with. So there are push
+     * and pop methods. This allows the client to push the sources, draw
+     * something using alternate sources, and then pop to restore the original
+     * sources.
+     *
+     * Aside from pushes and pops, a source remains valid until another source
+     * is set or resetVertexSource / resetIndexSource is called. Drawing from
+     * a reset source is an error.
+     *
+     * The 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,
+     *    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. When the data in the buffer is consumed depends on the
+     *    GrDrawTarget subclass. For deferred subclasses the caller has to
+     *    guarantee that the data is still available in the buffers at playback.
+     *    (TODO: Make this more automatic as we have done for read/write pixels)
+     */
+
+    /**
+     * Reserves space for vertices and/or indices. Zero can be specifed as
+     * either the vertex or index count if the caller desires to only reserve
+     * space for only indices or only vertices. If zero is specifed for
+     * vertexCount then the vertex source will be unmodified and likewise for
+     * indexCount.
+     *
+     * If the function returns true then the reserve suceeded and the vertices
+     * and indices pointers will point to the space created.
+     *
+     * If the target cannot make space for the request then this function will
+     * return false. If vertexCount was non-zero then upon failure the vertex
+     * source is reset and likewise for indexCount.
+     *
+     * The pointers to the space allocated for vertices and indices remain valid
+     * until a drawIndexed, drawNonIndexed, drawIndexedInstances, 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 indexCount   the number of indices to reserve space for. Can be 0.
+     * @param vertices     will point to reserved vertex space if vertexCount is
+     *                     non-zero. Illegal to pass NULL if vertexCount > 0.
+     * @param indices      will point to reserved index space if indexCount is
+     *                     non-zero. Illegal to pass NULL if indexCount > 0.
+     */
+     bool reserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
+                                     int vertexCount,
+                                     int indexCount,
+                                     void** vertices,
+                                     void** indices);
+
+    /**
+     * Provides hints to caller about the number of vertices and indices
+     * that can be allocated cheaply. This can be useful if caller is reserving
+     * space but doesn't know exactly how much geometry is needed.
+     *
+     * Also may hint whether the draw target should be flushed first. This is
+     * useful for deferred targets.
+     *
+     * @param vertexLayout layout 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
+     *                     allocated cheaply. Negative means no hint.
+     *                     Ignored if NULL.
+     * @param indexCount   in: hint about how many indices the caller would
+     *                     like to allocate.
+     *                     out: a hint about the number of indices that can be
+     *                     allocated cheaply. Negative means no hint.
+     *                     Ignored if NULL.
+     *
+     * @return  true if target should be flushed based on the input values.
+     */
+    virtual bool geometryHints(GrVertexLayout vertexLayout,
+                               int* vertexCount,
+                               int* indexCount) const;
+
+    /**
+     * Sets source of vertex data for the next draw. Array must contain
+     * the vertex data when this is called.
+     *
+     * @param array         cpu array containing vertex data.
+     * @param size          size of the vertex data.
+     * @param vertexCount   the number of vertices in the array.
+     */
+    void setVertexSourceToArray(GrVertexLayout vertexLayout,
+                                const void* vertexArray,
+                                int vertexCount);
+
+    /**
+     * Sets source of index data for the next indexed draw. Array must contain
+     * the indices when this is called.
+     *
+     * @param array         cpu array containing index data.
+     * @param indexCount    the number of indices in the array.
+     */
+    void setIndexSourceToArray(const void* indexArray, int indexCount);
+
+    /**
+     * Sets source of vertex data for the next draw. Data does not have to be
+     * in the buffer until drawIndexed, drawNonIndexed, or drawIndexedInstances.
+     *
+     * @param buffer        vertex buffer containing vertex data. Must be
+     *                      unlocked before draw call.
+     * @param vertexLayout  layout of the vertex data in the buffer.
+     */
+    void setVertexSourceToBuffer(GrVertexLayout vertexLayout,
+                                 const GrVertexBuffer* buffer);
+
+    /**
+     * Sets source of index data for the next indexed draw. Data does not have
+     * to be in the buffer until drawIndexed.
+     *
+     * @param buffer index buffer containing indices. Must be unlocked
+     *               before indexed draw call.
+     */
+    void setIndexSourceToBuffer(const GrIndexBuffer* buffer);
+
+    /**
+     * Resets vertex source. Drawing from reset vertices is illegal. Set vertex
+     * source to reserved, array, or buffer before next draw. May be able to free
+     * up temporary storage allocated by setVertexSourceToArray or
+     * reserveVertexSpace.
+     */
+    void resetVertexSource();
+
+    /**
+     * Resets index source. Indexed Drawing from reset indices is illegal. Set
+     * index source to reserved, array, or buffer before next indexed draw. May
+     * be able to free up temporary storage allocated by setIndexSourceToArray
+     * or reserveIndexSpace.
+     */
+    void resetIndexSource();
+
+    /**
+     * Query to find out if the vertex or index source is reserved.
+     */
+    bool hasReservedVerticesOrIndices() const {
+        return kReserved_GeometrySrcType == this->getGeomSrc().fVertexSrc ||
+        kReserved_GeometrySrcType == this->getGeomSrc().fIndexSrc;
+    }
+
+    /**
+     * Pushes and resets the vertex/index sources. Any reserved vertex / index
+     * data is finalized (i.e. cannot be updated after the matching pop but can
+     * be drawn from). Must be balanced by a pop.
+     */
+    void pushGeometrySource();
+
+    /**
+     * Pops the vertex / index sources from the matching push.
+     */
+    void popGeometrySource();
+
+    /**
+     * Draws indexed geometry using the current state and current vertex / index
+     * sources.
+     *
+     * @param type         The type of primitives to draw.
+     * @param startVertex  the vertex in the vertex array/buffer corresponding
+     *                     to index 0
+     * @param startIndex   first index to read from index src.
+     * @param vertexCount  one greater than the max index.
+     * @param indexCount   the number of index elements to read. The index count
+     *                     is effectively trimmed to the last completely
+     *                     specified primitive.
+     */
+    void drawIndexed(GrPrimitiveType type,
+                     int startVertex,
+                     int startIndex,
+                     int vertexCount,
+                     int indexCount);
+
+    /**
+     * Draws non-indexed geometry using the current state and current vertex
+     * sources.
+     *
+     * @param type         The type of primitives to draw.
+     * @param startVertex  the vertex in the vertex array/buffer corresponding
+     *                     to index 0
+     * @param vertexCount  one greater than the max index.
+     */
+    void drawNonIndexed(GrPrimitiveType type,
+                        int startVertex,
+                        int vertexCount);
+
+    /**
+     * 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*, GrPathFill);
+
+    /**
+     * 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.
+     * @param rect      the rect to draw
+     * @param matrix    optional matrix applied to rect (before viewMatrix)
+     * @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
+     *                  used as coordinates for stage i. Otherwise, if stage i
+     *                  is enabled then rect is used as the coordinates.
+     * @param srcMatrices   optional matrices applied to srcRects. If
+     *                      srcRect[i] is non-NULL and srcMatrices[i] is
+     *                      non-NULL then srcRect[i] will be transformed by
+     *                      srcMatrix[i]. srcMatrices can be NULL when no
+     *                      srcMatrices are desired.
+     */
+    virtual void drawRect(const GrRect& rect,
+                          const GrMatrix* matrix,
+                          const GrRect* srcRects[],
+                          const GrMatrix* srcMatrices[]);
+
+    /**
+     * 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. Morever, 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).
+     */
+    virtual void drawIndexedInstances(GrPrimitiveType type,
+                                      int instanceCount,
+                                      int verticesPerInstance,
+                                      int indicesPerInstance);
+
+    /**
+     * Helper for drawRect when the caller doesn't need separate src rects or
+     * matrices.
+     */
+    void drawSimpleRect(const GrRect& rect,
+                        const GrMatrix* matrix) {
+         drawRect(rect, matrix, NULL, NULL);
+    }
+
+    /**
+     * 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 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() {};
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * 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();
+
+        /**
+         * 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();
+
+        /**
+         * 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, ASRInit init);
+
+    private:
+        GrDrawTarget*        fDrawTarget;
+        SkTLazy<GrDrawState> fTempState;
+        GrDrawState*         fSavedState;
+    };
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    class AutoReleaseGeometry : ::GrNoncopyable {
+    public:
+        AutoReleaseGeometry(GrDrawTarget*  target,
+                            GrVertexLayout vertexLayout,
+                            int            vertexCount,
+                            int            indexCount);
+        AutoReleaseGeometry();
+        ~AutoReleaseGeometry();
+        bool set(GrDrawTarget*  target,
+                 GrVertexLayout vertexLayout,
+                 int            vertexCount,
+                 int            indexCount);
+        bool succeeded() const { return NULL != fTarget; }
+        void* vertices() const { GrAssert(this->succeeded()); return fVertices; }
+        void* indices() const { GrAssert(this->succeeded()); return fIndices; }
+        GrPoint* positions() const {
+            return static_cast<GrPoint*>(this->vertices());
+        }
+
+    private:
+        void reset();
+
+        GrDrawTarget* fTarget;
+        void*         fVertices;
+        void*         fIndices;
+    };
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    class AutoClipRestore : ::GrNoncopyable {
+    public:
+        AutoClipRestore(GrDrawTarget* target) {
+            fTarget = target;
+            fClip = fTarget->getClip();
+        }
+
+        ~AutoClipRestore() {
+            fTarget->setClip(fClip);
+        }
+    private:
+        GrDrawTarget*      fTarget;
+        const GrClipData*  fClip;
+    };
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    class AutoGeometryPush : ::GrNoncopyable {
+    public:
+        AutoGeometryPush(GrDrawTarget* target) {
+            GrAssert(NULL != target);
+            fTarget = target;
+            target->pushGeometrySource();
+        }
+        ~AutoGeometryPush() {
+            fTarget->popGeometrySource();
+        }
+    private:
+        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 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:
+
+    /**
+     * Optimizations for blending / coverage to be applied based on the current
+     * state.
+     * Subclasses that actually draw (as opposed to those that just buffer for
+     * playback) must implement the flags that replace the output color.
+     */
+    enum BlendOptFlags {
+        /**
+         * No optimization
+         */
+        kNone_BlendOpt = 0,
+        /**
+         * Don't draw at all
+         */
+        kSkipDraw_BlendOptFlag = 0x2,
+        /**
+         * Emit the src color, disable HW blending (replace dst with src)
+         */
+        kDisableBlend_BlendOptFlag = 0x4,
+        /**
+         * The coverage value does not have to be computed separately from
+         * alpha, the the output color can be the modulation of the two.
+         */
+        kCoverageAsAlpha_BlendOptFlag = 0x1,
+        /**
+         * Instead of emitting a src color, emit coverage in the alpha channel
+         * and r,g,b are "don't cares".
+         */
+        kEmitCoverage_BlendOptFlag = 0x10,
+        /**
+         * Emit transparent black instead of the src color, no need to compute
+         * coverage.
+         */
+        kEmitTransBlack_BlendOptFlag = 0x8,
+    };
+    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
+    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(GrVertexLayout vertexLayout) 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 {
+            // valid if src type is buffer
+            const GrVertexBuffer*   fVertexBuffer;
+            // valid if src type is reserved or array
+            int                     fVertexCount;
+        };
+
+        GeometrySrcType         fIndexSrc;
+        union {
+            // valid if src type is buffer
+            const GrIndexBuffer*    fIndexBuffer;
+            // valid if src type is reserved or array
+            int                     fIndexCount;
+        };
+
+        GrVertexLayout          fVertexLayout;
+    };
+
+    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;
+        }
+    }
+
+    bool isStageEnabled(int stage) const {
+        return this->getDrawState().isStageEnabled(stage);
+    }
+
+    // A sublcass can optionally overload this function to be notified before
+    // vertex and index space is reserved.
+    virtual void willReserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
+                                                int vertexCount,
+                                                int indexCount) {}
+
+
+    // implemented by subclass to allocate space for reserved geom
+    virtual bool onReserveVertexSpace(GrVertexLayout vertexLayout,
+                                      int vertexCount,
+                                      void** vertices) = 0;
+    virtual bool onReserveIndexSpace(int indexCount, void** indices) = 0;
+    // implemented by subclass to handle release of reserved geom space
+    virtual void releaseReservedVertexSpace() = 0;
+    virtual void releaseReservedIndexSpace() = 0;
+    // subclass must consume array contents when set
+    virtual void onSetVertexSourceToArray(const void* vertexArray,
+                                          int vertexCount) = 0;
+    virtual void onSetIndexSourceToArray(const void* indexArray,
+                                         int indexCount) = 0;
+    // subclass is notified that geom source will be set away from an array
+    virtual void releaseVertexArray() = 0;
+    virtual void releaseIndexArray() = 0;
+    // subclass overrides to be notified just before geo src state
+    // is pushed/popped.
+    virtual void geometrySourceWillPush() = 0;
+    virtual void geometrySourceWillPop(const GeometrySrcState& restoredState) = 0;
+    // subclass called to perform drawing
+    virtual void onDrawIndexed(GrPrimitiveType type,
+                               int startVertex,
+                               int startIndex,
+                               int vertexCount,
+                               int indexCount) = 0;
+    virtual void onDrawNonIndexed(GrPrimitiveType type,
+                                  int startVertex,
+                                  int vertexCount) = 0;
+    virtual void onStencilPath(const GrPath*, GrPathFill) = 0;
+
+    // subclass overrides to be notified when clip is set. Must call
+    // INHERITED::clipwillBeSet
+    virtual void clipWillBeSet(const GrClipData* clipData) {}
+
+    // Helpers for drawRect, protected so subclasses that override drawRect
+    // can use them.
+    static GrVertexLayout GetRectVertexLayout(const GrRect* srcRects[]);
+
+    static void SetRectVertices(const GrRect& rect,
+                                const GrMatrix* matrix,
+                                const GrRect* srcRects[],
+                                const GrMatrix* srcMatrices[],
+                                GrColor color,
+                                GrVertexLayout layout,
+                                void* vertices);
+
+    // accessors for derived classes
+    const GeometrySrcState& getGeomSrc() const {
+        return fGeoSrcStateStack.back();
+    }
+    // it is prefereable to call this rather than getGeomSrc()->fVertexLayout
+    // because of the assert.
+    GrVertexLayout getVertexLayout() const {
+        // the vertex layout is only valid if a vertex source has been
+        // specified.
+        GrAssert(this->getGeomSrc().fVertexSrc != kNone_GeometrySrcType);
+        return this->getGeomSrc().fVertexLayout;
+    }
+
+    // allows derived class to set the caps
+    CapsInternals* capsInternals() { return &fCaps.fInternals; }
+
+    const GrClipData* fClip;
+
+    GrDrawState* fDrawState;
+    GrDrawState fDefaultDrawState;
+
+    Caps fCaps;
+
+    // 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:
+    // helpers for reserving vertex and index space.
+    bool reserveVertexSpace(GrVertexLayout vertexLayout,
+                            int vertexCount,
+                            void** vertices);
+    bool reserveIndexSpace(int indexCount, void** indices);
+
+    // called by drawIndexed and drawNonIndexed. Use a negative indexCount to
+    // indicate non-indexed drawing.
+    bool checkDraw(GrPrimitiveType type, int startVertex,
+                   int startIndex, int vertexCount,
+                   int indexCount) const;
+    // called when setting a new vert/idx source to unref prev vb/ib
+    void releasePreviousVertexSource();
+    void releasePreviousIndexSource();
+
+    enum {
+        kPreallocGeoSrcStateStackCnt = 4,
+    };
+    SkSTArray<kPreallocGeoSrcStateStackCnt,
+              GeometrySrcState, true>           fGeoSrcStateStack;
+
+    typedef GrRefCnt INHERITED;
+};
+
+GR_MAKE_BITFIELD_OPS(GrDrawTarget::BlendOptFlags);
+
+#endif
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
new file mode 100644
index 0000000..c156fa3
--- /dev/null
+++ b/src/gpu/GrGeometryBuffer.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 GrGeometryBuffer_DEFINED
+#define GrGeometryBuffer_DEFINED
+
+#include "GrResource.h"
+
+class GrGpu;
+
+/**
+ * Parent class for vertex and index buffers
+ */
+class GrGeometryBuffer : public GrResource {
+public:
+    SK_DECLARE_INST_COUNT(GrGeometryBuffer);
+
+    /**
+     *Retrieves whether the buffer was created with the dynamic flag
+     *
+     * @return true if the buffer was created with the dynamic flag
+     */
+    bool dynamic() const { return fDynamic; }
+
+    /**
+     * Locks the buffer to be written by the CPU.
+     *
+     * The previous content of the buffer is invalidated. It is an error
+     * to draw from the buffer while it is locked. It is an error to call lock
+     * on an already locked buffer.
+     *
+     * @return a pointer to the data or NULL if the lock fails.
+     */
+    virtual void* lock() = 0;
+
+    /**
+     * Returns the same ptr that lock() returned at time of lock or NULL if the
+     * is not locked.
+     *
+     * @return ptr to locked buffer data or undefined if buffer is not locked.
+     */
+    virtual void* lockPtr() const = 0;
+
+    /**
+     * Unlocks the buffer.
+     *
+     * The pointer returned by the previous lock call will no longer be valid.
+     */
+    virtual void unlock() = 0;
+
+    /**
+     Queries whether the buffer has been locked.
+
+     @return true if the buffer is locked, false otherwise.
+     */
+    virtual bool isLocked() const = 0;
+
+    /**
+     * Updates the buffer data.
+     *
+     * The size of the buffer will be preserved. The src data will be
+     * placed at the begining of the buffer and any remaining contents will
+     * be undefined.
+     *
+     * @return returns true if the update succeeds, false otherwise.
+     */
+    virtual bool updateData(const void* src, size_t srcSizeInBytes) = 0;
+
+    // GrResource overrides
+    virtual size_t sizeInBytes() const { return fSizeInBytes; }
+
+protected:
+    GrGeometryBuffer(GrGpu* gpu, size_t sizeInBytes, bool dynamic)
+        : INHERITED(gpu)
+        , fSizeInBytes(sizeInBytes)
+        , fDynamic(dynamic) {}
+
+private:
+    size_t   fSizeInBytes;
+    bool     fDynamic;
+
+    typedef GrResource INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
new file mode 100644
index 0000000..4bc1574
--- /dev/null
+++ b/src/gpu/GrGpu.cpp
@@ -0,0 +1,560 @@
+
+/*
+ * 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 "GrGpu.h"
+
+#include "GrBufferAllocPool.h"
+#include "GrContext.h"
+#include "GrIndexBuffer.h"
+#include "GrStencilBuffer.h"
+#include "GrVertexBuffer.h"
+
+// probably makes no sense for this to be less than a page
+static const size_t VERTEX_POOL_VB_SIZE = 1 << 18;
+static const int VERTEX_POOL_VB_COUNT = 4;
+static const size_t INDEX_POOL_IB_SIZE = 1 << 16;
+static const int INDEX_POOL_IB_COUNT = 4;
+
+////////////////////////////////////////////////////////////////////////////////
+
+extern void gr_run_unittests();
+
+#define DEBUG_INVAL_BUFFER    0xdeadcafe
+#define DEBUG_INVAL_START_IDX -1
+
+GrGpu::GrGpu()
+    : fContext(NULL)
+    , fResetTimestamp(kExpiredTimestamp+1)
+    , fVertexPool(NULL)
+    , fIndexPool(NULL)
+    , fVertexPoolUseCnt(0)
+    , fIndexPoolUseCnt(0)
+    , fQuadIndexBuffer(NULL)
+    , fUnitSquareVertexBuffer(NULL)
+    , fContextIsDirty(true) {
+
+    fClipMaskManager.setGpu(this);
+
+#if GR_DEBUG
+    //gr_run_unittests();
+#endif
+
+    fGeomPoolStateStack.push_back();
+#if GR_DEBUG
+    GeometryPoolState& poolState = fGeomPoolStateStack.back();
+    poolState.fPoolVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER;
+    poolState.fPoolStartVertex = DEBUG_INVAL_START_IDX;
+    poolState.fPoolIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
+    poolState.fPoolStartIndex = DEBUG_INVAL_START_IDX;
+#endif
+
+    for (int i = 0; i < kGrPixelConfigCount; ++i) {
+        fConfigRenderSupport[i] = false;
+    };
+}
+
+GrGpu::~GrGpu() {
+    this->releaseResources();
+}
+
+void GrGpu::abandonResources() {
+
+    fClipMaskManager.releaseResources();
+
+    while (NULL != fResourceList.head()) {
+        fResourceList.head()->abandon();
+    }
+
+    GrAssert(NULL == fQuadIndexBuffer || !fQuadIndexBuffer->isValid());
+    GrAssert(NULL == fUnitSquareVertexBuffer ||
+             !fUnitSquareVertexBuffer->isValid());
+    GrSafeSetNull(fQuadIndexBuffer);
+    GrSafeSetNull(fUnitSquareVertexBuffer);
+    delete fVertexPool;
+    fVertexPool = NULL;
+    delete fIndexPool;
+    fIndexPool = NULL;
+}
+
+void GrGpu::releaseResources() {
+
+    fClipMaskManager.releaseResources();
+
+    while (NULL != fResourceList.head()) {
+        fResourceList.head()->release();
+    }
+
+    GrAssert(NULL == fQuadIndexBuffer || !fQuadIndexBuffer->isValid());
+    GrAssert(NULL == fUnitSquareVertexBuffer ||
+             !fUnitSquareVertexBuffer->isValid());
+    GrSafeSetNull(fQuadIndexBuffer);
+    GrSafeSetNull(fUnitSquareVertexBuffer);
+    delete fVertexPool;
+    fVertexPool = NULL;
+    delete fIndexPool;
+    fIndexPool = NULL;
+}
+
+void GrGpu::insertResource(GrResource* resource) {
+    GrAssert(NULL != resource);
+    GrAssert(this == resource->getGpu());
+
+    fResourceList.addToHead(resource);
+}
+
+void GrGpu::removeResource(GrResource* resource) {
+    GrAssert(NULL != resource);
+    GrAssert(this == resource->getGpu());
+
+    fResourceList.remove(resource);
+}
+
+
+void GrGpu::unimpl(const char msg[]) {
+#if GR_DEBUG
+    GrPrintf("--- GrGpu unimplemented(\"%s\")\n", msg);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrTexture* GrGpu::createTexture(const GrTextureDesc& desc,
+                                const void* srcData, size_t rowBytes) {
+    this->handleDirtyContext();
+    GrTexture* tex = this->onCreateTexture(desc, srcData, rowBytes);
+    if (NULL != tex &&
+        (kRenderTarget_GrTextureFlagBit & desc.fFlags) &&
+        !(kNoStencil_GrTextureFlagBit & desc.fFlags)) {
+        GrAssert(NULL != tex->asRenderTarget());
+        // TODO: defer this and attach dynamically
+        if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) {
+            tex->unref();
+            return NULL;
+        }
+    }
+    return tex;
+}
+
+bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt) {
+    GrAssert(NULL == rt->getStencilBuffer());
+    GrStencilBuffer* sb =
+        this->getContext()->findStencilBuffer(rt->width(),
+                                              rt->height(),
+                                              rt->numSamples());
+    if (NULL != sb) {
+        rt->setStencilBuffer(sb);
+        bool attached = this->attachStencilBufferToRenderTarget(sb, rt);
+        if (!attached) {
+            rt->setStencilBuffer(NULL);
+        }
+        return attached;
+    }
+    if (this->createStencilBufferForRenderTarget(rt,
+                                                 rt->width(), rt->height())) {
+        // Right now we're clearing the stencil buffer here after it is
+        // attached to an RT for the first time. When we start matching
+        // stencil buffers with smaller color targets this will no longer
+        // be correct because it won't be guaranteed to clear the entire
+        // sb.
+        // We used to clear down in the GL subclass using a special purpose
+        // FBO. But iOS doesn't allow a stencil-only FBO. It reports unsupported
+        // FBO status.
+        GrDrawState::AutoRenderTargetRestore artr(this->drawState(), rt);
+        this->clearStencil();
+        return true;
+    } else {
+        return false;
+    }
+}
+
+GrTexture* GrGpu::createPlatformTexture(const GrPlatformTextureDesc& desc) {
+    this->handleDirtyContext();
+    GrTexture* tex = this->onCreatePlatformTexture(desc);
+    if (NULL == tex) {
+        return NULL;
+    }
+    // TODO: defer this and attach dynamically
+    GrRenderTarget* tgt = tex->asRenderTarget();
+    if (NULL != tgt &&
+        !this->attachStencilBufferToRenderTarget(tgt)) {
+        tex->unref();
+        return NULL;
+    } else {
+        return tex;
+    }
+}
+
+GrRenderTarget* GrGpu::createPlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) {
+    this->handleDirtyContext();
+    return this->onCreatePlatformRenderTarget(desc);
+}
+
+GrVertexBuffer* GrGpu::createVertexBuffer(uint32_t size, bool dynamic) {
+    this->handleDirtyContext();
+    return this->onCreateVertexBuffer(size, dynamic);
+}
+
+GrIndexBuffer* GrGpu::createIndexBuffer(uint32_t size, bool dynamic) {
+    this->handleDirtyContext();
+    return this->onCreateIndexBuffer(size, dynamic);
+}
+
+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;
+    }
+    this->handleDirtyContext();
+    this->onClear(rect, color);
+}
+
+void GrGpu::forceRenderTargetFlush() {
+    this->handleDirtyContext();
+    this->onForceRenderTargetFlush();
+}
+
+bool GrGpu::readPixels(GrRenderTarget* target,
+                       int left, int top, int width, int height,
+                       GrPixelConfig config, void* buffer,
+                       size_t rowBytes, bool invertY) {
+    this->handleDirtyContext();
+    return this->onReadPixels(target, left, top, width, height,
+                              config, buffer, rowBytes, invertY);
+}
+
+void GrGpu::writeTexturePixels(GrTexture* texture,
+                               int left, int top, int width, int height,
+                               GrPixelConfig config, const void* buffer,
+                               size_t rowBytes) {
+    this->handleDirtyContext();
+    this->onWriteTexturePixels(texture, left, top, width, height,
+                               config, buffer, rowBytes);
+}
+
+void GrGpu::resolveRenderTarget(GrRenderTarget* target) {
+    GrAssert(target);
+    this->handleDirtyContext();
+    this->onResolveRenderTarget(target);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+static const int MAX_QUADS = 1 << 12; // max possible: (1 << 14) - 1;
+
+GR_STATIC_ASSERT(4 * MAX_QUADS <= 65535);
+
+static inline void fill_indices(uint16_t* indices, int quadCount) {
+    for (int i = 0; i < quadCount; ++i) {
+        indices[6 * i + 0] = 4 * i + 0;
+        indices[6 * i + 1] = 4 * i + 1;
+        indices[6 * i + 2] = 4 * i + 2;
+        indices[6 * i + 3] = 4 * i + 0;
+        indices[6 * i + 4] = 4 * i + 2;
+        indices[6 * i + 5] = 4 * i + 3;
+    }
+}
+
+const GrIndexBuffer* GrGpu::getQuadIndexBuffer() const {
+    if (NULL == fQuadIndexBuffer) {
+        static const int SIZE = sizeof(uint16_t) * 6 * MAX_QUADS;
+        GrGpu* me = const_cast<GrGpu*>(this);
+        fQuadIndexBuffer = me->createIndexBuffer(SIZE, false);
+        if (NULL != fQuadIndexBuffer) {
+            uint16_t* indices = (uint16_t*)fQuadIndexBuffer->lock();
+            if (NULL != indices) {
+                fill_indices(indices, MAX_QUADS);
+                fQuadIndexBuffer->unlock();
+            } else {
+                indices = (uint16_t*)GrMalloc(SIZE);
+                fill_indices(indices, MAX_QUADS);
+                if (!fQuadIndexBuffer->updateData(indices, SIZE)) {
+                    fQuadIndexBuffer->unref();
+                    fQuadIndexBuffer = NULL;
+                    GrCrash("Can't get indices into buffer!");
+                }
+                GrFree(indices);
+            }
+        }
+    }
+
+    return fQuadIndexBuffer;
+}
+
+const GrVertexBuffer* GrGpu::getUnitSquareVertexBuffer() const {
+    if (NULL == fUnitSquareVertexBuffer) {
+
+        static const GrPoint DATA[] = {
+            { 0,            0 },
+            { GR_Scalar1,   0 },
+            { GR_Scalar1,   GR_Scalar1 },
+            { 0,            GR_Scalar1 }
+#if 0
+            GrPoint(0,         0),
+            GrPoint(GR_Scalar1,0),
+            GrPoint(GR_Scalar1,GR_Scalar1),
+            GrPoint(0,         GR_Scalar1)
+#endif
+        };
+        static const size_t SIZE = sizeof(DATA);
+
+        GrGpu* me = const_cast<GrGpu*>(this);
+        fUnitSquareVertexBuffer = me->createVertexBuffer(SIZE, false);
+        if (NULL != fUnitSquareVertexBuffer) {
+            if (!fUnitSquareVertexBuffer->updateData(DATA, SIZE)) {
+                fUnitSquareVertexBuffer->unref();
+                fUnitSquareVertexBuffer = NULL;
+                GrCrash("Can't get vertices into buffer!");
+            }
+        }
+    }
+
+    return fUnitSquareVertexBuffer;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool GrGpu::setupClipAndFlushState(DrawType type) {
+
+    if (!fClipMaskManager.setupClipping(fClip)) {
+        return false;
+    }
+
+    if (!this->flushGraphicsState(type)) {
+        return false;
+    }
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrGpu::geometrySourceWillPush() {
+    const GeometrySrcState& geoSrc = this->getGeomSrc();
+    if (kArray_GeometrySrcType == geoSrc.fVertexSrc ||
+        kReserved_GeometrySrcType == geoSrc.fVertexSrc) {
+        this->finalizeReservedVertices();
+    }
+    if (kArray_GeometrySrcType == geoSrc.fIndexSrc ||
+        kReserved_GeometrySrcType == geoSrc.fIndexSrc) {
+        this->finalizeReservedIndices();
+    }
+    GeometryPoolState& newState = fGeomPoolStateStack.push_back();
+#if GR_DEBUG
+    newState.fPoolVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER;
+    newState.fPoolStartVertex = DEBUG_INVAL_START_IDX;
+    newState.fPoolIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
+    newState.fPoolStartIndex = DEBUG_INVAL_START_IDX;
+#endif
+}
+
+void GrGpu::geometrySourceWillPop(const GeometrySrcState& restoredState) {
+    // if popping last entry then pops are unbalanced with pushes
+    GrAssert(fGeomPoolStateStack.count() > 1);
+    fGeomPoolStateStack.pop_back();
+}
+
+void GrGpu::onDrawIndexed(GrPrimitiveType type,
+                          int startVertex,
+                          int startIndex,
+                          int vertexCount,
+                          int indexCount) {
+
+    this->handleDirtyContext();
+
+    if (!this->setupClipAndFlushState(PrimTypeToDrawType(type))) {
+        return;
+    }
+
+    int sVertex = startVertex;
+    int sIndex = startIndex;
+    setupGeometry(&sVertex, &sIndex, vertexCount, indexCount);
+
+    this->onGpuDrawIndexed(type, sVertex, sIndex,
+                           vertexCount, indexCount);
+}
+
+void GrGpu::onDrawNonIndexed(GrPrimitiveType type,
+                             int startVertex,
+                             int vertexCount) {
+    this->handleDirtyContext();
+
+    if (!this->setupClipAndFlushState(PrimTypeToDrawType(type))) {
+        return;
+    }
+
+    int sVertex = startVertex;
+    setupGeometry(&sVertex, NULL, vertexCount, 0);
+
+    this->onGpuDrawNonIndexed(type, sVertex, vertexCount);
+}
+
+void GrGpu::onStencilPath(const GrPath* path, GrPathFill fill) {
+    this->handleDirtyContext();
+
+    // TODO: make this more effecient (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;
+    }
+
+    this->onGpuStencilPath(path, fill);
+}
+
+void GrGpu::finalizeReservedVertices() {
+    GrAssert(NULL != fVertexPool);
+    fVertexPool->unlock();
+}
+
+void GrGpu::finalizeReservedIndices() {
+    GrAssert(NULL != fIndexPool);
+    fIndexPool->unlock();
+}
+
+void GrGpu::prepareVertexPool() {
+    if (NULL == fVertexPool) {
+        GrAssert(0 == fVertexPoolUseCnt);
+        fVertexPool = SkNEW_ARGS(GrVertexBufferAllocPool, (this, true,
+                                                  VERTEX_POOL_VB_SIZE,
+                                                  VERTEX_POOL_VB_COUNT));
+        fVertexPool->releaseGpuRef();
+    } else if (!fVertexPoolUseCnt) {
+        // the client doesn't have valid data in the pool
+        fVertexPool->reset();
+    }
+}
+
+void GrGpu::prepareIndexPool() {
+    if (NULL == fIndexPool) {
+        GrAssert(0 == fIndexPoolUseCnt);
+        fIndexPool = SkNEW_ARGS(GrIndexBufferAllocPool, (this, true,
+                                                INDEX_POOL_IB_SIZE,
+                                                INDEX_POOL_IB_COUNT));
+        fIndexPool->releaseGpuRef();
+    } else if (!fIndexPoolUseCnt) {
+        // the client doesn't have valid data in the pool
+        fIndexPool->reset();
+    }
+}
+
+bool GrGpu::onReserveVertexSpace(GrVertexLayout vertexLayout,
+                                 int vertexCount,
+                                 void** vertices) {
+    GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
+
+    GrAssert(vertexCount > 0);
+    GrAssert(NULL != vertices);
+
+    this->prepareVertexPool();
+
+    *vertices = fVertexPool->makeSpace(vertexLayout,
+                                       vertexCount,
+                                       &geomPoolState.fPoolVertexBuffer,
+                                       &geomPoolState.fPoolStartVertex);
+    if (NULL == *vertices) {
+        return false;
+    }
+    ++fVertexPoolUseCnt;
+    return true;
+}
+
+bool GrGpu::onReserveIndexSpace(int indexCount, void** indices) {
+    GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
+
+    GrAssert(indexCount > 0);
+    GrAssert(NULL != indices);
+
+    this->prepareIndexPool();
+
+    *indices = fIndexPool->makeSpace(indexCount,
+                                     &geomPoolState.fPoolIndexBuffer,
+                                     &geomPoolState.fPoolStartIndex);
+    if (NULL == *indices) {
+        return false;
+    }
+    ++fIndexPoolUseCnt;
+    return true;
+}
+
+void GrGpu::releaseReservedVertexSpace() {
+    const GeometrySrcState& geoSrc = this->getGeomSrc();
+    GrAssert(kReserved_GeometrySrcType == geoSrc.fVertexSrc);
+    size_t bytes = geoSrc.fVertexCount * VertexSize(geoSrc.fVertexLayout);
+    fVertexPool->putBack(bytes);
+    --fVertexPoolUseCnt;
+}
+
+void GrGpu::releaseReservedIndexSpace() {
+    const GeometrySrcState& geoSrc = this->getGeomSrc();
+    GrAssert(kReserved_GeometrySrcType == geoSrc.fIndexSrc);
+    size_t bytes = geoSrc.fIndexCount * sizeof(uint16_t);
+    fIndexPool->putBack(bytes);
+    --fIndexPoolUseCnt;
+}
+
+void GrGpu::onSetVertexSourceToArray(const void* vertexArray, int vertexCount) {
+    this->prepareVertexPool();
+    GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
+#if GR_DEBUG
+    bool success =
+#endif
+    fVertexPool->appendVertices(this->getVertexLayout(),
+                                vertexCount,
+                                vertexArray,
+                                &geomPoolState.fPoolVertexBuffer,
+                                &geomPoolState.fPoolStartVertex);
+    ++fVertexPoolUseCnt;
+    GR_DEBUGASSERT(success);
+}
+
+void GrGpu::onSetIndexSourceToArray(const void* indexArray, int indexCount) {
+    this->prepareIndexPool();
+    GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
+#if GR_DEBUG
+    bool success =
+#endif
+    fIndexPool->appendIndices(indexCount,
+                              indexArray,
+                              &geomPoolState.fPoolIndexBuffer,
+                              &geomPoolState.fPoolStartIndex);
+    ++fIndexPoolUseCnt;
+    GR_DEBUGASSERT(success);
+}
+
+void GrGpu::releaseVertexArray() {
+    // 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);
+    fVertexPool->putBack(bytes);
+    --fVertexPoolUseCnt;
+}
+
+void GrGpu::releaseIndexArray() {
+    // if index source was array, we stowed data in the pool
+    const GeometrySrcState& geoSrc = this->getGeomSrc();
+    GrAssert(kArray_GeometrySrcType == geoSrc.fIndexSrc);
+    size_t bytes = geoSrc.fIndexCount * sizeof(uint16_t);
+    fIndexPool->putBack(bytes);
+    --fIndexPoolUseCnt;
+}
+
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
new file mode 100644
index 0000000..60e49e7
--- /dev/null
+++ b/src/gpu/GrGpu.h
@@ -0,0 +1,597 @@
+
+/*
+ * 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 GrGpu_DEFINED
+#define GrGpu_DEFINED
+
+#include "GrDrawTarget.h"
+#include "GrRect.h"
+#include "GrRefCnt.h"
+#include "GrClipMaskManager.h"
+
+class GrContext;
+class GrIndexBufferAllocPool;
+class GrPath;
+class GrPathRenderer;
+class GrPathRendererChain;
+class GrResource;
+class GrStencilBuffer;
+class GrVertexBufferAllocPool;
+
+class GrGpu : public GrDrawTarget {
+
+public:
+
+    /**
+     * Additional blend coeffecients for dual source blending, not exposed
+     * through GrPaint/GrContext.
+     */
+    enum ExtendedBlendCoeffs {
+        // source 2 refers to second output color when
+        // using dual source blending.
+        kS2C_GrBlendCoeff = kPublicGrBlendCoeffCount,
+        kIS2C_GrBlendCoeff,
+        kS2A_GrBlendCoeff,
+        kIS2A_GrBlendCoeff,
+
+        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.
+     */
+    static GrGpu* Create(GrEngine, GrPlatform3DContext context3D);
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    GrGpu();
+    virtual ~GrGpu();
+
+    // The GrContext sets itself as the owner of this Gpu object
+    void setContext(GrContext* context) {
+        GrAssert(NULL == fContext);
+        fContext = context;
+        fClipMaskManager.setContext(context);
+    }
+    GrContext* getContext() { return fContext; }
+    const GrContext* getContext() const { return fContext; }
+
+    /**
+     * The GrGpu object normally assumes that no outsider is setting state
+     * within the underlying 3D API's context/device/whatever. This call informs
+     * the GrGpu that the state was modified and it shouldn't make assumptions
+     * about the state.
+     */
+    void markContextDirty() { fContextIsDirty = true; }
+
+    void unimpl(const char[]);
+
+    /**
+     * Creates a texture object. If desc width or height is not a power of
+     * two but underlying API requires a power of two texture then srcData
+     * will be embedded in a power of two texture. The extra width and height
+     * is filled as though srcData were rendered clamped into the texture.
+     *
+     * If kRenderTarget_TextureFlag is specified the GrRenderTarget is
+     * accessible via GrTexture::asRenderTarget(). The texture will hold a ref
+     * on the render target until its releaseRenderTarget() is called or it is
+     * destroyed.
+     *
+     * @param desc        describes the texture to be created.
+     * @param srcData     texel data to load texture. Begins with full-size
+     *                    palette data for paletted textures. Contains width*
+     *                    height texels. If NULL texture data is uninitialized.
+     *
+     * @return    The texture object if successful, otherwise NULL.
+     */
+    GrTexture* createTexture(const GrTextureDesc& desc,
+                             const void* srcData, size_t rowBytes);
+
+    /**
+     * Implements GrContext::createPlatformTexture
+     */
+    GrTexture* createPlatformTexture(const GrPlatformTextureDesc& desc);
+
+    /**
+     * Implements GrContext::createPlatformTexture
+     */
+    GrRenderTarget* createPlatformRenderTarget(const GrPlatformRenderTargetDesc& desc);
+
+    /**
+     * Creates a vertex buffer.
+     *
+     * @param size    size in bytes of the vertex buffer
+     * @param dynamic hints whether the data will be frequently changed
+     *                by either GrVertexBuffer::lock or
+     *                GrVertexBuffer::updateData.
+     *
+     * @return    The vertex buffer if successful, otherwise NULL.
+     */
+    GrVertexBuffer* createVertexBuffer(uint32_t size, bool dynamic);
+
+    /**
+     * Creates an index buffer.
+     *
+     * @param size    size in bytes of the index buffer
+     * @param dynamic hints whether the data will be frequently changed
+     *                by either GrIndexBuffer::lock or
+     *                GrIndexBuffer::updateData.
+     *
+     * @return The index buffer if successful, otherwise NULL.
+     */
+    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_GrPrimitiveType
+     * @ return the quad index buffer
+     */
+    const GrIndexBuffer* getQuadIndexBuffer() const;
+
+    /**
+     * Returns a vertex buffer with four position-only vertices [(0,0), (1,0),
+     * (1,1), (0,1)].
+     * @ return unit square vertex buffer
+     */
+    const GrVertexBuffer* getUnitSquareVertexBuffer() const;
+
+    /**
+     * Resolves MSAA.
+     */
+    void resolveRenderTarget(GrRenderTarget* target);
+
+    /**
+     * Ensures that the current render target is actually set in the
+     * underlying 3D API. Used when client wants to use 3D API to directly
+     * render to the RT.
+     */
+    void forceRenderTargetFlush();
+
+    /**
+     * 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 and component sizes. The caller is free to ignore the
+     * result and call readPixels with the original config.
+     */
+    virtual GrPixelConfig preferredReadPixelsConfig(GrPixelConfig config)
+                                                                        const {
+        return config;
+    }
+
+    /**
+     * Same as above but applies to writeTexturePixels
+     */
+    virtual GrPixelConfig preferredWritePixelsConfig(GrPixelConfig config)
+                                                                        const {
+        return config;
+    }
+
+    /**
+     * OpenGL's readPixels returns the result bottom-to-top while the skia
+     * API is top-to-bottom. Thus we have to do a y-axis flip. The obvious
+     * solution is to have the subclass do the flip using either the CPU or GPU.
+     * 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
+     * concurrently.
+     *
+     * This function returns true if a y-flip is required to put the pixels in
+     * top-to-bottom order and the subclass cannot do it for free.
+     *
+     * See read pixels for the params
+     * @return true if calling readPixels with the same set of params will
+     *              produce bottom-to-top data
+     */
+     virtual bool readPixelsWillPayForYFlip(GrRenderTarget* renderTarget,
+                                            int left, int top,
+                                            int width, int height,
+                                            GrPixelConfig config,
+                                            size_t rowBytes) const = 0;
+     /**
+      * This should return true if reading a NxM rectangle of pixels from a
+      * render target is faster if the target has dimensons N and M and the read
+      * rectangle has its top-left at 0,0.
+      */
+     virtual bool fullReadPixelsIsFasterThanPartial() const { return false; };
+
+    /**
+     * Reads a rectangle of pixels from a render target.
+     *
+     * @param renderTarget  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      the number of bytes between consecutive rows. Zero
+     *                      means rows are tightly packed.
+     * @param invertY       buffer should be populated bottom-to-top as opposed
+     *                      to top-to-bottom (skia's usual order)
+     *
+     * @return true if the read succeeded, false if not. The read can fail
+     *              because of a unsupported pixel config or because no render
+     *              target is currently set.
+     */
+    bool readPixels(GrRenderTarget* renderTarget,
+                    int left, int top, int width, int height,
+                    GrPixelConfig config, void* buffer, size_t rowBytes,
+                    bool invertY);
+
+    /**
+     * Updates the pixels in a rectangle of a texture.
+     *
+     * @param left          left edge of the rectangle to write (inclusive)
+     * @param top           top edge of the rectangle to write (inclusive)
+     * @param width         width of rectangle to write in pixels.
+     * @param height        height of rectangle to write in pixels.
+     * @param config        the pixel config of the source buffer
+     * @param buffer        memory to read pixels from
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
+     *                      means rows are tightly packed.
+     */
+    void writeTexturePixels(GrTexture* texture,
+                            int left, int top, int width, int height,
+                            GrPixelConfig config, const void* buffer,
+                            size_t rowBytes);
+
+    /**
+     * Called to tell Gpu object that all GrResources have been lost and should
+     * be abandoned. Overrides must call INHERITED::abandonResources().
+     */
+    virtual void abandonResources();
+
+    /**
+     * Called to tell Gpu object to release all GrResources. Overrides must call
+     * INHERITED::releaseResources().
+     */
+    void releaseResources();
+
+    /**
+     * Add resource to list of resources. Should only be called by GrResource.
+     * @param resource  the resource to add.
+     */
+    void insertResource(GrResource* resource);
+
+    /**
+     * Remove resource from list of resources. Should only be called by
+     * GrResource.
+     * @param resource  the resource to remove.
+     */
+    void removeResource(GrResource* resource);
+
+    // GrDrawTarget overrides
+    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.
+    // Each time this occurs the GrGpu bumps a timestamp.
+    // state of the 3D context
+    // At 10 resets / frame and 60fps a 64bit timestamp will overflow in about
+    // a billion years.
+    typedef uint64_t ResetTimestamp;
+
+    // This timestamp is always older than the current timestamp
+    static const ResetTimestamp kExpiredTimestamp = 0;
+    // Returns a timestamp based on the number of times the context was reset.
+    // This timestamp can be used to lazily detect when cached 3D context state
+    // is dirty.
+    ResetTimestamp getResetTimestamp() const {
+        return fResetTimestamp;
+    }
+
+    /**
+     * 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),
+
+        kModifyStencilClip_StateBit = kFirstBit, // allows draws to modify
+                                                 // stencil bits used for
+                                                 // clipping.
+    };
+
+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(DrawType);
+
+    // Functions used to map clip-respecting stencil tests into normal
+    // stencil funcs supported by GPUs.
+    static GrStencilFunc ConvertStencilFunc(bool stencilInClip,
+                                            GrStencilFunc func);
+    static void ConvertStencilFuncAndMask(GrStencilFunc func,
+                                          bool clipInStencil,
+                                          unsigned int clipBit,
+                                          unsigned int userBits,
+                                          unsigned int* ref,
+                                          unsigned int* mask);
+
+    GrClipMaskManager           fClipMaskManager;
+
+    struct GeometryPoolState {
+        const GrVertexBuffer* fPoolVertexBuffer;
+        int                   fPoolStartVertex;
+
+        const GrIndexBuffer*  fPoolIndexBuffer;
+        int                   fPoolStartIndex;
+    };
+    const GeometryPoolState& getGeomPoolState() {
+        return fGeomPoolStateStack.back();
+    }
+
+    // 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];
+
+    // GrDrawTarget overrides
+    virtual bool onReserveVertexSpace(GrVertexLayout vertexLayout,
+                                      int vertexCount,
+                                      void** vertices) SK_OVERRIDE;
+    virtual bool onReserveIndexSpace(int indexCount,
+                                     void** indices) SK_OVERRIDE;
+    virtual void releaseReservedVertexSpace() SK_OVERRIDE;
+    virtual void releaseReservedIndexSpace() SK_OVERRIDE;
+    virtual void onSetVertexSourceToArray(const void* vertexArray,
+                                          int vertexCount) SK_OVERRIDE;
+    virtual void onSetIndexSourceToArray(const void* indexArray,
+                                         int indexCount) SK_OVERRIDE;
+    virtual void releaseVertexArray() SK_OVERRIDE;
+    virtual void releaseIndexArray() SK_OVERRIDE;
+    virtual void geometrySourceWillPush() SK_OVERRIDE;
+    virtual void geometrySourceWillPop(
+        const GeometrySrcState& restoredState) SK_OVERRIDE;
+
+    // Helpers for setting up geometry state
+    void finalizeReservedVertices();
+    void finalizeReservedIndices();
+
+    // called when the 3D context state is unknown. Subclass should emit any
+    // assumed 3D context state and dirty any state cache.
+    virtual void onResetContext() = 0;
+
+
+    // overridden by API-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 GrPath* onCreatePath(const SkPath& path) = 0;
+
+    // overridden by API-specific derivated 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;
+
+    virtual void onGpuDrawNonIndexed(GrPrimitiveType type,
+                                     uint32_t vertexCount,
+                                     uint32_t numVertices) = 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&,
+                                        GrPathFill,
+                                        GrStencilSettings* settings) = 0;
+    // overridden by API-specific derived class to perform the path stenciling.
+    virtual void onGpuStencilPath(const GrPath*, GrPathFill) = 0;
+
+    // overridden by API-specific derived class to perform flush
+    virtual void onForceRenderTargetFlush() = 0;
+
+    // overridden by API-specific derived class to perform the read pixels.
+    virtual bool onReadPixels(GrRenderTarget* target,
+                              int left, int top, int width, int height,
+                              GrPixelConfig,
+                              void* buffer,
+                              size_t rowBytes,
+                              bool invertY) = 0;
+
+    // overridden by API-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
+    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;
+
+    // attaches an existing SB to an existing RT.
+    virtual bool attachStencilBufferToRenderTarget(GrStencilBuffer* sb,
+                                                   GrRenderTarget* rt) = 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
+    // returns false if current state is unsupported.
+    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
+
+    bool                        fContextIsDirty;
+
+    typedef SkTDLinkedList<GrResource> ResourceList;
+    ResourceList                fResourceList;
+
+    // Given a rt, find or create a stencil buffer and attach it
+    bool attachStencilBufferToRenderTarget(GrRenderTarget* target);
+
+    // GrDrawTarget overrides
+    virtual void onDrawIndexed(GrPrimitiveType type,
+                               int startVertex,
+                               int startIndex,
+                               int vertexCount,
+                               int indexCount) SK_OVERRIDE;
+    virtual void onDrawNonIndexed(GrPrimitiveType type,
+                                  int startVertex,
+                                  int vertexCount) SK_OVERRIDE;
+    virtual void onStencilPath(const GrPath* path, GrPathFill fill) SK_OVERRIDE;
+
+    // readies the pools to provide vertex/index data.
+    void prepareVertexPool();
+    void prepareIndexPool();
+
+    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;
+    }
+
+    void handleDirtyContext() {
+        if (fContextIsDirty) {
+            this->resetContext();
+            fContextIsDirty = false;
+        }
+    }
+
+    typedef GrDrawTarget INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrGpuFactory.cpp b/src/gpu/GrGpuFactory.cpp
new file mode 100644
index 0000000..0d4c381
--- /dev/null
+++ b/src/gpu/GrGpuFactory.cpp
@@ -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.
+ */
+
+
+#include "GrTypes.h"
+
+#include "gl/GrGLConfig.h"
+
+#include "GrGpu.h"
+#include "gl/GrGpuGL.h"
+
+GrGpu* GrGpu::Create(GrEngine engine, GrPlatform3DContext context3D) {
+
+    const GrGLInterface* glInterface = NULL;
+    SkAutoTUnref<const GrGLInterface> glInterfaceUnref;
+
+    if (kOpenGL_Shaders_GrEngine == engine) {
+        glInterface = reinterpret_cast<const GrGLInterface*>(context3D);
+        if (NULL == glInterface) {
+            glInterface = GrGLDefaultInterface();
+            // By calling GrGLDefaultInterface we've taken a ref on the
+            // returned object. We only want to hold that ref until after
+            // the GrGpu is constructed and has taken ownership.
+            glInterfaceUnref.reset(glInterface);
+        }
+        if (NULL == glInterface) {
+#if GR_DEBUG
+            GrPrintf("No GL interface provided!\n");
+#endif
+            return NULL;
+        }
+        GrGLContextInfo ctxInfo(glInterface);
+        if (ctxInfo.isInitialized()) {
+            return SkNEW_ARGS(GrGpuGL, (ctxInfo));
+        }
+    }
+    return NULL;
+}
diff --git a/src/gpu/GrGpuVertex.h b/src/gpu/GrGpuVertex.h
new file mode 100644
index 0000000..570a77c
--- /dev/null
+++ b/src/gpu/GrGpuVertex.h
@@ -0,0 +1,97 @@
+
+/*
+ * 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 GrGpuVertex_DEFINED
+#define GrGpuVertex_DEFINED
+
+#include "gl/GrGLConfig.h"
+#include "GrPoint.h"
+
+#if GR_TEXT_SCALAR_IS_USHORT
+    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 GrFixedToTextScalar(x)  (x)
+#elif GR_TEXT_SCALAR_IS_FLOAT
+    typedef float                   GrTextScalar;
+    #define GrIntToTextScalar(x)    ((GrTextScalar)x)
+    #define GrFixedToTextScalar(x)  GrFixedToFloat(x)
+#else
+    #error "Text scalar type not defined"
+#endif
+
+// text has its own vertex class, since it may want to be in fixed point (given)
+// that it starts with all integers) even when the default vertices are floats
+struct GrGpuTextVertex {
+    GrTextScalar fX;
+    GrTextScalar fY;
+
+    void set(GrTextScalar x, GrTextScalar y) {
+        fX = x;
+        fY = y;
+    }
+
+    void setI(int x, int y) {
+        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,
+                    GrTextScalar b) {
+        GrGpuTextVertex* v = this;
+        v[0].set(l, t);
+        v[1].set(l, b);
+        v[2].set(r, b);
+        v[3].set(r, t);
+    }
+
+    void setIRectFan(int l, int t, int r, int b) {
+        this->setRectFan(GrIntToTextScalar(l), GrIntToTextScalar(t),
+                         GrIntToTextScalar(r), GrIntToTextScalar(b));
+    }
+
+    void setIRectFan(int l, int t, int r, int b, size_t stride) {
+        GrAssert(stride > sizeof(GrGpuTextVertex));
+        char* v = (char*)this;
+        ((GrGpuTextVertex*)(v + 0*stride))->setI(l, t);
+        ((GrGpuTextVertex*)(v + 1*stride))->setI(l, b);
+        ((GrGpuTextVertex*)(v + 2*stride))->setI(r, b);
+        ((GrGpuTextVertex*)(v + 3*stride))->setI(r, t);
+    }
+
+    // counter-clockwise fan
+    void setXRectFan(GrFixed l, GrFixed t, GrFixed r, GrFixed b) {
+        this->setRectFan(GrFixedToTextScalar(l), GrFixedToTextScalar(t),
+                         GrFixedToTextScalar(r), GrFixedToTextScalar(b));
+    }
+
+    void setXRectFan(GrFixed l, GrFixed t, GrFixed r, GrFixed b, size_t stride) {
+        GrAssert(stride > sizeof(GrGpuTextVertex));
+        char* v = (char*)this;
+        ((GrGpuTextVertex*)(v + 0*stride))->setX(l, t);
+        ((GrGpuTextVertex*)(v + 1*stride))->setX(l, b);
+        ((GrGpuTextVertex*)(v + 2*stride))->setX(r, b);
+        ((GrGpuTextVertex*)(v + 3*stride))->setX(r, t);
+    }
+
+};
+
+#endif
+
diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp
new file mode 100644
index 0000000..2d1a0ef
--- /dev/null
+++ b/src/gpu/GrInOrderDrawBuffer.cpp
@@ -0,0 +1,923 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrInOrderDrawBuffer.h"
+#include "GrBufferAllocPool.h"
+#include "GrGpu.h"
+#include "GrIndexBuffer.h"
+#include "GrPath.h"
+#include "GrRenderTarget.h"
+#include "GrTexture.h"
+#include "GrVertexBuffer.h"
+
+GrInOrderDrawBuffer::GrInOrderDrawBuffer(const GrGpu* gpu,
+                                         GrVertexBufferAllocPool* vertexPool,
+                                         GrIndexBufferAllocPool* indexPool)
+    : fAutoFlushTarget(NULL)
+    , fClipSet(true)
+    , fVertexPool(*vertexPool)
+    , fIndexPool(*indexPool)
+    , fLastRectVertexLayout(0)
+    , fQuadIndexBuffer(NULL)
+    , fMaxQuads(0)
+    , fFlushing(false) {
+
+    fCaps = gpu->getCaps();
+
+    GrAssert(NULL != vertexPool);
+    GrAssert(NULL != indexPool);
+
+    GeometryPoolState& poolState = fGeoPoolStateStack.push_back();
+    poolState.fUsedPoolVertexBytes = 0;
+    poolState.fUsedPoolIndexBytes = 0;
+#if GR_DEBUG
+    poolState.fPoolVertexBuffer = (GrVertexBuffer*)~0;
+    poolState.fPoolStartVertex = ~0;
+    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::setQuadIndexBuffer(const GrIndexBuffer* indexBuffer) {
+    bool newIdxBuffer = fQuadIndexBuffer != indexBuffer;
+    if (newIdxBuffer) {
+        GrSafeUnref(fQuadIndexBuffer);
+        fQuadIndexBuffer = indexBuffer;
+        GrSafeRef(fQuadIndexBuffer);
+        fCurrQuad = 0;
+        fMaxQuads = (NULL == indexBuffer) ? 0 : indexBuffer->maxQuads();
+    } else {
+        GrAssert((NULL == indexBuffer && 0 == fMaxQuads) ||
+                 (indexBuffer->maxQuads() == fMaxQuads));
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrInOrderDrawBuffer::resetDrawTracking() {
+    fCurrQuad = 0;
+    fInstancedDrawTracker.reset();
+}
+
+void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
+                                   const GrMatrix* matrix,
+                                   const GrRect* srcRects[],
+                                   const GrMatrix* srcMatrices[]) {
+
+    GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad));
+    GrAssert(!(fDraws.empty() && fCurrQuad));
+    GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer));
+
+    GrDrawState* drawState = this->drawState();
+
+    // if we have a quad IB then either append to the previous run of
+    // rects or start a new run
+    if (fMaxQuads) {
+
+        bool appendToPreviousDraw = false;
+        GrVertexLayout layout = GetRectVertexLayout(srcRects);
+
+        // Batching across colors means we move the draw color into the
+        // rect's vertex colors to allow greater batching (a lot of rects
+        // in a row differing only in color is a common occurence in tables).
+        bool batchAcrossColors = true;
+        if (!this->getCaps().dualSourceBlendingSupport()) {
+            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+                if (this->getDrawState().isStageEnabled(s)) {
+                    // We disable batching across colors when there is a texture
+                    // present because (by pushing the the color to the vertices)
+                    // Ganesh loses track of the rect's opacity. This, in turn, can
+                    // cause some of the blending optimizations to be disabled. This
+                    // becomes a huge problem on some of the smaller devices where
+                    // shader derivatives and dual source blending aren't supported.
+                    // In those cases paths are often drawn to a texture and then
+                    // drawn as a texture (using this method). Because dual source
+                    // blending is disabled (and the blend optimizations are short
+                    // circuited) some of the more esoteric blend modes can no longer
+                    // be supported.
+                    // TODO: add tracking of batchAcrossColors's opacity
+                    batchAcrossColors = false;
+                    break;
+                }
+            }
+        }
+
+        if (batchAcrossColors) {
+            layout |= kColor_VertexLayoutBit;
+        }
+
+        AutoReleaseGeometry geo(this, layout, 4, 0);
+        if (!geo.succeeded()) {
+            GrPrintf("Failed to get space for vertices!\n");
+            return;
+        }
+        GrMatrix combinedMatrix = drawState->getViewMatrix();
+        // We go to device space so that matrix changes allow us to concat
+        // rect draws. When the caller has provided explicit source rects
+        // then we don't want to modify the sampler matrices. Otherwise
+        // we have to account for the view matrix change in the sampler
+        // matrices.
+        uint32_t explicitCoordMask = 0;
+        if (srcRects) {
+            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+                if (srcRects[s]) {
+                    explicitCoordMask |= (1 << s);
+                }
+            }
+        }
+        GrDrawState::AutoDeviceCoordDraw adcd(this->drawState(), explicitCoordMask);
+        if (!adcd.succeeded()) {
+            return;
+        }
+        if (NULL != matrix) {
+            combinedMatrix.preConcat(*matrix);
+        }
+
+        SetRectVertices(rect, &combinedMatrix, srcRects, srcMatrices,
+                        this->getDrawState().getColor(), layout, geo.vertices());
+
+        // Now that the paint's color is stored in the vertices set it to
+        // white so that the following code can batch all the rects regardless
+        // of paint color
+        GrDrawState::AutoColorRestore acr(this->drawState(),
+                                          batchAcrossColors ? SK_ColorWHITE
+                                                            : this->getDrawState().getColor());
+
+        // we don't want to miss an opportunity to batch rects together
+        // simply because the clip has changed if the clip doesn't affect
+        // the rect.
+        bool disabledClip = false;
+
+        if (drawState->isClipState()) {
+
+            GrRect devClipRect;
+            bool isIntersectionOfRects = false;
+
+            fClip->fClipStack->getConservativeBounds(-fClip->fOrigin.fX,
+                                                     -fClip->fOrigin.fY,
+                                                     drawState->getRenderTarget()->width(),
+                                                     drawState->getRenderTarget()->height(),
+                                                     &devClipRect,
+                                                     &isIntersectionOfRects);
+
+            if (isIntersectionOfRects) {
+                // If the clip rect touches the edge of the viewport, extended it
+                // out (close) to infinity to avoid bogus intersections.
+                // We might consider a more exact clip to viewport if this
+                // conservative test fails.
+                const GrRenderTarget* target = drawState->getRenderTarget();
+                if (0 >= devClipRect.fLeft) {
+                    devClipRect.fLeft = GR_ScalarMin;
+                }
+                if (target->width() <= devClipRect.fRight) {
+                    devClipRect.fRight = GR_ScalarMax;
+                }
+                if (0 >= devClipRect.top()) {
+                    devClipRect.fTop = GR_ScalarMin;
+                }
+                if (target->height() <= devClipRect.fBottom) {
+                    devClipRect.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 (!devClipRect.contains(p)) {
+                        insideClip = false;
+                        break;
+                    }
+                }
+                if (insideClip) {
+                    drawState->disableState(GrDrawState::kClip_StateBit);
+                    disabledClip = true;
+                }
+            }
+        }
+
+        if (!this->needsNewClip() &&
+            !this->needsNewState() &&
+            fCurrQuad > 0 &&
+            fCurrQuad < fMaxQuads &&
+            layout == fLastRectVertexLayout) {
+
+            int vsize = VertexSize(layout);
+
+            Draw& lastDraw = fDraws.back();
+
+            GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer);
+            GrAssert(kTriangles_GrPrimitiveType == lastDraw.fPrimitiveType);
+            GrAssert(0 == lastDraw.fVertexCount % 4);
+            GrAssert(0 == lastDraw.fIndexCount % 6);
+            GrAssert(0 == lastDraw.fStartIndex);
+
+            GeometryPoolState& poolState = fGeoPoolStateStack.back();
+
+            appendToPreviousDraw =
+                kDraw_Cmd == fCmds.back() &&
+                lastDraw.fVertexBuffer == poolState.fPoolVertexBuffer &&
+                (fCurrQuad * 4 + lastDraw.fStartVertex) == poolState.fPoolStartVertex;
+
+            if (appendToPreviousDraw) {
+                lastDraw.fVertexCount += 4;
+                lastDraw.fIndexCount += 6;
+                fCurrQuad += 1;
+                // we reserved above, so we should be the first
+                // use of this vertex reservation.
+                GrAssert(0 == poolState.fUsedPoolVertexBytes);
+                poolState.fUsedPoolVertexBytes = 4 * vsize;
+            }
+        }
+        if (!appendToPreviousDraw) {
+            this->setIndexSourceToBuffer(fQuadIndexBuffer);
+            this->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 4, 6);
+            fCurrQuad = 1;
+            fLastRectVertexLayout = layout;
+        }
+        if (disabledClip) {
+            drawState->enableState(GrDrawState::kClip_StateBit);
+        }
+        fInstancedDrawTracker.reset();
+    } else {
+        INHERITED::drawRect(rect, matrix, srcRects, srcMatrices);
+    }
+}
+
+void GrInOrderDrawBuffer::drawIndexedInstances(GrPrimitiveType type,
+                                               int instanceCount,
+                                               int verticesPerInstance,
+                                               int indicesPerInstance) {
+    if (!verticesPerInstance || !indicesPerInstance) {
+        return;
+    }
+
+    const GeometrySrcState& geomSrc = this->getGeomSrc();
+
+    // we only attempt to concat the case when reserved verts are used with
+    // an index buffer.
+    if (kReserved_GeometrySrcType == geomSrc.fVertexSrc &&
+        kBuffer_GeometrySrcType == geomSrc.fIndexSrc) {
+
+        if (this->needsNewClip()) {
+            this->recordClip();
+        }
+        if (this->needsNewState()) {
+            this->recordState();
+        }
+
+        Draw* draw = NULL;
+        // if the last draw used the same indices/vertices per shape then we
+        // may be able to append to it.
+        if (kDraw_Cmd == fCmds.back() &&
+            verticesPerInstance == fInstancedDrawTracker.fVerticesPerInstance &&
+            indicesPerInstance == fInstancedDrawTracker.fIndicesPerInstance) {
+            GrAssert(fDraws.count());
+            draw = &fDraws.back();
+        }
+
+        GeometryPoolState& poolState = fGeoPoolStateStack.back();
+        const GrVertexBuffer* vertexBuffer = poolState.fPoolVertexBuffer;
+
+        // Check whether the draw is compatible with this draw in order to
+        // append
+        if (NULL == draw ||
+            draw->fIndexBuffer != geomSrc.fIndexBuffer ||
+            draw->fPrimitiveType != type ||
+            draw->fVertexBuffer != vertexBuffer) {
+
+            draw = this->recordDraw();
+            draw->fIndexBuffer = geomSrc.fIndexBuffer;
+            geomSrc.fIndexBuffer->ref();
+            draw->fVertexBuffer = vertexBuffer;
+            vertexBuffer->ref();
+            draw->fPrimitiveType = type;
+            draw->fStartIndex = 0;
+            draw->fIndexCount = 0;
+            draw->fStartVertex = poolState.fPoolStartVertex;
+            draw->fVertexCount = 0;
+            draw->fVertexLayout = geomSrc.fVertexLayout;
+        } else {
+            GrAssert(!(draw->fIndexCount % indicesPerInstance));
+            GrAssert(!(draw->fVertexCount % verticesPerInstance));
+            GrAssert(poolState.fPoolStartVertex == draw->fStartVertex +
+                                                   draw->fVertexCount);
+        }
+
+        // how many instances can be in a single draw
+        int maxInstancesPerDraw = this->indexCountInCurrentSource() /
+                                  indicesPerInstance;
+        if (!maxInstancesPerDraw) {
+            return;
+        }
+        // how many instances should be concat'ed onto draw
+        int instancesToConcat = maxInstancesPerDraw - draw->fVertexCount /
+                                                      verticesPerInstance;
+        if (maxInstancesPerDraw > instanceCount) {
+            maxInstancesPerDraw = instanceCount;
+            if (instancesToConcat > instanceCount) {
+                instancesToConcat = instanceCount;
+            }
+        }
+
+        // update the amount of reserved data actually referenced in draws
+        size_t vertexBytes = instanceCount * verticesPerInstance *
+                             VertexSize(draw->fVertexLayout);
+        poolState.fUsedPoolVertexBytes =
+                            GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
+
+        while (instanceCount) {
+            if (!instancesToConcat) {
+                int startVertex = draw->fStartVertex + draw->fVertexCount;
+                draw = this->recordDraw();
+                draw->fIndexBuffer = geomSrc.fIndexBuffer;
+                geomSrc.fIndexBuffer->ref();
+                draw->fVertexBuffer = vertexBuffer;
+                vertexBuffer->ref();
+                draw->fPrimitiveType = type;
+                draw->fStartIndex = 0;
+                draw->fStartVertex = startVertex;
+                draw->fVertexCount = 0;
+                draw->fVertexLayout = geomSrc.fVertexLayout;
+                instancesToConcat = maxInstancesPerDraw;
+            }
+            draw->fVertexCount += instancesToConcat * verticesPerInstance;
+            draw->fIndexCount += instancesToConcat * indicesPerInstance;
+            instanceCount -= instancesToConcat;
+            instancesToConcat = 0;
+        }
+
+        // update draw tracking for next draw
+        fCurrQuad = 0;
+        fInstancedDrawTracker.fVerticesPerInstance = verticesPerInstance;
+        fInstancedDrawTracker.fIndicesPerInstance = indicesPerInstance;
+    } else {
+        this->INHERITED::drawIndexedInstances(type,
+                                              instanceCount,
+                                              verticesPerInstance,
+                                              indicesPerInstance);
+    }
+
+}
+
+void GrInOrderDrawBuffer::onDrawIndexed(GrPrimitiveType primitiveType,
+                                        int startVertex,
+                                        int startIndex,
+                                        int vertexCount,
+                                        int indexCount) {
+
+    if (!vertexCount || !indexCount) {
+        return;
+    }
+
+    this->resetDrawTracking();
+
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+
+    if (this->needsNewClip()) {
+       this->recordClip();
+    }
+    if (this->needsNewState()) {
+        this->recordState();
+    }
+
+    Draw* draw = this->recordDraw();
+
+    draw->fPrimitiveType = primitiveType;
+    draw->fStartVertex   = startVertex;
+    draw->fStartIndex    = startIndex;
+    draw->fVertexCount   = vertexCount;
+    draw->fIndexCount    = indexCount;
+
+    draw->fVertexLayout = this->getVertexLayout();
+    switch (this->getGeomSrc().fVertexSrc) {
+    case kBuffer_GeometrySrcType:
+        draw->fVertexBuffer = this->getGeomSrc().fVertexBuffer;
+        break;
+    case kReserved_GeometrySrcType: // fallthrough
+    case kArray_GeometrySrcType: {
+        size_t vertexBytes = (vertexCount + startVertex) *
+                             VertexSize(draw->fVertexLayout);
+        poolState.fUsedPoolVertexBytes =
+                            GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
+        draw->fVertexBuffer = poolState.fPoolVertexBuffer;
+        draw->fStartVertex += poolState.fPoolStartVertex;
+        break;
+    }
+    default:
+        GrCrash("unknown geom src type");
+    }
+    draw->fVertexBuffer->ref();
+
+    switch (this->getGeomSrc().fIndexSrc) {
+    case kBuffer_GeometrySrcType:
+        draw->fIndexBuffer = this->getGeomSrc().fIndexBuffer;
+        break;
+    case kReserved_GeometrySrcType: // fallthrough
+    case kArray_GeometrySrcType: {
+        size_t indexBytes = (indexCount + startIndex) * sizeof(uint16_t);
+        poolState.fUsedPoolIndexBytes =
+                            GrMax(poolState.fUsedPoolIndexBytes, indexBytes);
+        draw->fIndexBuffer = poolState.fPoolIndexBuffer;
+        draw->fStartIndex += poolState.fPoolStartIndex;
+        break;
+    }
+    default:
+        GrCrash("unknown geom src type");
+    }
+    draw->fIndexBuffer->ref();
+}
+
+void GrInOrderDrawBuffer::onDrawNonIndexed(GrPrimitiveType primitiveType,
+                                           int startVertex,
+                                           int vertexCount) {
+    if (!vertexCount) {
+        return;
+    }
+
+    this->resetDrawTracking();
+
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    if (this->needsNewClip()) {
+        this->recordClip();
+    }
+    if (this->needsNewState()) {
+        this->recordState();
+    }
+
+    Draw* draw = this->recordDraw();
+    draw->fPrimitiveType = primitiveType;
+    draw->fStartVertex   = startVertex;
+    draw->fStartIndex    = 0;
+    draw->fVertexCount   = vertexCount;
+    draw->fIndexCount    = 0;
+
+    draw->fVertexLayout = this->getVertexLayout();
+    switch (this->getGeomSrc().fVertexSrc) {
+    case kBuffer_GeometrySrcType:
+        draw->fVertexBuffer = this->getGeomSrc().fVertexBuffer;
+        break;
+    case kReserved_GeometrySrcType: // fallthrough
+    case kArray_GeometrySrcType: {
+        size_t vertexBytes = (vertexCount + startVertex) *
+                             VertexSize(draw->fVertexLayout);
+        poolState.fUsedPoolVertexBytes =
+                            GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
+        draw->fVertexBuffer = poolState.fPoolVertexBuffer;
+        draw->fStartVertex += poolState.fPoolStartVertex;
+        break;
+    }
+    default:
+        GrCrash("unknown geom src type");
+    }
+    draw->fVertexBuffer->ref();
+    draw->fIndexBuffer = NULL;
+}
+
+void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, GrPathFill fill) {
+    if (this->needsNewClip()) {
+        this->recordClip();
+    }
+    // Only compare the subset of GrDrawState relevant to path stenciling?
+    if (this->needsNewState()) {
+        this->recordState();
+    }
+    StencilPath* sp = this->recordStencilPath();
+    sp->fPath.reset(path);
+    path->ref();
+    sp->fFill = fill;
+}
+
+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, renderTarget->width(), renderTarget->height());
+        rect = &r;
+    }
+    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();
+    int numDraws = fDraws.count();
+    for (int d = 0; d < numDraws; ++d) {
+        // we always have a VB, but not always an IB
+        GrAssert(NULL != fDraws[d].fVertexBuffer);
+        fDraws[d].fVertexBuffer->unref();
+        GrSafeUnref(fDraws[d].fIndexBuffer);
+    }
+    fCmds.reset();
+    fDraws.reset();
+    fStencilPaths.reset();
+    fStates.reset();
+    fClears.reset();
+    fVertexPool.reset();
+    fIndexPool.reset();
+    fClips.reset();
+    fClipOrigins.reset();
+    fClipSet = true;
+
+    this->resetDrawTracking();
+
+    // we start off with a default clip and state so that we don't have
+    // to do count checks on fClips, fStates, or fCmds before checking their
+    // last entry.
+    this->recordDefaultState();
+    this->recordDefaultClip();
+}
+
+bool GrInOrderDrawBuffer::playback(GrDrawTarget* target) {
+    GrAssert(kReserved_GeometrySrcType != this->getGeomSrc().fVertexSrc);
+    GrAssert(kReserved_GeometrySrcType != this->getGeomSrc().fIndexSrc);
+
+    GrAssert(NULL != target);
+    GrAssert(target != this); // not considered and why?
+
+    int numCmds = fCmds.count();
+    GrAssert(numCmds >= 2);
+    if (2 == numCmds) {
+        GrAssert(kSetState_Cmd == fCmds[0]);
+        GrAssert(kSetClip_Cmd  == fCmds[1]);
+        return false;
+    }
+
+    fVertexPool.unlock();
+    fIndexPool.unlock();
+
+    GrDrawTarget::AutoClipRestore acr(target);
+    AutoGeometryPush agp(target);
+    GrDrawState* prevDrawState = target->drawState();
+    prevDrawState->ref();
+
+    GrClipData clipData;
+
+    int currState       = 0;
+    int currClip        = 0;
+    int currClear       = 0;
+    int currDraw        = 0;
+    int currStencilPath = 0;
+
+    for (int c = 0; c < numCmds; ++c) {
+        switch (fCmds[c]) {
+            case kDraw_Cmd: {
+                const Draw& draw = fDraws[currDraw];
+                target->setVertexSourceToBuffer(draw.fVertexLayout, draw.fVertexBuffer);
+                if (draw.fIndexCount) {
+                    target->setIndexSourceToBuffer(draw.fIndexBuffer);
+                }
+
+                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.fFill);
+                ++currStencilPath;
+                break;
+            }
+            case kSetState_Cmd:
+                target->setDrawState(&fStates[currState]);
+                ++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;
+        }
+    }
+    // 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();
+    return true;
+}
+
+void GrInOrderDrawBuffer::setAutoFlushTarget(GrDrawTarget* target) {
+    GrSafeAssign(fAutoFlushTarget, target);
+}
+
+void GrInOrderDrawBuffer::willReserveVertexAndIndexSpace(
+                                GrVertexLayout vertexLayout,
+                                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(vertexLayout, &vcount, &icount)) {
+
+            this->flushTo(fAutoFlushTarget);
+        }
+    }
+}
+
+bool GrInOrderDrawBuffer::geometryHints(GrVertexLayout vertexLayout,
+                                        int* vertexCount,
+                                        int* indexCount) const {
+    // we will recommend a flush if the data could fit in a single
+    // preallocated buffer but none are left and it can't fit
+    // in the current buffer (which may not be prealloced).
+    bool flush = false;
+    if (NULL != indexCount) {
+        int32_t currIndices = fIndexPool.currentBufferIndices();
+        if (*indexCount > currIndices &&
+            (!fIndexPool.preallocatedBuffersRemaining() &&
+             *indexCount <= fIndexPool.preallocatedBufferIndices())) {
+
+            flush = true;
+        }
+        *indexCount = currIndices;
+    }
+    if (NULL != vertexCount) {
+        int32_t currVertices = fVertexPool.currentBufferVertices(vertexLayout);
+        if (*vertexCount > currVertices &&
+            (!fVertexPool.preallocatedBuffersRemaining() &&
+             *vertexCount <= fVertexPool.preallocatedBufferVertices(vertexLayout))) {
+
+            flush = true;
+        }
+        *vertexCount = currVertices;
+    }
+    return flush;
+}
+
+bool GrInOrderDrawBuffer::onReserveVertexSpace(GrVertexLayout vertexLayout,
+                                               int vertexCount,
+                                               void** vertices) {
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    GrAssert(vertexCount > 0);
+    GrAssert(NULL != vertices);
+    GrAssert(0 == poolState.fUsedPoolVertexBytes);
+
+    *vertices = fVertexPool.makeSpace(vertexLayout,
+                                      vertexCount,
+                                      &poolState.fPoolVertexBuffer,
+                                      &poolState.fPoolStartVertex);
+    return NULL != *vertices;
+}
+
+bool GrInOrderDrawBuffer::onReserveIndexSpace(int indexCount, void** indices) {
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    GrAssert(indexCount > 0);
+    GrAssert(NULL != indices);
+    GrAssert(0 == poolState.fUsedPoolIndexBytes);
+
+    *indices = fIndexPool.makeSpace(indexCount,
+                                    &poolState.fPoolIndexBuffer,
+                                    &poolState.fPoolStartIndex);
+    return NULL != *indices;
+}
+
+void GrInOrderDrawBuffer::releaseReservedVertexSpace() {
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    const GeometrySrcState& geoSrc = this->getGeomSrc();
+
+    // If we get a release vertex space call then our current source should either be reserved
+    // or array (which we copied into reserved space).
+    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 = VertexSize(geoSrc.fVertexLayout) *
+                                 geoSrc.fVertexCount;
+    fVertexPool.putBack(reservedVertexBytes -
+                        poolState.fUsedPoolVertexBytes);
+    poolState.fUsedPoolVertexBytes = 0;
+    poolState.fPoolVertexBuffer = NULL;
+    poolState.fPoolStartVertex = 0;
+}
+
+void GrInOrderDrawBuffer::releaseReservedIndexSpace() {
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    const GeometrySrcState& geoSrc = this->getGeomSrc();
+
+    // If we get a release index space call then our current source should either be reserved
+    // or array (which we copied into reserved space).
+    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.fPoolIndexBuffer = NULL;
+    poolState.fPoolStartIndex = 0;
+}
+
+void GrInOrderDrawBuffer::onSetVertexSourceToArray(const void* vertexArray,
+                                                   int vertexCount) {
+
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    GrAssert(0 == poolState.fUsedPoolVertexBytes);
+#if GR_DEBUG
+    bool success =
+#endif
+    fVertexPool.appendVertices(this->getVertexLayout(),
+                               vertexCount,
+                               vertexArray,
+                               &poolState.fPoolVertexBuffer,
+                               &poolState.fPoolStartVertex);
+    GR_DEBUGASSERT(success);
+}
+
+void GrInOrderDrawBuffer::onSetIndexSourceToArray(const void* indexArray,
+                                                  int indexCount) {
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    GrAssert(0 == poolState.fUsedPoolIndexBytes);
+#if GR_DEBUG
+    bool success =
+#endif
+    fIndexPool.appendIndices(indexCount,
+                             indexArray,
+                             &poolState.fPoolIndexBuffer,
+                             &poolState.fPoolStartIndex);
+    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;
+    poolState.fUsedPoolIndexBytes = 0;
+    this->resetDrawTracking();
+#if GR_DEBUG
+    poolState.fPoolVertexBuffer = (GrVertexBuffer*)~0;
+    poolState.fPoolStartVertex = ~0;
+    poolState.fPoolIndexBuffer = (GrIndexBuffer*)~0;
+    poolState.fPoolStartIndex = ~0;
+#endif
+}
+
+void GrInOrderDrawBuffer::geometrySourceWillPop(
+                                        const GeometrySrcState& restoredState) {
+    GrAssert(fGeoPoolStateStack.count() > 1);
+    fGeoPoolStateStack.pop_back();
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    // we have to assume that any slack we had in our vertex/index data
+    // is now unreleasable because data may have been appended later in the
+    // pool.
+    if (kReserved_GeometrySrcType == restoredState.fVertexSrc ||
+        kArray_GeometrySrcType == restoredState.fVertexSrc) {
+        poolState.fUsedPoolVertexBytes =
+            VertexSize(restoredState.fVertexLayout) *
+            restoredState.fVertexCount;
+    }
+    if (kReserved_GeometrySrcType == restoredState.fIndexSrc ||
+        kArray_GeometrySrcType == restoredState.fIndexSrc) {
+        poolState.fUsedPoolIndexBytes = sizeof(uint16_t) *
+                                         restoredState.fIndexCount;
+    }
+    this->resetDrawTracking();
+}
+
+bool GrInOrderDrawBuffer::needsNewState() const {
+    // we should have recorded a default state in reset()
+    GrAssert(!fStates.empty());
+    return fStates.back() != this->getDrawState();
+}
+
+bool GrInOrderDrawBuffer::needsNewClip() const {
+   if (this->getDrawState().isClipState()) {
+       if (fClipSet &&
+           (fClips.back() != *fClip->fClipStack ||
+            fClipOrigins.back() != fClip->fOrigin)) {
+           return true;
+       }
+    }
+    return false;
+}
+
+void GrInOrderDrawBuffer::recordClip() {
+    fClips.push_back() = *fClip->fClipStack;
+    fClipOrigins.push_back() = fClip->fOrigin;
+    fClipSet = false;
+    fCmds.push_back(kSetClip_Cmd);
+}
+
+void GrInOrderDrawBuffer::recordDefaultClip() {
+    fClips.push_back() = SkClipStack();
+    fClipOrigins.push_back() = SkIPoint::Make(0, 0);
+    fCmds.push_back(kSetClip_Cmd);
+}
+
+void GrInOrderDrawBuffer::recordState() {
+    fStates.push_back(this->getDrawState());
+    fCmds.push_back(kSetState_Cmd);
+}
+
+void GrInOrderDrawBuffer::recordDefaultState() {
+    fStates.push_back(GrDrawState());
+    fCmds.push_back(kSetState_Cmd);
+}
+
+GrInOrderDrawBuffer::Draw* GrInOrderDrawBuffer::recordDraw() {
+    fCmds.push_back(kDraw_Cmd);
+    return &fDraws.push_back();
+}
+
+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;
+}
diff --git a/src/gpu/GrInOrderDrawBuffer.h b/src/gpu/GrInOrderDrawBuffer.h
new file mode 100644
index 0000000..08dd774
--- /dev/null
+++ b/src/gpu/GrInOrderDrawBuffer.h
@@ -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.
+ */
+
+
+
+#ifndef GrInOrderDrawBuffer_DEFINED
+#define GrInOrderDrawBuffer_DEFINED
+
+#include "GrDrawTarget.h"
+#include "GrAllocPool.h"
+#include "GrAllocator.h"
+#include "GrPath.h"
+
+#include "SkClipStack.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.
+ */
+
+class GrInOrderDrawBuffer : public GrDrawTarget {
+public:
+
+    /**
+     * Creates a GrInOrderDrawBuffer
+     *
+     * @param gpu        the gpu object where this will be played back
+     *                   (possible indirectly). GrResources used with the draw
+     *                   buffer are created by this gpu object.
+     * @param vertexPool pool where vertices for queued draws will be saved when
+     *                   the vertex source is either reserved or array.
+     * @param indexPool  pool where indices for queued draws will be saved when
+     *                   the index source is either reserved or array.
+     */
+    GrInOrderDrawBuffer(const GrGpu* gpu,
+                        GrVertexBufferAllocPool* vertexPool,
+                        GrIndexBufferAllocPool* indexPool);
+
+    virtual ~GrInOrderDrawBuffer();
+
+    /**
+     * Provides the buffer with an index buffer that can be used for quad rendering.
+     * The buffer may be able to batch consecutive drawRects if this is provided.
+     * @param indexBuffer   index buffer with quad indices.
+     */
+    void setQuadIndexBuffer(const GrIndexBuffer* indexBuffer);
+
+    /**
+     * Empties the draw buffer of any queued up draws. This must not be called
+     * while inside an unbalanced pushGeometrySource().
+     */
+    void reset();
+
+    /**
+     * plays the queued up draws to another target. Does not empty this buffer
+     * so that it can be played back multiple times. This buffer must not have
+     * an active reserved vertex or index source. Any reserved geometry on
+     * the target will be finalized because it's geometry source will be pushed
+     * before playback and popped afterwards.
+     *
+     * @return false if the playback trivially drew nothing because nothing was
+     *         recorded.
+     *
+     * @param target    the target to receive the playback
+     */
+    bool playback(GrDrawTarget* target);
+
+    /**
+     * A convenience method to do a playback followed by a reset. All the
+     * constraints and side-effects or playback() and reset apply().
+     */
+    void flushTo(GrDrawTarget* target) {
+        if (fFlushing) {
+            // When creating SW-only clip masks, the GrClipMaskManager can
+            // cause a GrContext::flush (when copying the mask results back
+            // to the GPU). Without a guard this results in a recursive call
+            // to this method.
+            return;
+        }
+
+        fFlushing = true;
+        if (this->playback(target)) {
+            this->reset();
+        }
+        fFlushing = false;
+    }
+
+    /**
+     * This function allows the draw buffer to automatically flush itself to
+     * another target. This means the buffer may internally call
+     * this->flushTo(target) when it is safe to do so.
+     *
+     * 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,
+                          const GrRect* srcRects[] = NULL,
+                          const GrMatrix* srcMatrices[] = NULL) SK_OVERRIDE;
+
+    virtual void drawIndexedInstances(GrPrimitiveType type,
+                                      int instanceCount,
+                                      int verticesPerInstance,
+                                      int indicesPerInstance)
+                                      SK_OVERRIDE;
+
+    virtual bool geometryHints(GrVertexLayout vertexLayout,
+                               int* vertexCount,
+                               int* indexCount) const SK_OVERRIDE;
+
+    virtual void clear(const GrIRect* rect,
+                       GrColor color,
+                       GrRenderTarget* renderTarget = NULL) SK_OVERRIDE;
+
+protected:
+    virtual void willReserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
+                                                int vertexCount,
+                                                int indexCount) 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;
+        GrVertexLayout          fVertexLayout;
+        const GrVertexBuffer*   fVertexBuffer;
+        const GrIndexBuffer*    fIndexBuffer;
+    };
+
+    struct StencilPath {
+        SkAutoTUnref<const GrPath>  fPath;
+        GrPathFill                  fFill;
+    };
+
+    struct Clear {
+        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) SK_OVERRIDE;
+    virtual void onDrawNonIndexed(GrPrimitiveType primitiveType,
+                                  int startVertex,
+                                  int vertexCount) SK_OVERRIDE;
+    virtual void onStencilPath(const GrPath*, GrPathFill) SK_OVERRIDE;
+    virtual bool onReserveVertexSpace(GrVertexLayout layout,
+                                      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;
+    virtual void clipWillBeSet(const GrClipData* newClip) SK_OVERRIDE;
+
+    // we lazily record state and clip changes in order to skip clips and states
+    // that have no effect.
+    bool needsNewState() const;
+    bool needsNewClip() const;
+
+    // these functions record a command
+    void            recordState();
+    void            recordDefaultState();
+    void            recordClip();
+    void            recordDefaultClip();
+    Draw*           recordDraw();
+    StencilPath*    recordStencilPath();
+    Clear*          recordClear();
+
+    // call this to invalidate the tracking data that is used to concatenate
+    // multiple draws into a single draw.
+    void resetDrawTracking();
+
+    enum {
+        kCmdPreallocCnt          = 32,
+        kDrawPreallocCnt         = 8,
+        kStencilPathPreallocCnt  = 8,
+        kStatePreallocCnt        = 8,
+        kClipPreallocCnt         = 8,
+        kClearPreallocCnt        = 4,
+        kGeoPoolStatePreAllocCnt = 4,
+    };
+
+    SkSTArray<kCmdPreallocCnt, uint8_t, true>           fCmds;
+    GrSTAllocator<kDrawPreallocCnt, Draw>               fDraws;
+    GrSTAllocator<kStatePreallocCnt, StencilPath>       fStencilPaths;
+    GrSTAllocator<kStatePreallocCnt, GrDrawState>       fStates;
+    GrSTAllocator<kClearPreallocCnt, Clear>             fClears;
+
+    GrSTAllocator<kClipPreallocCnt, SkClipStack>        fClips;
+    GrSTAllocator<kClipPreallocCnt, SkIPoint>           fClipOrigins;
+
+    GrDrawTarget*                   fAutoFlushTarget;
+
+    bool                            fClipSet;
+
+    GrVertexBufferAllocPool&        fVertexPool;
+
+    GrIndexBufferAllocPool&         fIndexPool;
+
+    // these are used to attempt to concatenate drawRect calls
+    GrVertexLayout                  fLastRectVertexLayout;
+    const GrIndexBuffer*            fQuadIndexBuffer;
+    int                             fMaxQuads;
+    int                             fCurrQuad;
+
+    // bookkeeping to attempt to concantenate drawIndexedInstances calls
+    struct {
+        int            fVerticesPerInstance;
+        int            fIndicesPerInstance;
+        void reset() {
+            fVerticesPerInstance = 0;
+            fIndicesPerInstance = 0;
+        }
+    } fInstancedDrawTracker;
+
+    struct GeometryPoolState {
+        const GrVertexBuffer*           fPoolVertexBuffer;
+        int                             fPoolStartVertex;
+        const GrIndexBuffer*            fPoolIndexBuffer;
+        int                             fPoolStartIndex;
+        // caller may conservatively over reserve vertices / indices.
+        // we release unused space back to allocator if possible
+        // can only do this if there isn't an intervening pushGeometrySource()
+        size_t                          fUsedPoolVertexBytes;
+        size_t                          fUsedPoolIndexBytes;
+    };
+    SkSTArray<kGeoPoolStatePreAllocCnt, GeometryPoolState> fGeoPoolStateStack;
+
+    bool                            fFlushing;
+
+    typedef GrDrawTarget INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrIndexBuffer.h b/src/gpu/GrIndexBuffer.h
new file mode 100644
index 0000000..a7e7a57
--- /dev/null
+++ b/src/gpu/GrIndexBuffer.h
@@ -0,0 +1,33 @@
+
+/*
+ * 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 GrIndexBuffer_DEFINED
+#define GrIndexBuffer_DEFINED
+
+#include "GrGeometryBuffer.h"
+
+class GrIndexBuffer : public GrGeometryBuffer {
+public:
+        /**
+         * Retrieves the maximum number of quads that could be rendered
+         * from the index buffer (using kTriangles_GrPrimitiveType).
+         * @return the maximum number of quads using full size of index buffer.
+         */
+        int maxQuads() const {
+            return this->sizeInBytes() / (sizeof(uint16_t) * 6);
+        }
+protected:
+    GrIndexBuffer(GrGpu* gpu, size_t sizeInBytes, bool dynamic)
+        : INHERITED(gpu, sizeInBytes, dynamic) {}
+private:
+    typedef GrGeometryBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrMatrix.cpp b/src/gpu/GrMatrix.cpp
new file mode 100644
index 0000000..e71636b
--- /dev/null
+++ b/src/gpu/GrMatrix.cpp
@@ -0,0 +1,713 @@
+
+/*
+ * 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
new file mode 100644
index 0000000..f6d3c7f
--- /dev/null
+++ b/src/gpu/GrMemory.cpp
@@ -0,0 +1,28 @@
+
+/*
+ * 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 <stdlib.h>
+#include "GrTypes.h"
+
+void* GrMalloc(size_t bytes) {
+    void* ptr = ::malloc(bytes);
+    if (NULL == ptr) {
+        ::exit(-1);
+    }
+    return ptr;
+}
+
+void GrFree(void* ptr) {
+    if (ptr) {
+        ::free(ptr);
+    }
+}
+
+
diff --git a/src/gpu/GrMemoryPool.cpp b/src/gpu/GrMemoryPool.cpp
new file mode 100644
index 0000000..17d70ab
--- /dev/null
+++ b/src/gpu/GrMemoryPool.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 "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() {
+    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);
+}
+
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..e8f0d58
--- /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) : INHERITED(gpu) {}
+
+    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
new file mode 100644
index 0000000..1daa6c9
--- /dev/null
+++ b/src/gpu/GrPathRenderer.cpp
@@ -0,0 +1,15 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrPathRenderer.h"
+
+SK_DEFINE_INST_COUNT(GrPathRenderer)
+
+GrPathRenderer::GrPathRenderer() {
+}
+
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
new file mode 100644
index 0000000..fc3d672
--- /dev/null
+++ b/src/gpu/GrPathRenderer.h
@@ -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.
+ */
+
+
+#ifndef GrPathRenderer_DEFINED
+#define GrPathRenderer_DEFINED
+
+#include "GrDrawTarget.h"
+#include "GrPathRendererChain.h"
+
+#include "SkTArray.h"
+
+class SkPath;
+
+struct GrPoint;
+
+/**
+ *  Base class for drawing paths into a GrDrawTarget.
+ *
+ *  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.
+     *
+     * @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);
+
+
+    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.
+     *
+     * @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.
+     */
+    virtual bool requiresStencilPass(const SkPath& path,
+                                     GrPathFill fill,
+                                     const GrDrawTarget* target) const {
+        return false;
+    }
+
+    /**
+     * 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 fill       The fill rule to use
+     * @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 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).
+     *
+     * @param path                  the path to draw.
+     * @param fill                  the path filling rule to use.
+     * @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,
+                          GrDrawTarget* target,
+                          bool antiAlias) {
+        GrAssert(this->canDrawPath(path, fill, target, antiAlias));
+        return this->onDrawPath(path, fill, 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_GrPathFill or kEvenOdd_GrPathFill.
+     *
+     * 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.
+     *
+     */
+    virtual void drawPathToStencil(const SkPath& path,
+                                   GrPathFill fill,
+                                   GrDrawTarget* target) {
+        GrCrash("Unexpected call to drawPathToStencil.");
+    }
+
+protected:
+    /**
+     * Draws the path into the draw target.
+     *
+     * @param path                  the path to draw.
+     * @param fill                  the path filling rule to use.
+     * @param target                target that the path will be rendered to
+     * @param antiAlias             whether antialiasing is enabled or not.
+     */
+    virtual bool onDrawPath(const SkPath& path,
+                            GrPathFill fill,
+                            GrDrawTarget* target,
+                            bool antiAlias) = 0;
+
+private:
+
+    typedef GrRefCnt INHERITED;
+};
+
+#endif
+
diff --git a/src/gpu/GrPathRendererChain.cpp b/src/gpu/GrPathRendererChain.cpp
new file mode 100644
index 0000000..2814f99
--- /dev/null
+++ b/src/gpu/GrPathRendererChain.cpp
@@ -0,0 +1,60 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrPathRendererChain.h"
+
+#include "GrContext.h"
+#include "GrDefaultPathRenderer.h"
+#include "GrGpu.h"
+
+SK_DEFINE_INST_COUNT(GrPathRendererChain)
+
+GrPathRendererChain::GrPathRendererChain(GrContext* context, UsageFlags flags)
+    : fInit(false)
+    , fOwner(context)
+    , fFlags(flags) {
+}
+
+GrPathRendererChain::~GrPathRendererChain() {
+    for (int i = 0; i < fChain.count(); ++i) {
+        fChain[i]->unref();
+    }
+}
+
+GrPathRenderer* GrPathRendererChain::addPathRenderer(GrPathRenderer* pr) {
+    fChain.push_back() = pr;
+    pr->ref();
+    return pr;
+}
+
+GrPathRenderer* GrPathRendererChain::getPathRenderer(const SkPath& path,
+                                                     GrPathFill fill,
+                                                     const GrDrawTarget* target,
+                                                     bool antiAlias) {
+    if (!fInit) {
+        this->init();
+    }
+    for (int i = 0; i < fChain.count(); ++i) {
+        if (fChain[i]->canDrawPath(path, fill, target, antiAlias)) {
+            return fChain[i];
+        }
+    }
+    return NULL;
+}
+
+void GrPathRendererChain::init() {
+    GrAssert(!fInit);
+    GrGpu* gpu = fOwner->getGpu();
+    bool twoSided = gpu->getCaps().twoSidedStencilSupport();
+    bool wrapOp = gpu->getCaps().stencilWrapOpsSupport();
+    GrPathRenderer::AddPathRenderers(fOwner, fFlags, this);
+    this->addPathRenderer(SkNEW_ARGS(GrDefaultPathRenderer,
+                                     (twoSided, wrapOp)))->unref();
+    fInit = true;
+}
diff --git a/src/gpu/GrPathRendererChain.h b/src/gpu/GrPathRendererChain.h
new file mode 100644
index 0000000..e5ccabb
--- /dev/null
+++ b/src/gpu/GrPathRendererChain.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 GrPathRendererChain_DEFINED
+#define GrPathRendererChain_DEFINED
+
+#include "GrDrawTarget.h"
+#include "GrRefCnt.h"
+#include "SkTArray.h"
+
+class GrContext;
+
+class SkPath;
+class GrPathRenderer;
+
+/**
+ * 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:
+    SK_DECLARE_INST_COUNT(GrPathRendererChain)
+
+    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;
+
+    typedef SkRefCnt INHERITED;
+};
+
+GR_MAKE_BITFIELD_OPS(GrPathRendererChain::UsageFlags)
+
+#endif
diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp
new file mode 100644
index 0000000..e0ddea8
--- /dev/null
+++ b/src/gpu/GrPathUtils.cpp
@@ -0,0 +1,479 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrPathUtils.h"
+#include "GrPoint.h"
+#include "SkGeometry.h"
+
+GrScalar GrPathUtils::scaleToleranceToSrc(GrScalar devTol,
+                                          const GrMatrix& 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;
+
+    if (stretch < 0) {
+        // take worst case mapRadius amoung four corners.
+        // (less than perfect)
+        for (int i = 0; i < 4; ++i) {
+            GrMatrix 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);
+    return srcTol;
+}
+
+static const int MAX_POINTS_PER_CURVE = 1 << 10;
+static const GrScalar gMinCurveTol = GrFloatToScalar(0.0001f);
+
+uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[],
+                                          GrScalar tol) {
+    if (tol < gMinCurveTol) {
+        tol = gMinCurveTol;
+    }
+    GrAssert(tol > 0);
+
+    GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
+    if (d <= tol) {
+        return 1;
+    } else {
+        // Each time we subdivide, d should be cut in 4. So we need to
+        // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
+        // points.
+        // 2^(log4(x)) = sqrt(x);
+        int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
+        int pow2 = GrNextPow2(temp);
+        // Because of NaNs & INFs we can wind up with a degenerate temp
+        // such that pow2 comes out negative. Also, our point generator
+        // will always output at least one pt.
+        if (pow2 < 1) {
+            pow2 = 1;
+        }
+        return GrMin(pow2, MAX_POINTS_PER_CURVE);
+    }
+}
+
+uint32_t GrPathUtils::generateQuadraticPoints(const GrPoint& p0,
+                                              const GrPoint& p1,
+                                              const GrPoint& p2,
+                                              GrScalar tolSqd,
+                                              GrPoint** points,
+                                              uint32_t pointsLeft) {
+    if (pointsLeft < 2 ||
+        (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
+        (*points)[0] = p2;
+        *points += 1;
+        return 1;
+    }
+
+    GrPoint q[] = {
+        { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
+        { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
+    };
+    GrPoint r = { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) };
+
+    pointsLeft >>= 1;
+    uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft);
+    uint32_t b = generateQuadraticPoints(r, q[1], p2, tolSqd, points, pointsLeft);
+    return a + b;
+}
+
+uint32_t GrPathUtils::cubicPointCount(const GrPoint points[],
+                                           GrScalar tol) {
+    if (tol < gMinCurveTol) {
+        tol = gMinCurveTol;
+    }
+    GrAssert(tol > 0);
+
+    GrScalar d = GrMax(
+        points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
+        points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
+    d = SkScalarSqrt(d);
+    if (d <= tol) {
+        return 1;
+    } else {
+        int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
+        int pow2 = GrNextPow2(temp);
+        // Because of NaNs & INFs we can wind up with a degenerate temp
+        // such that pow2 comes out negative. Also, our point generator
+        // will always output at least one pt.
+        if (pow2 < 1) {
+            pow2 = 1;
+        }
+        return GrMin(pow2, MAX_POINTS_PER_CURVE);
+    }
+}
+
+uint32_t GrPathUtils::generateCubicPoints(const GrPoint& p0,
+                                          const GrPoint& p1,
+                                          const GrPoint& p2,
+                                          const GrPoint& p3,
+                                          GrScalar tolSqd,
+                                          GrPoint** points,
+                                          uint32_t pointsLeft) {
+    if (pointsLeft < 2 ||
+        (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
+         p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
+            (*points)[0] = p3;
+            *points += 1;
+            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) }
+    };
+    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) }
+    };
+    GrPoint s = { GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(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 SkPath& path, int* subpaths,
+                                     GrScalar tol) {
+    if (tol < gMinCurveTol) {
+        tol = gMinCurveTol;
+    }
+    GrAssert(tol > 0);
+
+    int pointCount = 0;
+    *subpaths = 1;
+
+    bool first = true;
+
+    SkPath::Iter iter(path, false);
+    GrPathCmd cmd;
+
+    GrPoint pts[4];
+    while ((cmd = (GrPathCmd)iter.next(pts)) != kEnd_PathCmd) {
+
+        switch (cmd) {
+            case kLine_PathCmd:
+                pointCount += 1;
+                break;
+            case kQuadratic_PathCmd:
+                pointCount += quadraticPointCount(pts, tol);
+                break;
+            case kCubic_PathCmd:
+                pointCount += cubicPointCount(pts, tol);
+                break;
+            case kMove_PathCmd:
+                pointCount += 1;
+                if (!first) {
+                    ++(*subpaths);
+                }
+                break;
+            default:
+                break;
+        }
+        first = false;
+    }
+    return pointCount;
+}
+
+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,   GR_ScalarHalf,  GR_Scalar1,
+                 0,               0,  GR_Scalar1,
+                 SkScalarToPersp(GR_Scalar1),
+                 SkScalarToPersp(GR_Scalar1),
+                 SkScalarToPersp(GR_Scalar1));
+    m.setAll(qPts[0].fX, qPts[1].fX, qPts[2].fX,
+             qPts[0].fY, qPts[1].fY, qPts[2].fY,
+             SkScalarToPersp(GR_Scalar1),
+             SkScalarToPersp(GR_Scalar1),
+             SkScalarToPersp(GR_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]);
+        int maxEdge = 0;
+        SkScalar d = qPts[1].distanceToSqd(qPts[2]);
+        if (d > maxD) {
+            maxD = d;
+            maxEdge = 1;
+        }
+        d = qPts[2].distanceToSqd(qPts[0]);
+        if (d > maxD) {
+            maxD = d;
+            maxEdge = 2;
+        }
+        // We could have a tolerance here, not sure if it would improve anything
+        if (maxD > 0) {
+            // Set the matrix to give (u = 0, v = distance_to_line)
+            GrVec lineVec = qPts[(maxEdge + 1)%3] - qPts[maxEdge];
+            // when looking from the point 0 down the line we want positive
+            // distances to be to the left. This matches the non-degenerate
+            // case.
+            lineVec.setOrthog(lineVec, GrPoint::kLeft_Side);
+            lineVec.dot(qPts[0]);
+            // 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.
+            fM[0] = 0; fM[1] = 0; fM[2] = 100.f;
+            fM[3] = 0; fM[4] = 0; fM[5] = 100.f;
+        }
+    } else {
+        m.postConcat(UVpts);
+
+        // The matrix should not have perspective.
+        static const GrScalar gTOL = GrFloatToScalar(1.f / 100.f);
+        GrAssert(GrScalarAbs(m.get(SkMatrix::kMPersp0)) < gTOL);
+        GrAssert(GrScalarAbs(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 toleranceSqd,
+                                       bool constrainWithinTangents,
+                                       SkPath::Direction dir,
+                                       SkTArray<SkPoint, true>* quads,
+                                       int sublevel = 0) {
+
+    // 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(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 = sublevel > kMaxSubdivs ? 0 : c0.distanceToSqd(c1);
+    if (dSqd < toleranceSqd) {
+        SkPoint cAvg = c0;
+        cAvg += c1;
+        cAvg.scale(SK_ScalarHalf);
+
+        bool subdivide = false;
+
+        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, tolSqd, constrainWithinTangents, dir, quads);
+    }
+
+}
diff --git a/src/gpu/GrPathUtils.h b/src/gpu/GrPathUtils.h
new file mode 100644
index 0000000..31b6398
--- /dev/null
+++ b/src/gpu/GrPathUtils.h
@@ -0,0 +1,118 @@
+
+/*
+ * 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 GrPathUtils_DEFINED
+#define GrPathUtils_DEFINED
+
+#include "GrMatrix.h"
+#include "SkPath.h"
+#include "SkTArray.h"
+
+/**
+ *  Utilities for evaluating paths.
+ */
+namespace GrPathUtils {
+    GrScalar scaleToleranceToSrc(GrScalar devTol,
+                                 const GrMatrix& 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 SkPath&,
+                            int* subpaths,
+                            GrScalar 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 generateQuadraticPoints(const GrPoint& p0,
+                                     const GrPoint& p1,
+                                     const GrPoint& p2,
+                                     GrScalar 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 generateCubicPoints(const GrPoint& p0,
+                                 const GrPoint& p1,
+                                 const GrPoint& p2,
+                                 const GrPoint& p3,
+                                 GrScalar tolSqd,
+                                 GrPoint** points,
+                                 uint32_t pointsLeft);
+
+    // 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
new file mode 100644
index 0000000..6dd7114
--- /dev/null
+++ b/src/gpu/GrPlotMgr.h
@@ -0,0 +1,77 @@
+
+/*
+ * 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 GrPlotMgr_DEFINED
+#define GrPlotMgr_DEFINED
+
+#include "GrTypes.h"
+#include "GrPoint.h"
+
+class GrPlotMgr : GrNoncopyable {
+public:
+    GrPlotMgr(int width, int height) {
+        fDim.set(width, height);
+        size_t needed = width * height;
+        if (needed <= sizeof(fStorage)) {
+            fBusy = fStorage;
+        } else {
+            fBusy = SkNEW_ARRAY(char, needed);
+        }
+        this->reset();
+    }
+
+    ~GrPlotMgr() {
+        if (fBusy != fStorage) {
+            delete[] fBusy;
+        }
+    }
+
+    void reset() {
+        Gr_bzero(fBusy, fDim.fX * fDim.fY);
+    }
+
+    bool newPlot(GrIPoint16* loc) {
+        char* busy = fBusy;
+        for (int y = 0; y < fDim.fY; y++) {
+            for (int x = 0; x < fDim.fX; x++) {
+                if (!*busy) {
+                    *busy = true;
+                    loc->set(x, y);
+                    return true;
+                }
+                busy++;
+            }
+        }
+        return false;
+    }
+
+    bool isBusy(int x, int y) const {
+        GrAssert((unsigned)x < (unsigned)fDim.fX);
+        GrAssert((unsigned)y < (unsigned)fDim.fY);
+        return fBusy[y * fDim.fX + x] != 0;
+    }
+
+    void freePlot(int x, int y) {
+        GrAssert((unsigned)x < (unsigned)fDim.fX);
+        GrAssert((unsigned)y < (unsigned)fDim.fY);
+        fBusy[y * fDim.fX + x] = false;
+    }
+
+private:
+    enum {
+        STORAGE = 64
+    };
+    char fStorage[STORAGE];
+    char* fBusy;
+    GrIPoint16  fDim;
+};
+
+#endif
+
diff --git a/src/gpu/GrRandom.h b/src/gpu/GrRandom.h
new file mode 100644
index 0000000..c98a8fb
--- /dev/null
+++ b/src/gpu/GrRandom.h
@@ -0,0 +1,55 @@
+
+/*
+ * 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
new file mode 100644
index 0000000..87ea92e
--- /dev/null
+++ b/src/gpu/GrRectanizer.cpp
@@ -0,0 +1,123 @@
+
+/*
+ * 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 "GrRectanizer.h"
+#include "GrTBSearch.h"
+
+#define MIN_HEIGHT_POW2     2
+
+class GrRectanizerPow2 : public GrRectanizer {
+public:
+    GrRectanizerPow2(int w, int h) : GrRectanizer(w, h) {
+        fNextStripY = 0;
+        fAreaSoFar = 0;
+        Gr_bzero(fRows, sizeof(fRows));
+    }
+
+    virtual ~GrRectanizerPow2() {
+    }
+
+    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;
+        fNextStripY += rowHeight;
+    }
+};
+
+bool GrRectanizerPow2::addRect(int width, int height, GrIPoint16* loc) {
+    if ((unsigned)width > (unsigned)this->width() ||
+        (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
+        height == 2. Thus we set a minimum height.
+     */
+    height = GrNextPow2(height);
+    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;
+        }
+        this->initRow(row, height);
+    } else {
+        if (!row->canAddWidth(width, this->width())) {
+            if (!this->canAddStrip(height)) {
+                return false;
+            }
+            // that row is now "full", so retarget our Row record for
+            // another one
+            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());
+    fAreaSoFar += area;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrRectanizer* GrRectanizer::Factory(int width, int height) {
+    return SkNEW_ARGS(GrRectanizerPow2, (width, height));
+}
+
+
diff --git a/src/gpu/GrRectanizer.h b/src/gpu/GrRectanizer.h
new file mode 100644
index 0000000..d1f5033
--- /dev/null
+++ b/src/gpu/GrRectanizer.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 GrRectanizer_DEFINED
+#define GrRectanizer_DEFINED
+
+#include "GrRect.h"
+#include "GrTDArray.h"
+
+class GrRectanizerPurgeListener {
+public:
+    virtual ~GrRectanizerPurgeListener() {}
+
+    virtual void notifyPurgeStrip(void*, int yCoord) = 0;
+};
+
+class GrRectanizer {
+public:
+    GrRectanizer(int width, int height) : fWidth(width), fHeight(height) {
+        GrAssert(width >= 0);
+        GrAssert(height >= 0);
+    }
+
+    virtual ~GrRectanizer() {}
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+
+    virtual bool addRect(int width, int height, GrIPoint16* loc) = 0;
+    virtual float percentFull() const = 0;
+
+    // return the Y-coordinate of a strip that should be purged, given height
+    // i.e. return the oldest such strip, or some other criteria. Return -1
+    // if there is no candidate
+    virtual int stripToPurge(int height) const = 0;
+    virtual void purgeStripAtY(int yCoord) = 0;
+
+    /**
+     *  Our factory, which returns the subclass du jour
+     */
+    static GrRectanizer* Factory(int width, int height);
+
+private:
+    int fWidth;
+    int fHeight;
+};
+
+#endif
+
+
diff --git a/src/gpu/GrRectanizer_fifo.cpp b/src/gpu/GrRectanizer_fifo.cpp
new file mode 100644
index 0000000..aab012d
--- /dev/null
+++ b/src/gpu/GrRectanizer_fifo.cpp
@@ -0,0 +1,123 @@
+
+/*
+ * 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 "GrRectanizer.h"
+#include "GrTBSearch.h"
+
+#define MIN_HEIGHT_POW2     2
+
+class GrRectanizerFIFO : public GrRectanizer {
+public:
+    GrRectanizerFIFO(int w, int h) : GrRectanizer(w, h) {
+        fNextStripY = 0;
+        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;
+        fNextStripY += rowHeight;
+    }
+};
+
+bool GrRectanizerFIFO::addRect(int width, int height, GrIPoint16* loc) {
+    if ((unsigned)width > (unsigned)this->width() ||
+        (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
+     height == 2. Thus we set a minimum height.
+     */
+    height = GrNextPow2(height);
+    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;
+        }
+        this->initRow(row, height);
+    } else {
+        if (!row->canAddWidth(width, this->width())) {
+            if (!this->canAddStrip(height)) {
+                return false;
+            }
+            // that row is now "full", so retarget our Row record for
+            // another one
+            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());
+    fAreaSoFar += area;
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrRectanizer* GrRectanizer::Factory(int width, int height) {
+    return SkNEW_ARGS(GrRectanizerFIFO, (width, height));
+}
+
+
diff --git a/src/gpu/GrRedBlackTree.h b/src/gpu/GrRedBlackTree.h
new file mode 100644
index 0000000..8187d81
--- /dev/null
+++ b/src/gpu/GrRedBlackTree.h
@@ -0,0 +1,1118 @@
+
+/*
+ * 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 GrRedBlackTree_DEFINED
+#define GrRedBlackTree_DEFINED
+
+#include "GrNoncopyable.h"
+
+template <typename T>
+class GrLess {
+public:
+    bool operator()(const T& a, const T& b) const { return a < b; }
+};
+
+template <typename T>
+class GrLess<T*> {
+public:
+    bool operator()(const T* a, const T* b) const { return *a < *b; }
+};
+
+/**
+ * In debug build this will cause full traversals of the tree when the validate
+ * is called on insert and remove. Useful for debugging but very slow.
+ */
+#define DEEP_VALIDATE 0
+
+/**
+ * A sorted tree that uses the red-black tree algorithm. Allows duplicate
+ * entries. Data is of type T and is compared using functor C. A single C object
+ * will be created and used for all comparisons.
+ */
+template <typename T, typename C = GrLess<T> >
+class GrRedBlackTree : public GrNoncopyable {
+public:
+    /**
+     * Creates an empty tree.
+     */
+    GrRedBlackTree();
+    virtual ~GrRedBlackTree();
+
+    /**
+     * Class used to iterater through the tree. The valid range of the tree
+     * is given by [begin(), end()). It is legal to dereference begin() but not
+     * end(). The iterator has preincrement and predecrement operators, it is
+     * legal to decerement end() if the tree is not empty to get the last
+     * element. However, a last() helper is provided.
+     */
+    class Iter;
+
+    /**
+     * Add an element to the tree. Duplicates are allowed.
+     * @param t     the item to add.
+     * @return  an iterator to the item.
+     */
+    Iter insert(const T& t);
+
+    /**
+     * Removes all items in the tree.
+     */
+    void reset();
+
+    /**
+     * @return true if there are no items in the tree, false otherwise.
+     */
+    bool empty() const {return 0 == fCount;}
+
+    /**
+     * @return the number of items in the tree.
+     */
+    int  count() const {return fCount;}
+
+    /**
+     * @return  an iterator to the first item in sorted order, or end() if empty
+     */
+    Iter begin();
+    /**
+     * Gets the last valid iterator. This is always valid, even on an empty.
+     * However, it can never be dereferenced. Useful as a loop terminator.
+     * @return  an iterator that is just beyond the last item in sorted order.
+     */
+    Iter end();
+    /**
+     * @return  an iterator that to the last item in sorted order, or end() if
+     * empty.
+     */
+    Iter last();
+
+    /**
+     * Finds an occurrence of an item.
+     * @param t     the item to find.
+     * @return an iterator to a tree element equal to t or end() if none exists.
+     */
+    Iter find(const T& t);
+    /**
+     * Finds the first of an item in iterator order.
+     * @param t     the item to find.
+     * @return  an iterator to the first element equal to t or end() if
+     *          none exists.
+     */
+    Iter findFirst(const T& t);
+    /**
+     * Finds the last of an item in iterator order.
+     * @param t     the item to find.
+     * @return  an iterator to the last element equal to t or end() if
+     *          none exists.
+     */
+    Iter findLast(const T& t);
+    /**
+     * Gets the number of items in the tree equal to t.
+     * @param t     the item to count.
+     * @return  number of items equal to t in the tree
+     */
+    int countOf(const T& t) const;
+
+    /**
+     * Removes the item indicated by an iterator. The iterator will not be valid
+     * afterwards.
+     *
+     * @param iter      iterator of item to remove. Must be valid (not end()).
+     */
+    void remove(const Iter& iter) { deleteAtNode(iter.fN); }
+
+    static void UnitTest();
+
+private:
+    enum Color {
+        kRed_Color,
+        kBlack_Color
+    };
+
+    enum Child {
+        kLeft_Child  = 0,
+        kRight_Child = 1
+    };
+
+    struct Node {
+        T       fItem;
+        Color   fColor;
+
+        Node*   fParent;
+        Node*   fChildren[2];
+    };
+
+    void rotateRight(Node* n);
+    void rotateLeft(Node* n);
+
+    static Node* SuccessorNode(Node* x);
+    static Node* PredecessorNode(Node* x);
+
+    void deleteAtNode(Node* x);
+    static void RecursiveDelete(Node* x);
+
+    int onCountOf(const Node* n, const T& t) const;
+
+#if GR_DEBUG
+    void validate() const;
+    int checkNode(Node* n, int* blackHeight) const;
+    // checks relationship between a node and its children. allowRedRed means
+    // node may be in an intermediate state where a red parent has a red child.
+    bool validateChildRelations(const Node* n, bool allowRedRed) const;
+    // place to stick break point if validateChildRelations is failing.
+    bool validateChildRelationsFailed() const { return false; }
+#else
+    void validate() const {}
+#endif
+
+    int     fCount;
+    Node*   fRoot;
+    Node*   fFirst;
+    Node*   fLast;
+
+    const C fComp;
+};
+
+template <typename T, typename C>
+class GrRedBlackTree<T,C>::Iter {
+public:
+    Iter() {};
+    Iter(const Iter& i) {fN = i.fN; fTree = i.fTree;}
+    Iter& operator =(const Iter& i) {
+        fN = i.fN;
+        fTree = i.fTree;
+        return *this;
+    }
+    // altering the sort value of the item using this method will cause
+    // errors.
+    T& operator *() const { return fN->fItem; }
+    bool operator ==(const Iter& i) const {
+        return fN == i.fN && fTree == i.fTree;
+    }
+    bool operator !=(const Iter& i) const { return !(*this == i); }
+    Iter& operator ++() {
+        GrAssert(*this != fTree->end());
+        fN = SuccessorNode(fN);
+        return *this;
+    }
+    Iter& operator --() {
+        GrAssert(*this != fTree->begin());
+        if (NULL != fN) {
+            fN = PredecessorNode(fN);
+        } else {
+            *this = fTree->last();
+        }
+        return *this;
+    }
+
+private:
+    friend class GrRedBlackTree;
+    explicit Iter(Node* n, GrRedBlackTree* tree) {
+        fN = n;
+        fTree = tree;
+    }
+    Node* fN;
+    GrRedBlackTree* fTree;
+};
+
+template <typename T, typename C>
+GrRedBlackTree<T,C>::GrRedBlackTree() : fComp() {
+    fRoot = NULL;
+    fFirst = NULL;
+    fLast = NULL;
+    fCount = 0;
+    validate();
+}
+
+template <typename T, typename C>
+GrRedBlackTree<T,C>::~GrRedBlackTree() {
+    RecursiveDelete(fRoot);
+}
+
+template <typename T, typename C>
+typename GrRedBlackTree<T,C>::Iter GrRedBlackTree<T,C>::begin() {
+    return Iter(fFirst, this);
+}
+
+template <typename T, typename C>
+typename GrRedBlackTree<T,C>::Iter GrRedBlackTree<T,C>::end() {
+    return Iter(NULL, this);
+}
+
+template <typename T, typename C>
+typename GrRedBlackTree<T,C>::Iter GrRedBlackTree<T,C>::last() {
+    return Iter(fLast, this);
+}
+
+template <typename T, typename C>
+typename GrRedBlackTree<T,C>::Iter GrRedBlackTree<T,C>::find(const T& t) {
+    Node* n = fRoot;
+    while (NULL != n) {
+        if (fComp(t, n->fItem)) {
+            n = n->fChildren[kLeft_Child];
+        } else {
+            if (!fComp(n->fItem, t)) {
+                return Iter(n, this);
+            }
+            n = n->fChildren[kRight_Child];
+        }
+    }
+    return end();
+}
+
+template <typename T, typename C>
+typename GrRedBlackTree<T,C>::Iter GrRedBlackTree<T,C>::findFirst(const T& t) {
+    Node* n = fRoot;
+    Node* leftMost = NULL;
+    while (NULL != n) {
+        if (fComp(t, n->fItem)) {
+            n = n->fChildren[kLeft_Child];
+        } else {
+            if (!fComp(n->fItem, t)) {
+                // found one. check if another in left subtree.
+                leftMost = n;
+                n = n->fChildren[kLeft_Child];
+            } else {
+                n = n->fChildren[kRight_Child];
+            }
+        }
+    }
+    return Iter(leftMost, this);
+}
+
+template <typename T, typename C>
+typename GrRedBlackTree<T,C>::Iter GrRedBlackTree<T,C>::findLast(const T& t) {
+    Node* n = fRoot;
+    Node* rightMost = NULL;
+    while (NULL != n) {
+        if (fComp(t, n->fItem)) {
+            n = n->fChildren[kLeft_Child];
+        } else {
+            if (!fComp(n->fItem, t)) {
+                // found one. check if another in right subtree.
+                rightMost = n;
+            }
+            n = n->fChildren[kRight_Child];
+        }
+    }
+    return Iter(rightMost, this);
+}
+
+template <typename T, typename C>
+int GrRedBlackTree<T,C>::countOf(const T& t) const {
+    return onCountOf(fRoot, t);
+}
+
+template <typename T, typename C>
+int GrRedBlackTree<T,C>::onCountOf(const Node* n, const T& t) const {
+    // this is count*log(n) :(
+    while (NULL != n) {
+        if (fComp(t, n->fItem)) {
+            n = n->fChildren[kLeft_Child];
+        } else {
+            if (!fComp(n->fItem, t)) {
+                int count = 1;
+                count += onCountOf(n->fChildren[kLeft_Child], t);
+                count += onCountOf(n->fChildren[kRight_Child], t);
+                return count;
+            }
+            n = n->fChildren[kRight_Child];
+        }
+    }
+    return 0;
+
+}
+
+template <typename T, typename C>
+void GrRedBlackTree<T,C>::reset() {
+    RecursiveDelete(fRoot);
+    fRoot = NULL;
+    fFirst = NULL;
+    fLast = NULL;
+    fCount = 0;
+}
+
+template <typename T, typename C>
+typename GrRedBlackTree<T,C>::Iter GrRedBlackTree<T,C>::insert(const T& t) {
+    validate();
+
+    ++fCount;
+
+    Node* x = SkNEW(Node);
+    x->fChildren[kLeft_Child] = NULL;
+    x->fChildren[kRight_Child] = NULL;
+    x->fItem = t;
+
+    Node* returnNode = x;
+
+    Node* gp = NULL;
+    Node* p = NULL;
+    Node* n = fRoot;
+    Child pc = kLeft_Child; // suppress uninit warning
+    Child gpc = kLeft_Child;
+
+    bool first = true;
+    bool last = true;
+    while (NULL != n) {
+        gpc = pc;
+        pc = fComp(x->fItem, n->fItem) ? kLeft_Child : kRight_Child;
+        first = first && kLeft_Child == pc;
+        last = last && kRight_Child == pc;
+        gp = p;
+        p = n;
+        n = p->fChildren[pc];
+    }
+    if (last) {
+        fLast = x;
+    }
+    if (first) {
+        fFirst = x;
+    }
+
+    if (NULL == p) {
+        fRoot = x;
+        x->fColor = kBlack_Color;
+        x->fParent = NULL;
+        GrAssert(1 == fCount);
+        return Iter(returnNode, this);
+    }
+    p->fChildren[pc] = x;
+    x->fColor = kRed_Color;
+    x->fParent = p;
+
+    do {
+        // assumptions at loop start.
+        GrAssert(NULL != x);
+        GrAssert(kRed_Color == x->fColor);
+        // can't have a grandparent but no parent.
+        GrAssert(!(NULL != gp && NULL == p));
+        // make sure pc and gpc are correct
+        GrAssert(NULL == p  || p->fChildren[pc] == x);
+        GrAssert(NULL == gp || gp->fChildren[gpc] == p);
+
+        // if x's parent is black then we didn't violate any of the
+        // red/black properties when we added x as red.
+        if (kBlack_Color == p->fColor) {
+            return Iter(returnNode, this);
+        }
+        // gp must be valid because if p was the root then it is black
+        GrAssert(NULL != gp);
+        // gp must be black since it's child, p, is red.
+        GrAssert(kBlack_Color == gp->fColor);
+
+
+        // x and its parent are red, violating red-black property.
+        Node* u = gp->fChildren[1-gpc];
+        // if x's uncle (p's sibling) is also red then we can flip
+        // p and u to black and make gp red. But then we have to recurse
+        // up to gp since it's parent may also be red.
+        if (NULL != u && kRed_Color == u->fColor) {
+            p->fColor = kBlack_Color;
+            u->fColor = kBlack_Color;
+            gp->fColor = kRed_Color;
+            x = gp;
+            p = x->fParent;
+            if (NULL == p) {
+                // x (prev gp) is the root, color it black and be done.
+                GrAssert(fRoot == x);
+                x->fColor = kBlack_Color;
+                validate();
+                return Iter(returnNode, this);
+            }
+            gp = p->fParent;
+            pc = (p->fChildren[kLeft_Child] == x) ? kLeft_Child :
+                                                    kRight_Child;
+            if (NULL != gp) {
+                gpc = (gp->fChildren[kLeft_Child] == p) ? kLeft_Child :
+                                                          kRight_Child;
+            }
+            continue;
+        } break;
+    } while (true);
+    // Here p is red but u is black and we still have to resolve the fact
+    // that x and p are both red.
+    GrAssert(NULL == gp->fChildren[1-gpc] || kBlack_Color == gp->fChildren[1-gpc]->fColor);
+    GrAssert(kRed_Color == x->fColor);
+    GrAssert(kRed_Color == p->fColor);
+    GrAssert(kBlack_Color == gp->fColor);
+
+    // make x be on the same side of p as p is of gp. If it isn't already
+    // the case then rotate x up to p and swap their labels.
+    if (pc != gpc) {
+        if (kRight_Child == pc) {
+            rotateLeft(p);
+            Node* temp = p;
+            p = x;
+            x = temp;
+            pc = kLeft_Child;
+        } else {
+            rotateRight(p);
+            Node* temp = p;
+            p = x;
+            x = temp;
+            pc = kRight_Child;
+        }
+    }
+    // we now rotate gp down, pulling up p to be it's new parent.
+    // gp's child, u, that is not affected we know to be black. gp's new
+    // child is p's previous child (x's pre-rotation sibling) which must be
+    // black since p is red.
+    GrAssert(NULL == p->fChildren[1-pc] ||
+             kBlack_Color == p->fChildren[1-pc]->fColor);
+    // Since gp's two children are black it can become red if p is made
+    // black. This leaves the black-height of both of p's new subtrees
+    // preserved and removes the red/red parent child relationship.
+    p->fColor = kBlack_Color;
+    gp->fColor = kRed_Color;
+    if (kLeft_Child == pc) {
+        rotateRight(gp);
+    } else {
+        rotateLeft(gp);
+    }
+    validate();
+    return Iter(returnNode, this);
+}
+
+
+template <typename T, typename C>
+void GrRedBlackTree<T,C>::rotateRight(Node* n) {
+    /*            d?              d?
+     *           /               /
+     *          n               s
+     *         / \     --->    / \
+     *        s   a?          c?  n
+     *       / \                 / \
+     *      c?  b?              b?  a?
+     */
+    Node* d = n->fParent;
+    Node* s = n->fChildren[kLeft_Child];
+    GrAssert(NULL != s);
+    Node* b = s->fChildren[kRight_Child];
+
+    if (NULL != d) {
+        Child c = d->fChildren[kLeft_Child] == n ? kLeft_Child :
+                                             kRight_Child;
+        d->fChildren[c] = s;
+    } else {
+        GrAssert(fRoot == n);
+        fRoot = s;
+    }
+    s->fParent = d;
+    s->fChildren[kRight_Child] = n;
+    n->fParent = s;
+    n->fChildren[kLeft_Child] = b;
+    if (NULL != b) {
+        b->fParent = n;
+    }
+
+    GR_DEBUGASSERT(validateChildRelations(d, true));
+    GR_DEBUGASSERT(validateChildRelations(s, true));
+    GR_DEBUGASSERT(validateChildRelations(n, false));
+    GR_DEBUGASSERT(validateChildRelations(n->fChildren[kRight_Child], true));
+    GR_DEBUGASSERT(validateChildRelations(b, true));
+    GR_DEBUGASSERT(validateChildRelations(s->fChildren[kLeft_Child], true));
+}
+
+template <typename T, typename C>
+void GrRedBlackTree<T,C>::rotateLeft(Node* n) {
+
+    Node* d = n->fParent;
+    Node* s = n->fChildren[kRight_Child];
+    GrAssert(NULL != s);
+    Node* b = s->fChildren[kLeft_Child];
+
+    if (NULL != d) {
+        Child c = d->fChildren[kRight_Child] == n ? kRight_Child :
+                                                   kLeft_Child;
+        d->fChildren[c] = s;
+    } else {
+        GrAssert(fRoot == n);
+        fRoot = s;
+    }
+    s->fParent = d;
+    s->fChildren[kLeft_Child] = n;
+    n->fParent = s;
+    n->fChildren[kRight_Child] = b;
+    if (NULL != b) {
+        b->fParent = n;
+    }
+
+    GR_DEBUGASSERT(validateChildRelations(d, true));
+    GR_DEBUGASSERT(validateChildRelations(s, true));
+    GR_DEBUGASSERT(validateChildRelations(n, true));
+    GR_DEBUGASSERT(validateChildRelations(n->fChildren[kLeft_Child], true));
+    GR_DEBUGASSERT(validateChildRelations(b, true));
+    GR_DEBUGASSERT(validateChildRelations(s->fChildren[kRight_Child], true));
+}
+
+template <typename T, typename C>
+typename GrRedBlackTree<T,C>::Node* GrRedBlackTree<T,C>::SuccessorNode(Node* x) {
+    GrAssert(NULL != x);
+    if (NULL != x->fChildren[kRight_Child]) {
+        x = x->fChildren[kRight_Child];
+        while (NULL != x->fChildren[kLeft_Child]) {
+            x = x->fChildren[kLeft_Child];
+        }
+        return x;
+    }
+    while (NULL != x->fParent && x == x->fParent->fChildren[kRight_Child]) {
+        x = x->fParent;
+    }
+    return x->fParent;
+}
+
+template <typename T, typename C>
+typename GrRedBlackTree<T,C>::Node* GrRedBlackTree<T,C>::PredecessorNode(Node* x) {
+    GrAssert(NULL != x);
+    if (NULL != x->fChildren[kLeft_Child]) {
+        x = x->fChildren[kLeft_Child];
+        while (NULL != x->fChildren[kRight_Child]) {
+            x = x->fChildren[kRight_Child];
+        }
+        return x;
+    }
+    while (NULL != x->fParent && x == x->fParent->fChildren[kLeft_Child]) {
+        x = x->fParent;
+    }
+    return x->fParent;
+}
+
+template <typename T, typename C>
+void GrRedBlackTree<T,C>::deleteAtNode(Node* x) {
+    GrAssert(NULL != x);
+    validate();
+    --fCount;
+
+    bool hasLeft =  NULL != x->fChildren[kLeft_Child];
+    bool hasRight = NULL != x->fChildren[kRight_Child];
+    Child c = hasLeft ? kLeft_Child : kRight_Child;
+
+    if (hasLeft && hasRight) {
+        // first and last can't have two children.
+        GrAssert(fFirst != x);
+        GrAssert(fLast != x);
+        // if x is an interior node then we find it's successor
+        // and swap them.
+        Node* s = x->fChildren[kRight_Child];
+        while (NULL != s->fChildren[kLeft_Child]) {
+            s = s->fChildren[kLeft_Child];
+        }
+        GrAssert(NULL != s);
+        // this might be expensive relative to swapping node ptrs around.
+        // depends on T.
+        x->fItem = s->fItem;
+        x = s;
+        c = kRight_Child;
+    } else if (NULL == x->fParent) {
+        // if x was the root we just replace it with its child and make
+        // the new root (if the tree is not empty) black.
+        GrAssert(fRoot == x);
+        fRoot = x->fChildren[c];
+        if (NULL != fRoot) {
+            fRoot->fParent = NULL;
+            fRoot->fColor = kBlack_Color;
+            if (x == fLast) {
+                GrAssert(c == kLeft_Child);
+                fLast = fRoot;
+            } else if (x == fFirst) {
+                GrAssert(c == kRight_Child);
+                fFirst = fRoot;
+            }
+        } else {
+            GrAssert(fFirst == fLast && x == fFirst);
+            fFirst = NULL;
+            fLast = NULL;
+            GrAssert(0 == fCount);
+        }
+        delete x;
+        validate();
+        return;
+    }
+
+    Child pc;
+    Node* p = x->fParent;
+    pc = p->fChildren[kLeft_Child] == x ? kLeft_Child : kRight_Child;
+
+    if (NULL == x->fChildren[c]) {
+        if (fLast == x) {
+            fLast = p;
+            GrAssert(p == PredecessorNode(x));
+        } else if (fFirst == x) {
+            fFirst = p;
+            GrAssert(p == SuccessorNode(x));
+        }
+        // x has two implicit black children.
+        Color xcolor = x->fColor;
+        p->fChildren[pc] = NULL;
+        delete x;
+        x = NULL;
+        // when x is red it can be with an implicit black leaf without
+        // violating any of the red-black tree properties.
+        if (kRed_Color == xcolor) {
+            validate();
+            return;
+        }
+        // s is p's other child (x's sibling)
+        Node* s = p->fChildren[1-pc];
+
+        //s cannot be an implicit black node because the original
+        // black-height at x was >= 2 and s's black-height must equal the
+        // initial black height of x.
+        GrAssert(NULL != s);
+        GrAssert(p == s->fParent);
+
+        // assigned in loop
+        Node* sl;
+        Node* sr;
+        bool slRed;
+        bool srRed;
+
+        do {
+            // When we start this loop x may already be deleted it is/was
+            // p's child on its pc side. x's children are/were black. The
+            // first time through the loop they are implict children.
+            // On later passes we will be walking up the tree and they will
+            // be real nodes.
+            // The x side of p has a black-height that is one less than the
+            // s side. It must be rebalanced.
+            GrAssert(NULL != s);
+            GrAssert(p == s->fParent);
+            GrAssert(NULL == x || x->fParent == p);
+
+            //sl and sr are s's children, which may be implicit.
+            sl = s->fChildren[kLeft_Child];
+            sr = s->fChildren[kRight_Child];
+
+            // if the s is red we will rotate s and p, swap their colors so
+            // that x's new sibling is black
+            if (kRed_Color == s->fColor) {
+                // if s is red then it's parent must be black.
+                GrAssert(kBlack_Color == p->fColor);
+                // s's children must also be black since s is red. They can't
+                // be implicit since s is red and it's black-height is >= 2.
+                GrAssert(NULL != sl && kBlack_Color == sl->fColor);
+                GrAssert(NULL != sr && kBlack_Color == sr->fColor);
+                p->fColor = kRed_Color;
+                s->fColor = kBlack_Color;
+                if (kLeft_Child == pc) {
+                    rotateLeft(p);
+                    s = sl;
+                } else {
+                    rotateRight(p);
+                    s = sr;
+                }
+                sl = s->fChildren[kLeft_Child];
+                sr = s->fChildren[kRight_Child];
+            }
+            // x and s are now both black.
+            GrAssert(kBlack_Color == s->fColor);
+            GrAssert(NULL == x || kBlack_Color == x->fColor);
+            GrAssert(p == s->fParent);
+            GrAssert(NULL == x || p == x->fParent);
+
+            // when x is deleted its subtree will have reduced black-height.
+            slRed = (NULL != sl && kRed_Color == sl->fColor);
+            srRed = (NULL != sr && kRed_Color == sr->fColor);
+            if (!slRed && !srRed) {
+                // if s can be made red that will balance out x's removal
+                // to make both subtrees of p have the same black-height.
+                if (kBlack_Color == p->fColor) {
+                    s->fColor = kRed_Color;
+                    // now subtree at p has black-height of one less than
+                    // p's parent's other child's subtree. We move x up to
+                    // p and go through the loop again. At the top of loop
+                    // we assumed x and x's children are black, which holds
+                    // by above ifs.
+                    // if p is the root there is no other subtree to balance
+                    // against.
+                    x = p;
+                    p = x->fParent;
+                    if (NULL == p) {
+                        GrAssert(fRoot == x);
+                        validate();
+                        return;
+                    } else {
+                        pc = p->fChildren[kLeft_Child] == x ? kLeft_Child :
+                                                              kRight_Child;
+
+                    }
+                    s = p->fChildren[1-pc];
+                    GrAssert(NULL != s);
+                    GrAssert(p == s->fParent);
+                    continue;
+                } else if (kRed_Color == p->fColor) {
+                    // we can make p black and s red. This balance out p's
+                    // two subtrees and keep the same black-height as it was
+                    // before the delete.
+                    s->fColor = kRed_Color;
+                    p->fColor = kBlack_Color;
+                    validate();
+                    return;
+                }
+            }
+            break;
+        } while (true);
+        // if we made it here one or both of sl and sr is red.
+        // s and x are black. We make sure that a red child is on
+        // the same side of s as s is of p.
+        GrAssert(slRed || srRed);
+        if (kLeft_Child == pc && !srRed) {
+            s->fColor = kRed_Color;
+            sl->fColor = kBlack_Color;
+            rotateRight(s);
+            sr = s;
+            s = sl;
+            //sl = s->fChildren[kLeft_Child]; don't need this
+        } else if (kRight_Child == pc && !slRed) {
+            s->fColor = kRed_Color;
+            sr->fColor = kBlack_Color;
+            rotateLeft(s);
+            sl = s;
+            s = sr;
+            //sr = s->fChildren[kRight_Child]; don't need this
+        }
+        // now p is either red or black, x and s are red and s's 1-pc
+        // child is red.
+        // We rotate p towards x, pulling s up to replace p. We make
+        // p be black and s takes p's old color.
+        // Whether p was red or black, we've increased its pc subtree
+        // rooted at x by 1 (balancing the imbalance at the start) and
+        // we've also its subtree rooted at s's black-height by 1. This
+        // can be balanced by making s's red child be black.
+        s->fColor = p->fColor;
+        p->fColor = kBlack_Color;
+        if (kLeft_Child == pc) {
+            GrAssert(NULL != sr && kRed_Color == sr->fColor);
+            sr->fColor = kBlack_Color;
+            rotateLeft(p);
+        } else {
+            GrAssert(NULL != sl && kRed_Color == sl->fColor);
+            sl->fColor = kBlack_Color;
+            rotateRight(p);
+        }
+    }
+    else {
+        // x has exactly one implicit black child. x cannot be red.
+        // Proof by contradiction: Assume X is red. Let c0 be x's implicit
+        // child and c1 be its non-implicit child. c1 must be black because
+        // red nodes always have two black children. Then the two subtrees
+        // of x rooted at c0 and c1 will have different black-heights.
+        GrAssert(kBlack_Color == x->fColor);
+        // So we know x is black and has one implicit black child, c0. c1
+        // must be red, otherwise the subtree at c1 will have a different
+        // black-height than the subtree rooted at c0.
+        GrAssert(kRed_Color == x->fChildren[c]->fColor);
+        // replace x with c1, making c1 black, preserves all red-black tree
+        // props.
+        Node* c1 = x->fChildren[c];
+        if (x == fFirst) {
+            GrAssert(c == kRight_Child);
+            fFirst = c1;
+            while (NULL != fFirst->fChildren[kLeft_Child]) {
+                fFirst = fFirst->fChildren[kLeft_Child];
+            }
+            GrAssert(fFirst == SuccessorNode(x));
+        } else if (x == fLast) {
+            GrAssert(c == kLeft_Child);
+            fLast = c1;
+            while (NULL != fLast->fChildren[kRight_Child]) {
+                fLast = fLast->fChildren[kRight_Child];
+            }
+            GrAssert(fLast == PredecessorNode(x));
+        }
+        c1->fParent = p;
+        p->fChildren[pc] = c1;
+        c1->fColor = kBlack_Color;
+        delete x;
+        validate();
+    }
+    validate();
+}
+
+template <typename T, typename C>
+void GrRedBlackTree<T,C>::RecursiveDelete(Node* x) {
+    if (NULL != x) {
+        RecursiveDelete(x->fChildren[kLeft_Child]);
+        RecursiveDelete(x->fChildren[kRight_Child]);
+        delete x;
+    }
+}
+
+#if GR_DEBUG
+template <typename T, typename C>
+void GrRedBlackTree<T,C>::validate() const {
+    if (fCount) {
+        GrAssert(NULL == fRoot->fParent);
+        GrAssert(NULL != fFirst);
+        GrAssert(NULL != fLast);
+
+        GrAssert(kBlack_Color == fRoot->fColor);
+        if (1 == fCount) {
+            GrAssert(fFirst == fRoot);
+            GrAssert(fLast == fRoot);
+            GrAssert(0 == fRoot->fChildren[kLeft_Child]);
+            GrAssert(0 == fRoot->fChildren[kRight_Child]);
+        }
+    } else {
+        GrAssert(NULL == fRoot);
+        GrAssert(NULL == fFirst);
+        GrAssert(NULL == fLast);
+    }
+#if DEEP_VALIDATE
+    int bh;
+    int count = checkNode(fRoot, &bh);
+    GrAssert(count == fCount);
+#endif
+}
+
+template <typename T, typename C>
+int GrRedBlackTree<T,C>::checkNode(Node* n, int* bh) const {
+    if (NULL != n) {
+        GrAssert(validateChildRelations(n, false));
+        if (kBlack_Color == n->fColor) {
+            *bh += 1;
+        }
+        GrAssert(!fComp(n->fItem, fFirst->fItem));
+        GrAssert(!fComp(fLast->fItem, n->fItem));
+        int leftBh = *bh;
+        int rightBh = *bh;
+        int cl = checkNode(n->fChildren[kLeft_Child], &leftBh);
+        int cr = checkNode(n->fChildren[kRight_Child], &rightBh);
+        GrAssert(leftBh == rightBh);
+        *bh = leftBh;
+        return 1 + cl + cr;
+    }
+    return 0;
+}
+
+template <typename T, typename C>
+bool GrRedBlackTree<T,C>::validateChildRelations(const Node* n,
+                                                 bool allowRedRed) const {
+    if (NULL != n) {
+        if (NULL != n->fChildren[kLeft_Child] ||
+            NULL != n->fChildren[kRight_Child]) {
+            if (n->fChildren[kLeft_Child] == n->fChildren[kRight_Child]) {
+                return validateChildRelationsFailed();
+            }
+            if (n->fChildren[kLeft_Child] == n->fParent &&
+                NULL != n->fParent) {
+                return validateChildRelationsFailed();
+            }
+            if (n->fChildren[kRight_Child] == n->fParent &&
+                NULL != n->fParent) {
+                return validateChildRelationsFailed();
+            }
+            if (NULL != n->fChildren[kLeft_Child]) {
+                if (!allowRedRed &&
+                    kRed_Color == n->fChildren[kLeft_Child]->fColor &&
+                    kRed_Color == n->fColor) {
+                    return validateChildRelationsFailed();
+                }
+                if (n->fChildren[kLeft_Child]->fParent != n) {
+                    return validateChildRelationsFailed();
+                }
+                if (!(fComp(n->fChildren[kLeft_Child]->fItem, n->fItem) ||
+                      (!fComp(n->fChildren[kLeft_Child]->fItem, n->fItem) &&
+                       !fComp(n->fItem, n->fChildren[kLeft_Child]->fItem)))) {
+                    return validateChildRelationsFailed();
+                }
+            }
+            if (NULL != n->fChildren[kRight_Child]) {
+                if (!allowRedRed &&
+                    kRed_Color == n->fChildren[kRight_Child]->fColor &&
+                    kRed_Color == n->fColor) {
+                    return validateChildRelationsFailed();
+                }
+                if (n->fChildren[kRight_Child]->fParent != n) {
+                    return validateChildRelationsFailed();
+                }
+                if (!(fComp(n->fItem, n->fChildren[kRight_Child]->fItem) ||
+                      (!fComp(n->fChildren[kRight_Child]->fItem, n->fItem) &&
+                       !fComp(n->fItem, n->fChildren[kRight_Child]->fItem)))) {
+                    return validateChildRelationsFailed();
+                }
+            }
+        }
+    }
+    return true;
+}
+#endif
+
+#include "GrRandom.h"
+
+template <typename T, typename C>
+void GrRedBlackTree<T,C>::UnitTest() {
+    GrRedBlackTree<int> tree;
+    typedef GrRedBlackTree<int>::Iter iter;
+
+    GrRandom 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);
+        GrAssert(*xi == x);
+        ++count[x];
+    }
+
+    tree.insert(0);
+    ++count[0];
+    tree.insert(99);
+    ++count[99];
+    GrAssert(*tree.begin() == 0);
+    GrAssert(*tree.last() == 99);
+    GrAssert(--(++tree.begin()) == tree.begin());
+    GrAssert(--tree.end() == tree.last());
+    GrAssert(tree.count() == 10002);
+
+    int c = 0;
+    // check that we iterate through the correct number of
+    // elements and they are properly sorted.
+    for (Iter a = tree.begin(); tree.end() != a; ++a) {
+        Iter b = a;
+        ++b;
+        ++c;
+        GrAssert(b == tree.end() || *a <= *b);
+    }
+    GrAssert(c == tree.count());
+
+    // check that the tree reports the correct number of each int
+    // and that we can iterate through them correctly both forward
+    // and backward.
+    for (int i = 0; i < 100; ++i) {
+        int c;
+        c = tree.countOf(i);
+        GrAssert(c == count[i]);
+        c = 0;
+        Iter iter = tree.findFirst(i);
+        while (iter != tree.end() && *iter == i) {
+            ++c;
+            ++iter;
+        }
+        GrAssert(count[i] == c);
+        c = 0;
+        iter = tree.findLast(i);
+        if (iter != tree.end()) {
+            do {
+                if (*iter == i) {
+                    ++c;
+                } else {
+                    break;
+                }
+                if (iter != tree.begin()) {
+                    --iter;
+                } else {
+                    break;
+                }
+            } while (true);
+        }
+        GrAssert(c == count[i]);
+    }
+    // remove all the ints between 25 and 74. Randomly chose to remove
+    // the first, last, or any entry for each.
+    for (int i = 25; i < 75; ++i) {
+        while (0 != tree.countOf(i)) {
+            --count[i];
+            int x = r.nextU() % 3;
+            Iter iter;
+            switch (x) {
+            case 0:
+                iter = tree.findFirst(i);
+                break;
+            case 1:
+                iter = tree.findLast(i);
+                break;
+            case 2:
+            default:
+                iter = tree.find(i);
+                break;
+            }
+            tree.remove(iter);
+        }
+        GrAssert(0 == count[i]);
+        GrAssert(tree.findFirst(i) == tree.end());
+        GrAssert(tree.findLast(i) == tree.end());
+        GrAssert(tree.find(i) == tree.end());
+    }
+    // remove all of the 0 entries. (tests removing begin())
+    GrAssert(*tree.begin() == 0);
+    GrAssert(*(--tree.end()) == 99);
+    while (0 != tree.countOf(0)) {
+        --count[0];
+        tree.remove(tree.find(0));
+    }
+    GrAssert(0 == count[0]);
+    GrAssert(tree.findFirst(0) == tree.end());
+    GrAssert(tree.findLast(0) == tree.end());
+    GrAssert(tree.find(0) == tree.end());
+    GrAssert(0 < *tree.begin());
+
+    // remove all the 99 entries (tests removing last()).
+    while (0 != tree.countOf(99)) {
+        --count[99];
+        tree.remove(tree.find(99));
+    }
+    GrAssert(0 == count[99]);
+    GrAssert(tree.findFirst(99) == tree.end());
+    GrAssert(tree.findLast(99) == tree.end());
+    GrAssert(tree.find(99) == tree.end());
+    GrAssert(99 > *(--tree.end()));
+    GrAssert(tree.last() == --tree.end());
+
+    // Make sure iteration still goes through correct number of entries
+    // and is still sorted correctly.
+    c = 0;
+    for (Iter a = tree.begin(); tree.end() != a; ++a) {
+        Iter b = a;
+        ++b;
+        ++c;
+        GrAssert(b == tree.end() || *a <= *b);
+    }
+    GrAssert(c == tree.count());
+
+    // repeat check that correct number of each entry is in the tree
+    // and iterates correctly both forward and backward.
+    for (int i = 0; i < 100; ++i) {
+        GrAssert(tree.countOf(i) == count[i]);
+        int c = 0;
+        Iter iter = tree.findFirst(i);
+        while (iter != tree.end() && *iter == i) {
+            ++c;
+            ++iter;
+        }
+        GrAssert(count[i] == c);
+        c = 0;
+        iter = tree.findLast(i);
+        if (iter != tree.end()) {
+            do {
+                if (*iter == i) {
+                    ++c;
+                } else {
+                    break;
+                }
+                if (iter != tree.begin()) {
+                    --iter;
+                } else {
+                    break;
+                }
+            } while (true);
+        }
+        GrAssert(count[i] == c);
+    }
+
+    // remove all entries
+    while (!tree.empty()) {
+        tree.remove(tree.begin());
+    }
+
+    // test reset on empty tree.
+    tree.reset();
+}
+
+#endif
diff --git a/src/gpu/GrRenderTarget.cpp b/src/gpu/GrRenderTarget.cpp
new file mode 100644
index 0000000..0d36fd0
--- /dev/null
+++ b/src/gpu/GrRenderTarget.cpp
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrRenderTarget.h"
+
+#include "GrContext.h"
+#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,
+                                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,
+                                           pixelOpsFlags);
+}
+
+void GrRenderTarget::writePixels(int left, int top, int width, int height,
+                                 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,
+                                     pixelOpsFlags);
+}
+
+void GrRenderTarget::resolve() {
+    // go through context so that all necessary flushing occurs
+    GrContext* context = this->getContext();
+    if (NULL == context) {
+        return;
+    }
+    context->resolveRenderTarget(this);
+}
+
+size_t GrRenderTarget::sizeInBytes() const {
+    int colorBits;
+    if (kUnknown_GrPixelConfig == fDesc.fConfig) {
+        colorBits = 32; // don't know, make a guess
+    } else {
+        colorBits = GrBytesPerPixel(fDesc.fConfig);
+    }
+    uint64_t size = fDesc.fWidth;
+    size *= fDesc.fHeight;
+    size *= colorBits;
+    size *= GrMax(1, fDesc.fSampleCnt);
+    return (size_t)(size / 8);
+}
+
+void GrRenderTarget::flagAsNeedingResolve(const GrIRect* rect) {
+    if (kCanResolve_ResolveType == getResolveType()) {
+        if (NULL != rect) {
+            fResolveRect.join(*rect);
+            if (!fResolveRect.intersect(0, 0, this->width(), this->height())) {
+                fResolveRect.setEmpty();
+            }
+        } else {
+            fResolveRect.setLTRB(0, 0, this->width(), this->height());
+        }
+    }
+}
+
+void GrRenderTarget::overrideResolveRect(const GrIRect rect) {
+    fResolveRect = rect;
+    if (fResolveRect.isEmpty()) {
+        fResolveRect.setLargestInverted();
+    } else {
+        if (!fResolveRect.intersect(0, 0, this->width(), this->height())) {
+            fResolveRect.setLargestInverted();
+        }
+    }
+}
+
+void GrRenderTarget::setStencilBuffer(GrStencilBuffer* stencilBuffer) {
+    if (NULL != fStencilBuffer) {
+        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
new file mode 100644
index 0000000..2ff7df6
--- /dev/null
+++ b/src/gpu/GrResource.cpp
@@ -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.
+ */
+
+
+#include "GrResource.h"
+#include "GrGpu.h"
+
+SK_DEFINE_INST_COUNT(GrResource)
+
+GrResource::GrResource(GrGpu* gpu) {
+    fGpu        = gpu;
+    fCacheEntry = NULL;
+    fGpu->insertResource(this);
+}
+
+GrResource::~GrResource() {
+    // subclass should have released this.
+    GrAssert(!this->isValid());
+}
+
+void GrResource::release() {
+    if (NULL != fGpu) {
+        this->onRelease();
+        fGpu->removeResource(this);
+        fGpu = NULL;
+    }
+}
+
+void GrResource::abandon() {
+    if (NULL != fGpu) {
+        this->onAbandon();
+        fGpu->removeResource(this);
+        fGpu = NULL;
+    }
+}
+
+const GrContext* GrResource::getContext() const {
+    if (NULL != fGpu) {
+        return fGpu->getContext();
+    } else {
+        return NULL;
+    }
+}
+
+GrContext* GrResource::getContext() {
+    if (NULL != fGpu) {
+        return fGpu->getContext();
+    } else {
+        return NULL;
+    }
+}
+
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
new file mode 100644
index 0000000..89fce55
--- /dev/null
+++ b/src/gpu/GrResourceCache.cpp
@@ -0,0 +1,435 @@
+
+/*
+ * 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 "GrResourceCache.h"
+#include "GrResource.h"
+
+GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* resource)
+        : fKey(key), fResource(resource) {
+    // 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(fResource);
+    GrAssert(fResource->getCacheEntry() == this);
+    fResource->validate();
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrResourceCache::Key {
+    typedef GrResourceEntry T;
+
+    const GrResourceKey& fKey;
+public:
+    Key(const GrResourceKey& key) : fKey(key) {}
+
+    uint32_t getHash() const { return fKey.hashIndex(); }
+
+    static bool LT(const T& entry, const Key& key) {
+        return entry.key() < key.fKey;
+    }
+    static bool EQ(const T& entry, const Key& key) {
+        return entry.key() == key.fKey;
+    }
+#if GR_DEBUG
+    static uint32_t GetHash(const T& entry) {
+        return entry.key().hashIndex();
+    }
+    static bool LT(const T& a, const T& b) {
+        return a.key() < b.key();
+    }
+    static bool EQ(const T& a, const T& b) {
+        return a.key() == b.key();
+    }
+#endif
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) :
+        fMaxCount(maxCount),
+        fMaxBytes(maxBytes) {
+#if GR_CACHE_STATS
+    fHighWaterEntryCount          = 0;
+    fHighWaterEntryBytes          = 0;
+    fHighWaterClientDetachedCount = 0;
+    fHighWaterClientDetachedBytes = 0;
+#endif
+
+    fEntryCount                   = 0;
+    fEntryBytes                   = 0;
+    fClientDetachedCount          = 0;
+    fClientDetachedBytes          = 0;
+
+    fPurging = false;
+}
+
+GrResourceCache::~GrResourceCache() {
+    GrAutoResourceCacheValidate atcv(this);
+
+    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, false);
+
+        delete entry;
+    }
+}
+
+void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) const{
+    if (maxResources) {
+        *maxResources = fMaxCount;
+    }
+    if (maxResourceBytes) {
+        *maxResourceBytes = fMaxBytes;
+    }
+}
+
+void GrResourceCache::setLimits(int maxResources, size_t maxResourceBytes) {
+    bool smaller = (maxResources < fMaxCount) || (maxResourceBytes < fMaxBytes);
+
+    fMaxCount = maxResources;
+    fMaxBytes = maxResourceBytes;
+
+    if (smaller) {
+        this->purgeAsNeeded();
+    }
+}
+
+void GrResourceCache::internalDetach(GrResourceEntry* entry,
+                                    bool clientDetach) {
+    fList.remove(entry);
+
+    // update our stats
+    if (clientDetach) {
+        fClientDetachedCount += 1;
+        fClientDetachedBytes += entry->resource()->sizeInBytes();
+
+#if GR_CACHE_STATS
+        if (fHighWaterClientDetachedCount < fClientDetachedCount) {
+            fHighWaterClientDetachedCount = fClientDetachedCount;
+        }
+        if (fHighWaterClientDetachedBytes < fClientDetachedBytes) {
+            fHighWaterClientDetachedBytes = fClientDetachedBytes;
+        }
+#endif
+
+    } else {
+        fEntryCount -= 1;
+        fEntryBytes -= entry->resource()->sizeInBytes();
+    }
+}
+
+void GrResourceCache::attachToHead(GrResourceEntry* entry,
+                                   bool clientReattach) {
+    fList.addToHead(entry);
+
+    // update our stats
+    if (clientReattach) {
+        fClientDetachedCount -= 1;
+        fClientDetachedBytes -= entry->resource()->sizeInBytes();
+    } else {
+        fEntryCount += 1;
+        fEntryBytes += entry->resource()->sizeInBytes();
+
+#if GR_CACHE_STATS
+        if (fHighWaterEntryCount < fEntryCount) {
+            fHighWaterEntryCount = fEntryCount;
+        }
+        if (fHighWaterEntryBytes < fEntryBytes) {
+            fHighWaterEntryBytes = fEntryBytes;
+        }
+#endif
+    }
+}
+
+GrResource* GrResourceCache::find(const GrResourceKey& key) {
+    GrAutoResourceCacheValidate atcv(this);
+
+    GrResourceEntry* entry = fCache.find(key);
+    if (NULL == entry) {
+        return NULL;
+    }
+
+    this->internalDetach(entry, false);
+    this->attachToHead(entry, false);
+
+    return entry->fResource;
+}
+
+bool GrResourceCache::hasKey(const GrResourceKey& key) const {
+    return NULL != fCache.find(key);
+}
+
+void GrResourceCache::create(const GrResourceKey& key, GrResource* resource) {
+    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
+    // unlocks 1 thereby causing a new purge).
+    GrAssert(!fPurging);
+    GrAutoResourceCacheValidate atcv(this);
+
+    GrResourceEntry* entry = SkNEW_ARGS(GrResourceEntry, (key, resource));
+    resource->setCacheEntry(entry);
+
+    this->attachToHead(entry, false);
+    fCache.insert(key, entry);
+
+#if GR_DUMP_TEXTURE_UPLOAD
+    GrPrintf("--- add resource to cache %p, count=%d bytes= %d %d\n",
+             entry, fEntryCount, resource->sizeInBytes(), fEntryBytes);
+#endif
+}
+
+void GrResourceCache::makeExclusive(GrResourceEntry* entry) {
+    GrAutoResourceCacheValidate atcv(this);
+
+    this->internalDetach(entry, true);
+    fCache.remove(entry->key(), entry);
+
+#if GR_DEBUG
+    fExclusiveList.addToHead(entry);
+#endif
+}
+
+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);
+        fCache.insert(entry->key(), entry);
+    } else {
+        this->removeInvalidResource(entry);
+    }
+}
+
+/**
+ * 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
+ * the budget. There is an assertion in createAndLock to check against a
+ * resource's destructor inserting new resources into the cache. If these
+ * new resources were unlocked before purgeAsNeeded completed it could
+ * potentially make purgeAsNeeded loop infinitely.
+ */
+void GrResourceCache::purgeAsNeeded() {
+    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 {
+            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 = iter.prev();
+                if (1 == entry->fResource->getRefCnt()) {
+                    changed = true;
+
+                    // remove from our cache
+                    fCache.remove(entry->key(), entry);
+
+                    // remove from our llist
+                    this->internalDetach(entry, false);
+
+        #if GR_DUMP_TEXTURE_UPLOAD
+                    GrPrintf("--- ~resource from cache %p [%d %d]\n",
+                             entry->resource(),
+                             entry->resource()->width(),
+                             entry->resource()->height());
+        #endif
+
+                    delete entry;
+                }
+                entry = prev;
+            }
+        } while (!withinBudget && changed);
+        fPurging = false;
+    }
+}
+
+void GrResourceCache::purgeAllUnlocked() {
+    GrAutoResourceCacheValidate atcv(this);
+
+    // 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 = (size_t) -1;
+    fMaxCount = 0;
+    this->purgeAsNeeded();
+
+#if GR_DEBUG
+    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(fList.isEmpty());
+    }
+#endif
+
+    fMaxBytes = savedMaxBytes;
+    fMaxCount = savedMaxCount;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_DEBUG
+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 bytes;
+}
+
+static bool both_zero_or_nonzero(int count, size_t bytes) {
+    return (count == 0 && bytes == 0) || (count > 0 && bytes > 0);
+}
+
+void GrResourceCache::validate() const {
+    fList.validate();
+    fExclusiveList.validate();
+    GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes));
+    GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes));
+    GrAssert(fClientDetachedBytes <= fEntryBytes);
+    GrAssert(fClientDetachedCount <= fEntryCount);
+    GrAssert((fEntryCount - fClientDetachedCount) == fCache.count());
+
+    fCache.validate();
+
+
+    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;
+    for ( ; NULL != entry; entry = iter.next()) {
+        entry->validate();
+        GrAssert(fCache.find(entry->key()));
+        count += 1;
+    }
+    GrAssert(count == fEntryCount - fClientDetachedCount);
+
+    size_t bytes = countBytes(fList);
+    GrAssert(bytes == fEntryBytes  - fClientDetachedBytes);
+
+    bytes = countBytes(fExclusiveList);
+    GrAssert(bytes == fClientDetachedBytes);
+
+    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
new file mode 100644
index 0000000..7897b7a
--- /dev/null
+++ b/src/gpu/GrResourceCache.h
@@ -0,0 +1,344 @@
+
+/*
+ * 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 GrResourceCache_DEFINED
+#define GrResourceCache_DEFINED
+
+#include "GrConfig.h"
+#include "GrTypes.h"
+#include "GrTHashCache.h"
+#include "SkTDLinkedList.h"
+
+class GrResource;
+
+// 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 {
+        kHashBits   = 7,
+        kHashCount  = 1 << kHashBits,
+        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();
+    }
+
+    GrResourceKey(uint32_t v[4]) {
+        memcpy(fP, v, 4 * sizeof(uint32_t));
+        this->computeHashIndex();
+    }
+
+    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;
+    }
+
+    //!< 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));
+    }
+
+    friend bool operator!=(const GrResourceKey& a, const GrResourceKey& b) {
+        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
+        return !(a == b);
+    }
+
+    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];
+    }
+
+    uint32_t getValue32(int i) const {
+        GrAssert(i >=0 && i < 4);
+        return fP[i];
+    }
+private:
+
+    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 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;
+    }
+
+    uint32_t    fP[4];
+
+    // this is computed from the fP... fields
+    int         fHashIndex;
+
+    friend class GrContext;
+};
+
+
+class GrCacheKey {
+public:
+    GrCacheKey(const GrTextureDesc& desc, const GrResourceKey& key)
+        : fDesc(desc)
+        , fKey(key) {
+    }
+
+    void set(const GrTextureDesc& desc, const GrResourceKey& key) {
+        fDesc = desc;
+        fKey = key;
+    }
+
+    const GrTextureDesc& desc() const { return fDesc; }
+    const GrResourceKey& key() const { return fKey; }
+
+protected:
+    GrTextureDesc fDesc;
+    GrResourceKey fKey;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrResourceEntry {
+public:
+    GrResource* resource() const { return fResource; }
+    const GrResourceKey& key() const { return fKey; }
+
+#if GR_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+private:
+    GrResourceEntry(const GrResourceKey& key, GrResource* resource);
+    ~GrResourceEntry();
+
+    GrResourceKey    fKey;
+    GrResource*      fResource;
+
+    // we're a dlinklist
+    SK_DEFINE_DLINKEDLIST_INTERFACE(GrResourceEntry);
+
+    friend class GrResourceCache;
+    friend class GrDLinkedList;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "GrTHashCache.h"
+
+/**
+ *  Cache of GrResource objects.
+ *
+ *  These have a corresponding GrResourceKey, built from 128bits identifying the
+ *  resource.
+ *
+ *  The cache stores the entries in a double-linked list, which is its LRU.
+ *  When an entry is "locked" (i.e. given to the caller), it is moved to the
+ *  head of the list. If/when we must purge some of the entries, we walk the
+ *  list backwards from the tail, since those are the least recently used.
+ *
+ *  For fast searches, we maintain a sorted array (based on the GrResourceKey)
+ *  which we can bsearch. When a new entry is added, it is inserted into this
+ *  array.
+ *
+ *  For even faster searches, a hash is computed from the Key. If there is
+ *  a collision between two keys with the same hash, we fall back on the
+ *  bsearch, and update the hash to reflect the most recent Key requested.
+ */
+class GrResourceCache {
+public:
+    GrResourceCache(int maxCount, size_t maxBytes);
+    ~GrResourceCache();
+
+    /**
+     *  Return the current resource cache limits.
+     *
+     *  @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.
+     */
+    void getLimits(int* maxResources, size_t* maxBytes) const;
+
+    /**
+     *  Specify the resource cache limits. If the current cache exceeds either
+     *  of these, it will be purged (LRU) to keep the cache within these limits.
+     *
+     *  @param maxResources The maximum number of resources that can be held in
+     *                      the cache.
+     *  @param maxBytes     The maximum number of bytes of resource memory that
+     *                      can be held in the cache.
+     */
+    void setLimits(int maxResource, size_t maxResourceBytes);
+
+    /**
+     * Returns the number of bytes consumed by cached resources.
+     */
+    size_t getCachedResourceBytes() const { return fEntryBytes; }
+
+    /**
+     *  Search for an entry with the same Key. If found, return it.
+     *  If not found, return null.
+     */
+    GrResource* find(const GrResourceKey& key);
+
+    /**
+     *  Create a new cache entry, based on the provided key and resource, and
+     *  return it.
+     *
+     *  Ownership of the resource is transferred to the resource cache,
+     *  which will unref() it when it is purged or deleted.
+     */
+    void create(const GrResourceKey&, GrResource*);
+
+    /**
+     * Determines if the cache contains an entry matching a key. If a matching
+     * entry exists but was detached then it will not be found.
+     */
+    bool hasKey(const GrResourceKey& key) const;
+
+    /**
+     * 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 makeExclusive(GrResourceEntry* entry);
+
+    /**
+     * Restore 'entry' so that it can be found by future searches. 'entry'
+     * will also be purgeable (provided its lock count is now 0.)
+     */
+    void makeNonExclusive(GrResourceEntry* entry);
+
+    /**
+     * Removes every resource in the cache that isn't locked.
+     */
+    void purgeAllUnlocked();
+
+    /**
+     * 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;
+#else
+    void validate() const {}
+#endif
+
+#if GR_CACHE_STATS
+    void printStats();
+#endif
+
+private:
+    void internalDetach(GrResourceEntry*, bool);
+    void attachToHead(GrResourceEntry*, bool);
+
+    void removeInvalidResource(GrResourceEntry* entry);
+
+    class Key;
+    GrTHashTable<GrResourceEntry, Key, 8> fCache;
+
+    // manage the dlink list
+    typedef SkTDLinkedList<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;
+    size_t fEntryBytes;
+    int fClientDetachedCount;
+    size_t fClientDetachedBytes;
+
+    // prevents recursive purging
+    bool fPurging;
+
+#if GR_DEBUG
+    static size_t countBytes(const SkTDLinkedList<GrResourceEntry>& list);
+#endif
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_DEBUG
+    class GrAutoResourceCacheValidate {
+    public:
+        GrAutoResourceCacheValidate(GrResourceCache* cache) : fCache(cache) {
+            cache->validate();
+        }
+        ~GrAutoResourceCacheValidate() {
+            fCache->validate();
+        }
+    private:
+        GrResourceCache* fCache;
+    };
+#else
+    class GrAutoResourceCacheValidate {
+    public:
+        GrAutoResourceCacheValidate(GrResourceCache*) {}
+    };
+#endif
+
+#endif
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
new file mode 100644
index 0000000..ec0dbf1
--- /dev/null
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -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.
+ */
+
+#include "GrSWMaskHelper.h"
+#include "GrDrawState.h"
+#include "GrGpu.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::kMultiply_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];
+}
+
+////////////////////////////////////////////////////////////////////////////////
+SkPath::FillType gr_fill_to_sk_fill(GrPathFill fill) {
+    switch (fill) {
+        case kWinding_GrPathFill:
+            return SkPath::kWinding_FillType;
+        case kEvenOdd_GrPathFill:
+            return SkPath::kEvenOdd_FillType;
+        case kInverseWinding_GrPathFill:
+            return SkPath::kInverseWinding_FillType;
+        case kInverseEvenOdd_GrPathFill:
+            return SkPath::kInverseEvenOdd_FillType;
+        default:
+            GrCrash("Unexpected fill.");
+            return SkPath::kWinding_FillType;
+    }
+}
+
+}
+
+/**
+ * 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, SkRegion::Op op,
+                          GrPathFill fill, bool antiAlias, uint8_t alpha) {
+
+    SkPaint paint;
+    SkPath tmpPath;
+    const SkPath* pathToDraw = &path;
+    if (kHairLine_GrPathFill == 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;
+        }
+    }
+    SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
+
+    paint.setXfermode(mode);
+    paint.setAntiAlias(antiAlias);
+    paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
+
+    fDraw.drawPath(*pathToDraw, paint);
+
+    SkSafeUnref(mode);
+}
+
+bool GrSWMaskHelper::init(const GrIRect& resultBounds,
+                          const GrMatrix* 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, SkColorSetARGB(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 GrIRect& resultBounds,
+                                                 GrPathFill fill,
+                                                 bool antiAlias,
+                                                 GrMatrix* matrix) {
+    GrAutoScratchTexture ast;
+
+    GrSWMaskHelper helper(context);
+
+    if (!helper.init(resultBounds, matrix)) {
+        return NULL;
+    }
+
+    helper.draw(path, SkRegion::kReplace_Op, fill, 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->sampler(kPathMaskStage)->reset();
+    drawState->createTextureEffect(kPathMaskStage, texture);
+    GrScalar w = GrIntToScalar(rect.width());
+    GrScalar h = GrIntToScalar(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..c780769
--- /dev/null
+++ b/src/gpu/GrSWMaskHelper.h
@@ -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.
+ */
+
+#ifndef GrSWMaskHelper_DEFINED
+#define GrSWMaskHelper_DEFINED
+
+#include "GrColor.h"
+#include "GrMatrix.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 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 GrMatrix* 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, SkRegion::Op op,
+              GrPathFill fill, 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 GrIRect& resultBounds,
+                                            GrPathFill fill,
+                                            bool antiAlias,
+                                            GrMatrix* 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;
+    GrMatrix        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..3dd9606
--- /dev/null
+++ b/src/gpu/GrSoftwarePathRenderer.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 "GrSoftwarePathRenderer.h"
+#include "GrContext.h"
+#include "GrSWMaskHelper.h"
+
+////////////////////////////////////////////////////////////////////////////////
+bool GrSoftwarePathRenderer::canDrawPath(const SkPath& path,
+                                         GrPathFill fill,
+                                         const GrDrawTarget* target,
+                                         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;
+}
+
+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 GrMatrix& 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,
+                                        GrPathFill fill,
+                                        GrDrawTarget* target,
+                                        bool antiAlias) {
+
+    if (NULL == fContext) {
+        return false;
+    }
+
+    GrDrawState* drawState = target->drawState();
+
+    GrMatrix vm = drawState->getViewMatrix();
+
+    GrIRect devPathBounds, devClipBounds;
+    if (!get_path_and_clip_bounds(target, path, vm,
+                                  &devPathBounds, &devClipBounds)) {
+        if (GrIsFillInverted(fill)) {
+            draw_around_inv_path(target, devClipBounds, devPathBounds);
+        }
+        return true;
+    }
+
+    SkAutoTUnref<GrTexture> texture(
+            GrSWMaskHelper::DrawPathMaskToTexture(fContext, path,
+                                                  devPathBounds, fill,
+                                                  antiAlias, &vm));
+    if (NULL == texture) {
+        return false;
+    }
+
+    GrSWMaskHelper::DrawToTargetWithPathMask(texture, target, devPathBounds);
+
+    if (GrIsFillInverted(fill)) {
+        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..fdcc7bd
--- /dev/null
+++ b/src/gpu/GrSoftwarePathRenderer.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 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& path,
+                            GrPathFill fill,
+                            const GrDrawTarget* target,
+                            bool antiAlias) const SK_OVERRIDE;
+protected:
+    virtual bool onDrawPath(const SkPath& path,
+                            GrPathFill fill,
+                            GrDrawTarget* target,
+                            bool antiAlias) SK_OVERRIDE;
+
+private:
+    GrContext*     fContext;
+
+    typedef GrPathRenderer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrStencil.cpp b/src/gpu/GrStencil.cpp
new file mode 100644
index 0000000..7677260
--- /dev/null
+++ b/src/gpu/GrStencil.cpp
@@ -0,0 +1,395 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrStencil.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Stencil Rules for Merging user stencil space into clip
+
+// We can't include the clip bit in the ref or mask values because the division
+// between user and clip bits in the stencil depends on the number of stencil
+// bits in the runtime. Comments below indicate what the code should do to
+// incorporate the clip bit into these settings.
+
+///////
+// Replace
+
+// set the ref to be the clip bit, but mask it out for the test
+GR_STATIC_CONST_SAME_STENCIL(gUserToClipReplace,
+    kReplace_StencilOp,
+    kZero_StencilOp,
+    kLess_StencilFunc,
+    0xffff,           // unset clip bit
+    0x0000,           // set clip bit
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipReplace,
+    kReplace_StencilOp,
+    kZero_StencilOp,
+    kEqual_StencilFunc,
+    0xffff,           // unset clip bit
+    0x0000,           // set clip bit
+    0xffff);
+
+///////
+// Intersect
+GR_STATIC_CONST_SAME_STENCIL(gUserToClipIsect,
+    kReplace_StencilOp,
+    kZero_StencilOp,
+    kLess_StencilFunc,
+    0xffff,
+    0x0000,           // set clip bit
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipIsect,
+    kReplace_StencilOp,
+    kZero_StencilOp,
+    kEqual_StencilFunc,
+    0xffff,
+    0x0000,           // set clip bit
+    0xffff);
+
+///////
+// Difference
+GR_STATIC_CONST_SAME_STENCIL(gUserToClipDiff,
+    kReplace_StencilOp,
+    kZero_StencilOp,
+    kEqual_StencilFunc,
+    0xffff,
+    0x0000,           // set clip bit
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipDiff,
+    kReplace_StencilOp,
+    kZero_StencilOp,
+    kLess_StencilFunc,
+    0xffff,
+    0x0000,           // set clip bit
+    0xffff);
+
+///////
+// Union
+
+// first pass makes all the passing cases >= just clip bit set.
+GR_STATIC_CONST_SAME_STENCIL(gUserToClipUnionPass0,
+    kReplace_StencilOp,
+    kKeep_StencilOp,
+    kLEqual_StencilFunc,
+    0xffff,
+    0x0001,           // set clip bit
+    0xffff);
+
+// second pass allows anything greater than just clip bit set to pass
+GR_STATIC_CONST_SAME_STENCIL(gUserToClipUnionPass1,
+    kReplace_StencilOp,
+    kZero_StencilOp,
+    kLEqual_StencilFunc,
+    0xffff,
+    0x0000,           // set clip bit
+    0xffff);
+
+// first pass finds zeros in the user bits and if found sets
+// the clip bit to 1
+GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipUnionPass0,
+    kReplace_StencilOp,
+    kKeep_StencilOp,
+    kEqual_StencilFunc,
+    0xffff,
+    0x0000,           // set clip bit
+    0x0000            // set clip bit
+);
+
+// second pass zeros the user bits
+GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipUnionPass1,
+    kZero_StencilOp,
+    kZero_StencilOp,
+    kLess_StencilFunc,
+    0xffff,
+    0x0000,
+    0xffff            // unset clip bit
+);
+
+///////
+// Xor
+GR_STATIC_CONST_SAME_STENCIL(gUserToClipXorPass0,
+    kInvert_StencilOp,
+    kKeep_StencilOp,
+    kEqual_StencilFunc,
+    0xffff,           // unset clip bit
+    0x0000,
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gUserToClipXorPass1,
+    kReplace_StencilOp,
+    kZero_StencilOp,
+    kGreater_StencilFunc,
+    0xffff,
+    0x0000,          // set clip bit
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipXorPass0,
+    kInvert_StencilOp,
+    kKeep_StencilOp,
+    kEqual_StencilFunc,
+    0xffff,           // unset clip bit
+    0x0000,
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipXorPass1,
+    kReplace_StencilOp,
+    kZero_StencilOp,
+    kLess_StencilFunc,
+    0xffff,
+    0x0000,          // set clip bit
+    0xffff);
+
+///////
+// Reverse Diff
+GR_STATIC_CONST_SAME_STENCIL(gUserToClipRDiffPass0,
+    kInvert_StencilOp,
+    kZero_StencilOp,
+    kLess_StencilFunc,
+    0xffff,         // unset clip bit
+    0x0000,         // set clip bit
+    0xffff);
+
+GR_STATIC_CONST_SAME_STENCIL(gUserToClipRDiffPass1,
+    kReplace_StencilOp,
+    kZero_StencilOp,
+    kEqual_StencilFunc,
+    0x0000,          // set clip bit
+    0x0000,          // set clip bit
+    0xffff);
+
+// 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,
+    0xffff,
+    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
+
+// We can render a clip element directly without first writing to the client
+// portion of the clip when the fill is not inverse and the set operation will
+// 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 clip mask creation code doesn't allow midstream replace ops.
+GR_STATIC_CONST_SAME_STENCIL(gReplaceClip,
+    kReplace_StencilOp,
+    kReplace_StencilOp,
+    kAlways_StencilFunc,
+    0xffff,
+    0x0000,           // set clip bit
+    0x0000            // set clipBit
+);
+
+GR_STATIC_CONST_SAME_STENCIL(gUnionClip,
+    kReplace_StencilOp,
+    kReplace_StencilOp,
+    kAlways_StencilFunc,
+    0xffff,
+    0x0000,           // set clip bit
+    0x0000            // set clip bit
+);
+
+GR_STATIC_CONST_SAME_STENCIL(gXorClip,
+    kInvert_StencilOp,
+    kInvert_StencilOp,
+    kAlways_StencilFunc,
+    0xffff,
+    0x0000,
+    0x0000            // set clip bit
+);
+
+GR_STATIC_CONST_SAME_STENCIL(gDiffClip,
+    kZero_StencilOp,
+    kZero_StencilOp,
+    kAlways_StencilFunc,
+    0xffff,
+    0x0000,
+    0x0000            // set clip bit
+);
+
+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 SkRegion::kReplace_Op:
+                *numPasses = 1;
+                settings[0] = gReplaceClip;
+                break;
+            case SkRegion::kUnion_Op:
+                *numPasses = 1;
+                settings[0] = gUnionClip;
+                break;
+            case SkRegion::kXOR_Op:
+                *numPasses = 1;
+                settings[0] = gXorClip;
+                break;
+            case SkRegion::kDifference_Op:
+                *numPasses = 1;
+                settings[0] = gDiffClip;
+                break;
+            default: // suppress warning
+                break;
+        }
+        if (1 == *numPasses) {
+            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;
+        }
+    }
+    switch (op) {
+        // 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 SkRegion::kReplace_Op:
+            *numPasses= 1;
+            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 SkRegion::kIntersect_Op:
+            *numPasses = 1;
+            settings[0] = invertedFill ? gInvUserToClipIsect : gUserToClipIsect;
+            settings[0].fFuncRefs[kFront_Face] = stencilClipMask;
+            settings[0].fFuncRefs[kBack_Face] =
+                settings[0].fFuncRefs[kFront_Face];
+            break;
+        case SkRegion::kUnion_Op:
+            *numPasses = 2;
+            if (invertedFill) {
+                settings[0] = gInvUserToClipUnionPass0;
+                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].fWriteMasks[kFront_Face] &= ~stencilClipMask;
+                settings[1].fWriteMasks[kBack_Face] &=
+                    settings[1].fWriteMasks[kFront_Face];
+
+            } else {
+                settings[0] = gUserToClipUnionPass0;
+                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].fFuncRefs[kFront_Face] |= stencilClipMask;
+                settings[1].fFuncRefs[kBack_Face] =
+                    settings[1].fFuncRefs[kFront_Face];
+            }
+            break;
+        case SkRegion::kXOR_Op:
+            *numPasses = 2;
+            if (invertedFill) {
+                settings[0] = gInvUserToClipXorPass0;
+                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
+                settings[0].fFuncMasks[kBack_Face] =
+                    settings[0].fFuncMasks[kFront_Face];
+
+                settings[1] = gInvUserToClipXorPass1;
+                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
+                settings[1].fFuncRefs[kBack_Face] =
+                    settings[1].fFuncRefs[kFront_Face];
+            } else {
+                settings[0] = gUserToClipXorPass0;
+                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
+                settings[0].fFuncMasks[kBack_Face] =
+                    settings[0].fFuncMasks[kFront_Face];
+
+                settings[1] = gUserToClipXorPass1;
+                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
+                settings[1].fFuncRefs[kBack_Face] =
+                    settings[1].fFuncRefs[kFront_Face];
+            }
+            break;
+        case SkRegion::kDifference_Op:
+            *numPasses = 1;
+            settings[0] = invertedFill ? gInvUserToClipDiff : gUserToClipDiff;
+            settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
+            settings[0].fFuncRefs[kBack_Face] =
+                settings[0].fFuncRefs[kFront_Face];
+            break;
+        case SkRegion::kReverseDifference_Op:
+            if (invertedFill) {
+                *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].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].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:
+            GrCrash("Unknown set op");
+    }
+    return false;
+}
diff --git a/src/gpu/GrStencil.h b/src/gpu/GrStencil.h
new file mode 100644
index 0000000..1af98e6
--- /dev/null
+++ b/src/gpu/GrStencil.h
@@ -0,0 +1,395 @@
+
+/*
+ * 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 GrStencil_DEFINED
+#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
+ * bits available for other uses by external code (clients). Client code can
+ * modify these bits. GrDrawTarget will ignore ref, mask, and writemask bits
+ * provided by clients that overlap the bits used to implement clipping.
+ *
+ * When code outside the GrDrawTarget class uses the stencil buffer the contract
+ * is as follows:
+ *
+ * > Normal stencil funcs allow the client to pass / fail regardless of the
+ *   reserved clip bits.
+ * > Additional functions allow a test against the clip along with a limited
+ *   set of tests against the client bits.
+ * > Client can assume all client bits are zero initially.
+ * > Client must ensure that after all its passes are finished it has only
+ *   written to the color buffer in the region inside the clip. Furthermore, it
+ *   must zero all client bits that were modifed (both inside and outside the
+ *   clip).
+ */
+
+/**
+ * Determines which pixels pass / fail the stencil test.
+ * Stencil test passes if (ref & mask) FUNC (stencil & mask) is true
+ */
+enum GrStencilFunc {
+    kAlways_StencilFunc = 0,
+    kNever_StencilFunc,
+    kGreater_StencilFunc,
+    kGEqual_StencilFunc,
+    kLess_StencilFunc,
+    kLEqual_StencilFunc,
+    kEqual_StencilFunc,
+    kNotEqual_StencilFunc,
+
+    // Gr stores the current clip in the
+    // stencil buffer in the high bits that
+    // are not directly accessible modifiable
+    // via the GrDrawTarget interface. The below
+    // stencil funcs test against the current
+    // clip in addition to the GrDrawTarget
+    // client's stencil bits.
+
+    // pass if inside the clip
+    kAlwaysIfInClip_StencilFunc,
+    kEqualIfInClip_StencilFunc,
+    kLessIfInClip_StencilFunc,
+    kLEqualIfInClip_StencilFunc,
+    kNonZeroIfInClip_StencilFunc, // this one forces the ref to be 0
+
+    // counts
+    kStencilFuncCount,
+    kClipStencilFuncCount = kNonZeroIfInClip_StencilFunc -
+                            kAlwaysIfInClip_StencilFunc + 1,
+    kBasicStencilFuncCount = kStencilFuncCount - kClipStencilFuncCount
+};
+
+/**
+ * Operations to perform based on whether stencil test passed failed.
+ */
+enum GrStencilOp {
+    kKeep_StencilOp = 0,    // preserve existing stencil value
+    kReplace_StencilOp,     // replace with reference value from stencl test
+    kIncWrap_StencilOp,     // increment and wrap at max
+    kIncClamp_StencilOp,    // increment and clamp at max
+    kDecWrap_StencilOp,     // decrement and wrap at 0
+    kDecClamp_StencilOp,    // decrement and clamp at 0
+    kZero_StencilOp,        // zero stencil bits
+    kInvert_StencilOp,      // invert stencil bits
+
+    kStencilOpCount
+};
+
+enum GrStencilFlags {
+    kIsDisabled_StencilFlag      = 0x1,
+    kNotDisabled_StencilFlag     = 0x2,
+    kDoesWrite_StencilFlag       = 0x4,
+    kDoesNotWrite_StencilFlag    = 0x8,
+};
+
+/**
+ * GrStencilState needs to be a class with accessors and setters so that it
+ * can maintain flags related to its current state. However, we also want to
+ * be able to declare pre-made stencil settings at compile time (without
+ * inserting static initializer code). So all the data members are in this
+ * struct. A macro defined after the class can be used to jam an instance of
+ * this struct that is created from an initializer list into a
+ * GrStencilSettings. (We hang our heads in shame.)
+ */
+struct GrStencilSettingsStruct {
+    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)
+GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) % 4 == 0);
+GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) ==
+                 4*sizeof(uint8_t) + // ops
+                 2*sizeof(uint8_t) + // funcs
+                 2*sizeof(uint8_t) + // pads
+                 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
+// associated to disabling. It is used both to define constant structure
+// initializers and inside GrStencilSettings::isDisabled()
+//
+#define GR_STENCIL_SETTINGS_IS_DISABLED(                                     \
+    FRONT_PASS_OP,    BACK_PASS_OP,                                          \
+    FRONT_FAIL_OP,    BACK_FAIL_OP,                                          \
+    FRONT_FUNC,       BACK_FUNC)                                             \
+    ((FRONT_PASS_OP) == kKeep_StencilOp &&                                   \
+     (BACK_PASS_OP)  == kKeep_StencilOp &&                                   \
+     (FRONT_FAIL_OP) == kKeep_StencilOp &&                                   \
+     (BACK_FAIL_OP)  == kKeep_StencilOp &&                                   \
+     (FRONT_FUNC)    == kAlways_StencilFunc &&                               \
+     (BACK_FUNC)     == kAlways_StencilFunc)
+
+#define GR_STENCIL_SETTINGS_DOES_WRITE(                                      \
+    FRONT_PASS_OP,    BACK_PASS_OP,                                          \
+    FRONT_FAIL_OP,    BACK_FAIL_OP,                                          \
+    FRONT_FUNC,       BACK_FUNC)                                             \
+    (!(((FRONT_FUNC) == kNever_StencilFunc  ||                               \
+        (FRONT_PASS_OP) == kKeep_StencilOp)  &&                              \
+       ((BACK_FUNC) == kNever_StencilFunc  ||                                \
+        (BACK_PASS_OP)  == kKeep_StencilOp) &&                               \
+       ((FRONT_FUNC) == kAlways_StencilFunc ||                               \
+        (FRONT_FAIL_OP) == kKeep_StencilOp) &&                               \
+       ((BACK_FUNC)  == kAlways_StencilFunc ||                               \
+        (BACK_FAIL_OP)  == kKeep_StencilOp)))
+
+#define GR_STENCIL_SETTINGS_DEFAULT_FLAGS(                                   \
+    FRONT_PASS_OP,    BACK_PASS_OP,                                          \
+    FRONT_FAIL_OP,    BACK_FAIL_OP,                                          \
+    FRONT_FUNC,       BACK_FUNC)                                             \
+  ((GR_STENCIL_SETTINGS_IS_DISABLED(FRONT_PASS_OP,BACK_PASS_OP,              \
+      FRONT_FAIL_OP,BACK_FAIL_OP,FRONT_FUNC,BACK_FUNC) ?                     \
+      kIsDisabled_StencilFlag : kNotDisabled_StencilFlag) |                  \
+   (GR_STENCIL_SETTINGS_DOES_WRITE(FRONT_PASS_OP,BACK_PASS_OP,               \
+      FRONT_FAIL_OP,BACK_FAIL_OP,FRONT_FUNC,BACK_FUNC) ?                     \
+      kDoesWrite_StencilFlag : kDoesNotWrite_StencilFlag))
+
+/**
+ * Class representing stencil state.
+ */
+class GrStencilSettings : private GrStencilSettingsStruct {
+
+public:
+    enum Face {
+        kFront_Face = 0,
+        kBack_Face  = 1,
+    };
+
+    GrStencilSettings() {
+        fPad0 = fPad1 = 0;
+        this->setDisabled();
+    }
+
+    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,
+                 GrStencilFunc func,
+                 unsigned short funcMask,
+                 unsigned short funcRef,
+                 unsigned short 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;
+    }
+
+    void setDisabled() {
+        memset(this, 0, sizeof(*this));
+        GR_STATIC_ASSERT(0 == kKeep_StencilOp);
+        GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
+        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;
+        }
+        if (fFlags & kNotDisabled_StencilFlag) {
+            return false;
+        }
+        bool disabled = GR_STENCIL_SETTINGS_IS_DISABLED(
+                            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;
+    }
+
+    bool doesWrite() const {
+        if (fFlags & kDoesWrite_StencilFlag) {
+            return true;
+        }
+        if (fFlags & kDoesNotWrite_StencilFlag) {
+            return false;
+        }
+        bool writes = GR_STENCIL_SETTINGS_DOES_WRITE(
+                            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
+        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) ==
+                 (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 GrClipMaskManager;
+
+    enum {
+        kMaxStencilClipPasses = 2  // maximum number of passes to add a clip
+                                   // element to the stencil buffer.
+    };
+
+    /**
+     * Given a thing to draw into the stencil clip, a fill type, and a set op
+     * this function determines:
+     *      1. Whether the thing can be draw directly to the stencil clip or
+     *      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
+     *         always be non-inverted).
+     *
+     * @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
+     *                               element to the clip.
+     * @param settings          out: the stencil settings to use for each pass
+     *
+     * @return true if the clip element's geometry can be drawn directly to the
+     *         stencil clip bit. Will only be true if canBeDirect is true.
+     *         numPasses will be 1 if return value is true.
+     */
+    static bool GetClipPasses(SkRegion::Op op,
+                              bool canBeDirect,
+                              unsigned int stencilClipMask,
+                              bool invertedFill,
+                              int* numPasses,
+                              GrStencilSettings settings[kMaxStencilClipPasses]);
+};
+
+GR_STATIC_ASSERT(sizeof(GrStencilSettingsStruct) == sizeof(GrStencilSettings));
+
+#define GR_STATIC_CONST_STENCIL_STRUCT(STRUCT_NAME,                          \
+    FRONT_PASS_OP,    BACK_PASS_OP,                                          \
+    FRONT_FAIL_OP,    BACK_FAIL_OP,                                          \
+    FRONT_FUNC,       BACK_FUNC,                                             \
+    FRONT_MASK,       BACK_MASK,                                             \
+    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)      },                              \
+        (0),                (0),                                             \
+       {(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)                                           \
+    };
+
+#define GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(STRUCT_PTR)            \
+    reinterpret_cast<const GrStencilSettings*>(STRUCT_PTR)
+
+#define GR_STATIC_CONST_SAME_STENCIL_STRUCT(STRUCT_NAME,                     \
+    PASS_OP, FAIL_OP, FUNC, MASK, REF, WRITE_MASK)                           \
+    GR_STATIC_CONST_STENCIL_STRUCT(STRUCT_NAME, (PASS_OP), (PASS_OP),        \
+    (FAIL_OP),(FAIL_OP), (FUNC), (FUNC), (MASK), (MASK), (REF), (REF),       \
+    (WRITE_MASK),(WRITE_MASK))
+
+#define GR_STATIC_CONST_STENCIL(NAME,                                        \
+    FRONT_PASS_OP,    BACK_PASS_OP,                                          \
+    FRONT_FAIL_OP,    BACK_FAIL_OP,                                          \
+    FRONT_FUNC,       BACK_FUNC,                                             \
+    FRONT_MASK,       BACK_MASK,                                             \
+    FRONT_REF,        BACK_REF,                                              \
+    FRONT_WRITE_MASK, BACK_WRITE_MASK)                                       \
+    GR_STATIC_CONST_STENCIL_STRUCT(NAME ## _STRUCT,                          \
+    (FRONT_PASS_OP),(BACK_PASS_OP),(FRONT_FAIL_OP),(BACK_FAIL_OP),           \
+    (FRONT_FUNC),(BACK_FUNC),(FRONT_MASK),(BACK_MASK),                       \
+    (FRONT_REF),(BACK_REF),(FRONT_WRITE_MASK),(BACK_WRITE_MASK))             \
+    static const GrStencilSettings& NAME =                                   \
+        *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&(NAME ## _STRUCT));
+
+
+#define GR_STATIC_CONST_SAME_STENCIL(NAME,                                   \
+    PASS_OP, FAIL_OP, FUNC, MASK, REF, WRITE_MASK)                           \
+    GR_STATIC_CONST_STENCIL(NAME, (PASS_OP), (PASS_OP), (FAIL_OP),           \
+    (FAIL_OP), (FUNC), (FUNC), (MASK), (MASK), (REF), (REF), (WRITE_MASK),   \
+    (WRITE_MASK))
+
+#endif
diff --git a/src/gpu/GrStencilAndCoverPathRenderer.cpp b/src/gpu/GrStencilAndCoverPathRenderer.cpp
new file mode 100644
index 0000000..a3f57cd
--- /dev/null
+++ b/src/gpu/GrStencilAndCoverPathRenderer.cpp
@@ -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.
+ */
+
+
+#include "GrStencilAndCoverPathRenderer.h"
+#include "GrContext.h"
+#include "GrGpu.h"
+#include "GrPath.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,
+                                                GrPathFill fill,
+                                                const GrDrawTarget* target,
+                                                bool antiAlias) const {
+    return kHairLine_GrPathFill != fill &&
+           !antiAlias && // doesn't do per-path AA, relies on the target having MSAA
+           target->getDrawState().getStencil().isDisabled();
+}
+
+bool GrStencilAndCoverPathRenderer::requiresStencilPass(const SkPath& path,
+                                                        GrPathFill fill,
+                                                        const GrDrawTarget* target) const {
+    return true;
+}
+
+void GrStencilAndCoverPathRenderer::drawPathToStencil(const SkPath& path,
+                                                      GrPathFill fill,
+                                                      GrDrawTarget* target) {
+    GrAssert(kEvenOdd_GrPathFill == fill || kWinding_GrPathFill == fill);
+    SkAutoTUnref<GrPath> p(fGpu->createPath(path));
+    target->stencilPath(p, fill);
+}
+
+bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path,
+                                               GrPathFill fill,
+                                               GrDrawTarget* target,
+                                               bool antiAlias) {
+    GrAssert(!antiAlias);
+    GrAssert(kHairLine_GrPathFill != fill);
+
+    GrDrawState* drawState = target->drawState();
+    GrAssert(drawState->getStencil().isDisabled());
+
+    SkAutoTUnref<GrPath> p(fGpu->createPath(path));
+
+    GrPathFill nonInvertedFill = GrNonInvertedFill(fill);
+    target->stencilPath(p, 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();
+    GrScalar bloat = drawState->getViewMatrix().getMaxStretch() * GR_ScalarHalf;
+    GrDrawState::AutoDeviceCoordDraw adcd;
+
+    if (nonInvertedFill == fill) {
+        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);
+        GrMatrix vmi;
+        bounds.setLTRB(0, 0,
+                       GrIntToScalar(drawState->getRenderTarget()->width()),
+                       GrIntToScalar(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..e44ddb2
--- /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* context);
+
+    virtual ~GrStencilAndCoverPathRenderer();
+
+    virtual bool canDrawPath(const SkPath& path,
+                             GrPathFill fill,
+                             const GrDrawTarget* target,
+                             bool antiAlias) const SK_OVERRIDE;
+
+    virtual bool requiresStencilPass(const SkPath& path,
+                                     GrPathFill fill,
+                                     const GrDrawTarget* target) const SK_OVERRIDE;
+
+    virtual void drawPathToStencil(const SkPath& path,
+                                   GrPathFill fill,
+                                   GrDrawTarget* target) SK_OVERRIDE;
+
+protected:
+    virtual bool onDrawPath(const SkPath& path,
+                            GrPathFill fill,
+                            GrDrawTarget* target,
+                            bool antiAlias) SK_OVERRIDE;
+
+private:
+    GrStencilAndCoverPathRenderer(GrGpu* gpu);
+
+    GrGpu* fGpu;
+
+    typedef GrPathRenderer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrStencilBuffer.cpp b/src/gpu/GrStencilBuffer.cpp
new file mode 100644
index 0000000..180912e
--- /dev/null
+++ b/src/gpu/GrStencilBuffer.cpp
@@ -0,0 +1,51 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrStencilBuffer.h"
+
+#include "GrContext.h"
+#include "GrGpu.h"
+#include "GrResourceCache.h"
+
+SK_DEFINE_INST_COUNT(GrStencilBuffer)
+GR_DEFINE_RESOURCE_CACHE_TYPE(GrStencilBuffer)
+
+void GrStencilBuffer::transferToCache() {
+    GrAssert(NULL == this->getCacheEntry());
+
+    this->getGpu()->getContext()->addStencilBuffer(this);
+}
+
+namespace {
+// we should never have more than one stencil buffer with same combo of
+// (width,height,samplecount)
+void gen_stencil_key_values(int width,
+                            int height,
+                            int sampleCnt,
+                            GrCacheID* cacheID) {
+    cacheID->fPublicID = GrCacheID::kDefaultPublicCacheID;
+    cacheID->fResourceSpecific32 = width | (height << 16);
+    cacheID->fDomain = GrCacheData::kScratch_ResourceDomain;
+
+    GrAssert(sampleCnt >= 0 && sampleCnt < 256);
+    cacheID->fResourceSpecific16 = sampleCnt << 8;
+
+    // last 8 bits of 'fResourceSpecific16' is free for flags
+}
+}
+
+GrResourceKey GrStencilBuffer::ComputeKey(int width,
+                                          int height,
+                                          int sampleCnt) {
+    GrCacheID id(GrStencilBuffer::GetResourceType());
+    gen_stencil_key_values(width, height, sampleCnt, &id);
+
+    uint32_t v[4];
+    id.toRaw(v);
+    return GrResourceKey(v);
+}
diff --git a/src/gpu/GrStencilBuffer.h b/src/gpu/GrStencilBuffer.h
new file mode 100644
index 0000000..7439c76
--- /dev/null
+++ b/src/gpu/GrStencilBuffer.h
@@ -0,0 +1,96 @@
+
+/*
+ * 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 GrStencilBuffer_DEFINED
+#define GrStencilBuffer_DEFINED
+
+#include "GrClipData.h"
+#include "GrResource.h"
+#include "GrCacheID.h"
+
+class GrRenderTarget;
+class GrResourceEntry;
+class GrResourceKey;
+
+class GrStencilBuffer : public GrResource {
+public:
+    SK_DECLARE_INST_COUNT(GrStencilBuffer);
+    GR_DECLARE_RESOURCE_CACHE_TYPE()
+
+    virtual ~GrStencilBuffer() {
+        // TODO: allow SB to be purged and detach itself from rts
+    }
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+    int bits() const { return fBits; }
+    int numSamples() const { return fSampleCnt; }
+
+    // called to note the last clip drawn to this buffer.
+    void setLastClip(const GrClipData& clipData, int width, int height) {
+        // the clip stack needs to be copied separately (and deeply) since
+        // it could change beneath the stencil buffer
+        fLastClipStack = *clipData.fClipStack;
+        fLastClipData.fClipStack = &fLastClipStack;
+        fLastClipData.fOrigin = clipData.fOrigin;
+        fLastClipWidth = width;
+        fLastClipHeight = height;
+        GrAssert(width <= fWidth);
+        GrAssert(height <= fHeight);
+    }
+
+    // called to determine if we have to render the clip into SB.
+    bool mustRenderClip(const GrClipData& clipData, 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 ||
+               clipData != fLastClipData;
+    }
+
+    const GrClipData& getLastClip() const {
+        return fLastClipData;
+    }
+
+    // Places the sb in the cache. The cache takes a ref of the stencil buffer.
+    void transferToCache();
+
+    static GrResourceKey ComputeKey(int width, int height, int sampleCnt);
+
+protected:
+    GrStencilBuffer(GrGpu* gpu, int width, int height, int bits, int sampleCnt)
+        : GrResource(gpu)
+        , fWidth(width)
+        , fHeight(height)
+        , fBits(bits)
+        , fSampleCnt(sampleCnt)
+        , fLastClipStack()
+        , fLastClipData()
+        , fLastClipWidth(-1)
+        , fLastClipHeight(-1) {
+    }
+
+private:
+
+    int fWidth;
+    int fHeight;
+    int fBits;
+    int fSampleCnt;
+
+    SkClipStack fLastClipStack;
+    GrClipData  fLastClipData;
+    int         fLastClipWidth;
+    int         fLastClipHeight;
+
+    typedef GrResource INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrSurface.cpp b/src/gpu/GrSurface.cpp
new file mode 100644
index 0000000..3fd90d9
--- /dev/null
+++ b/src/gpu/GrSurface.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 "GrSurface.h"
+
+SK_DEFINE_INST_COUNT(GrSurface)
+
diff --git a/src/gpu/GrTBSearch.h b/src/gpu/GrTBSearch.h
new file mode 100644
index 0000000..1d77c95
--- /dev/null
+++ b/src/gpu/GrTBSearch.h
@@ -0,0 +1,46 @@
+
+/*
+ * 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 GrTBSearch_DEFINED
+#define GrTBSearch_DEFINED
+
+template <typename ELEM, typename KEY>
+int GrTBSearch(const ELEM array[], int count, KEY target) {
+    GrAssert(count >= 0);
+    if (0 == count) {
+        // we should insert it at 0
+        return ~0;
+    }
+
+    int high = count - 1;
+    int low = 0;
+    while (high > low) {
+        int index = (low + high) >> 1;
+        if (LT(array[index], target)) {
+            low = index + 1;
+        } else {
+            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;
+    }
+    return ~high;
+}
+
+#endif
+
diff --git a/src/gpu/GrTDArray.h b/src/gpu/GrTDArray.h
new file mode 100644
index 0000000..0e5b6bb
--- /dev/null
+++ b/src/gpu/GrTDArray.h
@@ -0,0 +1,216 @@
+
+/*
+ * 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
new file mode 100644
index 0000000..4494f9f
--- /dev/null
+++ b/src/gpu/GrTHashCache.h
@@ -0,0 +1,252 @@
+
+/*
+ * 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 GrTHashCache_DEFINED
+#define GrTHashCache_DEFINED
+
+#include "GrTDArray.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
+ *      static bool EQ(const Entry&, const HashKey&);
+ *      static bool LT(const Entry&, const HashKey&);
+ *      uint32_t getHash() const;
+ *
+ *  Allows duplicate key entries but on find you may get
+ *  any of the duplicate entries returned.
+ */
+template <typename T, typename Key, size_t kHashBits> class GrTHashTable {
+public:
+    GrTHashTable() { Gr_bzero(fHash, sizeof(fHash)); }
+    ~GrTHashTable() {}
+
+    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*);
+    T* removeAt(int index, uint32_t hash);
+    void removeAll();
+    void deleteAll();
+    void unrefAll();
+
+    /**
+     *  Return the index for the element, using a linear search.
+     */
+    int slowFindIndex(T* elem) const { return fSorted.find(elem); }
+
+#if GR_DEBUG
+    void validate() const;
+    bool contains(T*) const;
+#endif
+
+    // testing
+    const GrTDArray<T*>& getArray() const { return fSorted; }
+private:
+    enum {
+        kHashCount = 1 << kHashBits,
+        kHashMask  = kHashCount - 1
+    };
+    static unsigned hash2Index(uint32_t hash) {
+        hash ^= hash >> 16;
+        if (kHashBits <= 8) {
+            hash ^= hash >> 8;
+        }
+        return hash & kHashMask;
+    }
+
+    mutable T* fHash[kHashCount];
+    GrTDArray<T*> fSorted;
+
+    // search fSorted, and return the found index, or ~index of where it
+    // should be inserted
+    int searchArray(const Key&) const;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T, typename Key, size_t kHashBits>
+int GrTHashTable<T, Key, kHashBits>::searchArray(const Key& key) const {
+    int count = fSorted.count();
+    if (0 == count) {
+        // we should insert it at 0
+        return ~0;
+    }
+
+    const T* const* array = fSorted.begin();
+    int high = count - 1;
+    int low = 0;
+    while (high > low) {
+        int index = (low + high) >> 1;
+        if (Key::LT(*array[index], key)) {
+            low = index + 1;
+        } else {
+            high = index;
+        }
+    }
+
+    // check if we found it
+    if (Key::EQ(*array[high], key)) {
+        // above search should have found the first occurrence if there
+        // are multiple.
+        GrAssert(0 == high || Key::LT(*array[high - 1], key));
+        return high;
+    }
+
+    // now return the ~ of where we should insert it
+    if (Key::LT(*array[high], key)) {
+        high += 1;
+    }
+    return ~high;
+}
+
+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) && findFunc(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>
+bool GrTHashTable<T, Key, kHashBits>::insert(const Key& key, T* elem) {
+    int index = this->searchArray(key);
+    bool first = index < 0;
+    if (first) {
+        // turn it into the actual index
+        index = ~index;
+    }
+    // add it to our array
+    *fSorted.insert(index) = elem;
+    // update our hash table (overwrites any dupe's position in the hash)
+    fHash[hash2Index(key.getHash())] = elem;
+    return first;
+}
+
+template <typename T, typename Key, size_t kHashBits>
+void GrTHashTable<T, Key, kHashBits>::remove(const Key& key, const T* elem) {
+    int index = hash2Index(key.getHash());
+    if (fHash[index] == elem) {
+        fHash[index] = NULL;
+    }
+
+    // remove from our sorted array
+    index = this->searchArray(key);
+    GrAssert(index >= 0);
+    // if there are multiple matches searchArray will give us the first match
+    // march forward until we find elem.
+    while (elem != fSorted[index]) {
+        ++index;
+        GrAssert(index < fSorted.count());
+    }
+    GrAssert(elem == fSorted[index]);
+    fSorted.remove(index);
+}
+
+template <typename T, typename Key, size_t kHashBits>
+T* GrTHashTable<T, Key, kHashBits>::removeAt(int elemIndex, uint32_t hash) {
+    int hashIndex = hash2Index(hash);
+    if (fHash[hashIndex] == fSorted[elemIndex]) {
+        fHash[hashIndex] = NULL;
+    }
+    // remove from our sorted array
+    T* elem = fSorted[elemIndex];
+    fSorted.remove(elemIndex);
+    return elem;
+}
+
+template <typename T, typename Key, size_t kHashBits>
+void GrTHashTable<T, Key, kHashBits>::removeAll() {
+    fSorted.reset();
+    Gr_bzero(fHash, sizeof(fHash));
+}
+
+template <typename T, typename Key, size_t kHashBits>
+void GrTHashTable<T, Key, kHashBits>::deleteAll() {
+    fSorted.deleteAll();
+    Gr_bzero(fHash, sizeof(fHash));
+}
+
+template <typename T, typename Key, size_t kHashBits>
+void GrTHashTable<T, Key, kHashBits>::unrefAll() {
+    fSorted.unrefAll();
+    Gr_bzero(fHash, sizeof(fHash));
+}
+
+#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]) ||
+                 Key::EQ(*fSorted[i - 1], *fSorted[i]));
+    }
+}
+
+template <typename T, typename Key, size_t kHashBits>
+bool GrTHashTable<T, Key, kHashBits>::contains(T* elem) const {
+    int index = fSorted.find(elem);
+    return index >= 0;
+}
+
+#endif
+
+#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/GrTextContext.cpp b/src/gpu/GrTextContext.cpp
new file mode 100644
index 0000000..59b9cb3
--- /dev/null
+++ b/src/gpu/GrTextContext.cpp
@@ -0,0 +1,264 @@
+/*
+ * 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 "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 "SkPath.h"
+
+enum {
+    kGlyphMaskStage = GrPaint::kTotalStages,
+};
+
+void GrTextContext::flushGlyphs() {
+    if (NULL == fDrawTarget) {
+        return;
+    }
+    GrDrawState* drawState = fDrawTarget->drawState();
+    if (fCurrVertex > 0) {
+        // setup our sampler state for our text texture/atlas
+        drawState->sampler(kGlyphMaskStage)->reset();
+
+        GrAssert(GrIsALIGN4(fCurrVertex));
+        GrAssert(fCurrTexture);
+        GrTextureParams params(SkShader::kRepeat_TileMode, false);
+        drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, GrMatrix::I(), params);
+
+        if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
+            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.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.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
+            drawState->setColor(fPaint.getColor());
+        }
+
+        int nGlyphs = fCurrVertex / 4;
+        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
+        fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
+                                          nGlyphs,
+                                          4, 6);
+        fDrawTarget->resetVertexSource();
+        fVertices = NULL;
+        fMaxVertices = 0;
+        fCurrVertex = 0;
+        GrSafeSetNull(fCurrTexture);
+    }
+    drawState->disableStages();
+    fDrawTarget = NULL;
+}
+
+GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) {
+    fContext = context;
+    fStrike = NULL;
+
+    fCurrTexture = NULL;
+    fCurrVertex = 0;
+
+    const GrClipData* clipData = context->getClip();
+
+    GrRect devConservativeBound;
+    clipData->fClipStack->getConservativeBounds(
+                                     -clipData->fOrigin.fX,
+                                     -clipData->fOrigin.fY,
+                                     context->getRenderTarget()->width(),
+                                     context->getRenderTarget()->height(),
+                                     &devConservativeBound);
+
+    devConservativeBound.roundOut(&fClipRect);
+
+    fAutoMatrix.setIdentity(fContext, &fPaint);
+
+    fDrawTarget = NULL;
+
+    fVertices = NULL;
+    fMaxVertices = 0;
+
+    fVertexLayout =
+        GrDrawTarget::kTextFormat_VertexLayoutBit |
+        GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
+}
+
+GrTextContext::~GrTextContext() {
+    this->flushGlyphs();
+    if (fDrawTarget) {
+        fDrawTarget->drawState()->disableStages();
+    }
+}
+
+void GrTextContext::flush() {
+    this->flushGlyphs();
+}
+
+static inline void setRectFan(GrGpuTextVertex v[4], int l, int t, int r, int b,
+                              int stride) {
+    v[0 * stride].setI(l, t);
+    v[1 * stride].setI(l, b);
+    v[2 * stride].setI(r, b);
+    v[3 * stride].setI(r, t);
+}
+
+void GrTextContext::drawPackedGlyph(GrGlyph::PackedID packed,
+                                    GrFixed vx, GrFixed vy,
+                                    GrFontScaler* scaler) {
+    if (NULL == fStrike) {
+        fStrike = fContext->getFontCache()->getStrike(scaler);
+    }
+
+    GrGlyph* glyph = fStrike->getGlyph(packed, scaler);
+    if (NULL == glyph || glyph->fBounds.isEmpty()) {
+        return;
+    }
+
+    vx += GrIntToFixed(glyph->fBounds.fLeft);
+    vy += GrIntToFixed(glyph->fBounds.fTop);
+
+    // keep them as ints until we've done the clip-test
+    GrFixed width = glyph->fBounds.width();
+    GrFixed height = glyph->fBounds.height();
+
+    // check if we clipped out
+    if (true || NULL == glyph->fAtlas) {
+        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
+            return;
+        }
+    }
+
+    if (NULL == glyph->fAtlas) {
+        if (fStrike->getGlyphAtlas(glyph, scaler)) {
+            goto HAS_ATLAS;
+        }
+
+        // before we purge the cache, we must flush any accumulated draws
+        this->flushGlyphs();
+        fContext->flush();
+
+        // try to purge
+        fContext->getFontCache()->purgeExceptFor(fStrike);
+        if (fStrike->getGlyphAtlas(glyph, scaler)) {
+            goto HAS_ATLAS;
+        }
+
+        if (NULL == glyph->fPath) {
+            SkPath* path = SkNEW(SkPath);
+            if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
+                // flag the glyph as being dead?
+                delete path;
+                return;
+            }
+            glyph->fPath = path;
+        }
+
+        GrContext::AutoMatrix am;
+        GrMatrix translate;
+        translate.setTranslate(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
+                               GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
+        GrPaint tmpPaint(fPaint);
+        am.setPreConcat(fContext, translate, &tmpPaint);
+        fContext->drawPath(tmpPaint, *glyph->fPath, kWinding_GrPathFill);
+        return;
+    }
+
+HAS_ATLAS:
+    GrAssert(glyph->fAtlas);
+
+    // now promote them to fixed
+    width = GrIntToFixed(width);
+    height = GrIntToFixed(height);
+
+    GrTexture* texture = glyph->fAtlas->texture();
+    GrAssert(texture);
+
+    if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) {
+        this->flushGlyphs();
+        fCurrTexture = texture;
+        fCurrTexture->ref();
+    }
+
+    if (NULL == fVertices) {
+        // 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 = (NULL != fDrawTarget) &&
+                     fDrawTarget->geometryHints(fVertexLayout,
+                                                &fMaxVertices,
+                                                NULL);
+        if (flush) {
+            this->flushGlyphs();
+            fContext->flush();
+        }
+        fDrawTarget = fContext->getTextTarget(fPaint);
+        fMaxVertices = kDefaultRequestedVerts;
+        // ignore return, no point in flushing again.
+        fDrawTarget->geometryHints(fVertexLayout,
+                                   &fMaxVertices,
+                                   NULL);
+
+        int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
+        if (fMaxVertices < kMinRequestedVerts) {
+            fMaxVertices = kDefaultRequestedVerts;
+        } else if (fMaxVertices > maxQuadVertices) {
+            // don't exceed the limit of the index buffer
+            fMaxVertices = maxQuadVertices;
+        }
+        bool success = fDrawTarget->reserveVertexAndIndexSpace(
+                                                   fVertexLayout,
+                                                   fMaxVertices,
+                                                   0,
+                                                   GrTCast<void**>(&fVertices),
+                                                   NULL);
+        GrAlwaysAssert(success);
+    }
+
+    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
+    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
+
+#if GR_TEXT_SCALAR_IS_USHORT
+    int x = vx >> 16;
+    int y = vy >> 16;
+    int w = width >> 16;
+    int h = height >> 16;
+
+    setRectFan(&fVertices[2*fCurrVertex], x, y, x + w, y + h, 2);
+    setRectFan(&fVertices[2*fCurrVertex+1],
+               texture->normalizeFixedX(tx),
+               texture->normalizeFixedY(ty),
+               texture->normalizeFixedX(tx + width),
+               texture->normalizeFixedY(ty + height),
+               2);
+#else
+    fVertices[2*fCurrVertex].setXRectFan(vx, vy, vx + width, vy + height,
+                                        2 * sizeof(GrGpuTextVertex));
+    fVertices[2*fCurrVertex+1].setXRectFan(texture->normalizeFixedX(tx),
+                                          texture->normalizeFixedY(ty),
+                                          texture->normalizeFixedX(tx + width),
+                                          texture->normalizeFixedY(ty + height),
+                                          2 * sizeof(GrGpuTextVertex));
+#endif
+    fCurrVertex += 4;
+}
+
diff --git a/src/gpu/GrTextStrike.cpp b/src/gpu/GrTextStrike.cpp
new file mode 100644
index 0000000..aebaaf8
--- /dev/null
+++ b/src/gpu/GrTextStrike.cpp
@@ -0,0 +1,211 @@
+
+/*
+ * 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 "GrAtlas.h"
+#include "GrGpu.h"
+#include "GrRectanizer.h"
+#include "GrTextStrike.h"
+#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;
+
+    fHead = fTail = NULL;
+}
+
+GrFontCache::~GrFontCache() {
+    fCache.deleteAll();
+    delete fAtlasMgr;
+    fGpu->unref();
+}
+
+GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
+                                          const Key& key) {
+    if (NULL == fAtlasMgr) {
+        fAtlasMgr = SkNEW_ARGS(GrAtlasMgr, (fGpu));
+    }
+    GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
+                                      (this, scaler->getKey(),
+                                       scaler->getMaskFormat(), fAtlasMgr));
+    fCache.insert(key, strike);
+
+    if (fHead) {
+        fHead->fPrev = strike;
+    } else {
+        GrAssert(NULL == fTail);
+        fTail = strike;
+    }
+    strike->fPrev = NULL;
+    strike->fNext = fHead;
+    fHead = strike;
+
+    return strike;
+}
+
+void GrFontCache::freeAll() {
+    fCache.deleteAll();
+    delete fAtlasMgr;
+    fAtlasMgr = NULL;
+    fHead = NULL;
+    fTail = NULL;
+}
+
+void GrFontCache::purgeExceptFor(GrTextStrike* preserveStrike) {
+    GrTextStrike* strike = fTail;
+    while (strike) {
+        if (strike == preserveStrike) {
+            strike = strike->fPrev;
+            continue;
+        }
+        GrTextStrike* strikeToPurge = strike;
+        // keep going if we won't free up any atlases with this strike.
+        strike = (NULL == strikeToPurge->fAtlas) ? strikeToPurge->fPrev : NULL;
+        int index = fCache.slowFindIndex(strikeToPurge);
+        GrAssert(index >= 0);
+        fCache.removeAt(index, strikeToPurge->fFontScalerKey->getHash());
+        this->detachStrikeFromList(strikeToPurge);
+        delete strikeToPurge;
+    }
+}
+
+#if GR_DEBUG
+void GrFontCache::validate() const {
+    int count = fCache.count();
+    if (0 == count) {
+        GrAssert(!fHead);
+        GrAssert(!fTail);
+    } else if (1 == count) {
+        GrAssert(fHead == fTail);
+    } else {
+        GrAssert(fHead != fTail);
+    }
+
+    int count2 = 0;
+    const GrTextStrike* strike = fHead;
+    while (strike) {
+        count2 += 1;
+        strike = strike->fNext;
+    }
+    GrAssert(count == count2);
+
+    count2 = 0;
+    strike = fTail;
+    while (strike) {
+        count2 += 1;
+        strike = strike->fPrev;
+    }
+    GrAssert(count == count2);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_DEBUG
+    static int gCounter;
+#endif
+
+/*
+    The text strike is specific to a given font/style/matrix setup, which is
+    represented by the GrHostFontScaler object we are given in getGlyph().
+
+    We map a 32bit glyphID to a GrGlyph record, which in turn points to a
+    atlas and a position within that texture.
+ */
+
+GrTextStrike::GrTextStrike(GrFontCache* cache, const GrKey* key,
+                           GrMaskFormat format,
+                           GrAtlasMgr* atlasMgr) : fPool(64) {
+    fFontScalerKey = key;
+    fFontScalerKey->ref();
+
+    fFontCache = cache;     // no need to ref, it won't go away before we do
+    fAtlasMgr = atlasMgr;   // no need to ref, it won't go away before we do
+    fAtlas = NULL;
+
+    fMaskFormat = format;
+
+#if GR_DEBUG
+//    GrPrintf(" GrTextStrike %p %d\n", this, gCounter);
+    gCounter += 1;
+#endif
+}
+
+static void FreeGlyph(GrGlyph*& glyph) { glyph->free(); }
+
+GrTextStrike::~GrTextStrike() {
+    GrAtlas::FreeLList(fAtlas);
+    fFontScalerKey->unref();
+    fCache.getArray().visit(FreeGlyph);
+
+#if GR_DEBUG
+    gCounter -= 1;
+//    GrPrintf("~GrTextStrike %p %d\n", this, gCounter);
+#endif
+}
+
+GrGlyph* GrTextStrike::generateGlyph(GrGlyph::PackedID packed,
+                                     GrFontScaler* scaler) {
+    GrIRect bounds;
+    if (!scaler->getPackedGlyphBounds(packed, &bounds)) {
+        return NULL;
+    }
+
+    GrGlyph* glyph = fPool.alloc();
+    glyph->init(packed, bounds);
+    fCache.insert(packed, glyph);
+    return glyph;
+}
+
+bool GrTextStrike::getGlyphAtlas(GrGlyph* glyph, GrFontScaler* scaler) {
+#if 0   // testing hack to force us to flush our cache often
+    static int gCounter;
+    if ((++gCounter % 10) == 0) return false;
+#endif
+
+    GrAssert(glyph);
+    GrAssert(scaler);
+    GrAssert(fCache.contains(glyph));
+    if (glyph->fAtlas) {
+        return true;
+    }
+
+    GrAutoRef ar(scaler);
+
+    int bytesPerPixel = GrMaskFormatBytesPerPixel(fMaskFormat);
+    size_t size = glyph->fBounds.area() * bytesPerPixel;
+    SkAutoSMalloc<1024> storage(size);
+    if (!scaler->getPackedGlyphImage(glyph->fPackedID, glyph->width(),
+                                     glyph->height(),
+                                     glyph->width() * bytesPerPixel,
+                                     storage.get())) {
+        return false;
+    }
+
+    GrAtlas* atlas = fAtlasMgr->addToAtlas(fAtlas, glyph->width(),
+                                           glyph->height(), storage.get(),
+                                           fMaskFormat,
+                                           &glyph->fAtlasLocation);
+    if (NULL == atlas) {
+        return false;
+    }
+
+    // update fAtlas as well, since they may be chained in a linklist
+    glyph->fAtlas = fAtlas = atlas;
+    return true;
+}
+
+
diff --git a/src/gpu/GrTextStrike.h b/src/gpu/GrTextStrike.h
new file mode 100644
index 0000000..701acea
--- /dev/null
+++ b/src/gpu/GrTextStrike.h
@@ -0,0 +1,115 @@
+
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrTextStrike_DEFINED
+#define GrTextStrike_DEFINED
+
+#include "GrAllocPool.h"
+#include "GrFontScaler.h"
+#include "GrTHashCache.h"
+#include "GrPoint.h"
+#include "GrGlyph.h"
+
+class GrAtlasMgr;
+class GrFontCache;
+class GrGpu;
+class GrFontPurgeListener;
+
+/**
+ *  The textcache maps a hostfontscaler instance to a dictionary of
+ *  glyphid->strike
+ */
+class GrTextStrike {
+public:
+    GrTextStrike(GrFontCache*, const GrKey* fontScalerKey, GrMaskFormat,
+                 GrAtlasMgr*);
+    ~GrTextStrike();
+
+    const GrKey* getFontScalerKey() const { return fFontScalerKey; }
+    GrFontCache* getFontCache() const { return fFontCache; }
+    GrMaskFormat getMaskFormat() const { return fMaskFormat; }
+
+    inline GrGlyph* getGlyph(GrGlyph::PackedID, GrFontScaler*);
+    bool getGlyphAtlas(GrGlyph*, GrFontScaler*);
+
+    // testing
+    int countGlyphs() const { return fCache.getArray().count(); }
+    const GrGlyph* glyphAt(int index) const {
+        return fCache.getArray()[index];
+    }
+    GrAtlas* getAtlas() const { return fAtlas; }
+
+public:
+    // for LRU
+    GrTextStrike*   fPrev;
+    GrTextStrike*   fNext;
+
+private:
+    class Key;
+    GrTHashTable<GrGlyph, Key, 7> fCache;
+    const GrKey* fFontScalerKey;
+    GrTAllocPool<GrGlyph> fPool;
+
+    GrFontCache*    fFontCache;
+    GrAtlasMgr*     fAtlasMgr;
+    GrAtlas*        fAtlas;     // linklist
+
+    GrMaskFormat fMaskFormat;
+
+    GrGlyph* generateGlyph(GrGlyph::PackedID packed, GrFontScaler* scaler);
+    // returns true if after the purge, the strike is empty
+    bool purgeAtlasAtY(GrAtlas* atlas, int yCoord);
+
+    friend class GrFontCache;
+};
+
+class GrFontCache {
+public:
+    GrFontCache(GrGpu*);
+    ~GrFontCache();
+
+    inline GrTextStrike* getStrike(GrFontScaler*);
+
+    void freeAll();
+
+    void purgeExceptFor(GrTextStrike*);
+
+    // testing
+    int countStrikes() const { return fCache.getArray().count(); }
+    const GrTextStrike* strikeAt(int index) const {
+        return fCache.getArray()[index];
+    }
+    GrTextStrike* getHeadStrike() const { return fHead; }
+
+#if GR_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+private:
+    friend class GrFontPurgeListener;
+
+    class Key;
+    GrTHashTable<GrTextStrike, Key, 8> fCache;
+    // for LRU
+    GrTextStrike* fHead;
+    GrTextStrike* fTail;
+
+    GrGpu*      fGpu;
+    GrAtlasMgr* fAtlasMgr;
+
+
+    GrTextStrike* generateStrike(GrFontScaler*, const Key&);
+    inline void detachStrikeFromList(GrTextStrike*);
+};
+
+#endif
+
diff --git a/src/gpu/GrTextStrike_impl.h b/src/gpu/GrTextStrike_impl.h
new file mode 100644
index 0000000..a3f37b3
--- /dev/null
+++ b/src/gpu/GrTextStrike_impl.h
@@ -0,0 +1,106 @@
+
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrTextStrike_impl_DEFINED
+#define GrTextStrike_impl_DEFINED
+
+class GrFontCache::Key {
+public:
+    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;
+};
+
+void GrFontCache::detachStrikeFromList(GrTextStrike* strike) {
+    if (strike->fPrev) {
+        GrAssert(fHead != strike);
+        strike->fPrev->fNext = strike->fNext;
+    } else {
+        GrAssert(fHead == strike);
+        fHead = strike->fNext;
+    }
+
+    if (strike->fNext) {
+        GrAssert(fTail != strike);
+        strike->fNext->fPrev = strike->fPrev;
+    } else {
+        GrAssert(fTail == strike);
+        fTail = strike->fPrev;
+    }
+}
+
+GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler) {
+    this->validate();
+
+    Key key(scaler);
+    GrTextStrike* strike = fCache.find(key);
+    if (NULL == strike) {
+        strike = this->generateStrike(scaler, key);
+    } else if (strike->fPrev) {
+        // Need to put the strike at the head of its dllist, since that is how
+        // we age the strikes for purging (we purge from the back of the list
+        this->detachStrikeFromList(strike);
+        // attach at the head
+        fHead->fPrev = strike;
+        strike->fNext = fHead;
+        strike->fPrev = NULL;
+        fHead = strike;
+    }
+
+    this->validate();
+    return strike;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  This Key just wraps a glyphID, and matches the protocol need for
+ *  GrTHashTable
+ */
+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;
+};
+
+GrGlyph* GrTextStrike::getGlyph(GrGlyph::PackedID packed,
+                                GrFontScaler* scaler) {
+    GrGlyph* glyph = fCache.find(packed);
+    if (NULL == glyph) {
+        glyph = this->generateGlyph(packed, scaler);
+    }
+    return glyph;
+}
+
+#endif
+
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
new file mode 100644
index 0000000..c31d774
--- /dev/null
+++ b/src/gpu/GrTexture.cpp
@@ -0,0 +1,212 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrTexture.h"
+
+#include "GrContext.h"
+#include "GrGpu.h"
+#include "GrRenderTarget.h"
+#include "GrResourceCache.h"
+
+SK_DEFINE_INST_COUNT(GrTexture)
+GR_DEFINE_RESOURCE_CACHE_TYPE(GrTexture)
+
+/**
+ * This method allows us to interrupt the normal deletion process and place
+ * 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, 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,
+                                      pixelOpsFlags);
+}
+
+void GrTexture::writePixels(int left, int top, int width, int height,
+                            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->writeTexturePixels(this,
+                                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 <= 8 bits so they can be folded into the texture
+// key
+enum TextureBits {
+    /*
+     * The kNPOT bit is set when the texture is NPOT and is being repeated
+     * but the hardware doesn't support that feature.
+     */
+    kNPOT_TextureBit            = 0x1,
+    /*
+     * The kFilter bit can only be set when the kNPOT flag is set and indicates
+     * whether the resizing of the texture should use filtering. This is
+     * to handle cases where the original texture is indexed to disable
+     * filtering.
+     */
+    kFilter_TextureBit          = 0x2,
+    /*
+     * The kScratch bit is set if the texture is being used as a scratch
+     * texture.
+     */
+    kScratch_TextureBit         = 0x4,
+};
+
+namespace {
+void gen_texture_key_values(const GrGpu* gpu,
+                            const GrTextureParams* params,
+                            const GrTextureDesc& desc,
+                            const GrCacheData& cacheData,
+                            bool scratch,
+                            GrCacheID* cacheID) {
+
+    uint64_t clientKey = cacheData.fClientCacheID;
+
+    if (scratch) {
+        // Instead of a client-provided key of the texture contents
+        // we create a key from the descriptor.
+        GrAssert(GrCacheData::kScratch_CacheID == clientKey);
+        clientKey = (desc.fFlags << 8) | ((uint64_t) desc.fConfig << 32);
+    }
+
+    cacheID->fPublicID = clientKey;
+    cacheID->fDomain = cacheData.fResourceDomain;
+
+    // we assume we only need 16 bits of width and height
+    // assert that texture creation will fail anyway if this assumption
+    // would cause key collisions.
+    GrAssert(gpu->getCaps().maxTextureSize() <= SK_MaxU16);
+    cacheID->fResourceSpecific32 = desc.fWidth | (desc.fHeight << 16);
+
+    GrAssert(desc.fSampleCnt >= 0 && desc.fSampleCnt < 256);
+    cacheID->fResourceSpecific16 = desc.fSampleCnt << 8;
+
+    if (!gpu->getCaps().npotTextureTileSupport()) {
+        bool isPow2 = GrIsPow2(desc.fWidth) && GrIsPow2(desc.fHeight);
+
+        bool tiled = NULL != params && params->isTiled();
+
+        if (tiled && !isPow2) {
+            cacheID->fResourceSpecific16 |= kNPOT_TextureBit;
+            if (params->isBilerp()) {
+                cacheID->fResourceSpecific16 |= kFilter_TextureBit;
+            }
+        }
+    }
+
+    if (scratch) {
+        cacheID->fResourceSpecific16 |= kScratch_TextureBit;
+    }
+}
+}
+
+GrResourceKey GrTexture::ComputeKey(const GrGpu* gpu,
+                                    const GrTextureParams* params,
+                                    const GrTextureDesc& desc,
+                                    const GrCacheData& cacheData,
+                                    bool scratch) {
+    GrCacheID id(GrTexture::GetResourceType());
+    gen_texture_key_values(gpu, params, desc, cacheData, scratch, &id);
+
+    uint32_t v[4];
+    id.toRaw(v);
+    return GrResourceKey(v);
+}
+
+bool GrTexture::NeedsResizing(const GrResourceKey& key) {
+    return 0 != (key.getValue32(3) & kNPOT_TextureBit);
+}
+
+bool GrTexture::IsScratchTexture(const GrResourceKey& key) {
+    return 0 != (key.getValue32(3) & kScratch_TextureBit);
+}
+
+bool GrTexture::NeedsFiltering(const GrResourceKey& key) {
+    return 0 != (key.getValue32(3) & kFilter_TextureBit);
+}
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
new file mode 100644
index 0000000..bda235c
--- /dev/null
+++ b/src/gpu/GrVertexBuffer.h
@@ -0,0 +1,24 @@
+
+/*
+ * 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 GrVertexBuffer_DEFINED
+#define GrVertexBuffer_DEFINED
+
+#include "GrGeometryBuffer.h"
+
+class GrVertexBuffer : public GrGeometryBuffer {
+protected:
+    GrVertexBuffer(GrGpu* gpu, size_t sizeInBytes, bool dynamic)
+        : INHERITED(gpu, sizeInBytes, dynamic) {}
+private:
+    typedef GrGeometryBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
new file mode 100644
index 0000000..02bfdbd
--- /dev/null
+++ b/src/gpu/SkGpuDevice.cpp
@@ -0,0 +1,1937 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkGpuDevice.h"
+
+#include "effects/GrColorTableEffect.h"
+#include "effects/GrTextureDomainEffect.h"
+
+#include "GrContext.h"
+#include "GrTextContext.h"
+
+#include "SkGrTexturePixelRef.h"
+
+#include "SkColorFilter.h"
+#include "SkDeviceImageFilterProxy.h"
+#include "SkDrawProcs.h"
+#include "SkGlyphCache.h"
+#include "SkImageFilter.h"
+#include "SkUtils.h"
+
+#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1
+
+#if 0
+    extern bool (*gShouldDrawProc)();
+    #define CHECK_SHOULD_DRAW(draw, forceI)                     \
+        do {                                                    \
+            if (gShouldDrawProc && !gShouldDrawProc()) return;  \
+            this->prepareDraw(draw, forceI);                    \
+        } while (0)
+#else
+    #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,
+    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.
+#define MAX_BLUR_RADIUS SkIntToScalar(128)
+// This constant approximates the scaling done in the software path's
+// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
+// IMHO, it actually should be 1:  we blur "less" than we should do
+// according to the CSS and canvas specs, simply because Safari does the same.
+// Firefox used to do the same too, until 4.0 where they fixed it.  So at some
+// 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)
+
+#define DO_DEFERRED_CLEAR()     \
+    do {                        \
+        if (fNeedClear) {       \
+            this->clear(0x0);   \
+        }                       \
+    } while (false)             \
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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) {
+            GrUnlockCachedBitmapTexture(fTexture);
+        }
+    }
+
+    GrTexture* set(SkGpuDevice* device,
+                   const SkBitmap& bitmap,
+                   const GrTextureParams* params) {
+        if (NULL != fTexture) {
+            GrUnlockCachedBitmapTexture(fTexture);
+            fTexture = NULL;
+        }
+        fDevice = device;
+        GrTexture* result = (GrTexture*)bitmap.getTexture();
+        if (NULL == result) {
+            // Cannot return the native texture so look it up in our cache
+            fTexture = GrLockCachedBitmapTexture(device->context(), bitmap, params);
+            result = fTexture;
+        }
+        return result;
+    }
+
+private:
+    SkGpuDevice* fDevice;
+    GrTexture*   fTexture;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct GrSkDrawProcs : public SkDrawProcs {
+public:
+    GrContext* fContext;
+    GrTextContext* fTextContext;
+    GrFontScaler* fFontScaler;  // cached in the skia glyphcache
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBitmap::Config grConfig2skConfig(GrPixelConfig config, bool* isOpaque) {
+    switch (config) {
+        case kAlpha_8_GrPixelConfig:
+            *isOpaque = false;
+            return SkBitmap::kA8_Config;
+        case kRGB_565_GrPixelConfig:
+            *isOpaque = true;
+            return SkBitmap::kRGB_565_Config;
+        case kRGBA_4444_GrPixelConfig:
+            *isOpaque = false;
+            return SkBitmap::kARGB_4444_Config;
+        case kSkia8888_PM_GrPixelConfig:
+            // we don't currently have a way of knowing whether
+            // a 8888 is opaque based on the config.
+            *isOpaque = false;
+            return SkBitmap::kARGB_8888_Config;
+        default:
+            *isOpaque = false;
+            return SkBitmap::kNo_Config;
+    }
+}
+
+static SkBitmap make_bitmap(GrContext* context, GrRenderTarget* renderTarget) {
+    GrPixelConfig config = renderTarget->config();
+
+    bool isOpaque;
+    SkBitmap bitmap;
+    bitmap.setConfig(grConfig2skConfig(config, &isOpaque),
+                     renderTarget->width(), renderTarget->height());
+    bitmap.setIsOpaque(isOpaque);
+    return bitmap;
+}
+
+SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture)
+: SkDevice(make_bitmap(context, texture->asRenderTarget())) {
+    this->initFromRenderTarget(context, texture->asRenderTarget(), false);
+}
+
+SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget)
+: SkDevice(make_bitmap(context, renderTarget)) {
+    this->initFromRenderTarget(context, renderTarget, false);
+}
+
+void SkGpuDevice::initFromRenderTarget(GrContext* context,
+                                       GrRenderTarget* renderTarget,
+                                       bool cached) {
+    fDrawProcs = NULL;
+
+    fContext = context;
+    fContext->ref();
+
+    fRenderTarget = NULL;
+    fNeedClear = false;
+
+    GrAssert(NULL != renderTarget);
+    fRenderTarget = renderTarget;
+    fRenderTarget->ref();
+
+    // 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)
+    : SkDevice(config, width, height, false /*isOpaque*/) {
+
+    fDrawProcs = NULL;
+
+    fContext = context;
+    fContext->ref();
+
+    fRenderTarget = NULL;
+    fNeedClear = false;
+
+    if (config != SkBitmap::kRGB_565_Config) {
+        config = SkBitmap::kARGB_8888_Config;
+    }
+    SkBitmap bm;
+    bm.setConfig(config, width, height);
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fConfig = SkBitmapConfig2GrPixelConfig(bm.config());
+
+    SkAutoTUnref<GrTexture> texture(fContext->createUncachedTexture(desc, NULL, 0));
+
+    if (NULL != texture) {
+        fRenderTarget = texture->asRenderTarget();
+        fRenderTarget->ref();
+
+        GrAssert(NULL != fRenderTarget);
+
+        // wrap the bitmap with a pixelref to expose our texture
+        SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (texture));
+        this->setPixelRef(pr, 0)->unref();
+    } else {
+        GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",
+                 width, height);
+        GrAssert(false);
+    }
+}
+
+SkGpuDevice::~SkGpuDevice() {
+    if (fDrawProcs) {
+        delete fDrawProcs;
+    }
+
+    // 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);
+    }
+
+    SkSafeUnref(fRenderTarget);
+    fContext->unref();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::makeRenderTargetCurrent() {
+    DO_DEFERRED_CLEAR();
+    fContext->setRenderTarget(fRenderTarget);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) {
+    switch (config8888) {
+        case SkCanvas::kNative_Premul_Config8888:
+            *flags = 0;
+            return kSkia8888_GrPixelConfig;
+        case SkCanvas::kNative_Unpremul_Config8888:
+            *flags = GrContext::kUnpremul_PixelOpsFlag;
+            return kSkia8888_PM_GrPixelConfig;
+        case SkCanvas::kBGRA_Premul_Config8888:
+            *flags = 0;
+            return kBGRA_8888_GrPixelConfig;
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            *flags = GrContext::kUnpremul_PixelOpsFlag;
+            return kBGRA_8888_GrPixelConfig;
+        case SkCanvas::kRGBA_Premul_Config8888:
+            *flags = 0;
+            return kRGBA_8888_GrPixelConfig;
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            *flags = GrContext::kUnpremul_PixelOpsFlag;
+            return kRGBA_8888_GrPixelConfig;
+        default:
+            GrCrash("Unexpected Config8888.");
+            *flags = 0; // suppress warning
+            return kSkia8888_PM_GrPixelConfig;
+    }
+}
+}
+
+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;
+    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(),
+                                            flags);
+}
+
+void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,
+                              SkCanvas::Config8888 config8888) {
+    SkAutoLockPixels alp(bitmap);
+    if (!bitmap.readyToDraw()) {
+        return;
+    }
+
+    GrPixelConfig config;
+    uint32_t flags;
+    if (SkBitmap::kARGB_8888_Config == bitmap.config()) {
+        config = config8888_to_grconfig_and_flags(config8888, &flags);
+    } else {
+        flags = 0;
+        config= SkBitmapConfig2GrPixelConfig(bitmap.config());
+    }
+
+    fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(),
+                               config, bitmap.getPixels(), bitmap.rowBytes(), flags);
+}
+
+namespace {
+void purgeClipCB(int genID, void* data) {
+    GrContext* context = (GrContext*) data;
+
+    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
+
+///////////////////////////////////////////////////////////////////////////////
+
+// call this every draw call, to ensure that the context reflects our state,
+// and not the state from some other canvas/device
+void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) {
+    GrAssert(NULL != fClipData.fClipStack);
+
+    fContext->setRenderTarget(fRenderTarget);
+
+    SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack);
+
+    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) {
+    GrTexture* texture = fRenderTarget->asTexture();
+    if (NULL != texture) {
+        paint->colorSampler(kBitmapTextureIdx)->setCustomStage(
+            SkNEW_ARGS(GrSingleTextureEffect, (texture)))->unref();
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_COMPILE_ASSERT(SkShader::kNone_BitmapType == 0, shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kDefault_BitmapType == 1, shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kRadial_BitmapType == 2, shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,
+                  shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kTwoPointConical_BitmapType == 5,
+                  shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kLinear_BitmapType == 6, shader_type_mismatch);
+SK_COMPILE_ASSERT(SkShader::kLast_BitmapType == 6, shader_type_mismatch);
+
+namespace {
+
+// 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,
+                                    SkGpuDevice::SkAutoCachedTexture* act,
+                                    GrPaint* grPaint) {
+
+    grPaint->setDither(skPaint.isDither());
+    grPaint->setAntiAlias(skPaint.isAntiAlias());
+
+    SkXfermode::Coeff sm = SkXfermode::kOne_Coeff;
+    SkXfermode::Coeff dm = SkXfermode::kISA_Coeff;
+
+    SkXfermode* mode = skPaint.getXfermode();
+    if (mode) {
+        if (!mode->asCoeff(&sm, &dm)) {
+            //SkDEBUGCODE(SkDebugf("Unsupported xfer mode.\n");)
+#if 0
+            return false;
+#endif
+        }
+    }
+    grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
+
+    if (justAlpha) {
+        uint8_t alpha = skPaint.getAlpha();
+        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->setColor(SkColor2GrColor(skPaint.getColor()));
+        GrAssert(!grPaint->isColorStageEnabled(kShaderTextureIdx));
+    }
+    SkColorFilter* colorFilter = skPaint.getColorFilter();
+    SkColor color;
+    SkXfermode::Mode filterMode;
+    SkScalar matrix[20];
+    SkBitmap colorTransformTable;
+    // TODO: SkColorFilter::asCustomStage()
+    if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) {
+        if (!constantColor) {
+            grPaint->setXfermodeColorFilter(filterMode, SkColor2GrColor(color));
+        } else {
+            SkColor filtered = colorFilter->filterColor(skPaint.getColor());
+            grPaint->setColor(SkColor2GrColor(filtered));
+        }
+    } else if (colorFilter != NULL && colorFilter->asColorMatrix(matrix)) {
+        grPaint->setColorMatrix(matrix);
+    } else if (colorFilter != NULL && colorFilter->asComponentTable(&colorTransformTable)) {
+        // pass NULL because the color table effect doesn't use tiling or filtering.
+        GrTexture* texture = act->set(dev, colorTransformTable, NULL);
+        GrSamplerState* colorSampler = grPaint->colorSampler(kColorFilterTextureIdx);
+        colorSampler->reset();
+        colorSampler->setCustomStage(SkNEW_ARGS(GrColorTableEffect, (texture)))->unref();
+    }
+    return true;
+}
+
+// This function is similar to skPaint2GrPaintNoShader but also converts
+// skPaint's shader to a GrTexture/GrSamplerState 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,
+                                  SkGpuDevice::SkAutoCachedTexture textures[GrPaint::kMaxColorStages],
+                                  GrPaint* grPaint) {
+    SkShader* shader = skPaint.getShader();
+    if (NULL == shader) {
+        return skPaint2GrPaintNoShader(dev,
+                                       skPaint,
+                                       false,
+                                       constantColor,
+                                       &textures[kColorFilterTextureIdx],
+                                       grPaint);
+    } else if (!skPaint2GrPaintNoShader(dev, skPaint, true, false,
+                                        &textures[kColorFilterTextureIdx], grPaint)) {
+        return false;
+    }
+
+    GrSamplerState* sampler = grPaint->colorSampler(kShaderTextureIdx);
+    if (shader->asNewCustomStage(dev->context(), sampler)) {
+        return true;
+    }
+
+    SkBitmap bitmap;
+    SkMatrix matrix;
+    SkShader::TileMode tileModes[2];
+    SkShader::BitmapType bmptype = shader->asABitmap(&bitmap, &matrix, tileModes);
+
+    if (SkShader::kNone_BitmapType == bmptype) {
+        SkShader::GradientInfo info;
+        SkColor                color;
+
+        info.fColors = &color;
+        info.fColorOffsets = NULL;
+        info.fColorCount = 1;
+        if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
+            SkPaint copy(skPaint);
+            copy.setShader(NULL);
+            // modulate the paint alpha by the shader's solid color alpha
+            U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
+            copy.setColor(SkColorSetA(color, newA));
+            return skPaint2GrPaintNoShader(dev,
+                                           copy,
+                                           false,
+                                           constantColor,
+                                           &textures[kColorFilterTextureIdx],
+                                           grPaint);
+        }
+        return false;
+    }
+
+    // Must set wrap and filter on the sampler before requesting a texture.
+    GrTextureParams params(tileModes, skPaint.isFilterBitmap());
+    GrTexture* texture = textures[kShaderTextureIdx].set(dev, bitmap, &params);
+
+    if (NULL == texture) {
+        SkDebugf("Couldn't convert bitmap to texture.\n");
+        return false;
+    }
+
+    // since our texture coords will be in local space, we whack the texture
+    // matrix to map them back into 0...1 before we load it
+    SkMatrix localM;
+    if (shader->getLocalMatrix(&localM)) {
+        SkMatrix inverse;
+        if (localM.invert(&inverse)) {
+            matrix.preConcat(inverse);
+        }
+    }
+    if (SkShader::kDefault_BitmapType == bmptype) {
+        GrScalar sx = SkFloatToScalar(1.f / bitmap.width());
+        GrScalar sy = SkFloatToScalar(1.f / bitmap.height());
+        matrix.postScale(sx, sy);
+    }
+    sampler->setCustomStage(SkNEW_ARGS(GrSingleTextureEffect, (texture, params)), matrix)->unref();
+
+    return true;
+}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void SkGpuDevice::clear(SkColor color) {
+    fContext->clear(NULL, color, fRenderTarget);
+    fNeedClear = false;
+}
+
+void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    GrPaint grPaint;
+    SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
+    if (!skPaint2GrPaintShader(this,
+                               paint,
+                               true,
+                               textures,
+                               &grPaint)) {
+        return;
+    }
+
+    fContext->drawPaint(grPaint);
+}
+
+// must be in SkCanvas::PointMode order
+static const GrPrimitiveType gPointMode2PrimtiveType[] = {
+    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, false);
+
+    SkScalar width = paint.getStrokeWidth();
+    if (width < 0) {
+        return;
+    }
+
+    // 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 textures[GrPaint::kMaxColorStages];
+    if (!skPaint2GrPaintShader(this,
+                               paint,
+                               true,
+                               textures,
+                               &grPaint)) {
+        return;
+    }
+
+    fContext->drawVertices(grPaint,
+                           gPointMode2PrimtiveType[mode],
+                           count,
+                           (GrPoint*)pts,
+                           NULL,
+                           NULL,
+                           NULL,
+                           0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
+                           const SkPaint& paint) {
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
+
+    bool doStroke = paint.getStyle() != SkPaint::kFill_Style;
+    SkScalar width = paint.getStrokeWidth();
+
+    /*
+        We have special code for hairline strokes, miter-strokes, and fills.
+        Anything else we just call our path code.
+     */
+    bool usePath = doStroke && width > 0 &&
+                    paint.getStrokeJoin() != SkPaint::kMiter_Join;
+    // 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() && !fContext->getMatrix().rectStaysRect()) {
+        usePath = true;
+    }
+    // small miter limit means right angles show bevel...
+    if (SkPaint::kMiter_Join == paint.getStrokeJoin() &&
+        paint.getStrokeMiter() < SK_ScalarSqrt2)
+    {
+        usePath = true;
+    }
+    // until we can both stroke and fill rectangles
+    if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
+        usePath = true;
+    }
+
+    if (usePath) {
+        SkPath path;
+        path.addRect(rect);
+        this->drawPath(draw, path, paint, NULL, true);
+        return;
+    }
+
+    GrPaint grPaint;
+    SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
+    if (!skPaint2GrPaintShader(this,
+                               paint,
+                               true,
+                               textures,
+                               &grPaint)) {
+        return;
+    }
+    fContext->drawRect(grPaint, rect, doStroke ? width : -1);
+}
+
+#include "SkMaskFilter.h"
+#include "SkBounder.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+// helpers for applying mask filters
+namespace {
+
+GrPathFill skToGrFillType(SkPath::FillType fillType) {
+    switch (fillType) {
+        case SkPath::kWinding_FillType:
+            return kWinding_GrPathFill;
+        case SkPath::kEvenOdd_FillType:
+            return kEvenOdd_GrPathFill;
+        case SkPath::kInverseWinding_FillType:
+            return kInverseWinding_GrPathFill;
+        case SkPath::kInverseEvenOdd_FillType:
+            return kInverseEvenOdd_GrPathFill;
+        default:
+            SkDebugf("Unsupported path fill type\n");
+            return kHairLine_GrPathFill;
+    }
+}
+
+// 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;
+    }
+    return false;
+}
+
+bool drawWithGPUMaskFilter(GrContext* context, const SkPath& devPath,
+                           SkMaskFilter* filter, const SkRegion& clip,
+                           SkBounder* bounder, GrPaint* grp, GrPathFill pathFillType) {
+#ifdef SK_DISABLE_GPU_BLUR
+    return false;
+#endif
+    SkMaskFilter::BlurInfo info;
+    SkMaskFilter::BlurType blurType = filter->asABlur(&info);
+    if (SkMaskFilter::kNone_BlurType == blurType) {
+        return false;
+    }
+    SkScalar radius = info.fIgnoreTransform ? 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 clipRect;
+    clipRect.set(clip.getBounds());
+
+    // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
+    srcRect.inset(SkFloatToScalar(-sigma3), SkFloatToScalar(-sigma3));
+    clipRect.inset(SkFloatToScalar(-sigma3), SkFloatToScalar(-sigma3));
+    srcRect.intersect(clipRect);
+    SkRect finalRect = srcRect;
+    SkIRect finalIRect;
+    finalRect.roundOut(&finalIRect);
+    if (clip.quickReject(finalIRect)) {
+        return true;
+    }
+    if (bounder && !bounder->doIRect(finalIRect)) {
+        return true;
+    }
+    GrPoint offset = GrPoint::Make(-srcRect.fLeft, -srcRect.fTop);
+    srcRect.offset(offset);
+    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;
+    }
+
+    SkAutoTUnref<GrTexture> blurTexture;
+
+    {
+        GrContext::AutoRenderTarget art(context, pathTexture->asRenderTarget());
+        GrContext::AutoClip ac(context, srcRect);
+
+        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);
+        }
+
+        GrContext::AutoMatrix am;
+
+        // Draw hard shadow to pathTexture with path top-left at origin using tempPaint.
+        GrMatrix translate;
+        translate.setTranslate(offset.fX, offset.fY);
+        am.set(context, translate);
+        context->drawPath(tempPaint, devPath, pathFillType);
+
+        // 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 (!isNormalBlur) {
+            context->setIdentityMatrix();
+            GrPaint paint;
+            GrMatrix matrix;
+            matrix.setIDiv(pathTexture->width(), pathTexture->height());
+            // Blend pathTexture over blurTexture.
+            context->setRenderTarget(blurTexture->asRenderTarget());
+            paint.colorSampler(0)->setCustomStage(SkNEW_ARGS(GrSingleTextureEffect, (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);
+        }
+    }
+
+    GrContext::AutoMatrix am;
+    if (!am.setIdentity(context, grp)) {
+        return false;
+    }
+
+    static const int MASK_IDX = GrPaint::kMaxCoverageStages - 1;
+    // we assume the last mask index is available for use
+    GrAssert(!grp->isCoverageStageEnabled(MASK_IDX));
+
+    GrMatrix matrix;
+    matrix.setTranslate(-finalRect.fLeft, -finalRect.fTop);
+    matrix.postIDiv(blurTexture->width(), blurTexture->height());
+
+    grp->coverageSampler(MASK_IDX)->reset();
+    grp->coverageSampler(MASK_IDX)->setCustomStage(SkNEW_ARGS(GrSingleTextureEffect, (blurTexture)), matrix)->unref();
+    context->drawRect(*grp, finalRect);
+    return true;
+}
+
+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(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM,
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
+        return false;
+    }
+    SkAutoMaskFreeImage autoSrc(srcM.fImage);
+
+    if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) {
+        return false;
+    }
+    // this will free-up dstM when we're done (allocated in filterMask())
+    SkAutoMaskFreeImage autoDst(dstM.fImage);
+
+    if (clip.quickReject(dstM.fBounds)) {
+        return false;
+    }
+    if (bounder && !bounder->doIRect(dstM.fBounds)) {
+        return false;
+    }
+
+    // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
+    // the current clip (and identity matrix) and GrPaint settings
+    GrContext::AutoMatrix am;
+    am.setIdentity(context, grp);
+
+    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();
+
+    if (NULL == texture) {
+        return false;
+    }
+    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                               dstM.fImage, dstM.fRowBytes);
+
+    static const int MASK_IDX = GrPaint::kMaxCoverageStages - 1;
+    // we assume the last mask index is available for use
+    GrAssert(!grp->isCoverageStageEnabled(MASK_IDX));
+
+    GrMatrix m;
+    m.setTranslate(-dstM.fBounds.fLeft*SK_Scalar1, -dstM.fBounds.fTop*SK_Scalar1);
+    m.postIDiv(texture->width(), texture->height());
+
+    grp->coverageSampler(MASK_IDX)->setCustomStage(SkNEW_ARGS(GrSingleTextureEffect, (texture)), m)->unref();
+    GrRect d;
+    d.setLTRB(GrIntToScalar(dstM.fBounds.fLeft),
+              GrIntToScalar(dstM.fBounds.fTop),
+              GrIntToScalar(dstM.fBounds.fRight),
+              GrIntToScalar(dstM.fBounds.fBottom));
+
+    context->drawRect(*grp, d);
+    return true;
+}
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
+                           const SkPaint& paint, const SkMatrix* prePathMatrix,
+                           bool pathIsMutable) {
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
+
+    bool             doFill = true;
+
+    GrPaint grPaint;
+    SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
+    if (!skPaint2GrPaintShader(this,
+                               paint,
+                               true,
+                               textures,
+                               &grPaint)) {
+        return;
+    }
+
+    // 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)
+    SkScalar hairlineCoverage;
+    if (SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage)) {
+        doFill = false;
+        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;
+
+    if (prePathMatrix) {
+        SkPath* result = pathPtr;
+
+        if (!pathIsMutable) {
+            result = &tmpPath;
+            pathIsMutable = true;
+        }
+        // should I push prePathMatrix on our MV stack temporarily, instead
+        // of applying it here? See SkDraw.cpp
+        pathPtr->transform(*prePathMatrix, result);
+        pathPtr = result;
+    }
+    // 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;
+    }
+
+    if (paint.getMaskFilter()) {
+        // avoid possibly allocating a new path in transform if we can
+        SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
+
+        // transform the path into device space
+        pathPtr->transform(fContext->getMatrix(), devPathPtr);
+        GrPathFill pathFillType = doFill ?
+            skToGrFillType(devPathPtr->getFillType()) : kHairLine_GrPathFill;
+        if (!drawWithGPUMaskFilter(fContext, *devPathPtr, paint.getMaskFilter(),
+                                   *draw.fClip, draw.fBounder, &grPaint, pathFillType)) {
+            SkPaint::Style style = doFill ? SkPaint::kFill_Style :
+                SkPaint::kStroke_Style;
+            drawWithMaskFilter(fContext, *devPathPtr, paint.getMaskFilter(),
+                               *draw.fClip, draw.fBounder, &grPaint, style);
+        }
+        return;
+    }
+
+    GrPathFill fill = kHairLine_GrPathFill;
+
+    if (doFill) {
+        switch (pathPtr->getFillType()) {
+            case SkPath::kWinding_FillType:
+                fill = kWinding_GrPathFill;
+                break;
+            case SkPath::kEvenOdd_FillType:
+                fill = kEvenOdd_GrPathFill;
+                break;
+            case SkPath::kInverseWinding_FillType:
+                fill = kInverseWinding_GrPathFill;
+                break;
+            case SkPath::kInverseEvenOdd_FillType:
+                fill = kInverseEvenOdd_GrPathFill;
+                break;
+            default:
+                SkDebugf("Unsupported path fill type\n");
+                return;
+        }
+    }
+
+    fContext->drawPath(grPaint, *pathPtr, fill);
+}
+
+namespace {
+
+inline int get_tile_count(int l, int t, int r, int b, int tileSize)  {
+    int tilesX = (r / tileSize) - (l / tileSize) + 1;
+    int tilesY = (b / tileSize) - (t / tileSize) + 1;
+    return tilesX * tilesY;
+}
+
+inline int determine_tile_size(const SkBitmap& bitmap,
+                               const SkRect& src,
+                               int maxTextureSize) {
+    static const int kSmallTileSize = 1 << 10;
+    if (maxTextureSize <= kSmallTileSize) {
+        return maxTextureSize;
+    }
+
+    size_t maxTexTotalTileSize;
+    size_t smallTotalTileSize;
+
+    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;
+
+    if (maxTexTotalTileSize > 2 * smallTotalTileSize) {
+        return kSmallTileSize;
+    } else {
+        return maxTextureSize;
+    }
+}
+}
+
+bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
+                                   const GrTextureParams& params,
+                                   const SkRect* srcRectPtr) const {
+    // if bitmap is explictly texture backed then just use the texture
+    if (NULL != bitmap.getTexture()) {
+        return false;
+    }
+    // if it's larger than the max texture size, then we have no choice but
+    // tiling
+    const int maxTextureSize = fContext->getMaxTextureSize();
+    if (bitmap.width() > maxTextureSize ||
+        bitmap.height() > maxTextureSize) {
+        return true;
+    }
+    // if we are going to have to draw the whole thing, then don't tile
+    if (NULL == srcRectPtr) {
+        return false;
+    }
+    // if the entire texture is already in our cache then no reason to tile it
+    if (this->isBitmapInTextureCache(bitmap, params)) {
+        return false;
+    }
+
+    // At this point we know we could do the draw by uploading the entire bitmap
+    // as a texture. However, if the texture would be large compared to the
+    // cache size and we don't require most of it for this draw then tile to
+    // reduce the amount of upload and cache spill.
+
+    // assumption here is that sw bitmap size is a good proxy for its size as
+    // a texture
+    size_t bmpSize = bitmap.getSize();
+    size_t cacheSize;
+    fContext->getTextureCacheLimits(NULL, &cacheSize);
+    if (bmpSize < cacheSize / 2) {
+        return false;
+    }
+
+    SkScalar fracUsed = SkScalarMul(srcRectPtr->width() / bitmap.width(),
+                                    srcRectPtr->height() / bitmap.height());
+    if (fracUsed <= SK_ScalarHalf) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void SkGpuDevice::drawBitmap(const SkDraw& draw,
+                             const SkBitmap& bitmap,
+                             const SkIRect* srcRectPtr,
+                             const SkMatrix& m,
+                             const SkPaint& paint) {
+
+    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, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
+    } else {
+        srcRect = *srcRectPtr;
+    }
+
+    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 (NULL != srcRectPtr) {
+            SkIRect iSrc;
+            srcRect.roundOut(&iSrc);
+            if (!bitmap.extractSubset(&tmp, iSrc)) {
+                return;     // extraction failed
+            }
+            bitmapPtr = &tmp;
+            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();
+
+        // 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(fContext->getMatrix(), newM);
+        SkDraw transformedDraw(draw);
+        transformedDraw.fMatrix = &drawMatrix;
+
+        this->drawRect(transformedDraw, srcRect, paintWithTexture);
+
+        return;
+    }
+
+    GrPaint grPaint;
+    SkAutoCachedTexture colorLutTexture;
+    if (!skPaint2GrPaintNoShader(this, paint, true, false, &colorLutTexture, &grPaint)) {
+        return;
+    }
+    GrTextureParams params;
+    params.setBilerp(paint.isFilterBitmap());
+
+    if (!this->shouldTileBitmap(bitmap, params, srcRectPtr)) {
+        // take the simple case
+        this->internalDrawBitmap(bitmap, srcRect, m, params, &grPaint);
+    } else {
+        this->drawTiledBitmap(bitmap, srcRect, m, params, &grPaint);
+    }
+}
+
+// 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
+    SkRect clipRect;
+    {
+        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(fContext->getMatrix(), m);
+        if (!matrix.invert(&inverse)) {
+            return;
+        }
+        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++) {
+            SkRect tileR;
+            tileR.set(SkIntToScalar(x * tileSize),
+                      SkIntToScalar(y * tileSize),
+                      SkIntToScalar((x + 1) * tileSize),
+                      SkIntToScalar((y + 1) * tileSize));
+
+            if (!SkRect::Intersects(tileR, clipRect)) {
+                continue;
+            }
+
+            if (!tileR.intersect(srcRect)) {
+                continue;
+            }
+
+            SkBitmap tmpB;
+            SkIRect iTileR;
+            tileR.roundOut(&iTileR);
+            if (bitmap.extractSubset(&tmpB, iTileR)) {
+                // now offset it to make it "local" to our tmp bitmap
+                tileR.offset(SkIntToScalar(-iTileR.fLeft), SkIntToScalar(-iTileR.fTop));
+                SkMatrix tmpM(m);
+                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.
+ *
+ *  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 SkBitmap& bitmap,
+                                     const SkRect& srcRect,
+                                     const SkMatrix& m,
+                                     const GrTextureParams& params,
+                                     GrPaint* grPaint) {
+    SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
+             bitmap.height() <= fContext->getMaxTextureSize());
+
+    SkAutoLockPixels alp(bitmap, !bitmap.getTexture());
+    if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
+        SkDebugf("nothing to draw\n");
+        return;
+    }
+
+    GrSamplerState* sampler = grPaint->colorSampler(kBitmapTextureIdx);
+
+    GrTexture* texture;
+    SkAutoCachedTexture act(this, bitmap, &params, &texture);
+    if (NULL == texture) {
+        return;
+    }
+
+    GrRect dstRect(srcRect);
+    GrRect paintRect;
+    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));
+
+    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<GrCustomStage> stage;
+    if (needsTextureDomain) {
+        // Use a constrained texture domain to avoid color bleeding
+        GrScalar left, top, right, bottom;
+        if (srcRect.width() > GR_Scalar1) {
+            GrScalar border = GR_ScalarHalf / bitmap.width();
+            left = paintRect.left() + border;
+            right = paintRect.right() - border;
+        } else {
+            left = right = GrScalarHalf(paintRect.left() + paintRect.right());
+        }
+        if (srcRect.height() > GR_Scalar1) {
+            GrScalar border = GR_ScalarHalf / bitmap.height();
+            top = paintRect.top() + border;
+            bottom = paintRect.bottom() - border;
+        } else {
+            top = bottom = GrScalarHalf(paintRect.top() + paintRect.bottom());
+        }
+        textureDomain.setLTRB(left, top, right, bottom);
+        stage.reset(SkNEW_ARGS(GrTextureDomainEffect, (texture, textureDomain, params)));
+    } else {
+        stage.reset(SkNEW_ARGS(GrSingleTextureEffect, (texture, params)));
+    }
+    grPaint->colorSampler(kBitmapTextureIdx)->setCustomStage(stage);
+    fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
+}
+
+namespace {
+
+void apply_custom_stage(GrContext* context,
+                        GrTexture* srcTexture,
+                        GrTexture* dstTexture,
+                        const GrRect& rect,
+                        GrCustomStage* stage) {
+    SkASSERT(srcTexture && srcTexture->getContext() == context);
+    GrContext::AutoMatrix am;
+    am.setIdentity(context);
+    GrContext::AutoRenderTarget art(context, dstTexture->asRenderTarget());
+    GrContext::AutoClip acs(context, rect);
+
+    GrMatrix sampleM;
+    sampleM.setIDiv(srcTexture->width(), srcTexture->height());
+    GrPaint paint;
+    paint.colorSampler(0)->setCustomStage(stage, sampleM);
+    context->drawRect(paint, rect);
+}
+
+};
+
+static GrTexture* filter_texture(SkDevice* device, GrContext* context,
+                                 GrTexture* texture, SkImageFilter* filter,
+                                 const GrRect& rect) {
+    GrAssert(filter);
+    SkDeviceImageFilterProxy proxy(device);
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit,
+    desc.fWidth = SkScalarCeilToInt(rect.width());
+    desc.fHeight = SkScalarCeilToInt(rect.height());
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    GrCustomStage* stage;
+
+    if (filter->canFilterImageGPU()) {
+        texture = filter->onFilterImageGPU(&proxy, texture, rect);
+    } else if (filter->asNewCustomStage(&stage, texture)) {
+        GrAutoScratchTexture dst(context, desc);
+        apply_custom_stage(context, texture, dst.texture(), rect, stage);
+        texture = dst.detach();
+        stage->unref();
+    }
+    return texture;
+}
+
+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;
+    }
+
+    int w = bitmap.width();
+    int h = bitmap.height();
+
+    GrPaint grPaint;
+    SkAutoCachedTexture colorLutTexture;
+    if(!skPaint2GrPaintNoShader(this, paint, true, false, &colorLutTexture, &grPaint)) {
+        return;
+    }
+
+    GrSamplerState* sampler = grPaint.colorSampler(kBitmapTextureIdx);
+
+    GrTexture* texture;
+    sampler->reset();
+    // draw sprite uses the default texture params
+    SkAutoCachedTexture act(this, bitmap, NULL, &texture);
+    grPaint.colorSampler(kBitmapTextureIdx)->setCustomStage(SkNEW_ARGS
+        (GrSingleTextureEffect, (texture)))->unref();
+
+    SkImageFilter* filter = paint.getImageFilter();
+    if (NULL != filter) {
+        GrTexture* filteredTexture = filter_texture(this, fContext, texture, filter,
+                 GrRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h)));
+        if (filteredTexture) {
+            grPaint.colorSampler(kBitmapTextureIdx)->setCustomStage(SkNEW_ARGS
+                (GrSingleTextureEffect, (filteredTexture)))->unref();
+            texture = filteredTexture;
+            filteredTexture->unref();
+        }
+    }
+
+    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()));
+}
+
+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;
+    SkAutoCachedTexture colorLutTexture;
+    grPaint.colorSampler(kBitmapTextureIdx)->reset();
+    if (!dev->bindDeviceAsTexture(&grPaint) ||
+        !skPaint2GrPaintNoShader(this, paint, true, false, &colorLutTexture, &grPaint)) {
+        return;
+    }
+
+    GrTexture* devTex = grPaint.getColorSampler(kBitmapTextureIdx).getCustomStage()->texture(0);
+    SkASSERT(NULL != devTex);
+
+    SkImageFilter* filter = paint.getImageFilter();
+    if (NULL != filter) {
+        GrRect rect = GrRect::MakeWH(SkIntToScalar(devTex->width()),
+                                     SkIntToScalar(devTex->height()));
+        GrTexture* filteredTexture = filter_texture(this, fContext, devTex, filter, rect);
+        if (filteredTexture) {
+            grPaint.colorSampler(kBitmapTextureIdx)->setCustomStage(SkNEW_ARGS
+                (GrSingleTextureEffect, (filteredTexture)))->unref();
+            devTex = filteredTexture;
+            filteredTexture->unref();
+        }
+    }
+
+    const SkBitmap& bm = dev->accessBitmap(false);
+    int w = bm.width();
+    int h = bm.height();
+
+    GrRect dstRect = GrRect::MakeXYWH(GrIntToScalar(x),
+                                      GrIntToScalar(y),
+                                      GrIntToScalar(w),
+                                      GrIntToScalar(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());
+
+    fContext->drawRectToRect(grPaint, dstRect, srcRect);
+}
+
+bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) {
+    if (!filter->asNewCustomStage(NULL, NULL) &&
+        !filter->canFilterImageGPU()) {
+        return false;
+    }
+    return true;
+}
+
+bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
+                              const SkMatrix& ctm,
+                              SkBitmap* result, SkIPoint* offset) {
+    // want explicitly our impl, so guard against a subclass of us overriding it
+    if (!this->SkGpuDevice::canHandleImageFilter(filter)) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(src, !src.getTexture());
+    if (!src.getTexture() && !src.readyToDraw()) {
+        return false;
+    }
+
+    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);
+
+    result->setConfig(src.config(), src.width(), src.height());
+    GrRect rect = GrRect::MakeWH(SkIntToScalar(src.width()),
+                                 SkIntToScalar(src.height()));
+    GrTexture* resultTexture = filter_texture(this, fContext, texture, filter, rect);
+    if (resultTexture) {
+        result->setPixelRef(SkNEW_ARGS(SkGrTexturePixelRef,
+                                       (resultTexture)))->unref();
+        resultTexture->unref();
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// must be in SkCanvas::VertexMode order
+static const GrPrimitiveType gVertexMode2PrimitiveType[] = {
+    kTriangles_GrPrimitiveType,
+    kTriangleStrip_GrPrimitiveType,
+    kTriangleFan_GrPrimitiveType,
+};
+
+void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+                              int vertexCount, const SkPoint vertices[],
+                              const SkPoint texs[], const SkColor colors[],
+                              SkXfermode* xmode,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    GrPaint grPaint;
+    SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
+    // we ignore the shader if texs is null.
+    if (NULL == texs) {
+        if (!skPaint2GrPaintNoShader(this,
+                                     paint,
+                                     false,
+                                     NULL == colors,
+                                     &textures[kColorFilterTextureIdx],
+                                     &grPaint)) {
+            return;
+        }
+    } else {
+        if (!skPaint2GrPaintShader(this,
+                                   paint,
+                                   NULL == colors,
+                                   textures,
+                                   &grPaint)) {
+            return;
+        }
+    }
+
+    if (NULL != xmode && NULL != texs && NULL != colors) {
+        if (!SkXfermode::IsMode(xmode, SkXfermode::kMultiply_Mode)) {
+            SkDebugf("Unsupported vertex-color/texture xfer mode.\n");
+#if 0
+            return
+#endif
+        }
+    }
+
+    SkAutoSTMalloc<128, GrColor> convertedColors(0);
+    if (NULL != colors) {
+        // need to convert byte order and from non-PM to PM
+        convertedColors.reset(vertexCount);
+        for (int i = 0; i < vertexCount; ++i) {
+            convertedColors[i] = SkColor2GrColor(colors[i]);
+        }
+        colors = convertedColors.get();
+    }
+    fContext->drawVertices(grPaint,
+                           gVertexMode2PrimitiveType[vmode],
+                           vertexCount,
+                           (GrPoint*) vertices,
+                           (GrPoint*) texs,
+                           colors,
+                           indices,
+                           indexCount);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void GlyphCacheAuxProc(void* data) {
+    GrFontScaler* scaler = (GrFontScaler*)data;
+    SkSafeUnref(scaler);
+}
+
+static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {
+    void* auxData;
+    GrFontScaler* scaler = NULL;
+    if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
+        scaler = (GrFontScaler*)auxData;
+    }
+    if (NULL == scaler) {
+        scaler = SkNEW_ARGS(SkGrFontScaler, (cache));
+        cache->setAuxProc(GlyphCacheAuxProc, scaler);
+    }
+    return scaler;
+}
+
+static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,
+                             SkFixed fx, SkFixed fy,
+                             const SkGlyph& glyph) {
+    SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+
+    GrSkDrawProcs* procs = static_cast<GrSkDrawProcs*>(state.fDraw->fProcs);
+
+    if (NULL == procs->fFontScaler) {
+        procs->fFontScaler = get_gr_font_scaler(state.fCache);
+    }
+
+    procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(),
+                                                       glyph.getSubXFixed(),
+                                                       glyph.getSubYFixed()),
+                                         SkFixedFloorToFixed(fx),
+                                         SkFixedFloorToFixed(fy),
+                                         procs->fFontScaler);
+}
+
+SkDrawProcs* SkGpuDevice::initDrawForText(GrTextContext* context) {
+
+    // deferred allocation
+    if (NULL == fDrawProcs) {
+        fDrawProcs = SkNEW(GrSkDrawProcs);
+        fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;
+        fDrawProcs->fContext = fContext;
+    }
+
+    // init our (and GL's) state
+    fDrawProcs->fTextContext = context;
+    fDrawProcs->fFontScaler = NULL;
+    return fDrawProcs;
+}
+
+void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
+                          size_t byteLength, SkScalar x, SkScalar y,
+                          const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    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 textures[GrPaint::kMaxColorStages];
+        if (!skPaint2GrPaintShader(this,
+                                   paint,
+                                   true,
+                                   textures,
+                                   &grPaint)) {
+            return;
+        }
+        GrTextContext context(fContext, grPaint);
+        myDraw.fProcs = this->initDrawForText(&context);
+        this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
+    }
+}
+
+void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
+                             size_t byteLength, const SkScalar pos[],
+                             SkScalar constY, int scalarsPerPos,
+                             const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    if (fContext->getMatrix().hasPerspective()) {
+        // this guy will just call our drawPath()
+        draw.drawPosText((const char*)text, byteLength, pos, constY,
+                         scalarsPerPos, paint);
+    } else {
+        SkDraw myDraw(draw);
+
+        GrPaint grPaint;
+        SkAutoCachedTexture textures[GrPaint::kMaxColorStages];
+        if (!skPaint2GrPaintShader(this,
+                                   paint,
+                                   true,
+                                   textures,
+                                   &grPaint)) {
+            return;
+        }
+        GrTextContext context(fContext, grPaint);
+        myDraw.fProcs = this->initDrawForText(&context);
+        this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
+                                     scalarsPerPos, paint);
+    }
+}
+
+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, false);
+
+    SkASSERT(draw.fDevice == this);
+    draw.drawTextOnPath((const char*)text, len, path, m, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGpuDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) {
+    if (!paint.isLCDRenderText()) {
+        // we're cool with the paint as is
+        return false;
+    }
+
+    if (paint.getShader() ||
+        paint.getXfermode() || // unless its srcover
+        paint.getMaskFilter() ||
+        paint.getRasterizer() ||
+        paint.getColorFilter() ||
+        paint.getPathEffect() ||
+        paint.isFakeBoldText() ||
+        paint.getStyle() != SkPaint::kFill_Style) {
+        // 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;
+}
+
+void SkGpuDevice::flush() {
+    DO_DEFERRED_CLEAR();
+    fContext->resolveRenderTarget(fRenderTarget);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGpuDevice::isBitmapInTextureCache(const SkBitmap& bitmap,
+                                         const GrTextureParams& params) const {
+    uint64_t key = bitmap.getGenerationID();
+    key |= ((uint64_t) bitmap.pixelRefOffset()) << 32;
+
+    GrTextureDesc desc;
+    desc.fWidth = bitmap.width();
+    desc.fHeight = bitmap.height();
+    desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config());
+
+    GrCacheData cacheData(key);
+
+    return this->context()->isTextureInCache(desc, cacheData, &params);
+}
+
+
+SkDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,
+                                                int width, int height,
+                                                bool isOpaque,
+                                                Usage usage) {
+    GrTextureDesc desc;
+    desc.fConfig = fRenderTarget->config();
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fSampleCnt = fRenderTarget->numSamples();
+
+    GrTexture* texture;
+    SkAutoTUnref<GrTexture> tunref;
+    // Skia's convention is to only clear a device if it is non-opaque.
+    bool needClear = !isOpaque;
+
+#if CACHE_COMPATIBLE_DEVICE_TEXTURES
+    // layers are never draw in repeat modes, so we can request an approx
+    // match and ignore any padding.
+    GrContext::ScratchTexMatch matchType = (kSaveLayer_Usage == usage) ?
+                                    GrContext::kApprox_ScratchTexMatch :
+                                    GrContext::kExact_ScratchTexMatch;
+    texture = fContext->lockScratchTexture(desc, matchType);
+#else
+    tunref.reset(fContext->createUncachedTexture(desc, NULL, 0));
+    texture = tunref.get();
+#endif
+    if (texture) {
+        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
new file mode 100644
index 0000000..79bc75d
--- /dev/null
+++ b/src/gpu/SkGr.cpp
@@ -0,0 +1,197 @@
+
+/*
+ * 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 "SkGr.h"
+
+/*  Fill out buffer with the compressed format Ganesh expects from a colortable
+ based bitmap. [palette (colortable) + indices].
+
+ At the moment Ganesh only supports 8bit version. If Ganesh allowed we others
+ we could detect that the colortable.count is <= 16, and then repack the
+ indices as nibbles to save RAM, but it would take more time (i.e. a lot
+ slower than memcpy), so skipping that for now.
+
+ Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big
+ as the colortable.count says it is.
+ */
+static void build_compressed_data(void* buffer, const SkBitmap& bitmap) {
+    SkASSERT(SkBitmap::kIndex8_Config == bitmap.config());
+
+    SkAutoLockPixels apl(bitmap);
+    if (!bitmap.readyToDraw()) {
+        SkDEBUGFAIL("bitmap not ready to draw!");
+        return;
+    }
+
+    SkColorTable* ctable = bitmap.getColorTable();
+    char* dst = (char*)buffer;
+
+    memcpy(dst, ctable->lockColors(), ctable->count() * sizeof(SkPMColor));
+    ctable->unlockColors(false);
+
+    // always skip a full 256 number of entries, even if we memcpy'd fewer
+    dst += kGrColorTableSize;
+
+    if (bitmap.width() == bitmap.rowBytes()) {
+        memcpy(dst, bitmap.getPixels(), bitmap.getSize());
+    } else {
+        // need to trim off the extra bytes per row
+        size_t width = bitmap.width();
+        size_t rowBytes = bitmap.rowBytes();
+        const char* src = (const char*)bitmap.getPixels();
+        for (int y = 0; y < bitmap.height(); y++) {
+            memcpy(dst, src, width);
+            src += rowBytes;
+            dst += width;
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx,
+                                              uint64_t key,
+                                              const GrTextureParams* params,
+                                              const SkBitmap& origBitmap) {
+    SkAutoLockPixels alp(origBitmap);
+
+    if (!origBitmap.readyToDraw()) {
+        return NULL;
+    }
+
+    SkBitmap tmpBitmap;
+
+    const SkBitmap* bitmap = &origBitmap;
+
+    GrTextureDesc desc;
+    desc.fWidth = bitmap->width();
+    desc.fHeight = bitmap->height();
+    desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap->config());
+
+    GrCacheData cacheData(key);
+
+    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(params,
+                                           bitmap->width(), bitmap->height())) {
+            size_t imagesize = bitmap->width() * bitmap->height() +
+                                kGrColorTableSize;
+            SkAutoMalloc storage(imagesize);
+
+            build_compressed_data(storage.get(), origBitmap);
+
+            // our compressed data will be trimmed, so pass width() for its
+            // "rowBytes", since they are the same now.
+
+            if (GrCacheData::kScratch_CacheID != key) {
+                return ctx->createTexture(params, desc, cacheData,
+                                          storage.get(),
+                                          bitmap->width());
+            } else {
+                GrTexture* result = ctx->lockScratchTexture(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());
+    if (GrCacheData::kScratch_CacheID != key) {
+        // This texture is likely to be used again so leave it in the cache
+        // but locked.
+        return ctx->createTexture(params, desc, cacheData,
+                                  bitmap->getPixels(),
+                                  bitmap->rowBytes());
+    } else {
+        // This texture is unlikely to be used again (in its present form) so
+        // just use a scratch texture. This will remove the texture from the
+        // cache so no one else can find it. Additionally, once unlocked, the
+        // scratch texture will go to the end of the list for purging so will
+        // likely be available for this volatile bitmap the next time around.
+        GrTexture* result = ctx->lockScratchTexture(desc,
+                                         GrContext::kExact_ScratchTexMatch);
+        result->writePixels(0, 0,
+                            bitmap->width(), bitmap->height(),
+                            desc.fConfig,
+                            bitmap->getPixels(),
+                            bitmap->rowBytes());
+        return result;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrTexture* GrLockCachedBitmapTexture(GrContext* ctx,
+                                     const SkBitmap& bitmap,
+                                     const GrTextureParams* params) {
+    GrTexture* result = NULL;
+
+    if (!bitmap.isVolatile()) {
+        // If the bitmap isn't changing try to find a cached copy first
+        uint64_t key = bitmap.getGenerationID();
+        key |= ((uint64_t) bitmap.pixelRefOffset()) << 32;
+
+        GrTextureDesc desc;
+        desc.fWidth = bitmap.width();
+        desc.fHeight = bitmap.height();
+        desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config());
+
+        GrCacheData cacheData(key);
+
+        result = ctx->findTexture(desc, cacheData, params);
+        if (NULL == result) {
+            // didn't find a cached copy so create one
+            result = sk_gr_create_bitmap_texture(ctx, key, params, bitmap);
+        }
+    } else {
+        result = sk_gr_create_bitmap_texture(ctx, GrCacheData::kScratch_CacheID, params, bitmap);
+    }
+    if (NULL == result) {
+        GrPrintf("---- failed to create texture for cache [%d %d]\n",
+                    bitmap.width(), bitmap.height());
+    }
+    return result;
+}
+
+void GrUnlockCachedBitmapTexture(GrTexture* texture) {
+    GrAssert(NULL != texture->getContext());
+
+    texture->getContext()->unlockScratchTexture(texture);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrPixelConfig SkBitmapConfig2GrPixelConfig(SkBitmap::Config config) {
+    switch (config) {
+        case SkBitmap::kA8_Config:
+            return kAlpha_8_GrPixelConfig;
+        case SkBitmap::kIndex8_Config:
+            return kIndex_8_GrPixelConfig;
+        case SkBitmap::kRGB_565_Config:
+            return kRGB_565_GrPixelConfig;
+        case SkBitmap::kARGB_4444_Config:
+            return kRGBA_4444_GrPixelConfig;
+        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
new file mode 100644
index 0000000..dc637a8
--- /dev/null
+++ b/src/gpu/SkGrFontScaler.cpp
@@ -0,0 +1,204 @@
+
+/*
+ * 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 "GrTemplates.h"
+#include "SkGr.h"
+#include "SkDescriptor.h"
+#include "SkGlyphCache.h"
+
+class SkGrDescKey : public GrKey {
+public:
+    explicit SkGrDescKey(const SkDescriptor& desc);
+    virtual ~SkGrDescKey();
+
+protected:
+    // overrides
+    virtual bool lt(const GrKey& rh) const;
+    virtual bool eq(const GrKey& rh) const;
+
+private:
+    SkDescriptor* fDesc;
+    enum {
+        kMaxStorageInts = 16
+    };
+    uint32_t fStorage[kMaxStorageInts];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGrDescKey::SkGrDescKey(const SkDescriptor& desc) : GrKey(desc.getChecksum()) {
+    size_t size = desc.getLength();
+    if (size <= sizeof(fStorage)) {
+        fDesc = GrTCast<SkDescriptor*>(fStorage);
+    } else {
+        fDesc = SkDescriptor::Alloc(size);
+    }
+    memcpy(fDesc, &desc, size);
+}
+
+SkGrDescKey::~SkGrDescKey() {
+    if (fDesc != GrTCast<SkDescriptor*>(fStorage)) {
+        SkDescriptor::Free(fDesc);
+    }
+}
+
+bool SkGrDescKey::lt(const GrKey& rh) const {
+    const SkDescriptor* srcDesc = ((const SkGrDescKey*)&rh)->fDesc;
+    size_t lenLH = fDesc->getLength();
+    size_t lenRH = srcDesc->getLength();
+    int cmp = memcmp(fDesc, srcDesc, SkMin32(lenLH, lenRH));
+    if (0 == cmp) {
+        return lenLH < lenRH;
+    } else {
+        return cmp < 0;
+    }
+}
+
+bool SkGrDescKey::eq(const GrKey& rh) const {
+    const SkDescriptor* srcDesc = ((const SkGrDescKey*)&rh)->fDesc;
+    return fDesc->equals(*srcDesc);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGrFontScaler::SkGrFontScaler(SkGlyphCache* strike) {
+    fStrike = strike;
+    fKey = NULL;
+}
+
+SkGrFontScaler::~SkGrFontScaler() {
+    GrSafeUnref(fKey);
+}
+
+GrMaskFormat SkGrFontScaler::getMaskFormat() {
+    SkMask::Format format = fStrike->getMaskFormat();
+    switch (format) {
+        case SkMask::kBW_Format:
+            // fall through to kA8 -- we store BW glyphs in our 8-bit cache
+        case SkMask::kA8_Format:
+            return kA8_GrMaskFormat;
+        case SkMask::kLCD16_Format:
+            return kA565_GrMaskFormat;
+        case SkMask::kLCD32_Format:
+            return kA888_GrMaskFormat;
+        default:
+            GrAssert(!"unsupported SkMask::Format");
+            return kA8_GrMaskFormat;
+    }
+}
+
+const GrKey* SkGrFontScaler::getKey() {
+    if (NULL == fKey) {
+        fKey = SkNEW_ARGS(SkGrDescKey, (fStrike->getDescriptor()));
+    }
+    return fKey;
+}
+
+bool SkGrFontScaler::getPackedGlyphBounds(GrGlyph::PackedID packed,
+                                          GrIRect* bounds) {
+    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed),
+                                              GrGlyph::UnpackFixedX(packed),
+                                              GrGlyph::UnpackFixedY(packed));
+    bounds->setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
+    return true;
+
+}
+
+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,
+                                         int dstRB, void* dst) {
+    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(GrGlyph::UnpackID(packed),
+                                              GrGlyph::UnpackFixedX(packed),
+                                              GrGlyph::UnpackFixedY(packed));
+    GrAssert(glyph.fWidth == width);
+    GrAssert(glyph.fHeight == height);
+    const void* src = fStrike->findImage(glyph);
+    if (NULL == src) {
+        return false;
+    }
+
+    int srcRB = glyph.rowBytes();
+    // 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);
+        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);
+    } else {
+        const int bbp = GrMaskFormatBytesPerPixel(this->getMaskFormat());
+        for (int y = 0; y < height; y++) {
+            memcpy(dst, src, width * bbp);
+            src = (const char*)src + srcRB;
+            dst = (char*)dst + dstRB;
+        }
+    }
+    return true;
+}
+
+// we should just return const SkPath* (NULL means false)
+bool SkGrFontScaler::getGlyphPath(uint16_t glyphID, SkPath* path) {
+
+    const SkGlyph& glyph = fStrike->getGlyphIDMetrics(glyphID);
+    const SkPath* skPath = fStrike->findPath(glyph);
+    if (skPath) {
+        *path = *skPath;
+        return true;
+    }
+    return false;
+}
+
+
+
diff --git a/src/gpu/SkGrPixelRef.cpp b/src/gpu/SkGrPixelRef.cpp
new file mode 100644
index 0000000..e770ef0
--- /dev/null
+++ b/src/gpu/SkGrPixelRef.cpp
@@ -0,0 +1,165 @@
+
+/*
+ * 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) {
+    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 = SkBitmapConfig2GrPixelConfig(dstConfig);
+
+    GrTexture* dst = context->createUncachedTexture(desc, NULL, 0);
+    if (NULL == dst) {
+        return NULL;
+    }
+
+    context->copyTexture(texture, dst->asRenderTarget());
+
+    // 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) {
+    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);
+}
+
+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);
+    dst->allocPixels();
+    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
new file mode 100644
index 0000000..ae2bbdd
--- /dev/null
+++ b/src/gpu/SkGrTexturePixelRef.cpp
@@ -0,0 +1,11 @@
+
+/*
+ * 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 "SkGrTexturePixelRef.h"
diff --git a/src/gpu/effects/Gr1DKernelEffect.h b/src/gpu/effects/Gr1DKernelEffect.h
new file mode 100644
index 0000000..9ef7652
--- /dev/null
+++ b/src/gpu/effects/Gr1DKernelEffect.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 Gr1DKernelEffect_DEFINED
+#define Gr1DKernelEffect_DEFINED
+
+#include "GrSingleTextureEffect.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)
+        , 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/GrColorTableEffect.cpp b/src/gpu/effects/GrColorTableEffect.cpp
new file mode 100644
index 0000000..165cd60
--- /dev/null
+++ b/src/gpu/effects/GrColorTableEffect.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 "GrColorTableEffect.h"
+ #include "gl/GrGLProgramStage.h"
+ #include "GrProgramStageFactory.h"
+ #include "SkString.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLColorTableEffect : public GrGLProgramStage {
+public:
+    GrGLColorTableEffect(const GrProgramStageFactory& factory,
+                         const GrCustomStage& stage);
+
+    virtual void setupVariables(GrGLShaderBuilder* state) SK_OVERRIDE {}
+    virtual void emitVS(GrGLShaderBuilder* state,
+                        const char* vertexCoords) SK_OVERRIDE {}
+    virtual void emitFS(GrGLShaderBuilder* state,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE {}
+
+    static StageKey GenKey(const GrCustomStage&, const GrGLCaps&);
+
+private:
+
+    typedef GrGLProgramStage INHERITED;
+};
+
+GrGLColorTableEffect::GrGLColorTableEffect(
+    const GrProgramStageFactory& factory, const GrCustomStage& stage)
+    : INHERITED(factory) {
+ }
+
+void GrGLColorTableEffect::emitFS(GrGLShaderBuilder* builder,
+                                  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\tvec4 coord = vec4(%s.rgb / %s.a, %s.a);\n",
+                      inputColor, inputColor, 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);
+}
+
+GrGLProgramStage::StageKey GrGLColorTableEffect::GenKey(const GrCustomStage& s,
+                                                        const GrGLCaps& caps) {
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrColorTableEffect::GrColorTableEffect(GrTexture* texture)
+    : INHERITED(1)
+    , fTextureAccess(texture, "a") {
+}
+
+GrColorTableEffect::~GrColorTableEffect() {
+}
+
+const GrProgramStageFactory&  GrColorTableEffect::getFactory() const {
+    return GrTProgramStageFactory<GrColorTableEffect>::getInstance();
+}
+
+bool GrColorTableEffect::isEqual(const GrCustomStage& sBase) const {
+    return INHERITED::isEqual(sBase);
+}
+
+const GrTextureAccess& GrColorTableEffect::textureAccess(int index) const {
+    GrAssert(0 == index);
+    return fTextureAccess;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrColorTableEffect);
+
+GrCustomStage* GrColorTableEffect::TestCreate(SkRandom* random,
+                                              GrContext* context,
+                                              GrTexture* textures[]) {
+    return SkNEW_ARGS(GrColorTableEffect, (textures[GrCustomStageUnitTest::kAlphaTextureIdx]));
+}
diff --git a/src/gpu/effects/GrColorTableEffect.h b/src/gpu/effects/GrColorTableEffect.h
new file mode 100644
index 0000000..c064600
--- /dev/null
+++ b/src/gpu/effects/GrColorTableEffect.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 GrColorTableEffect_DEFINED
+#define GrColorTableEffect_DEFINED
+
+#include "GrSingleTextureEffect.h"
+#include "GrTexture.h"
+
+class GrGLColorTableEffect;
+
+/**
+ * LUT-based color transformation effect. This class implements the Gr
+ * counterpart to the SkTable_ColorFilter effect. A 256 * 4 (single-channel)
+ * LUT is used to transform the input colors of the image.
+ */
+class GrColorTableEffect : public GrCustomStage {
+public:
+
+    explicit GrColorTableEffect(GrTexture* texture);
+    virtual ~GrColorTableEffect();
+
+    static const char* Name() { return "ColorTable"; }
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+
+    virtual const GrTextureAccess& textureAccess(int index) const SK_OVERRIDE;
+
+    typedef GrGLColorTableEffect GLProgramStage;
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+
+    GrTextureAccess fTextureAccess;
+
+    typedef GrCustomStage INHERITED;
+};
+#endif
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
new file mode 100644
index 0000000..b9c9f63
--- /dev/null
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright 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 "gl/GrGLProgramStage.h"
+
+class GrGLConfigConversionEffect : public GrGLProgramStage {
+public:
+    GrGLConfigConversionEffect(const GrProgramStageFactory& factory,
+                               const GrCustomStage& s) : INHERITED (factory) {
+        const GrConfigConversionEffect& stage = static_cast<const GrConfigConversionEffect&>(s);
+        fSwapRedAndBlue = stage.swapsRedAndBlue();
+        fPMConversion = stage.pmConversion();
+    }
+
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE { }
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray& samplers) SK_OVERRIDE {
+        builder->fFSCode.appendf("\t\t%s = ", outputColor);
+        builder->appendTextureLookup(&builder->fFSCode, samplers[0]);
+        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);
+    }
+
+    static inline StageKey GenKey(const GrCustomStage& s, const GrGLCaps&) {
+        const GrConfigConversionEffect& stage = static_cast<const GrConfigConversionEffect&>(s);
+        return static_cast<int>(stage.swapsRedAndBlue()) | (stage.pmConversion() << 1);
+    }
+
+private:
+    bool                                    fSwapRedAndBlue;
+    GrConfigConversionEffect::PMConversion  fPMConversion;
+
+    typedef GrGLProgramStage INHERITED;
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
+                                                   bool swapRedAndBlue,
+                                                   PMConversion pmConversion)
+    : GrSingleTextureEffect(texture)
+    , 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 GrProgramStageFactory& GrConfigConversionEffect::getFactory() const {
+    return GrTProgramStageFactory<GrConfigConversionEffect>::getInstance();
+}
+
+bool GrConfigConversionEffect::isEqual(const GrCustomStage& s) const {
+    const GrConfigConversionEffect& other = static_cast<const GrConfigConversionEffect&>(s);
+    return other.fSwapRedAndBlue == fSwapRedAndBlue && other.fPMConversion == fPMConversion;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrConfigConversionEffect);
+
+GrCustomStage* 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();
+    }
+    return SkNEW_ARGS(GrConfigConversionEffect,
+            (textures[GrCustomStageUnitTest::kSkiaPMTextureIdx], swapRB, pmConv));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+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(GrIntToScalar(256), GrIntToScalar(256));
+        static const GrRect kSrcRect = GrRect::MakeWH(GR_Scalar1, GR_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;
+
+        SkAutoTUnref<GrCustomStage> pmToUPMStage1(SkNEW_ARGS(GrConfigConversionEffect,
+                                                             (dataTex, false, *pmToUPMRule)));
+        SkAutoTUnref<GrCustomStage> upmToPMStage(SkNEW_ARGS(GrConfigConversionEffect,
+                                                            (readTex, false, *upmToPMRule)));
+        SkAutoTUnref<GrCustomStage> pmToUPMStage2(SkNEW_ARGS(GrConfigConversionEffect,
+                                                             (tempTex, false, *pmToUPMRule)));
+
+        context->setRenderTarget(readTex->asRenderTarget());
+        paint.colorSampler(0)->setCustomStage(pmToUPMStage1);
+        context->drawRectToRect(paint, kDstRect, kSrcRect);
+
+        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead);
+
+        context->setRenderTarget(tempTex->asRenderTarget());
+        paint.colorSampler(0)->setCustomStage(upmToPMStage);
+        context->drawRectToRect(paint, kDstRect, kSrcRect);
+        context->setRenderTarget(readTex->asRenderTarget());
+        paint.colorSampler(0)->setCustomStage(pmToUPMStage2);
+        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;
+    }
+}
+
+GrCustomStage* GrConfigConversionEffect::Create(GrTexture* texture,
+                                                bool swapRedAndBlue,
+                                                PMConversion pmConversion) {
+    if (!swapRedAndBlue && kNone_PMConversion == pmConversion) {
+        // If we returned a GrConfigConversionEffect that was equivalent to a GrSingleTextureEffect
+        // then we may pollute our texture cache with redundant shaders. So in the case that no
+        // conversions were requested we instead return a GrSingleTextureEffect.
+        return SkNEW_ARGS(GrSingleTextureEffect, (texture));
+    } 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;
+        }
+        return SkNEW_ARGS(GrConfigConversionEffect, (texture, swapRedAndBlue, pmConversion));
+    }
+}
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
new file mode 100644
index 0000000..9bb7e3a
--- /dev/null
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 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 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
+    };
+
+    // This will fail if the config is not 8888 and a PM conversion is requested.
+    static GrCustomStage* Create(GrTexture*,
+                                 bool swapRedAndBlue,
+                                 PMConversion pmConversion = kNone_PMConversion);
+
+    static const char* Name() { return "Config Conversion"; }
+    typedef GrGLConfigConversionEffect GLProgramStage;
+
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+    virtual bool isEqual(const GrCustomStage&) 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);
+
+    bool            fSwapRedAndBlue;
+    PMConversion    fPMConversion;
+
+    GR_DECLARE_CUSTOM_STAGE_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..c189ec0
--- /dev/null
+++ b/src/gpu/effects/GrConvolutionEffect.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright 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/GrGLProgramStage.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrProgramStageFactory.h"
+
+// For brevity
+typedef GrGLUniformManager::UniformHandle UniformHandle;
+static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
+
+class GrGLConvolutionEffect : public GrGLProgramStage {
+public:
+    GrGLConvolutionEffect(const GrProgramStageFactory& factory,
+                          const GrCustomStage& stage);
+
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE {};
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void setData(const GrGLUniformManager& uman,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
+    static inline StageKey GenKey(const GrCustomStage&, const GrGLCaps&);
+
+private:
+    int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); }
+
+    int             fRadius;
+    UniformHandle   fKernelUni;
+    UniformHandle   fImageIncrementUni;
+
+    typedef GrGLProgramStage INHERITED;
+};
+
+GrGLConvolutionEffect::GrGLConvolutionEffect(const GrProgramStageFactory& factory,
+                                             const GrCustomStage& stage)
+    : GrGLProgramStage(factory)
+    , fKernelUni(kInvalidUniformHandle)
+    , fImageIncrementUni(kInvalidUniformHandle) {
+    const GrConvolutionEffect& c =
+        static_cast<const GrConvolutionEffect&>(stage);
+    fRadius = c.radius();
+}
+
+void GrGLConvolutionEffect::setupVariables(GrGLShaderBuilder* builder) {
+    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                             kVec2f_GrSLType, "ImageIncrement");
+    fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
+                                          kFloat_GrSLType, "Kernel", this->width());
+}
+
+void GrGLConvolutionEffect::emitFS(GrGLShaderBuilder* builder,
+                                   const char* outputColor,
+                                   const char* inputColor,
+                                   const TextureSamplerArray& samplers) {
+    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",
+                  builder->defaultTexCoordsName(), 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 GrCustomStage& data,
+                                    const GrRenderTarget*,
+                                    int stageNum) {
+    const GrConvolutionEffect& conv =
+        static_cast<const GrConvolutionEffect&>(data);
+    GrTexture& texture = *data.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());
+}
+
+GrGLProgramStage::StageKey GrGLConvolutionEffect::GenKey(const GrCustomStage& s,
+                                                         const GrGLCaps& caps) {
+    return static_cast<const GrConvolutionEffect&>(s).radius();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
+                                         Direction direction,
+                                         int radius,
+                                         const float* kernel)
+    : Gr1DKernelEffect(texture, direction, radius) {
+    GrAssert(radius <= kMaxKernelRadius);
+    int width = this->width();
+    if (NULL != kernel) {
+        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 GrProgramStageFactory& GrConvolutionEffect::getFactory() const {
+    return GrTProgramStageFactory<GrConvolutionEffect>::getInstance();
+}
+
+bool GrConvolutionEffect::isEqual(const GrCustomStage& sBase) const {
+     const GrConvolutionEffect& s =
+        static_cast<const GrConvolutionEffect&>(sBase);
+    return (INHERITED::isEqual(sBase) &&
+            this->radius() == s.radius() &&
+            this->direction() == s.direction() &&
+            0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrConvolutionEffect);
+
+GrCustomStage* GrConvolutionEffect::TestCreate(SkRandom* random,
+                                              GrContext* context,
+                                              GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrCustomStageUnitTest::kSkiaPMTextureIdx :
+                                      GrCustomStageUnitTest::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 SkNEW_ARGS(GrConvolutionEffect, (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..4c42a6c
--- /dev/null
+++ b/src/gpu/effects/GrConvolutionEffect.h
@@ -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.
+ */
+
+#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
+    GrConvolutionEffect(GrTexture*, Direction,
+                        int halfWidth, const float* kernel = NULL);
+
+    /// Convolve with a gaussian kernel
+    GrConvolutionEffect(GrTexture*, Direction,
+                        int halfWidth, float gaussianSigma);
+    virtual ~GrConvolutionEffect();
+
+    const float* kernel() const { return fKernel; }
+
+    static const char* Name() { return "Convolution"; }
+
+    typedef GrGLConvolutionEffect GLProgramStage;
+
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+
+    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:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+
+    typedef Gr1DKernelEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrSingleTextureEffect.cpp b/src/gpu/effects/GrSingleTextureEffect.cpp
new file mode 100644
index 0000000..f6f7b8c
--- /dev/null
+++ b/src/gpu/effects/GrSingleTextureEffect.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 "effects/GrSingleTextureEffect.h"
+#include "gl/GrGLProgramStage.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrProgramStageFactory.h"
+#include "GrTexture.h"
+
+class GrGLSingleTextureEffect : public GrGLProgramStage {
+public:
+    GrGLSingleTextureEffect(const GrProgramStageFactory& factory,
+                            const GrCustomStage& stage) : INHERITED (factory) { }
+
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE { }
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray& samplers) SK_OVERRIDE {
+        builder->fFSCode.appendf("\t%s = ", outputColor);
+        builder->appendTextureLookupAndModulate(&builder->fFSCode, inputColor, samplers[0]);
+        builder->fFSCode.append(";\n");
+    }
+
+    static inline StageKey GenKey(const GrCustomStage&, const GrGLCaps&) { return 0; }
+
+private:
+
+    typedef GrGLProgramStage INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture)
+    : INHERITED(1)
+    , fTextureAccess(texture) {
+}
+
+GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, bool bilerp)
+    : INHERITED(1)
+    , fTextureAccess(texture, bilerp) {
+}
+
+GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, const GrTextureParams& params)
+    : INHERITED(1)
+    , fTextureAccess(texture, params) {
+}
+
+GrSingleTextureEffect::~GrSingleTextureEffect() {
+}
+
+const GrTextureAccess& GrSingleTextureEffect::textureAccess(int index) const {
+    GrAssert(0 == index);
+    return fTextureAccess;
+}
+
+const GrProgramStageFactory& GrSingleTextureEffect::getFactory() const {
+    return GrTProgramStageFactory<GrSingleTextureEffect>::getInstance();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrSingleTextureEffect);
+
+GrCustomStage* GrSingleTextureEffect::TestCreate(SkRandom* random,
+                                                 GrContext* context,
+                                                 GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrCustomStageUnitTest::kSkiaPMTextureIdx :
+                                      GrCustomStageUnitTest::kAlphaTextureIdx;
+    return SkNEW_ARGS(GrSingleTextureEffect, (textures[texIdx]));
+}
diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h
new file mode 100644
index 0000000..ad47eb7
--- /dev/null
+++ b/src/gpu/effects/GrSingleTextureEffect.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 GrSingleTextureEffect_DEFINED
+#define GrSingleTextureEffect_DEFINED
+
+#include "GrCustomStage.h"
+
+class GrGLSingleTextureEffect;
+
+/**
+ * An effect that merely blits a single texture; commonly used as a base class.
+ */
+class GrSingleTextureEffect : public GrCustomStage {
+
+public:
+    /** Uses default texture params (unfiltered, clamp) */
+    GrSingleTextureEffect(GrTexture* texture);
+
+    /** Uses default tile mode (clamp) */
+    GrSingleTextureEffect(GrTexture* texture, bool bilerp);
+
+    GrSingleTextureEffect(GrTexture* texture, const GrTextureParams&);
+
+    virtual ~GrSingleTextureEffect();
+
+    virtual const GrTextureAccess& textureAccess(int index) const SK_OVERRIDE;
+
+    static const char* Name() { return "Single Texture"; }
+
+    typedef GrGLSingleTextureEffect GLProgramStage;
+
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_TEST;
+
+    GrTextureAccess fTextureAccess;
+
+    typedef GrCustomStage INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrTextureDomainEffect.cpp b/src/gpu/effects/GrTextureDomainEffect.cpp
new file mode 100644
index 0000000..c00f40f
--- /dev/null
+++ b/src/gpu/effects/GrTextureDomainEffect.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 "GrTextureDomainEffect.h"
+#include "gl/GrGLProgramStage.h"
+#include "GrProgramStageFactory.h"
+
+class GrGLTextureDomainEffect : public GrGLProgramStage {
+public:
+    GrGLTextureDomainEffect(const GrProgramStageFactory& factory,
+                            const GrCustomStage& stage);
+
+    virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE;
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) SK_OVERRIDE { }
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage&,
+                         const GrRenderTarget*,
+                         int stageNum) SK_OVERRIDE;
+
+    static inline StageKey GenKey(const GrCustomStage&, const GrGLCaps&) { return 0; }
+
+private:
+    GrGLUniformManager::UniformHandle fNameUni;
+
+    typedef GrGLProgramStage INHERITED;
+};
+
+GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrProgramStageFactory& factory,
+                                                 const GrCustomStage& stage)
+    : GrGLProgramStage(factory)
+    , fNameUni(GrGLUniformManager::kInvalidUniformHandle) {
+}
+
+void GrGLTextureDomainEffect::setupVariables(GrGLShaderBuilder* builder) {
+    fNameUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                   kVec4f_GrSLType, "TexDom");
+};
+
+void GrGLTextureDomainEffect::emitFS(GrGLShaderBuilder* builder,
+                                     const char* outputColor,
+                                     const char* inputColor,
+                                     const TextureSamplerArray& samplers) {
+    builder->fFSCode.appendf("\tvec2 clampCoord = clamp(%s, %s.xy, %s.zw);\n",
+                           builder->defaultTexCoordsName(),
+                           builder->getUniformCStr(fNameUni),
+                           builder->getUniformCStr(fNameUni));
+
+    builder->fFSCode.appendf("\t%s = ", outputColor);
+    builder->appendTextureLookupAndModulate(&builder->fFSCode,
+                                            inputColor,
+                                            samplers[0],
+                                            "clampCoord");
+    builder->fFSCode.append(";\n");
+}
+
+void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman,
+                                      const GrCustomStage& data,
+                                      const GrRenderTarget*,
+                                      int stageNum) {
+    const GrTextureDomainEffect& effect = static_cast<const GrTextureDomainEffect&>(data);
+    const GrRect& domain = effect.domain();
+
+    float values[4] = {
+        GrScalarToFloat(domain.left()),
+        GrScalarToFloat(domain.top()),
+        GrScalarToFloat(domain.right()),
+        GrScalarToFloat(domain.bottom())
+    };
+    // vertical flip if necessary
+    const GrGLTexture* texture = static_cast<const GrGLTexture*>(effect.texture(0));
+    if (GrGLTexture::kBottomUp_Orientation == texture->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]);
+    }
+    uman.set4fv(fNameUni, 0, 1, values);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture, const GrRect& domain)
+    : GrSingleTextureEffect(texture)
+    , fTextureDomain(domain) {
+}
+
+GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture,
+                                             const GrRect& domain,
+                                             const GrTextureParams& params)
+    : GrSingleTextureEffect(texture, params)
+    , fTextureDomain(domain) {
+}
+
+GrTextureDomainEffect::~GrTextureDomainEffect() {
+
+}
+
+const GrProgramStageFactory& GrTextureDomainEffect::getFactory() const {
+    return GrTProgramStageFactory<GrTextureDomainEffect>::getInstance();
+}
+
+bool GrTextureDomainEffect::isEqual(const GrCustomStage& sBase) const {
+    const GrTextureDomainEffect& s = static_cast<const GrTextureDomainEffect&>(sBase);
+    return (INHERITED::isEqual(sBase) && this->fTextureDomain == s.fTextureDomain);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_CUSTOM_STAGE_TEST(GrTextureDomainEffect);
+
+GrCustomStage* GrTextureDomainEffect::TestCreate(SkRandom* random,
+                                                 GrContext* context,
+                                                 GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrCustomStageUnitTest::kSkiaPMTextureIdx :
+                                      GrCustomStageUnitTest::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);
+    return SkNEW_ARGS(GrTextureDomainEffect, (textures[texIdx], domain));
+}
diff --git a/src/gpu/effects/GrTextureDomainEffect.h b/src/gpu/effects/GrTextureDomainEffect.h
new file mode 100644
index 0000000..872d57d
--- /dev/null
+++ b/src/gpu/effects/GrTextureDomainEffect.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 GrTextureDomainEffect_DEFINED
+#define GrTextureDomainEffect_DEFINED
+
+//#include "GrCustomStage.h"
+#include "GrSingleTextureEffect.h"
+#include "GrRect.h"
+
+class GrGLTextureDomainEffect;
+
+/**
+ * Limits a texture's lookup coordinates to a domain.
+ */
+class GrTextureDomainEffect : public GrSingleTextureEffect {
+
+public:
+    /** Uses default texture params (no filter, clamp) */
+    GrTextureDomainEffect(GrTexture*, const GrRect& domain);
+
+    GrTextureDomainEffect(GrTexture*, const GrRect& domain, const GrTextureParams& params);
+
+    virtual ~GrTextureDomainEffect();
+
+    static const char* Name() { return "TextureDomain"; }
+
+    typedef GrGLTextureDomainEffect GLProgramStage;
+
+    virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
+    virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
+
+    const GrRect& domain() const { return fTextureDomain; }
+
+protected:
+
+    GrRect fTextureDomain;
+
+private:
+    GR_DECLARE_CUSTOM_STAGE_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..92f5ad5
--- /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
+
+GR_DEFINE_RESOURCE_CACHE_DOMAIN(GrTextureStripAtlas, GetTextureStripAtlasDomain)
+
+
+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)
+    : fCacheID(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;
+    GrCacheData cacheData(fCacheID);
+    cacheData.fResourceDomain = GetTextureStripAtlasDomain();
+    fTexture = fDesc.fContext->findTexture(texDesc, cacheData, &params);
+    if (NULL == fTexture) {
+        fTexture = fDesc.fContext->createTexture(&params, texDesc, cacheData, NULL, 0);
+        // This is a new texture, so all of our cache info is now invalid
+        this->initLRU();
+        fKeyTable.rewind();
+    }
+    GrAssert(NULL != fTexture);
+    fTexture->ref();
+}
+
+void GrTextureStripAtlas::unlockTexture() {
+    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..a6833e0
--- /dev/null
+++ b/src/gpu/effects/GrTextureStripAtlas.h
@@ -0,0 +1,184 @@
+
+/*
+ * Copyright 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:
+    GR_DECLARE_RESOURCE_CACHE_DOMAIN(GetTextureStripAtlasDomain)
+
+    /**
+     * 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.
+     */
+    GrScalar getYOffset(int row) const { return SkIntToScalar(row) / fNumRows; }
+    GrScalar 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 uint64_t fCacheID;
+
+    // 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
new file mode 100644
index 0000000..52c480a
--- /dev/null
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLCaps.h"
+#include "GrGLContextInfo.h"
+#include "SkTSearch.h"
+
+GrGLCaps::GrGLCaps() {
+    this->reset();
+}
+
+void GrGLCaps::reset() {
+    fVerifiedColorConfigs.reset();
+    fStencilFormats.reset();
+    fStencilVerifiedColorConfigs.reset();
+    fMSFBOType = kNone_MSFBOType;
+    fMaxSampleCount = 0;
+    fCoverageAAType = kNone_CoverageAAType;
+    fMaxFragmentUniformVectors = 0;
+    fMaxVertexAttributes = 0;
+    fRGBA8RenderbufferSupport = false;
+    fBGRAFormatSupport = false;
+    fBGRAIsInternalFormat = false;
+    fTextureSwizzleSupport = false;
+    fUnpackRowLengthSupport = false;
+    fUnpackFlipYSupport = false;
+    fPackRowLengthSupport = false;
+    fPackFlipYSupport = false;
+    fTextureUsageSupport = false;
+    fTexStorageSupport = false;
+    fTextureRedSupport = false;
+    fImagingSupport = false;
+    fTwoFormatLimit = false;
+}
+
+GrGLCaps::GrGLCaps(const GrGLCaps& caps) {
+    *this = caps;
+}
+
+GrGLCaps& GrGLCaps::operator = (const GrGLCaps& caps) {
+    fVerifiedColorConfigs = caps.fVerifiedColorConfigs;
+    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;
+    fTextureSwizzleSupport = caps.fTextureSwizzleSupport;
+    fUnpackRowLengthSupport = caps.fUnpackRowLengthSupport;
+    fUnpackFlipYSupport = caps.fUnpackFlipYSupport;
+    fPackRowLengthSupport = caps.fPackRowLengthSupport;
+    fPackFlipYSupport = caps.fPackFlipYSupport;
+    fTextureUsageSupport = caps.fTextureUsageSupport;
+    fTexStorageSupport = caps.fTexStorageSupport;
+    fTextureRedSupport = caps.fTextureRedSupport;
+    fImagingSupport = caps.fImagingSupport;
+    fTwoFormatLimit = caps.fTwoFormatLimit;
+
+    return *this;
+}
+
+void GrGLCaps::init(const GrGLContextInfo& ctxInfo) {
+
+    this->reset();
+    if (!ctxInfo.isInitialized()) {
+        return;
+    }
+
+    const GrGLInterface* gli = ctxInfo.interface();
+    GrGLBinding binding = ctxInfo.binding();
+    GrGLVersion version = ctxInfo.version();
+
+    if (kES2_GrGLBinding == binding) {
+        GR_GL_GetIntegerv(gli, GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS,
+                          &fMaxFragmentUniformVectors);
+    } else {
+        GrAssert(kDesktop_GrGLBinding == binding);
+        GrGLint max;
+        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;
+    } else {
+        fRGBA8RenderbufferSupport = ctxInfo.hasExtension("GL_OES_rgb8_rgba8") ||
+                                    ctxInfo.hasExtension("GL_ARM_rgba8");
+    }
+
+    if (kDesktop_GrGLBinding == binding) {
+        fBGRAFormatSupport = version >= GR_GL_VER(1,2) ||
+                             ctxInfo.hasExtension("GL_EXT_bgra");
+    } else {
+        if (ctxInfo.hasExtension("GL_APPLE_texture_format_BGRA8888")) {
+            fBGRAFormatSupport = true;
+        } else if (ctxInfo.hasExtension("GL_EXT_texture_format_BGRA8888")) {
+            fBGRAFormatSupport = true;
+            fBGRAIsInternalFormat = true;
+        }
+        GrAssert(fBGRAFormatSupport ||
+                 kSkia8888_GrPixelConfig != kBGRA_8888_GrPixelConfig);
+    }
+
+    if (kDesktop_GrGLBinding == binding) {
+        fTextureSwizzleSupport = version >= GR_GL_VER(3,3) ||
+                                 ctxInfo.hasExtension("GL_ARB_texture_swizzle");
+    } else {
+        fTextureSwizzleSupport = false;
+    }
+
+    if (kDesktop_GrGLBinding == binding) {
+        fUnpackRowLengthSupport = true;
+        fUnpackFlipYSupport = false;
+        fPackRowLengthSupport = true;
+        fPackFlipYSupport = false;
+    } else {
+        fUnpackRowLengthSupport =ctxInfo.hasExtension("GL_EXT_unpack_subimage");
+        fUnpackFlipYSupport = ctxInfo.hasExtension("GL_CHROMIUM_flipy");
+        // no extension for pack row length
+        fPackRowLengthSupport = false;
+        fPackFlipYSupport =
+            ctxInfo.hasExtension("GL_ANGLE_pack_reverse_row_order");
+    }
+
+    fTextureUsageSupport = (kES2_GrGLBinding == binding) &&
+                            ctxInfo.hasExtension("GL_ANGLE_texture_usage");
+
+    // Tex storage is in desktop 4.2 and can be an extension to desktop or ES.
+    fTexStorageSupport = (kDesktop_GrGLBinding == binding &&
+                          version >= GR_GL_VER(4,2)) ||
+                         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;
+
+    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;
+    if (kDesktop_GrGLBinding != ctxInfo.binding()) {
+       if (ctxInfo.hasExtension("GL_CHROMIUM_framebuffer_multisample")) {
+           // chrome's extension is equivalent to the EXT msaa
+           // and fbo_blit extensions.
+           fMSFBOType = kDesktopEXT_MSFBOType;
+       } else if (ctxInfo.hasExtension("GL_APPLE_framebuffer_multisample")) {
+           fMSFBOType = kAppleES_MSFBOType;
+       }
+    } else {
+        if ((ctxInfo.version() >= GR_GL_VER(3,0)) ||
+            ctxInfo.hasExtension("GL_ARB_framebuffer_object")) {
+            fMSFBOType = GrGLCaps::kDesktopARB_MSFBOType;
+        } else if (ctxInfo.hasExtension("GL_EXT_framebuffer_multisample") &&
+                   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];
+    }
+}
+
+namespace {
+const GrGLuint kUnknownBitCount = GrGLStencilBuffer::kUnknownBitCount;
+}
+
+void GrGLCaps::initStencilFormats(const GrGLContextInfo& ctxInfo) {
+
+    // Build up list of legal stencil formats (though perhaps not supported on
+    // the particular gpu/driver) from most preferred to least.
+
+    // these consts are in order of most preferred to least preferred
+    // we don't bother with GL_STENCIL_INDEX1 or GL_DEPTH32F_STENCIL8
+
+    static const StencilFormat
+                  // internal Format      stencil bits      total bits        packed?
+        gS8    = {GR_GL_STENCIL_INDEX8,   8,                8,                false},
+        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},
+        gDS    = {GR_GL_DEPTH_STENCIL,    kUnknownBitCount, kUnknownBitCount, true };
+
+    if (kDesktop_GrGLBinding == ctxInfo.binding()) {
+        bool supportsPackedDS =
+            ctxInfo.version() >= GR_GL_VER(3,0) ||
+            ctxInfo.hasExtension("GL_EXT_packed_depth_stencil") ||
+            ctxInfo.hasExtension("GL_ARB_framebuffer_object");
+
+        // S1 thru S16 formats are in GL 3.0+, EXT_FBO, and ARB_FBO since we
+        // require FBO support we can expect these are legal formats and don't
+        // check. These also all support the unsized GL_STENCIL_INDEX.
+        fStencilFormats.push_back() = gS8;
+        fStencilFormats.push_back() = gS16;
+        if (supportsPackedDS) {
+            fStencilFormats.push_back() = gD24S8;
+        }
+        fStencilFormats.push_back() = gS4;
+        if (supportsPackedDS) {
+            fStencilFormats.push_back() = gDS;
+        }
+    } else {
+        // ES2 has STENCIL_INDEX8 without extensions but requires extensions
+        // for other formats.
+        // ES doesn't support using the unsized format.
+
+        fStencilFormats.push_back() = gS8;
+        //fStencilFormats.push_back() = gS16;
+        if (ctxInfo.hasExtension("GL_OES_packed_depth_stencil")) {
+            fStencilFormats.push_back() = gD24S8;
+        }
+        if (ctxInfo.hasExtension("GL_OES_stencil4")) {
+            fStencilFormats.push_back() = gS4;
+        }
+    }
+    GrAssert(0 == fStencilVerifiedColorConfigs.count());
+    fStencilVerifiedColorConfigs.push_back_n(fStencilFormats.count());
+}
+
+void GrGLCaps::markColorConfigAndStencilFormatAsVerified(
+                                    GrPixelConfig config,
+                                    const GrGLStencilBuffer::Format& format) {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+    return;
+#endif
+    GrAssert((unsigned)config < kGrPixelConfigCount);
+    GrAssert(fStencilFormats.count() == fStencilVerifiedColorConfigs.count());
+    int count = fStencilFormats.count();
+    // we expect a really small number of possible formats so linear search
+    // should be OK
+    GrAssert(count < 16);
+    for (int i = 0; i < count; ++i) {
+        if (format.fInternalFormat ==
+            fStencilFormats[i].fInternalFormat) {
+            fStencilVerifiedColorConfigs[i].markVerified(config);
+            return;
+        }
+    }
+    GrCrash("Why are we seeing a stencil format that "
+            "GrGLCaps doesn't know about.");
+}
+
+bool GrGLCaps::isColorConfigAndStencilFormatVerified(
+                                GrPixelConfig config,
+                                const GrGLStencilBuffer::Format& format) const {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+    return false;
+#endif
+    GrAssert((unsigned)config < kGrPixelConfigCount);
+    int count = fStencilFormats.count();
+    // we expect a really small number of possible formats so linear search
+    // should be OK
+    GrAssert(count < 16);
+    for (int i = 0; i < count; ++i) {
+        if (format.fInternalFormat ==
+            fStencilFormats[i].fInternalFormat) {
+            return fStencilVerifiedColorConfigs[i].isVerified(config);
+        }
+    }
+    GrCrash("Why are we seeing a stencil format that "
+            "GLCaps doesn't know about.");
+    return false;
+}
+
+void GrGLCaps::print() const {
+    for (int i = 0; i < fStencilFormats.count(); ++i) {
+        GrPrintf("Stencil Format %d, stencil bits: %02d, total bits: %02d\n",
+                 i,
+                 fStencilFormats[i].fStencilBits,
+                 fStencilFormats[i].fTotalBits);
+    }
+
+    GR_STATIC_ASSERT(0 == kNone_MSFBOType);
+    GR_STATIC_ASSERT(1 == kDesktopARB_MSFBOType);
+    GR_STATIC_ASSERT(2 == kDesktopEXT_MSFBOType);
+    GR_STATIC_ASSERT(3 == kAppleES_MSFBOType);
+    static const char* gMSFBOExtStr[] = {
+        "None",
+        "ARB",
+        "EXT",
+        "Apple",
+    };
+    GrPrintf("MSAA Type: %s\n", gMSFBOExtStr[fMSFBOType]);
+    GrPrintf("Max FS Uniform Vectors: %d\n", fMaxFragmentUniformVectors);
+    GrPrintf("Support RGBA8 Render Buffer: %s\n",
+             (fRGBA8RenderbufferSupport ? "YES": "NO"));
+    GrPrintf("BGRA is an internal format: %s\n",
+             (fBGRAIsInternalFormat ? "YES": "NO"));
+    GrPrintf("Support texture swizzle: %s\n",
+             (fTextureSwizzleSupport ? "YES": "NO"));
+    GrPrintf("Unpack Row length support: %s\n",
+             (fUnpackRowLengthSupport ? "YES": "NO"));
+    GrPrintf("Unpack Flip Y support: %s\n",
+             (fUnpackFlipYSupport ? "YES": "NO"));
+    GrPrintf("Pack Row length support: %s\n",
+             (fPackRowLengthSupport ? "YES": "NO"));
+    GrPrintf("Pack Flip Y support: %s\n",
+             (fPackFlipYSupport ? "YES": "NO"));
+    GrPrintf("Two Format Limit: %s\n", (fTwoFormatLimit ? "YES": "NO"));
+}
+
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
new file mode 100644
index 0000000..1cc50c2
--- /dev/null
+++ b/src/gpu/gl/GrGLCaps.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLCaps_DEFINED
+#define GrGLCaps_DEFINED
+
+#include "SkTArray.h"
+#include "SkTDArray.h"
+#include "GrGLStencilBuffer.h"
+
+class GrGLContextInfo;
+
+/**
+ * Stores some capabilities of a GL context. Most are determined by the GL
+ * version and the extensions string. It also tracks formats that have passed
+ * the FBO completeness test.
+ */
+class GrGLCaps {
+public:
+    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.
+     */
+    enum MSFBOType {
+        /**
+         * no support for MSAA FBOs
+         */
+        kNone_MSFBOType = 0,
+        /**
+         * GL3.0-style MSAA FBO (GL_ARB_framebuffer_object)
+         */
+        kDesktopARB_MSFBOType,
+        /**
+         * earlier GL_EXT_framebuffer* extensions
+         */
+        kDesktopEXT_MSFBOType,
+        /**
+         * GL_APPLE_framebuffer_multisample ES extension
+         */
+        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.
+     */
+    GrGLCaps();
+
+    GrGLCaps(const GrGLCaps& caps);
+
+    GrGLCaps& operator = (const GrGLCaps& caps);
+
+    /**
+     * Resets the caps such that nothing is supported.
+     */
+    void reset();
+
+    /**
+     * Initializes the GrGLCaps to the set of features supported in the current
+     * OpenGL context accessible via ctxInfo.
+     */
+    void init(const GrGLContextInfo& ctxInfo);
+
+    /**
+     * Call to note that a color config has been verified as a valid color
+     * attachment. This may save future calls to glCheckFramebufferStatus
+     * using isConfigVerifiedColorAttachment().
+     */
+    void markConfigAsValidColorAttachment(GrPixelConfig config) {
+        fVerifiedColorConfigs.markVerified(config);
+    }
+
+    /**
+     * Call to check whether a config has been verified as a valid color
+     * attachment.
+     */
+    bool isConfigVerifiedColorAttachment(GrPixelConfig config) const {
+        return fVerifiedColorConfigs.isVerified(config);
+    }
+
+    /**
+     * Call to note that a color config / stencil format pair passed
+     * FBO status check. We may skip calling glCheckFramebufferStatus for
+     * this combination in the future using
+     * isColorConfigAndStencilFormatVerified().
+     */
+    void markColorConfigAndStencilFormatAsVerified(
+                    GrPixelConfig config,
+                    const GrGLStencilBuffer::Format& format);
+
+    /**
+     * Call to check whether color config / stencil format pair has already
+     * passed FBO status check.
+     */
+    bool isColorConfigAndStencilFormatVerified(
+                    GrPixelConfig config,
+                    const GrGLStencilBuffer::Format& format) const;
+
+    /**
+     * Reports the type of MSAA FBO support.
+     */
+    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;
+
+    /**
+     * Gets an array of legal stencil formats. These formats are not guaranteed
+     * to be supported by the driver but are legal GLenum names given the GL
+     * version and extensions supported.
+     */
+    const SkTArray<StencilFormat, true>& stencilFormats() const {
+        return fStencilFormats;
+    }
+
+    /// 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; }
+
+    /// Is GL_BGRA supported
+    bool bgraFormatSupport() const { return fBGRAFormatSupport; }
+
+    /**
+     * Depending on the ES extensions present the BGRA external format may
+     * correspond either a BGRA or RGBA internalFormat. On desktop GL it is
+     * RGBA.
+     */
+    bool bgraIsInternalFormat() const { return fBGRAIsInternalFormat; }
+
+    /// GL_ARB_texture_swizzle support
+    bool textureSwizzleSupport() const { return fTextureSwizzleSupport; }
+
+    /// Is there support for GL_UNPACK_ROW_LENGTH
+    bool unpackRowLengthSupport() const { return fUnpackRowLengthSupport; }
+
+    /// Is there support for GL_UNPACK_FLIP_Y
+    bool unpackFlipYSupport() const { return fUnpackFlipYSupport; }
+
+    /// Is there support for GL_PACK_ROW_LENGTH
+    bool packRowLengthSupport() const { return fPackRowLengthSupport; }
+
+    /// Is there support for GL_PACK_REVERSE_ROW_ORDER
+    bool packFlipYSupport() const { return fPackFlipYSupport; }
+
+    /// Is there support for texture parameter GL_TEXTURE_USAGE
+    bool textureUsageSupport() const { return fTextureUsageSupport; }
+
+    /// 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; }
+
+    // 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
+     * performing glCheckFrameBufferStatus for the same config.
+     */
+    struct VerifiedColorConfigs {
+        VerifiedColorConfigs() {
+            this->reset();
+        }
+
+        void reset() {
+            for (int i = 0; i < kNumUints; ++i) {
+                fVerifiedColorConfigs[i] = 0;
+            }
+        }
+
+        static const int kNumUints = (kGrPixelConfigCount  + 31) / 32;
+        uint32_t fVerifiedColorConfigs[kNumUints];
+
+        void markVerified(GrPixelConfig config) {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+                return;
+#endif
+            int u32Idx = config / 32;
+            int bitIdx = config % 32;
+            fVerifiedColorConfigs[u32Idx] |= 1 << bitIdx;
+        }
+
+        bool isVerified(GrPixelConfig config) const {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+            return false;
+#endif
+            int u32Idx = config / 32;
+            int bitIdx = config % 32;
+            return SkToBool(fVerifiedColorConfigs[u32Idx] & (1 << bitIdx));
+        }
+    };
+
+    void initFSAASupport(const GrGLContextInfo& ctxInfo);
+    void initStencilFormats(const GrGLContextInfo& ctxInfo);
+
+    // tracks configs that have been verified to pass the FBO completeness when
+    // used as a color attachment
+    VerifiedColorConfigs fVerifiedColorConfigs;
+
+    SkTArray<StencilFormat, true> fStencilFormats;
+    // tracks configs that have been verified to pass the FBO completeness when
+    // used as a color attachment when a particular stencil format is used
+    // as a stencil attachment.
+    SkTArray<VerifiedColorConfigs, true> fStencilVerifiedColorConfigs;
+
+    int fMaxFragmentUniformVectors;
+    int fMaxVertexAttributes;
+
+    MSFBOType fMSFBOType;
+    int fMaxSampleCount;
+    CoverageAAType fCoverageAAType;
+    SkTDArray<MSAACoverageMode> fMSAACoverageModes;
+
+    bool fRGBA8RenderbufferSupport : 1;
+    bool fBGRAFormatSupport : 1;
+    bool fBGRAIsInternalFormat : 1;
+    bool fTextureSwizzleSupport : 1;
+    bool fUnpackRowLengthSupport : 1;
+    bool fUnpackFlipYSupport : 1;
+    bool fPackRowLengthSupport : 1;
+    bool fPackFlipYSupport : 1;
+    bool fTextureUsageSupport : 1;
+    bool fTexStorageSupport : 1;
+    bool fTextureRedSupport : 1;
+    bool fImagingSupport  : 1;
+    bool fTwoFormatLimit : 1;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLContextInfo.cpp b/src/gpu/gl/GrGLContextInfo.cpp
new file mode 100644
index 0000000..9802e65
--- /dev/null
+++ b/src/gpu/gl/GrGLContextInfo.cpp
@@ -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.
+ */
+
+#include "GrGLContextInfo.h"
+
+GrGLContextInfo::~GrGLContextInfo() {
+    GrSafeUnref(fInterface);
+}
+
+GrGLContextInfo::GrGLContextInfo() {
+    this->reset();
+}
+
+GrGLContextInfo::GrGLContextInfo(const GrGLInterface* interface) {
+    fInterface = NULL;
+    this->initialize(interface);
+}
+
+GrGLContextInfo::GrGLContextInfo(const GrGLContextInfo& ctx) {
+    fInterface = NULL;
+    *this = ctx;
+}
+
+GrGLContextInfo& GrGLContextInfo::operator = (const GrGLContextInfo& ctx) {
+    GrSafeAssign(fInterface, ctx.fInterface);
+    fBindingInUse = ctx.fBindingInUse;
+    fGLVersion = ctx.fGLVersion;
+    fGLSLGeneration = ctx.fGLSLGeneration;
+    fExtensionString = ctx.fExtensionString;
+    fGLCaps = ctx.fGLCaps;
+    return *this;
+}
+
+void GrGLContextInfo::reset() {
+    GrSafeSetNull(fInterface);
+    fBindingInUse = kNone_GrGLBinding;
+    fGLVersion = GR_GL_VER(0, 0);
+    fGLSLGeneration = static_cast<GrGLSLGeneration>(0);
+    fExtensionString = "";
+    fGLCaps.reset();
+}
+
+bool GrGLContextInfo::initialize(const GrGLInterface* interface) {
+    this->reset();
+    // We haven't validated the GrGLInterface yet, so check for GetString
+    // function pointer
+    if (NULL != interface->fGetString) {
+
+        const GrGLubyte* verUByte;
+        GR_GL_CALL_RET(interface, verUByte, GetString(GR_GL_VERSION));
+        const char* ver = reinterpret_cast<const char*>(verUByte);
+        GrGLBinding binding = GrGLGetBindingInUseFromString(ver);
+
+        if (interface->validate(binding)) {
+
+            fInterface = interface;
+            interface->ref();
+
+            fBindingInUse = binding;
+
+            fGLVersion = GrGLGetVersionFromString(ver);
+
+            fGLSLGeneration = GrGetGLSLGeneration(fBindingInUse,
+                                                  this->interface());
+
+            const GrGLubyte* ext;
+            GR_GL_CALL_RET(interface, ext, GetString(GR_GL_EXTENSIONS));
+            fExtensionString = reinterpret_cast<const char*>(ext);
+
+            fGLCaps.init(*this);
+            return true;
+        }
+    }
+    return false;
+}
+
+bool GrGLContextInfo::isInitialized() const {
+    return kNone_GrGLBinding != fBindingInUse;
+}
+
diff --git a/src/gpu/gl/GrGLContextInfo.h b/src/gpu/gl/GrGLContextInfo.h
new file mode 100644
index 0000000..44fc985
--- /dev/null
+++ b/src/gpu/gl/GrGLContextInfo.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 GrGLContextInfo_DEFINED
+#define GrGLContextInfo_DEFINED
+
+#include "gl/GrGLInterface.h"
+#include "GrGLCaps.h"
+#include "GrGLSL.h"
+#include "GrGLUtil.h"
+
+#include "SkString.h"
+
+/**
+ * Encapsulates information about an OpenGL context including the GrGLInterface
+ * used to make GL calls, the OpenGL version, the GrGLBinding type of the
+ * context, and GLSL version.
+ */
+class GrGLContextInfo {
+public:
+
+    /**
+     * Default constructor, creates an uninitialized GrGLContextInfo
+     */
+    GrGLContextInfo();
+
+    /**
+     * Creates a GrGLContextInfo from a GrGLInterface and the currently
+     * bound OpenGL context accesible by the GrGLInterface.
+     */
+    explicit GrGLContextInfo(const GrGLInterface* interface);
+
+    /**
+     * Copies a GrGLContextInfo
+     */
+    GrGLContextInfo(const GrGLContextInfo& ctx);
+
+    ~GrGLContextInfo();
+
+    /**
+     * Copies a GrGLContextInfo
+     */
+    GrGLContextInfo& operator = (const GrGLContextInfo& ctx);
+
+    /**
+     * Initializes a GrGLContextInfo from a GrGLInterface and the currently
+     * bound OpenGL context accessible by the GrGLInterface.
+     */
+    bool initialize(const GrGLInterface* interface);
+    bool isInitialized() const;
+
+    const GrGLInterface* interface() const { return fInterface; }
+    GrGLBinding binding() const { return fBindingInUse; }
+    GrGLVersion version() const { return fGLVersion; }
+    GrGLSLGeneration glslGeneration() const { return fGLSLGeneration; }
+    const GrGLCaps& caps() const { return fGLCaps; }
+    GrGLCaps& caps() { return fGLCaps; }
+
+    /**
+     * Checks for extension support using a cached copy of the GL_EXTENSIONS
+     * string.
+     */
+    bool hasExtension(const char* ext) const {
+        if (!this->isInitialized()) {
+            return false;
+        }
+        return GrGLHasExtensionFromString(ext, fExtensionString.c_str());
+    }
+
+private:
+    void reset();
+
+    const GrGLInterface* fInterface;
+    GrGLBinding          fBindingInUse;
+    GrGLVersion          fGLVersion;
+    GrGLSLGeneration     fGLSLGeneration;
+    SkString             fExtensionString;
+    GrGLCaps             fGLCaps;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLCreateNativeInterface_none.cpp b/src/gpu/gl/GrGLCreateNativeInterface_none.cpp
new file mode 100644
index 0000000..e0c72c5
--- /dev/null
+++ b/src/gpu/gl/GrGLCreateNativeInterface_none.cpp
@@ -0,0 +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 "gl/GrGLInterface.h"
+
+const GrGLInterface* GrGLCreateNativeInterface() {
+    return NULL;
+}
diff --git a/src/gpu/gl/GrGLCreateNullInterface.cpp b/src/gpu/gl/GrGLCreateNullInterface.cpp
new file mode 100644
index 0000000..d0e2bb6
--- /dev/null
+++ b/src/gpu/gl/GrGLCreateNullInterface.cpp
@@ -0,0 +1,513 @@
+/*
+ * Copyright 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 "GrTDArray.h"
+#include "GrGLDefines.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) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBeginQuery(GrGLenum target, GrGLuint id) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindAttribLocation(GrGLuint program, GrGLuint index, const char* name) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindTexture(GrGLenum target, GrGLuint texture) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBlendColor(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFragDataLocation(GrGLuint program, GrGLuint colorNumber, const GrGLchar* name) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBlendFunc(GrGLenum sfactor, GrGLenum dfactor) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, GrGLenum usage) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferSubData(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, const GrGLvoid* data) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLClear(GrGLbitfield mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLClearColor(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLClearStencil(GrGLint s) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLColorMask(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLCompileShader(GrGLuint shader) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLCompressedTexImage2D(GrGLenum target, GrGLint level, GrGLenum internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid* data) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLCullFace(GrGLenum mode) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDepthMask(GrGLboolean flag) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDisable(GrGLenum cap) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDisableVertexAttribArray(GrGLuint index) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawArrays(GrGLenum mode, GrGLint first, GrGLsizei count) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawBuffer(GrGLenum mode) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawBuffers(GrGLsizei n, const GrGLenum* bufs) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawElements(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid* indices) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLEnable(GrGLenum cap) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLEnableVertexAttribArray(GrGLuint index) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLEndQuery(GrGLenum target) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFinish() {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFlush() {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFrontFace(GrGLenum mode) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLLineWidth(GrGLfloat width) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLLinkProgram(GrGLuint program) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLPixelStorei(GrGLenum pname, GrGLint param) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLQueryCounter(GrGLuint id, GrGLenum target) {}
+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) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLShaderSource(GrGLuint shader, GrGLsizei count, const char** str, const GrGLint* length) {}
+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) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilMaskSeparate(GrGLenum face, GrGLuint mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilOp(GrGLenum fail, GrGLenum zfail, GrGLenum zpass) {}
+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) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1i(GrGLint location, GrGLint v0) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2f(GrGLint location, GrGLfloat v0, GrGLfloat v1) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2i(GrGLint location, GrGLint v0, GrGLint v1) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3f(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3i(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4f(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2, GrGLfloat v3) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4i(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2, GrGLint v3) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniformMatrix2fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniformMatrix3fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniformMatrix4fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUseProgram(GrGLuint program) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLVertexAttrib4fv(GrGLuint indx, const GrGLfloat* values) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLVertexAttribPointer(GrGLuint indx, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, const GrGLvoid* ptr) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLViewport(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFramebuffer(GrGLenum target, GrGLuint framebuffer) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindRenderbuffer(GrGLenum target, GrGLuint renderbuffer) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteFramebuffers(GrGLsizei n, const GrGLuint *framebuffers) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteRenderbuffers(GrGLsizei n, const GrGLuint *renderbuffers) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferRenderbuffer(GrGLenum target, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetFramebufferAttachmentParameteriv(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetRenderbufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLRenderbufferStorage(GrGLenum target, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLRenderbufferStorageMultisample(GrGLenum target, GrGLsizei samples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBlitFramebuffer(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 nullGLResolveMultisampleFramebuffer() {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFragDataLocationIndexed(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name) {}
+
+GrGLenum GR_GL_FUNCTION_TYPE nullGLCheckFramebufferStatus(GrGLenum target) {
+    return GR_GL_FRAMEBUFFER_COMPLETE;
+}
+
+GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateProgram() {
+    static int gCurrID = 0;
+    return ++gCurrID;
+}
+
+GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateShader(GrGLenum type) {
+    static int gCurrID = 0;
+    return ++gCurrID;
+}
+
+// same delete used for shaders and programs
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDelete(GrGLuint program) {
+}
+
+// same function used for all glGen*(GLsize i, GLuint*) functions
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGenIds(GrGLsizei n, GrGLuint* ids) {
+    static int gCurrID = 0;
+    for (int i = 0; i < n; ++i) {
+        ids[i] = ++gCurrID;
+    }
+}
+// same delete function for all glDelete*(GLsize i, const GLuint*) except buffers
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteIds(GrGLsizei n, const GrGLuint* ids) {}
+
+// In debug builds we do asserts that ensure we agree with GL about when a buffer
+// is mapped.
+static GrTDArray<GrGLuint> gMappedBuffers;
+static GrGLuint gCurrArrayBuffer;
+static GrGLuint gCurrElementArrayBuffer;
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindBuffer(GrGLenum target, GrGLuint buffer) {
+    switch (target) {
+    case GR_GL_ARRAY_BUFFER:
+        gCurrArrayBuffer = buffer;
+        break;
+    case GR_GL_ELEMENT_ARRAY_BUFFER:
+        gCurrElementArrayBuffer = buffer;
+        break;
+    }
+}
+
+// deleting a bound buffer has the side effect of binding 0
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteBuffers(GrGLsizei n, const GrGLuint* ids) {
+    for (int i = 0; i < n; ++i) {
+        if (ids[i] == gCurrArrayBuffer) {
+            gCurrArrayBuffer = 0;
+        }
+        if (ids[i] == gCurrElementArrayBuffer) {
+            gCurrElementArrayBuffer = 0;
+        }
+        for (int j = 0; j < gMappedBuffers.count(); ++j) {
+            if (gMappedBuffers[j] == ids[i]) {
+                gMappedBuffers.remove(j);
+                // don't break b/c we didn't check for dupes on insert
+                --j;
+            }
+        }
+    }
+}
+
+GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBuffer(GrGLenum target, GrGLenum access) {
+    // We just reserve 32MB of RAM for all locks and hope its big enough
+    static SkAutoMalloc gBufferData(32 * (1 << 20));
+    GrGLuint buf = 0;
+    switch (target) {
+        case GR_GL_ARRAY_BUFFER:
+            buf = gCurrArrayBuffer;
+            break;
+        case GR_GL_ELEMENT_ARRAY_BUFFER:
+            buf = gCurrElementArrayBuffer;
+            break;
+    }
+    if (buf) {
+        *gMappedBuffers.append() = buf;
+    }
+    return gBufferData.get();
+}
+
+GrGLboolean GR_GL_FUNCTION_TYPE nullGLUnmapBuffer(GrGLenum target) {
+    GrGLuint buf = 0;
+    switch (target) {
+    case GR_GL_ARRAY_BUFFER:
+        buf = gCurrArrayBuffer;
+        break;
+    case GR_GL_ELEMENT_ARRAY_BUFFER:
+        buf = gCurrElementArrayBuffer;
+        break;
+    }
+    if (buf) {
+        for (int i = 0; i < gMappedBuffers.count(); ++i) {
+            if (gMappedBuffers[i] == buf) {
+                gMappedBuffers.remove(i);
+                // don't break b/c we didn't check for dupes on insert
+                --i;
+            }
+        }
+    }
+    return GR_GL_TRUE;
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {
+    switch (pname) {
+        case GR_GL_BUFFER_MAPPED: {
+            *params = GR_GL_FALSE;
+            GrGLuint buf = 0;
+            switch (target) {
+                case GR_GL_ARRAY_BUFFER:
+                    buf = gCurrArrayBuffer;
+                    break;
+                case GR_GL_ELEMENT_ARRAY_BUFFER:
+                    buf = gCurrElementArrayBuffer;
+                    break;
+            }
+            if (buf) {
+                for (int i = 0; i < gMappedBuffers.count(); ++i) {
+                    if (gMappedBuffers[i] == buf) {
+                        *params = GR_GL_TRUE;
+                        break;
+                    }
+                }
+            }
+            break; }
+        default:
+            GrCrash("Unexpected pname to GetBufferParamateriv");
+            break;
+    }
+};
+
+GrGLenum GR_GL_FUNCTION_TYPE nullGLGetError() {
+    return GR_GL_NO_ERROR;
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetIntegerv(GrGLenum pname, GrGLint* params) {
+    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_FRAGMENT_UNIFORM_VECTORS:
+            *params = 16;
+            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 = 16;
+            break;
+        default:
+            GrCrash("Unexpected pname to GetIntegerv");
+    }
+}
+// used for both the program and shader info logs
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetInfoLog(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 nullGLGetShaderOrProgramiv(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 nullGLGetQueryiv(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 nullGLGetQueryObjecti64v(GrGLuint id, GrGLenum pname, GrGLint64 *params) {
+    query_result(id, pname, params);
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjectiv(GrGLuint id, GrGLenum pname, GrGLint *params) {
+    query_result(id, pname, params);
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjectui64v(GrGLuint id, GrGLenum pname, GrGLuint64 *params) {
+    query_result(id, pname, params);
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjectuiv(GrGLuint id, GrGLenum pname, GrGLuint *params) {
+    query_result(id, pname, params);
+}
+
+const GrGLubyte* GR_GL_FUNCTION_TYPE nullGLGetString(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 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;
+    }
+}
+
+// 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 nullGLGetTexLevelParameteriv(GrGLenum target, GrGLint level, GrGLenum pname, GrGLint* params) {
+    GrCrash("Should never query texture parameters.");
+}
+
+GrGLint GR_GL_FUNCTION_TYPE nullGLGetUniformLocation(GrGLuint program, const char* name) {
+    static int gUniLocation = 0;
+    return ++gUniLocation;
+}
+
+} // end anonymous namespace
+
+const GrGLInterface* GrGLCreateNullInterface() {
+    // The gl functions are not context-specific so we create one global
+    // interface
+    static SkAutoTUnref<GrGLInterface> glInterface;
+    if (!glInterface.get()) {
+        GrGLInterface* interface = SkNEW(GrGLInterface);
+        glInterface.reset(interface);
+        interface->fBindingsExported = kDesktop_GrGLBinding;
+        interface->fActiveTexture = nullGLActiveTexture;
+        interface->fAttachShader = nullGLAttachShader;
+        interface->fBeginQuery = nullGLBeginQuery;
+        interface->fBindAttribLocation = nullGLBindAttribLocation;
+        interface->fBindBuffer = nullGLBindBuffer;
+        interface->fBindFragDataLocation = nullGLBindFragDataLocation;
+        interface->fBindTexture = nullGLBindTexture;
+        interface->fBlendColor = nullGLBlendColor;
+        interface->fBlendFunc = nullGLBlendFunc;
+        interface->fBufferData = nullGLBufferData;
+        interface->fBufferSubData = nullGLBufferSubData;
+        interface->fClear = nullGLClear;
+        interface->fClearColor = nullGLClearColor;
+        interface->fClearStencil = nullGLClearStencil;
+        interface->fColorMask = nullGLColorMask;
+        interface->fCompileShader = nullGLCompileShader;
+        interface->fCompressedTexImage2D = nullGLCompressedTexImage2D;
+        interface->fCreateProgram = nullGLCreateProgram;
+        interface->fCreateShader = nullGLCreateShader;
+        interface->fCullFace = nullGLCullFace;
+        interface->fDeleteBuffers = nullGLDeleteBuffers;
+        interface->fDeleteProgram = nullGLDelete;
+        interface->fDeleteQueries = nullGLDeleteIds;
+        interface->fDeleteShader = nullGLDelete;
+        interface->fDeleteTextures = nullGLDeleteIds;
+        interface->fDepthMask = nullGLDepthMask;
+        interface->fDisable = nullGLDisable;
+        interface->fDisableVertexAttribArray = nullGLDisableVertexAttribArray;
+        interface->fDrawArrays = nullGLDrawArrays;
+        interface->fDrawBuffer = nullGLDrawBuffer;
+        interface->fDrawBuffers = nullGLDrawBuffers;
+        interface->fDrawElements = nullGLDrawElements;
+        interface->fEnable = nullGLEnable;
+        interface->fEnableVertexAttribArray = nullGLEnableVertexAttribArray;
+        interface->fEndQuery = nullGLEndQuery;
+        interface->fFinish = nullGLFinish;
+        interface->fFlush = nullGLFlush;
+        interface->fFrontFace = nullGLFrontFace;
+        interface->fGenBuffers = nullGLGenIds;
+        interface->fGenQueries = nullGLGenIds;
+        interface->fGenTextures = nullGLGenIds;
+        interface->fGetBufferParameteriv = nullGLGetBufferParameteriv;
+        interface->fGetError = nullGLGetError;
+        interface->fGetIntegerv = nullGLGetIntegerv;
+        interface->fGetQueryObjecti64v = nullGLGetQueryObjecti64v;
+        interface->fGetQueryObjectiv = nullGLGetQueryObjectiv;
+        interface->fGetQueryObjectui64v = nullGLGetQueryObjectui64v;
+        interface->fGetQueryObjectuiv = nullGLGetQueryObjectuiv;
+        interface->fGetQueryiv = nullGLGetQueryiv;
+        interface->fGetProgramInfoLog = nullGLGetInfoLog;
+        interface->fGetProgramiv = nullGLGetShaderOrProgramiv;
+        interface->fGetShaderInfoLog = nullGLGetInfoLog;
+        interface->fGetShaderiv = nullGLGetShaderOrProgramiv;
+        interface->fGetString = nullGLGetString;
+        interface->fGetTexLevelParameteriv = nullGLGetTexLevelParameteriv;
+        interface->fGetUniformLocation = nullGLGetUniformLocation;
+        interface->fLineWidth = nullGLLineWidth;
+        interface->fLinkProgram = nullGLLinkProgram;
+        interface->fPixelStorei = nullGLPixelStorei;
+        interface->fQueryCounter = nullGLQueryCounter;
+        interface->fReadBuffer = nullGLReadBuffer;
+        interface->fReadPixels = nullGLReadPixels;
+        interface->fScissor = nullGLScissor;
+        interface->fShaderSource = nullGLShaderSource;
+        interface->fStencilFunc = nullGLStencilFunc;
+        interface->fStencilFuncSeparate = nullGLStencilFuncSeparate;
+        interface->fStencilMask = nullGLStencilMask;
+        interface->fStencilMaskSeparate = nullGLStencilMaskSeparate;
+        interface->fStencilOp = nullGLStencilOp;
+        interface->fStencilOpSeparate = nullGLStencilOpSeparate;
+        interface->fTexImage2D = nullGLTexImage2D;
+        interface->fTexParameteri = nullGLTexParameteri;
+        interface->fTexParameteriv = nullGLTexParameteriv;
+        interface->fTexSubImage2D = nullGLTexSubImage2D;
+        interface->fTexStorage2D = nullGLTexStorage2D;
+        interface->fUniform1f = nullGLUniform1f;
+        interface->fUniform1i = nullGLUniform1i;
+        interface->fUniform1fv = nullGLUniform1fv;
+        interface->fUniform1iv = nullGLUniform1iv;
+        interface->fUniform2f = nullGLUniform2f;
+        interface->fUniform2i = nullGLUniform2i;
+        interface->fUniform2fv = nullGLUniform2fv;
+        interface->fUniform2iv = nullGLUniform2iv;
+        interface->fUniform3f = nullGLUniform3f;
+        interface->fUniform3i = nullGLUniform3i;
+        interface->fUniform3fv = nullGLUniform3fv;
+        interface->fUniform3iv = nullGLUniform3iv;
+        interface->fUniform4f = nullGLUniform4f;
+        interface->fUniform4i = nullGLUniform4i;
+        interface->fUniform4fv = nullGLUniform4fv;
+        interface->fUniform4iv = nullGLUniform4iv;
+        interface->fUniformMatrix2fv = nullGLUniformMatrix2fv;
+        interface->fUniformMatrix3fv = nullGLUniformMatrix3fv;
+        interface->fUniformMatrix4fv = nullGLUniformMatrix4fv;
+        interface->fUseProgram = nullGLUseProgram;
+        interface->fVertexAttrib4fv = nullGLVertexAttrib4fv;
+        interface->fVertexAttribPointer = nullGLVertexAttribPointer;
+        interface->fViewport = nullGLViewport;
+        interface->fBindFramebuffer = nullGLBindFramebuffer;
+        interface->fBindRenderbuffer = nullGLBindRenderbuffer;
+        interface->fCheckFramebufferStatus = nullGLCheckFramebufferStatus;
+        interface->fDeleteFramebuffers = nullGLDeleteFramebuffers;
+        interface->fDeleteRenderbuffers = nullGLDeleteRenderbuffers;
+        interface->fFramebufferRenderbuffer = nullGLFramebufferRenderbuffer;
+        interface->fFramebufferTexture2D = nullGLFramebufferTexture2D;
+        interface->fGenFramebuffers = nullGLGenIds;
+        interface->fGenRenderbuffers = nullGLGenIds;
+        interface->fGetFramebufferAttachmentParameteriv = nullGLGetFramebufferAttachmentParameteriv;
+        interface->fGetRenderbufferParameteriv = nullGLGetRenderbufferParameteriv;
+        interface->fRenderbufferStorage = nullGLRenderbufferStorage;
+        interface->fRenderbufferStorageMultisample = nullGLRenderbufferStorageMultisample;
+        interface->fBlitFramebuffer = nullGLBlitFramebuffer;
+        interface->fResolveMultisampleFramebuffer = nullGLResolveMultisampleFramebuffer;
+        interface->fMapBuffer = nullGLMapBuffer;
+        interface->fUnmapBuffer = nullGLUnmapBuffer;
+        interface->fBindFragDataLocationIndexed = nullGLBindFragDataLocationIndexed;
+    }
+    glInterface.get()->ref();
+    return glInterface.get();
+}
diff --git a/src/gpu/gl/GrGLDefaultInterface_native.cpp b/src/gpu/gl/GrGLDefaultInterface_native.cpp
new file mode 100644
index 0000000..e695f15
--- /dev/null
+++ b/src/gpu/gl/GrGLDefaultInterface_native.cpp
@@ -0,0 +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 "gl/GrGLInterface.h"
+
+const GrGLInterface* GrGLDefaultInterface() {
+    return GrGLCreateNativeInterface();
+}
diff --git a/src/gpu/gl/GrGLDefaultInterface_none.cpp b/src/gpu/gl/GrGLDefaultInterface_none.cpp
new file mode 100644
index 0000000..84c7f7c
--- /dev/null
+++ b/src/gpu/gl/GrGLDefaultInterface_none.cpp
@@ -0,0 +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 "gl/GrGLInterface.h"
+
+const GrGLInterface* GrGLDefaultInterface() {
+    return NULL;
+}
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/GrGLIRect.h b/src/gpu/gl/GrGLIRect.h
new file mode 100644
index 0000000..038520d
--- /dev/null
+++ b/src/gpu/gl/GrGLIRect.h
@@ -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.
+ */
+
+
+
+#ifndef GrGLIRect_DEFINED
+#define GrGLIRect_DEFINED
+
+#include "gl/GrGLInterface.h"
+#include "GrGLUtil.h"
+
+/**
+ * Helper struct for dealing with the fact that Ganesh and GL use different
+ * window coordinate systems (top-down vs bottom-up)
+ */
+struct GrGLIRect {
+    GrGLint   fLeft;
+    GrGLint   fBottom;
+    GrGLsizei fWidth;
+    GrGLsizei fHeight;
+
+    void pushToGLViewport(const GrGLInterface* gl) const {
+        GR_GL_CALL(gl, Viewport(fLeft, fBottom, fWidth, fHeight));
+    }
+
+    void pushToGLScissor(const GrGLInterface* gl) const {
+        GR_GL_CALL(gl, Scissor(fLeft, fBottom, fWidth, fHeight));
+    }
+
+    void setFromGLViewport(const GrGLInterface* gl) {
+        GR_STATIC_ASSERT(sizeof(GrGLIRect) == 4*sizeof(GrGLint));
+        GR_GL_GetIntegerv(gl, GR_GL_VIEWPORT, (GrGLint*) this);
+    }
+
+    // sometimes we have a GrIRect from the client that we
+    // want to simultaneously make relative to GL's viewport
+    // and convert from top-down to bottom-up.
+    void setRelativeTo(const GrGLIRect& glRect,
+                       int leftOffset,
+                       int topOffset,
+                       int width,
+                       int height) {
+        fLeft = glRect.fLeft + leftOffset;
+        fWidth = width;
+        fBottom = glRect.fBottom + (glRect.fHeight - topOffset - height);
+        fHeight = height;
+
+        GrAssert(fLeft >= 0);
+        GrAssert(fWidth >= 0);
+        GrAssert(fBottom >= 0);
+        GrAssert(fHeight >= 0);
+    }
+
+    bool contains(const GrGLIRect& glRect) const {
+        return fLeft <= glRect.fLeft &&
+               fBottom <= glRect.fBottom &&
+               fLeft + fWidth >=  glRect.fLeft + glRect.fWidth &&
+               fBottom + fHeight >=  glRect.fBottom + glRect.fHeight;
+    }
+
+    void invalidate() {fLeft = fWidth = fBottom = fHeight = -1;}
+
+    bool operator ==(const GrGLIRect& glRect) const {
+        return 0 == memcmp(this, &glRect, sizeof(GrGLIRect));
+    }
+
+    bool operator !=(const GrGLIRect& glRect) const {return !(*this == glRect);}
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLIndexBuffer.cpp b/src/gpu/gl/GrGLIndexBuffer.cpp
new file mode 100644
index 0000000..66ee095
--- /dev/null
+++ b/src/gpu/gl/GrGLIndexBuffer.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 "GrGLIndexBuffer.h"
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+GrGLIndexBuffer::GrGLIndexBuffer(GrGpuGL* gpu,
+                                 GrGLuint id,
+                                 size_t sizeInBytes,
+                                 bool dynamic)
+    : INHERITED(gpu, sizeInBytes, dynamic)
+    , fBufferID(id)
+    , fLockPtr(NULL) {
+
+}
+
+void GrGLIndexBuffer::onRelease() {
+    // make sure we've not been abandoned
+    if (fBufferID) {
+        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 {
+    GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, fBufferID));
+    GPUGL->notifyIndexBufferBind(this);
+}
+
+GrGLuint GrGLIndexBuffer::bufferID() const {
+    return fBufferID;
+}
+
+void* GrGLIndexBuffer::lock() {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    if (this->getGpu()->getCaps().bufferLockSupport()) {
+        this->bind();
+        // Let driver know it can discard the old data
+        GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+                           this->sizeInBytes(),
+                           NULL,
+                           this->dynamic() ? GR_GL_DYNAMIC_DRAW :
+                                             GR_GL_STATIC_DRAW));
+        GR_GL_CALL_RET(GPUGL->glInterface(),
+                       fLockPtr,
+                       MapBuffer(GR_GL_ELEMENT_ARRAY_BUFFER,
+                                 GR_GL_WRITE_ONLY));
+
+        return fLockPtr;
+    }
+    return NULL;
+}
+
+void* GrGLIndexBuffer::lockPtr() const {
+    return fLockPtr;
+}
+
+void GrGLIndexBuffer::unlock() {
+    GrAssert(fBufferID);
+    GrAssert(isLocked());
+    GrAssert(this->getGpu()->getCaps().bufferLockSupport());
+
+    this->bind();
+    GL_CALL(UnmapBuffer(GR_GL_ELEMENT_ARRAY_BUFFER));
+    fLockPtr = NULL;
+}
+
+bool GrGLIndexBuffer::isLocked() const {
+    // this check causes a lot of noise in the gl log
+#if 0
+    if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) {
+        this->bind();
+        GrGLint mapped;
+        GL_CALL(GetBufferParameteriv(GR_GL_ELEMENT_ARRAY_BUFFER,
+                                     GR_GL_BUFFER_MAPPED, &mapped));
+        GrAssert(!!mapped == !!fLockPtr);
+    }
+#endif
+    return NULL != fLockPtr;
+}
+
+bool GrGLIndexBuffer::updateData(const void* src, size_t srcSizeInBytes) {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    if (srcSizeInBytes > this->sizeInBytes()) {
+        return false;
+    }
+    this->bind();
+    GrGLenum usage = dynamic() ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW;
+
+#if GR_GL_USE_BUFFER_DATA_NULL_HINT
+    if (this->sizeInBytes() == srcSizeInBytes) {
+        GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+                            srcSizeInBytes, src, usage));
+    } else {
+        // Before we call glBufferSubData we give the driver a hint using
+        // glBufferData with NULL. This makes the old buffer contents
+        // inaccessible to future draws. The GPU may still be processing
+        // 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_ELEMENT_ARRAY_BUFFER,
+                           this->sizeInBytes(), NULL, usage));
+        GL_CALL(BufferSubData(GR_GL_ELEMENT_ARRAY_BUFFER,
+                              0, srcSizeInBytes, src));
+    }
+#else
+    // 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,
+                       srcSizeInBytes, src, usage));
+#endif
+    return true;
+}
+
diff --git a/src/gpu/gl/GrGLIndexBuffer.h b/src/gpu/gl/GrGLIndexBuffer.h
new file mode 100644
index 0000000..e282001
--- /dev/null
+++ b/src/gpu/gl/GrGLIndexBuffer.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 GrGLIndexBuffer_DEFINED
+#define GrGLIndexBuffer_DEFINED
+
+#include "GrIndexBuffer.h"
+#include "gl/GrGLInterface.h"
+
+class GrGpuGL;
+
+class GrGLIndexBuffer : public GrIndexBuffer {
+
+public:
+
+    virtual ~GrGLIndexBuffer() { this->release(); }
+
+    GrGLuint bufferID() const;
+
+    // overrides of GrIndexBuffer
+    virtual void* lock();
+    virtual void* lockPtr() const;
+    virtual void unlock();
+    virtual bool isLocked() const;
+    virtual bool updateData(const void* src, size_t srcSizeInBytes);
+
+protected:
+    GrGLIndexBuffer(GrGpuGL* gpu,
+                    GrGLuint id,
+                    size_t sizeInBytes,
+                    bool dynamic);
+
+    // overrides of GrResource
+    virtual void onAbandon() SK_OVERRIDE;
+    virtual void onRelease() SK_OVERRIDE;
+
+private:
+    void bind() const;
+
+    GrGLuint     fBufferID;
+    void*        fLockPtr;
+
+    friend class GrGpuGL;
+
+    typedef GrIndexBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
new file mode 100644
index 0000000..297e1fc
--- /dev/null
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright 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 <stdio.h>
+
+SK_DEFINE_INST_COUNT(GrGLInterface)
+
+#if GR_GL_PER_GL_FUNC_CALLBACK
+namespace {
+void GrGLDefaultInterfaceCallback(const GrGLInterface*) {}
+}
+#endif
+
+GrGLInterface::GrGLInterface() {
+    fBindingsExported = kNone_GrGLBinding;
+
+#if GR_GL_PER_GL_FUNC_CALLBACK
+    fCallback = GrGLDefaultInterfaceCallback;
+    fCallbackData = 0;
+#endif
+}
+
+bool GrGLInterface::validate(GrGLBinding binding) const {
+
+    // kNone must be 0 so that the check we're about to do can never succeed if
+    // binding == kNone.
+    GR_STATIC_ASSERT(kNone_GrGLBinding == 0);
+
+    if (0 == (binding & fBindingsExported)) {
+        return false;
+    }
+
+    // functions that are always required
+    if (NULL == fActiveTexture ||
+        NULL == fAttachShader ||
+        NULL == fBindAttribLocation ||
+        NULL == fBindBuffer ||
+        NULL == fBindTexture ||
+        NULL == fBlendFunc ||
+        NULL == fBlendColor ||      // -> GL >= 1.4, ES >= 2.0 or extension
+        NULL == fBufferData ||
+        NULL == fBufferSubData ||
+        NULL == fClear ||
+        NULL == fClearColor ||
+        NULL == fClearStencil ||
+        NULL == fColorMask ||
+        NULL == fCompileShader ||
+        NULL == fCreateProgram ||
+        NULL == fCreateShader ||
+        NULL == fCullFace ||
+        NULL == fDeleteBuffers ||
+        NULL == fDeleteProgram ||
+        NULL == fDeleteShader ||
+        NULL == fDeleteTextures ||
+        NULL == fDepthMask ||
+        NULL == fDisable ||
+        NULL == fDisableVertexAttribArray ||
+        NULL == fDrawArrays ||
+        NULL == fDrawElements ||
+        NULL == fEnable ||
+        NULL == fEnableVertexAttribArray ||
+        NULL == fFrontFace ||
+        NULL == fGenBuffers ||
+        NULL == fGenTextures ||
+        NULL == fGetBufferParameteriv ||
+        NULL == fGetError ||
+        NULL == fGetIntegerv ||
+        NULL == fGetProgramInfoLog ||
+        NULL == fGetProgramiv ||
+        NULL == fGetShaderInfoLog ||
+        NULL == fGetShaderiv ||
+        NULL == fGetString ||
+        NULL == fGetUniformLocation ||
+        NULL == fLinkProgram ||
+        NULL == fPixelStorei ||
+        NULL == fReadPixels ||
+        NULL == fScissor ||
+        NULL == fShaderSource ||
+        NULL == fStencilFunc ||
+        NULL == fStencilMask ||
+        NULL == fStencilOp ||
+        NULL == fTexImage2D ||
+        NULL == fTexParameteri ||
+        NULL == fTexParameteriv ||
+        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 == fFinish ||
+        NULL == fFlush ||
+        NULL == fFramebufferRenderbuffer ||
+        NULL == fFramebufferTexture2D ||
+        NULL == fGetFramebufferAttachmentParameteriv ||
+        NULL == fGetRenderbufferParameteriv ||
+        NULL == fGenFramebuffers ||
+        NULL == fGenRenderbuffers ||
+        NULL == fRenderbufferStorage) {
+        return false;
+    }
+
+    const char* ext;
+    GrGLVersion glVer = GrGLGetVersion(this);
+    ext = (const char*)fGetString(GR_GL_EXTENSIONS);
+
+    // Now check that baseline ES/Desktop fns not covered above are present
+    // and that we have fn pointers for any advertised extensions that we will
+    // try to use.
+
+    // these functions are part of ES2, we assume they are available
+    // 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 == fStencilFuncSeparate ||
+            NULL == fStencilMaskSeparate ||
+            NULL == fStencilOpSeparate) {
+            return false;
+        }
+    } else if (kDesktop_GrGLBinding == binding) {
+
+        if (glVer >= GR_GL_VER(2,0)) {
+            if (NULL == fStencilFuncSeparate ||
+                NULL == fStencilMaskSeparate ||
+                NULL == fStencilOpSeparate) {
+                return false;
+            }
+        }
+        if (glVer >= GR_GL_VER(3,0) && NULL == fBindFragDataLocation) {
+            return false;
+        }
+        if (glVer >= GR_GL_VER(2,0) ||
+            GrGLHasExtensionFromString("GL_ARB_draw_buffers", ext)) {
+            if (NULL == fDrawBuffers) {
+                return false;
+            }
+        }
+
+        if (glVer >= GR_GL_VER(1,5) ||
+            GrGLHasExtensionFromString("GL_ARB_occlusion_query", ext)) {
+            if (NULL == fGenQueries ||
+                NULL == fDeleteQueries ||
+                NULL == fBeginQuery ||
+                NULL == fEndQuery ||
+                NULL == fGetQueryiv ||
+                NULL == fGetQueryObjectiv ||
+                NULL == fGetQueryObjectuiv) {
+                return false;
+            }
+        }
+        if (glVer >= GR_GL_VER(3,3) ||
+            GrGLHasExtensionFromString("GL_ARB_timer_query", ext) ||
+            GrGLHasExtensionFromString("GL_EXT_timer_query", ext)) {
+            if (NULL == fGetQueryObjecti64v ||
+                NULL == fGetQueryObjectui64v) {
+                return false;
+            }
+        }
+        if (glVer >= GR_GL_VER(3,3) ||
+            GrGLHasExtensionFromString("GL_ARB_timer_query", ext)) {
+            if (NULL == fQueryCounter) {
+                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
+    if (kDesktop_GrGLBinding != binding ||
+        (glVer >= GR_GL_VER(1,3) ||
+        GrGLHasExtensionFromString("GL_ARB_texture_compression", ext))) {
+        if (NULL == fCompressedTexImage2D) {
+            return false;
+        }
+    }
+
+    // part of desktop GL, but not ES
+    if (kDesktop_GrGLBinding == binding &&
+        (NULL == fLineWidth ||
+         NULL == fGetTexLevelParameteriv ||
+         NULL == fDrawBuffer ||
+         NULL == fReadBuffer)) {
+        return false;
+    }
+
+    // GL_EXT_texture_storage is part of desktop 4.2
+    // There is a desktop ARB extension and an ES+desktop EXT extension
+    if (kDesktop_GrGLBinding == binding) {
+        if (glVer >= GR_GL_VER(4,2) ||
+            GrGLHasExtensionFromString("GL_ARB_texture_storage", ext) ||
+            GrGLHasExtensionFromString("GL_EXT_texture_storage", ext)) {
+            if (NULL == fTexStorage2D) {
+                return false;
+            }
+        }
+    } else if (GrGLHasExtensionFromString("GL_EXT_texture_storage", ext)) {
+        if (NULL == fTexStorage2D) {
+            return false;
+        }
+    }
+
+    // FBO MSAA
+    if (kDesktop_GrGLBinding == binding) {
+        // GL 3.0 and the ARB extension have multisample + blit
+        if (glVer >= GR_GL_VER(3,0) || GrGLHasExtensionFromString("GL_ARB_framebuffer_object", ext)) {
+            if (NULL == fRenderbufferStorageMultisample ||
+                NULL == fBlitFramebuffer) {
+                return false;
+            }
+        } else {
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_blit", ext) &&
+                NULL == fBlitFramebuffer) {
+                return false;
+            }
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample", ext) &&
+                NULL == fRenderbufferStorageMultisample) {
+                return false;
+            }
+        }
+    } else {
+        if (GrGLHasExtensionFromString("GL_CHROMIUM_framebuffer_multisample", ext)) {
+            if (NULL == fRenderbufferStorageMultisample ||
+                NULL == fBlitFramebuffer) {
+                return false;
+            }
+        }
+        if (GrGLHasExtensionFromString("GL_APPLE_framebuffer_multisample", ext)) {
+            if (NULL == fRenderbufferStorageMultisample ||
+                NULL == fResolveMultisampleFramebuffer) {
+                return false;
+            }
+        }
+    }
+
+    // On ES buffer mapping is an extension. On Desktop
+    // buffer mapping was part of original VBO extension
+    // which we require.
+    if (kDesktop_GrGLBinding == binding ||
+        GrGLHasExtensionFromString("GL_OES_mapbuffer", ext)) {
+        if (NULL == fMapBuffer ||
+            NULL == fUnmapBuffer) {
+            return false;
+        }
+    }
+
+    // Dual source blending
+    if (kDesktop_GrGLBinding == binding &&
+        (glVer >= GR_GL_VER(3,3) ||
+         GrGLHasExtensionFromString("GL_ARB_blend_func_extended", ext))) {
+        if (NULL == fBindFragDataLocationIndexed) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
diff --git a/src/gpu/gl/GrGLPath.cpp b/src/gpu/gl/GrGLPath.cpp
new file mode 100644
index 0000000..5324dcd
--- /dev/null
+++ b/src/gpu/gl/GrGLPath.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 "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];
+}
+
+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];
+}
+}
+
+GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path) : INHERITED(gpu) {
+    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) {
+        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
new file mode 100644
index 0000000..2703110
--- /dev/null
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -0,0 +1,993 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLProgram.h"
+
+#include "GrAllocator.h"
+#include "GrCustomStage.h"
+#include "GrGLProgramStage.h"
+#include "gl/GrGLShaderBuilder.h"
+#include "GrGLShaderVar.h"
+#include "GrProgramStageFactory.h"
+#include "SkTrace.h"
+#include "SkXfermode.h"
+
+SK_DEFINE_INST_COUNT(GrGLProgram)
+
+#define GL_CALL(X) GR_GL_CALL(fContextInfo.interface(), X)
+#define GL_CALL_RET(R, X) GR_GL_CALL_RET(fContextInfo.interface(), R, X)
+
+#define PRINT_SHADERS 0
+
+typedef GrGLProgram::Desc::StageDesc StageDesc;
+
+#define POS_ATTR_NAME "aPosition"
+#define COL_ATTR_NAME "aColor"
+#define COV_ATTR_NAME "aCoverage"
+#define EDGE_ATTR_NAME "aEdge"
+
+namespace {
+inline void tex_attr_name(int coordIdx, SkString* s) {
+    *s = "aTexCoord";
+    s->appendS32(coordIdx);
+}
+
+inline const char* float_vector_type_str(int count) {
+    return GrGLShaderVar::TypeString(GrSLFloatVectorType(count));
+}
+
+inline const char* vector_all_coords(int count) {
+    static const char* ALL[] = {"ERROR", "", ".xy", ".xyz", ".xyzw"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ALL));
+    return ALL[count];
+}
+
+inline const char* declared_color_output_name() { return "fsColorOut"; }
+inline const char* dual_source_output_name() { return "dualSourceOut"; }
+
+}
+
+GrGLProgram* GrGLProgram::Create(const GrGLContextInfo& gl,
+                                 const Desc& desc,
+                                 const GrCustomStage** customStages) {
+    GrGLProgram* program = SkNEW_ARGS(GrGLProgram, (gl, desc, customStages));
+    if (!program->succeeded()) {
+        delete program;
+        program = NULL;
+    }
+    return program;
+}
+
+GrGLProgram::GrGLProgram(const GrGLContextInfo& gl,
+                         const Desc& desc,
+                         const GrCustomStage** customStages)
+: fContextInfo(gl)
+, fUniformManager(gl) {
+    fDesc = desc;
+    fVShaderID = 0;
+    fGShaderID = 0;
+    fFShaderID = 0;
+    fProgramID = 0;
+
+    fViewMatrix = GrMatrix::InvalidMatrix();
+    fViewportSize.set(-1, -1);
+    fColor = GrColor_ILLEGAL;
+    fColorFilterColor = GrColor_ILLEGAL;
+
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        fProgramStage[s] = NULL;
+        fTextureMatrices[s] = GrMatrix::InvalidMatrix();
+        // this is arbitrary, just initialize to something
+        fTextureOrientation[s] = GrGLTexture::kBottomUp_Orientation;
+    }
+
+    this->genProgram(customStages);
+}
+
+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 fProgramStage[i];
+    }
+}
+
+void GrGLProgram::abandon() {
+    fVShaderID = 0;
+    fGShaderID = 0;
+    fFShaderID = 0;
+    fProgramID = 0;
+}
+
+void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
+                                GrBlendCoeff* dstCoeff) const {
+    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 Desc::kCoverage_DualSrcOutput:
+        case Desc::kCoverageISA_DualSrcOutput:
+        case Desc::kCoverageISC_DualSrcOutput:
+        *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
+        break;
+        default:
+            GrCrash("Unexpected dual source blend output");
+            break;
+    }
+}
+
+// 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) {
+    if (SkXfermode::kZero_Coeff == srcCoeff) {
+        switch (dstCoeff) {
+            // these all read the src
+            case SkXfermode::kSC_Coeff:
+            case SkXfermode::kISC_Coeff:
+            case SkXfermode::kSA_Coeff:
+            case SkXfermode::kISA_Coeff:
+                *needSrcValue = true;
+                break;
+            default:
+                *needSrcValue = false;
+                break;
+        }
+    } else {
+        *needSrcValue = true;
+    }
+    if (SkXfermode::kZero_Coeff == dstCoeff) {
+        switch (srcCoeff) {
+            // these all read the dst
+            case SkXfermode::kDC_Coeff:
+            case SkXfermode::kIDC_Coeff:
+            case SkXfermode::kDA_Coeff:
+            case SkXfermode::kIDA_Coeff:
+                *needDstValue = true;
+                break;
+            default:
+                *needDstValue = false;
+                break;
+        }
+    } else {
+        *needDstValue = true;
+    }
+}
+
+/**
+ * Create a blend_coeff * value string to be used in shader code. Sets empty
+ * string if result is trivially zero.
+ */
+static void blendTermString(SkString* str, SkXfermode::Coeff coeff,
+                             const char* src, const char* dst,
+                             const char* value) {
+    switch (coeff) {
+    case SkXfermode::kZero_Coeff:    /** 0 */
+        *str = "";
+        break;
+    case SkXfermode::kOne_Coeff:     /** 1 */
+        *str = value;
+        break;
+    case SkXfermode::kSC_Coeff:
+        str->printf("(%s * %s)", src, value);
+        break;
+    case SkXfermode::kISC_Coeff:
+        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)", GrGLSLOnesVecf(4), dst, value);
+        break;
+    case SkXfermode::kSA_Coeff:      /** src alpha */
+        str->printf("(%s.a * %s)", src, value);
+        break;
+    case SkXfermode::kISA_Coeff:     /** inverse src alpha (i.e. 1 - sa) */
+        str->printf("((1.0 - %s.a) * %s)", src, value);
+        break;
+    case SkXfermode::kDA_Coeff:      /** dst alpha */
+        str->printf("(%s.a * %s)", dst, value);
+        break;
+    case SkXfermode::kIDA_Coeff:     /** inverse dst alpha (i.e. 1 - da) */
+        str->printf("((1.0 - %s.a) * %s)", dst, value);
+        break;
+    default:
+        GrCrash("Unexpected xfer coeff.");
+        break;
+    }
+}
+/**
+ * Adds a line to the fragment shader code which modifies the color by
+ * the specified color filter.
+ */
+static void addColorFilter(SkString* fsCode, const char * outputVar,
+                           SkXfermode::Coeff uniformCoeff,
+                           SkXfermode::Coeff colorCoeff,
+                           const char* filterColor,
+                           const char* inColor) {
+    SkString colorStr, constStr;
+    blendTermString(&colorStr, colorCoeff, filterColor, inColor, inColor);
+    blendTermString(&constStr, uniformCoeff, filterColor, inColor, filterColor);
+
+    fsCode->appendf("\t%s = ", outputVar);
+    GrGLSLAdd4f(fsCode, colorStr.c_str(), constStr.c_str());
+    fsCode->append(";\n");
+}
+
+bool GrGLProgram::genEdgeCoverage(SkString* coverageVar,
+                                  GrGLShaderBuilder* segments) const {
+    if (fDesc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit) {
+        const char *vsName, *fsName;
+        segments->addVarying(kVec4f_GrSLType, "Edge", &vsName, &fsName);
+        segments->fVSAttrs.push_back().set(kVec4f_GrSLType,
+            GrGLShaderVar::kAttribute_TypeModifier, EDGE_ATTR_NAME);
+        segments->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
+        switch (fDesc.fVertexEdgeType) {
+        case GrDrawState::kHairLine_EdgeType:
+            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");
+            break;
+        case GrDrawState::kQuad_EdgeType:
+            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);
+            // 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 == fContextInfo.binding()) {
+                segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
+            }
+            break;
+        case GrDrawState::kHairQuad_EdgeType:
+            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 == fContextInfo.binding()) {
+                segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
+            }
+            break;
+        case GrDrawState::kCircle_EdgeType:
+            segments->fFSCode.append("\tfloat edgeAlpha;\n");
+            segments->fFSCode.appendf("\tfloat d = distance(gl_FragCoord.xy, %s.xy);\n", fsName);
+            segments->fFSCode.appendf("\tfloat outerAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName);
+            segments->fFSCode.appendf("\tfloat innerAlpha = %s.w == 0.0 ? 1.0 : smoothstep(%s.w - 0.5, %s.w + 0.5, d);\n", fsName, fsName, fsName);
+            segments->fFSCode.append("\tedgeAlpha = outerAlpha * innerAlpha;\n");
+            break;
+        default:
+            GrCrash("Unknown Edge Type!");
+            break;
+        }
+        *coverageVar = "edgeAlpha";
+        return true;
+    } else {
+        coverageVar->reset();
+        return false;
+    }
+}
+
+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;
+            builder->addVarying(kVec4f_GrSLType, "Color", &vsName, &fsName);
+            builder->fVSCode.appendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
+            *inColor = fsName;
+            } break;
+        case GrGLProgram::Desc::kUniform_ColorInput: {
+            const char* name;
+            fUniforms.fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                      kVec4f_GrSLType, "Color", &name);
+            *inColor = name;
+            break;
+        }
+        case GrGLProgram::Desc::kTransBlack_ColorInput:
+            GrAssert(!"needComputedColor should be false.");
+            break;
+        case GrGLProgram::Desc::kSolidWhite_ColorInput:
+            break;
+        default:
+            GrCrash("Unknown color type.");
+            break;
+    }
+}
+
+void GrGLProgram::genUniformCoverage(GrGLShaderBuilder* builder, SkString* inOutCoverage) {
+    const char* covUniName;
+    fUniforms.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;
+    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",
+                                  fsName, inOutCoverage->c_str());
+        *inOutCoverage = "attrCoverage";
+    } else {
+        *inOutCoverage = fsName;
+    }
+}
+}
+
+void GrGLProgram::genGeometryShader(GrGLShaderBuilder* segments) const {
+#if GR_GL_EXPERIMENTAL_GS
+    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 (fDesc.fEmitsPointSize) {
+            segments->fGSCode.append("\t\tgl_PointSize = 1.0;\n");
+        }
+        GrAssert(segments->fGSInputs.count() == segments->fGSOutputs.count());
+        int count = segments->fGSInputs.count();
+        for (int i = 0; i < count; ++i) {
+            segments->fGSCode.appendf("\t\t%s = %s[i];\n",
+                                      segments->fGSOutputs[i].getName().c_str(),
+                                      segments->fGSInputs[i].getName().c_str());
+        }
+        segments->fGSCode.append("\t\tEmitVertex();\n"
+                                 "\t}\n"
+                                 "\tEndPrimitive();\n"
+                                 "}\n");
+    }
+#endif
+}
+
+const char* GrGLProgram::adjustInColor(const SkString& inColor) const {
+    if (inColor.size()) {
+          return inColor.c_str();
+    } else {
+        if (Desc::kSolidWhite_ColorInput == fDesc.fColorInput) {
+            return GrGLSLOnesVecf(4);
+        } else {
+            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]);
+        }
+    }
+}
+
+// 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());
+
+    GrGLuint shader;
+    GR_GL_CALL_RET(gl.interface(), shader, CreateShader(type));
+    if (0 == shader) {
+        return 0;
+    }
+
+    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 PRINT_SHADERS
+    GrPrintf(shader.c_str());
+    GrPrintf("\n");
+#endif
+    if (!(fVShaderID = compile_shader(fContextInfo, GR_GL_VERTEX_SHADER, shader))) {
+        return false;
+    }
+
+    if (builder.fUsesGS) {
+        builder.getShader(GrGLShaderBuilder::kGeometry_ShaderType, &shader);
+#if PRINT_SHADERS
+        GrPrintf(shader.c_str());
+        GrPrintf("\n");
+#endif
+        if (!(fGShaderID = compile_shader(fContextInfo, GR_GL_GEOMETRY_SHADER, shader))) {
+            return false;
+        }
+    } else {
+        fGShaderID = 0;
+    }
+
+    builder.getShader(GrGLShaderBuilder::kFragment_ShaderType, &shader);
+#if PRINT_SHADERS
+    GrPrintf(shader.c_str());
+    GrPrintf("\n");
+#endif
+    if (!(fFShaderID = compile_shader(fContextInfo, GR_GL_FRAGMENT_SHADER, shader))) {
+        return false;
+    }
+
+    return true;
+}
+
+bool GrGLProgram::genProgram(const GrCustomStage** customStages) {
+    GrAssert(0 == fProgramID);
+
+    GrGLShaderBuilder builder(fContextInfo, fUniformManager);
+    const uint32_t& layout = fDesc.fVertexLayout;
+
+#if GR_GL_EXPERIMENTAL_GS
+    builder.fUsesGS = fDesc.fExperimentalGS;
+#endif
+
+    SkXfermode::Coeff colorCoeff, uniformCoeff;
+    bool applyColorMatrix = SkToBool(fDesc.fColorMatrixEnabled);
+    // The rest of transfer mode color filters have not been implemented
+    if (fDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
+        GR_DEBUGCODE(bool success =)
+            SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>
+                                    (fDesc.fColorFilterXfermode),
+                                    &uniformCoeff, &colorCoeff);
+        GR_DEBUGASSERT(success);
+    } else {
+        colorCoeff = SkXfermode::kOne_Coeff;
+        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 (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
+    // come out false below.
+    if (Desc::kTransBlack_ColorInput == fDesc.fColorInput) {
+        colorCoeff = SkXfermode::kZero_Coeff;
+        if (SkXfermode::kDC_Coeff == uniformCoeff ||
+            SkXfermode::kDA_Coeff == uniformCoeff) {
+            uniformCoeff = SkXfermode::kZero_Coeff;
+        } else if (SkXfermode::kIDC_Coeff == uniformCoeff ||
+                   SkXfermode::kIDA_Coeff == uniformCoeff) {
+            uniformCoeff = SkXfermode::kOne_Coeff;
+        }
+    }
+
+    bool needColorFilterUniform;
+    bool needComputedColor;
+    needBlendInputs(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;
+    builder.fHeader.append(GrGetGLSLVersionDecl(fContextInfo.binding(),
+                                                fContextInfo.glslGeneration()));
+
+    GrGLShaderVar colorOutput;
+    bool isColorDeclared = GrGLSLSetupFSColorOuput(fContextInfo.glslGeneration(),
+                                                   declared_color_output_name(),
+                                                   &colorOutput);
+    if (isColorDeclared) {
+        builder.fFSOutputs.push_back(colorOutput);
+    }
+
+    const char* viewMName;
+    fUniforms.fViewMatrixUni = builder.addUniform(GrGLShaderBuilder::kVertex_ShaderType,
+                                                  kMat33f_GrSLType, "ViewM", &viewMName);
+
+    builder.fVSAttrs.push_back().set(kVec2f_GrSLType,
+                                     GrGLShaderVar::kAttribute_TypeModifier,
+                                     POS_ATTR_NAME);
+
+    builder.fVSCode.appendf("void main() {\n"
+                              "\tvec3 pos3 = %s * vec3("POS_ATTR_NAME", 1);\n"
+                              "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n",
+                            viewMName);
+
+    // incoming color to current stage being processed.
+    SkString inColor;
+
+    if (needComputedColor) {
+        this->genInputColor(&builder, &inColor);
+    }
+
+    // we output point size in the GS if present
+    if (fDesc.fEmitsPointSize && !builder.fUsesGS){
+        builder.fVSCode.append("\tgl_PointSize = 1.0;\n");
+    }
+
+    builder.fFSCode.append("void main() {\n");
+
+    // add texture coordinates that are used to the list of vertex attr decls
+    SkString texCoordAttrs[GrDrawState::kMaxTexCoords];
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        if (GrDrawTarget::VertexUsesTexCoordIdx(t, layout)) {
+            tex_attr_name(t, texCoordAttrs + t);
+            builder.fVSAttrs.push_back().set(kVec2f_GrSLType,
+                GrGLShaderVar::kAttribute_TypeModifier,
+                texCoordAttrs[t].c_str());
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // compute the final color
+
+    // 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) {
+        SkString outColor;
+        for (int s = 0; s < fDesc.fFirstCoverageStage; ++s) {
+            if (fDesc.fStages[s].isEnabled()) {
+                // create var to hold stage result
+                outColor = "color";
+                outColor.appendS32(s);
+                builder.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
+
+                const char* inCoords;
+                // figure out what our input coords are
+                int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
+                if (tcIdx < 0) {
+                    inCoords = POS_ATTR_NAME;
+                } else {
+                    // must have input tex coordinates if stage is enabled.
+                    GrAssert(texCoordAttrs[tcIdx].size());
+                    inCoords = texCoordAttrs[tcIdx].c_str();
+                }
+
+                builder.setCurrentStage(s);
+                fProgramStage[s] = GenStageCode(customStages[s],
+                                                fDesc.fStages[s],
+                                                &fUniforms.fStages[s],
+                                                inColor.size() ? inColor.c_str() : NULL,
+                                                outColor.c_str(),
+                                                inCoords,
+                                                &builder);
+                builder.setNonStage();
+                inColor = outColor;
+            }
+        }
+    }
+
+    // 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(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);
+        }
+    }
+    const char* colorFilterColorUniName = NULL;
+    if (needColorFilterUniform) {
+        fUniforms.fColorFilterUni = builder.addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                       kVec4f_GrSLType, "FilterColor",
+                                                       &colorFilterColorUniName);
+    }
+    bool wroteFragColorZero = false;
+    if (SkXfermode::kZero_Coeff == uniformCoeff &&
+        SkXfermode::kZero_Coeff == colorCoeff &&
+        !applyColorMatrix) {
+        builder.fFSCode.appendf("\t%s = %s;\n",
+                                colorOutput.getName().c_str(),
+                                GrGLSLZerosVecf(4));
+        wroteFragColorZero = true;
+    } else if (SkXfermode::kDst_Mode != fDesc.fColorFilterXfermode) {
+        builder.fFSCode.append("\tvec4 filteredColor;\n");
+        const char* color = adjustInColor(inColor);
+        addColorFilter(&builder.fFSCode, "filteredColor", uniformCoeff,
+                       colorCoeff, colorFilterColorUniName, color);
+        inColor = "filteredColor";
+    }
+    if (applyColorMatrix) {
+        const char* colMatrixName;
+        const char* colMatrixVecName;
+        fUniforms.fColorMatrixUni = builder.addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                       kMat44f_GrSLType, "ColorMatrix",
+                                                       &colMatrixName);
+        fUniforms.fColorMatrixVecUni = builder.addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                          kVec4f_GrSLType, "ColorMatrixVec",
+                                                          &colMatrixVecName);
+        const char* color = adjustInColor(inColor);
+        builder.fFSCode.appendf("\tvec4 matrixedColor = %s * vec4(%s.rgb / %s.a, %s.a) + %s;\n",
+                                colMatrixName, color, color, color, colMatrixVecName);
+        builder.fFSCode.append("\tmatrixedColor.rgb *= matrixedColor.a;\n");
+
+        inColor = "matrixedColor";
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // compute the partial coverage (coverage stages and edge aa)
+
+    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 || Desc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
+
+        if (!coverageIsZero) {
+            bool inCoverageIsScalar  = this->genEdgeCoverage(&inCoverage, &builder);
+
+            switch (fDesc.fCoverageInput) {
+                case Desc::kSolidWhite_ColorInput:
+                    // empty string implies solid white
+                    break;
+                case Desc::kAttribute_ColorInput:
+                    gen_attribute_coverage(&builder, &inCoverage);
+                    inCoverageIsScalar = false;
+                    break;
+                case Desc::kUniform_ColorInput:
+                    this->genUniformCoverage(&builder, &inCoverage);
+                    inCoverageIsScalar = false;
+                    break;
+                default:
+                    GrCrash("Unexpected input coverage.");
+            }
+
+            SkString outCoverage;
+            const int& startStage = fDesc.fFirstCoverageStage;
+            for (int s = startStage; s < GrDrawState::kNumStages; ++s) {
+                if (fDesc.fStages[s].isEnabled()) {
+                    // create var to hold stage output
+                    outCoverage = "coverage";
+                    outCoverage.appendS32(s);
+                    builder.fFSCode.appendf("\tvec4 %s;\n", outCoverage.c_str());
+
+                    const char* inCoords;
+                    // figure out what our input coords are
+                    int tcIdx =
+                        GrDrawTarget::VertexTexCoordsForStage(s, layout);
+                    if (tcIdx < 0) {
+                        inCoords = POS_ATTR_NAME;
+                    } else {
+                        // must have input tex coordinates if stage is
+                        // enabled.
+                        GrAssert(texCoordAttrs[tcIdx].size());
+                        inCoords = texCoordAttrs[tcIdx].c_str();
+                    }
+
+                    // 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);
+                    fProgramStage[s] = GenStageCode(customStages[s],
+                                                    fDesc.fStages[s],
+                                                    &fUniforms.fStages[s],
+                                                    inCoverage.size() ? inCoverage.c_str() : NULL,
+                                                    outCoverage.c_str(),
+                                                    inCoords,
+                                                    &builder);
+                    builder.setNonStage();
+                    inCoverage = outCoverage;
+                }
+            }
+        }
+
+        if (Desc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
+            builder.fFSOutputs.push_back().set(kVec4f_GrSLType,
+                                               GrGLShaderVar::kOut_TypeModifier,
+                                               dual_source_output_name());
+            bool outputIsZero = coverageIsZero;
+            SkString coeff;
+            if (!outputIsZero &&
+                Desc::kCoverage_DualSrcOutput != fDesc.fDualSrcOutput && !wroteFragColorZero) {
+                if (!inColor.size()) {
+                    outputIsZero = true;
+                } else {
+                    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());
+                    }
+                }
+            }
+            if (outputIsZero) {
+                builder.fFSCode.appendf("\t%s = %s;\n",
+                                        dual_source_output_name(),
+                                        GrGLSLZerosVecf(4));
+            } else {
+                builder.fFSCode.appendf("\t%s =", dual_source_output_name());
+                GrGLSLModulate4f(&builder.fFSCode, coeff.c_str(), inCoverage.c_str());
+                builder.fFSCode.append(";\n");
+            }
+            dualSourceOutputWritten = true;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // combine color and coverage as frag color
+
+    if (!wroteFragColorZero) {
+        if (coverageIsZero) {
+            builder.fFSCode.appendf("\t%s = %s;\n",
+                                    colorOutput.getName().c_str(),
+                                    GrGLSLZerosVecf(4));
+        } else {
+            builder.fFSCode.appendf("\t%s = ", colorOutput.getName().c_str());
+            GrGLSLModulate4f(&builder.fFSCode, inColor.c_str(), inCoverage.c_str());
+            builder.fFSCode.append(";\n");
+        }
+    }
+
+    builder.fVSCode.append("}\n");
+    builder.fFSCode.append("}\n");
+
+    ///////////////////////////////////////////////////////////////////////////
+    // insert GS
+#if GR_DEBUG
+    this->genGeometryShader(&builder);
+#endif
+
+    ///////////////////////////////////////////////////////////////////////////
+    // compile and setup attribs and unis
+
+    if (!this->compileShaders(builder)) {
+        return false;
+    }
+
+    if (!this->bindOutputsAttribsAndLinkProgram(texCoordAttrs,
+                                                isColorDeclared,
+                                                dualSourceOutputWritten)) {
+        return false;
+    }
+
+    builder.finished(fProgramID);
+    this->initSamplerUniforms();
+
+    return true;
+}
+
+bool GrGLProgram::bindOutputsAttribsAndLinkProgram(SkString texCoordAttrNames[],
+                                                   bool bindColorOut,
+                                                   bool bindDualSrcOut) {
+    GL_CALL_RET(fProgramID, CreateProgram());
+    if (!fProgramID) {
+        return false;
+    }
+
+    GL_CALL(AttachShader(fProgramID, fVShaderID));
+    if (fGShaderID) {
+        GL_CALL(AttachShader(fProgramID, fGShaderID));
+    }
+    GL_CALL(AttachShader(fProgramID, fFShaderID));
+
+    if (bindColorOut) {
+        GL_CALL(BindFragDataLocation(fProgramID, 0, declared_color_output_name()));
+    }
+    if (bindDualSrcOut) {
+        GL_CALL(BindFragDataLocationIndexed(fProgramID, 0, 1, dual_source_output_name()));
+    }
+
+    // Bind the attrib locations to same values for all shaders
+    GL_CALL(BindAttribLocation(fProgramID, PositionAttributeIdx(), POS_ATTR_NAME));
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        if (texCoordAttrNames[t].size()) {
+            GL_CALL(BindAttribLocation(fProgramID,
+                                       TexCoordAttributeIdx(t),
+                                       texCoordAttrNames[t].c_str()));
+        }
+    }
+
+    GL_CALL(BindAttribLocation(fProgramID, ColorAttributeIdx(), COL_ATTR_NAME));
+    GL_CALL(BindAttribLocation(fProgramID, CoverageAttributeIdx(), COV_ATTR_NAME));
+    GL_CALL(BindAttribLocation(fProgramID, EdgeAttributeIdx(), EDGE_ATTR_NAME));
+
+    GL_CALL(LinkProgram(fProgramID));
+
+    GrGLint linked = GR_GL_INIT_ZERO;
+    GL_CALL(GetProgramiv(fProgramID, GR_GL_LINK_STATUS, &linked));
+    if (!linked) {
+        GrGLint infoLen = GR_GL_INIT_ZERO;
+        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(fProgramID,
+                                      infoLen+1,
+                                      &length,
+                                      (char*)log.get()));
+            GrPrintf((char*)log.get());
+        }
+        GrAssert(!"Error linking program");
+        GL_CALL(DeleteProgram(fProgramID));
+        fProgramID = 0;
+        return false;
+    }
+    return true;
+}
+
+void GrGLProgram::initSamplerUniforms() {
+    GL_CALL(UseProgram(fProgramID));
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        int count = fUniforms.fStages[s].fSamplerUniforms.count();
+        // FIXME: We're still always reserving one texture per stage. After GrTextureParams are
+        // expressed by the custom stage rather than the GrSamplerState we can move texture binding
+        // into GrGLProgram and it should be easier to fix this.
+        GrAssert(count <= 1);
+        for (int t = 0; t < count; ++t) {
+            UniformHandle uh = fUniforms.fStages[s].fSamplerUniforms[t];
+            if (GrGLUniformManager::kInvalidUniformHandle != uh) {
+                fUniformManager.setSampler(uh, s);
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Stage code generation
+
+// TODO: Move this function to GrGLShaderBuilder
+GrGLProgramStage* GrGLProgram::GenStageCode(const GrCustomStage* stage,
+                                            const StageDesc& desc,
+                                            StageUniforms* uniforms,
+                                            const char* fsInColor, // NULL means no incoming color
+                                            const char* fsOutColor,
+                                            const char* vsInCoord,
+                                            GrGLShaderBuilder* builder) {
+
+    GrGLProgramStage* glStage = stage->getFactory().createGLInstance(*stage);
+
+    /// 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;
+    GrSLType texCoordVaryingType;
+    if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
+        texCoordVaryingType = kVec2f_GrSLType;
+    } else {
+        uniforms->fTextureMatrixUni = builder->addUniform(GrGLShaderBuilder::kVertex_ShaderType,
+                                                         kMat33f_GrSLType, "TexM", &matName);
+        builder->getUniformVariable(uniforms->fTextureMatrixUni);
+
+        if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
+            texCoordVaryingType = kVec2f_GrSLType;
+        } else {
+            texCoordVaryingType = kVec3f_GrSLType;
+        }
+    }
+    const char *varyingVSName, *varyingFSName;
+    builder->addVarying(texCoordVaryingType,
+                        "Stage",
+                        &varyingVSName,
+                        &varyingFSName);
+    builder->setupTextureAccess(varyingFSName, texCoordVaryingType);
+
+    // Must setup variables after calling setupTextureAccess
+    glStage->setupVariables(builder);
+
+    int numTextures = stage->numTextures();
+    SkSTArray<8, GrGLShaderBuilder::TextureSampler> textureSamplers;
+
+    textureSamplers.push_back_n(numTextures);
+
+    for (int i = 0; i < numTextures; ++i) {
+        textureSamplers[i].init(builder, &stage->textureAccess(i));
+        uniforms->fSamplerUniforms.push_back(textureSamplers[i].fSamplerUniform);
+    }
+
+    if (!matName) {
+        GrAssert(kVec2f_GrSLType == texCoordVaryingType);
+        builder->fVSCode.appendf("\t%s = %s;\n", varyingVSName, vsInCoord);
+    } else {
+        // varying = texMatrix * texCoord
+        builder->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
+                                  varyingVSName, matName, vsInCoord,
+                                  vector_all_coords(GrSLTypeToVecLength(texCoordVaryingType)));
+    }
+
+    builder->fVSCode.appendf("\t{ // %s\n", glStage->name());
+    glStage->emitVS(builder, varyingVSName);
+    builder->fVSCode.appendf("\t}\n");
+
+    // Enclose custom code in a block to avoid namespace conflicts
+    builder->fFSCode.appendf("\t{ // %s \n", glStage->name());
+    glStage->emitFS(builder, fsOutColor, fsInColor, textureSamplers);
+    builder->fFSCode.appendf("\t}\n");
+
+    return glStage;
+}
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
new file mode 100644
index 0000000..e51f663
--- /dev/null
+++ b/src/gpu/gl/GrGLProgram.h
@@ -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.
+ */
+
+
+#ifndef GrGLProgram_DEFINED
+#define GrGLProgram_DEFINED
+
+#include "GrDrawState.h"
+#include "GrGLContextInfo.h"
+#include "GrGLSL.h"
+#include "GrGLTexture.h"
+#include "GrGLUniformManager.h"
+
+#include "SkString.h"
+#include "SkXfermode.h"
+
+class GrBinHashKeyBuilder;
+class GrGLProgramStage;
+class GrGLShaderBuilder;
+
+// optionally compile the experimental GS code. Set to GR_DEBUG
+// so that debug build bots will execute the code.
+#define GR_GL_EXPERIMENTAL_GS GR_DEBUG
+
+/**
+ * This class manages a GPU program and records per-program information.
+ * We can specify the attribute locations so that they are constant
+ * across our shaders. But the driver determines the uniform locations
+ * at link time. We don't need to remember the sampler uniform location
+ * because we will bind a texture slot to it and never change it
+ * Uniforms are program-local so we can't rely on fHWState to hold the
+ * previous uniform state after a program change.
+ */
+class GrGLProgram : public GrRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrGLProgram)
+
+    struct Desc;
+
+    static GrGLProgram* Create(const GrGLContextInfo& gl,
+                               const Desc& desc,
+                               const GrCustomStage** customStages);
+
+    virtual ~GrGLProgram();
+
+    /** Call to abandon GL objects owned by this program */
+    void abandon();
+
+    /**
+     * The shader may modify the blend coeffecients. Params are in/out
+     */
+    void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
+
+    const Desc& getDesc() { return fDesc; }
+
+    /**
+     * Attribute indices. These should not overlap. Matrices consume 3 slots.
+     */
+    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 ViewMatrixAttributeIdx() {
+        return 4 + GrDrawState::kMaxTexCoords;
+    }
+    static int TextureMatrixAttributeIdx(int stage) {
+        return 7 + GrDrawState::kMaxTexCoords + 3 * stage;
+    }
+
+    // Parameters that affect code generation
+    // These structs should be kept compact; they are the input to an
+    // expensive hash key generator.
+    struct Desc {
+        Desc() {
+            // since we use this as part of a key we can't have any unitialized
+            // padding
+            memset(this, 0, sizeof(Desc));
+        }
+
+        // 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);
+        }
+
+        struct StageDesc {
+            enum OptFlagBits {
+                kNoPerspective_OptFlagBit       = 1 << 0,
+                kIdentityMatrix_OptFlagBit      = 1 << 1,
+                kIsEnabled_OptFlagBit           = 1 << 7
+            };
+
+            uint8_t fOptFlags;
+
+            /** Non-zero if user-supplied code will write the stage's
+                contribution to the fragment shader. */
+            GrProgramStageFactory::StageKey fCustomStageKey;
+
+            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.
+        enum ColorInput {
+            kSolidWhite_ColorInput,
+            kTransBlack_ColorInput,
+            kAttribute_ColorInput,
+            kUniform_ColorInput,
+
+            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
+        // secondary source is output and what value it holds.
+        enum DualSrcOutput {
+            kNone_DualSrcOutput,
+            kCoverage_DualSrcOutput,
+            kCoverageISA_DualSrcOutput,
+            kCoverageISC_DualSrcOutput,
+
+            kDualSrcOutputCnt
+        };
+
+        GrDrawState::VertexEdgeType fVertexEdgeType;
+
+        // stripped of bits that don't affect prog generation
+        GrVertexLayout fVertexLayout;
+
+        StageDesc fStages[GrDrawState::kNumStages];
+
+        // To enable experimental geometry shader code (not for use in
+        // production)
+#if GR_GL_EXPERIMENTAL_GS
+        bool fExperimentalGS;
+#endif
+
+        uint8_t fColorInput;        // casts to enum ColorInput
+        uint8_t fCoverageInput;     // casts to enum CoverageInput
+        uint8_t fDualSrcOutput;     // casts to enum DualSrcOutput
+        int8_t fFirstCoverageStage;
+        SkBool8 fEmitsPointSize;
+        SkBool8 fColorMatrixEnabled;
+
+        uint8_t fColorFilterXfermode;  // casts to enum SkXfermode::Mode
+    };
+    GR_STATIC_ASSERT(!(sizeof(Desc) % 4));
+
+    // for code readability
+    typedef Desc::StageDesc StageDesc;
+
+private:
+    struct StageUniforms;
+
+    GrGLProgram(const GrGLContextInfo& gl,
+                const Desc& desc,
+                const GrCustomStage** customStages);
+
+    bool succeeded() const { return 0 != fProgramID; }
+
+    /**
+     *  This is the heavy initilization routine for building a GLProgram.
+     */
+    bool genProgram(const GrCustomStage** customStages);
+
+    void genInputColor(GrGLShaderBuilder* builder, SkString* inColor);
+
+    static GrGLProgramStage* GenStageCode(const GrCustomStage* stage,
+                                          const StageDesc& desc, // TODO: Eliminate this
+                                          StageUniforms* stageUniforms, // TODO: Eliminate this
+                                          const char* fsInColor, // NULL means no incoming color
+                                          const char* fsOutColor,
+                                          const char* vsInCoord,
+                                          GrGLShaderBuilder* builder);
+
+    void genGeometryShader(GrGLShaderBuilder* segments) const;
+
+    typedef GrGLUniformManager::UniformHandle UniformHandle;
+
+    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(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;
+
+    struct StageUniforms {
+        UniformHandle fTextureMatrixUni;
+        SkTArray<UniformHandle, true> fSamplerUniforms;
+        StageUniforms() {
+            fTextureMatrixUni = GrGLUniformManager::kInvalidUniformHandle;
+        }
+    };
+
+    struct Uniforms {
+        UniformHandle fViewMatrixUni;
+        UniformHandle fColorUni;
+        UniformHandle fCoverageUni;
+        UniformHandle fColorFilterUni;
+        UniformHandle fColorMatrixUni;
+        UniformHandle fColorMatrixVecUni;
+        StageUniforms fStages[GrDrawState::kNumStages];
+        Uniforms() {
+            fViewMatrixUni = GrGLUniformManager::kInvalidUniformHandle;
+            fColorUni = GrGLUniformManager::kInvalidUniformHandle;
+            fCoverageUni = GrGLUniformManager::kInvalidUniformHandle;
+            fColorFilterUni = GrGLUniformManager::kInvalidUniformHandle;
+            fColorMatrixUni = GrGLUniformManager::kInvalidUniformHandle;
+            fColorMatrixVecUni = GrGLUniformManager::kInvalidUniformHandle;
+        }
+    };
+
+    // 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.
+    GrMatrix  fViewMatrix;
+    SkISize   fViewportSize;
+
+    // these reflect the current values of uniforms
+    // (GL uniform values travel with program)
+    GrColor                     fColor;
+    GrColor                     fCoverage;
+    GrColor                     fColorFilterColor;
+    /// When it is sent to GL, the texture matrix will be flipped if the texture orientation
+    /// (below) requires.
+    GrMatrix                    fTextureMatrices[GrDrawState::kNumStages];
+    GrGLTexture::Orientation    fTextureOrientation[GrDrawState::kNumStages];
+
+    GrGLProgramStage*           fProgramStage[GrDrawState::kNumStages];
+
+    Desc fDesc;
+    const GrGLContextInfo&      fContextInfo;
+
+    GrGLUniformManager          fUniformManager;
+    Uniforms                    fUniforms;
+
+    friend class GrGpuGL; // TODO: remove this by adding getters and moving functionality.
+
+    typedef GrRefCnt INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLProgramStage.cpp b/src/gpu/gl/GrGLProgramStage.cpp
new file mode 100644
index 0000000..b7d0c65
--- /dev/null
+++ b/src/gpu/gl/GrGLProgramStage.cpp
@@ -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 "GrGLSL.h"
+#include "GrGLProgramStage.h"
+
+GrGLProgramStage::GrGLProgramStage(const GrProgramStageFactory& factory)
+    : fFactory(factory) {
+}
+
+GrGLProgramStage::~GrGLProgramStage() {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLProgramStage::setupVariables(GrGLShaderBuilder*) {
+
+}
+
+void GrGLProgramStage::setData(const GrGLUniformManager&,
+                               const GrCustomStage&,
+                               const GrRenderTarget*,
+                               int stageNum) {
+}
+
+GrGLProgramStage::StageKey GrGLProgramStage::GenTextureKey(const GrCustomStage& stage,
+                                                           const GrGLCaps& caps) {
+    StageKey key = 0;
+    for (int index = 0; index < stage.numTextures(); ++index) {
+        const GrTextureAccess& access = stage.textureAccess(index);
+        StageKey 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/GrGLProgramStage.h b/src/gpu/gl/GrGLProgramStage.h
new file mode 100644
index 0000000..28d3f49
--- /dev/null
+++ b/src/gpu/gl/GrGLProgramStage.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 GrGLCustomStage_DEFINED
+#define GrGLCustomStage_DEFINED
+
+#include "GrAllocator.h"
+#include "GrCustomStage.h"
+#include "GrGLProgram.h"
+#include "GrGLShaderBuilder.h"
+#include "GrGLShaderVar.h"
+#include "GrGLSL.h"
+
+struct GrGLInterface;
+class GrGLTexture;
+
+/** @file
+    This file contains specializations for OpenGL of the shader stages
+    declared in src/gpu/GrCustomStage.h. All the functions emit
+    GLSL shader code and OpenGL calls.
+
+    These objects are created by a factory function on the
+    GrCustomStage.
+*/
+
+class GrGLProgramStage {
+
+public:
+    typedef GrCustomStage::StageKey StageKey;
+    enum {
+        // the number of bits in StageKey available to GenKey
+        kProgramStageKeyBits = GrProgramStageFactory::kProgramStageKeyBits,
+    };
+
+    typedef GrGLShaderBuilder::TextureSamplerArray TextureSamplerArray;
+
+    GrGLProgramStage(const GrProgramStageFactory&);
+
+    virtual ~GrGLProgramStage();
+
+    /** Create any uniforms or varyings the vertex shader requires. */
+    virtual void setupVariables(GrGLShaderBuilder* builder);
+
+    /** Appends vertex code to the appropriate SkString
+        on the state.
+        The code will be inside an otherwise-empty block.
+        Vertex shader input is a vec2 of coordinates, which may
+        be altered.
+        The code will be inside an otherwise-empty block. */
+    virtual void emitVS(GrGLShaderBuilder* builder,
+                        const char* vertexCoords) = 0;
+
+    /** Appends fragment code to the appropriate SkString
+        on the state.
+        The code will be inside an otherwise-empty block.
+        Fragment shader inputs are a vec2 of coordinates, one texture,
+        and a color; output is a color. The input color may be NULL which
+        indicates that the input color is solid white. TODO: Better system
+        for communicating optimization info (e.g. input color is solid white,
+        trans black, known to be opaque, etc.) that allows the custom stage
+        to communicate back similar known info about its output.
+        */
+    virtual void emitFS(GrGLShaderBuilder* builder,
+                        const char* outputColor,
+                        const char* inputColor,
+                        const TextureSamplerArray&) = 0;
+
+    /** A GrGLCustomStage instance can be reused with any GrCustomStage
+        that produces the same stage key; this function reads data from
+        a stage and uploads any uniform variables required by the shaders
+        created in emit*(). */
+    virtual void setData(const GrGLUniformManager&,
+                         const GrCustomStage& stage,
+                         const GrRenderTarget* renderTarget,
+                         int stageNum);
+
+    const char* name() const { return fFactory.name(); }
+
+    static StageKey GenTextureKey(const GrCustomStage&, const GrGLCaps&);
+
+protected:
+
+    const GrProgramStageFactory& fFactory;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
new file mode 100644
index 0000000..3e72757
--- /dev/null
+++ b/src/gpu/gl/GrGLRenderTarget.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 "GrGLRenderTarget.h"
+
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+void GrGLRenderTarget::init(const Desc& desc,
+                            const GrGLIRect& viewport,
+                            GrGLTexID* texID) {
+    fRTFBOID                = desc.fRTFBOID;
+    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,
+                texture,
+                MakeDesc(kNone_GrTextureFlags,
+                         viewport.fWidth, viewport.fHeight,
+                         desc.fConfig, desc.fSampleCnt)) {
+    GrAssert(NULL != texID);
+    GrAssert(NULL != texture);
+    // FBO 0 can't also be a texture, right?
+    GrAssert(0 != desc.fRTFBOID);
+    GrAssert(0 != desc.fTexFBOID);
+
+    // we assume this is true, TODO: get rid of viewport as a param.
+    GrAssert(viewport.fWidth == texture->width());
+    GrAssert(viewport.fHeight == texture->height());
+
+    this->init(desc, viewport, texID);
+}
+
+GrGLRenderTarget::GrGLRenderTarget(GrGpuGL* gpu,
+                                   const Desc& desc,
+                                   const GrGLIRect& viewport)
+    : INHERITED(gpu,
+                NULL,
+                MakeDesc(kNone_GrTextureFlags,
+                         viewport.fWidth, viewport.fHeight,
+                         desc.fConfig, desc.fSampleCnt)) {
+    this->init(desc, viewport, NULL);
+}
+
+void GrGLRenderTarget::onRelease() {
+    GPUGL->notifyRenderTargetDelete(this);
+    if (fOwnIDs) {
+        if (fTexFBOID) {
+            GL_CALL(DeleteFramebuffers(1, &fTexFBOID));
+        }
+        if (fRTFBOID && fRTFBOID != fTexFBOID) {
+            GL_CALL(DeleteFramebuffers(1, &fRTFBOID));
+        }
+        if (fMSColorRenderbufferID) {
+            GL_CALL(DeleteRenderbuffers(1, &fMSColorRenderbufferID));
+        }
+    }
+    fRTFBOID                = 0;
+    fTexFBOID               = 0;
+    fMSColorRenderbufferID  = 0;
+    GrSafeUnref(fTexIDObj);
+    fTexIDObj = NULL;
+    INHERITED::onRelease();
+}
+
+void GrGLRenderTarget::onAbandon() {
+    fRTFBOID                = 0;
+    fTexFBOID               = 0;
+    fMSColorRenderbufferID  = 0;
+    if (NULL != fTexIDObj) {
+        fTexIDObj->abandon();
+        fTexIDObj = NULL;
+    }
+    INHERITED::onAbandon();
+}
+
diff --git a/src/gpu/gl/GrGLRenderTarget.h b/src/gpu/gl/GrGLRenderTarget.h
new file mode 100644
index 0000000..493e90e
--- /dev/null
+++ b/src/gpu/gl/GrGLRenderTarget.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLRenderTarget_DEFINED
+#define GrGLRenderTarget_DEFINED
+
+#include "GrGLIRect.h"
+#include "GrRenderTarget.h"
+#include "GrScalar.h"
+
+class GrGpuGL;
+class GrGLTexture;
+class GrGLTexID;
+
+class GrGLRenderTarget : public GrRenderTarget {
+
+public:
+    // set fTexFBOID to this value to indicate that it is multisampled but
+    // Gr doesn't know how to resolve it.
+    enum { kUnresolvableFBOID = 0 };
+
+    struct Desc {
+        GrGLuint      fRTFBOID;
+        GrGLuint      fTexFBOID;
+        GrGLuint      fMSColorRenderbufferID;
+        bool          fOwnIDs;
+        GrPixelConfig fConfig;
+        int           fSampleCnt;
+    };
+
+    // creates a GrGLRenderTarget associated with a texture
+    GrGLRenderTarget(GrGpuGL*          gpu,
+                     const Desc&       desc,
+                     const GrGLIRect&  viewport,
+                     GrGLTexID*        texID,
+                     GrGLTexture*      texture);
+
+    // creates an independent GrGLRenderTarget
+    GrGLRenderTarget(GrGpuGL*          gpu,
+                     const Desc&       desc,
+                     const GrGLIRect&  viewport);
+
+    virtual ~GrGLRenderTarget() { this->release(); }
+
+    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
+    // 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();
+    }
+    virtual intptr_t getRenderTargetResolvedHandle() const {
+        return this->textureFBOID();
+    }
+    virtual ResolveType getResolveType() const {
+
+        if (!this->isMultisampled() ||
+            fRTFBOID == fTexFBOID) {
+            // catches FBO 0 and non MSAA case
+            return kAutoResolves_ResolveType;
+        } else if (kUnresolvableFBOID == fTexFBOID) {
+            return kCantResolve_ResolveType;
+        } else {
+            return kCanResolve_ResolveType;
+        }
+    }
+
+protected:
+    // override of GrResource
+    virtual void onAbandon() SK_OVERRIDE;
+    virtual void onRelease() SK_OVERRIDE;
+
+private:
+    GrGLuint      fRTFBOID;
+    GrGLuint      fTexFBOID;
+
+    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
+    // 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;
+
+    // non-NULL if this RT was created by Gr with an associated GrGLTexture.
+    GrGLTexID* fTexIDObj;
+
+    void init(const Desc& desc, const GrGLIRect& viewport, GrGLTexID* texID);
+
+    typedef GrRenderTarget INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLSL.cpp b/src/gpu/gl/GrGLSL.cpp
new file mode 100644
index 0000000..420af03
--- /dev/null
+++ b/src/gpu/gl/GrGLSL.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 "GrGLSL.h"
+#include "GrGLShaderVar.h"
+#include "SkString.h"
+
+GrGLSLGeneration GrGetGLSLGeneration(GrGLBinding binding,
+                                   const GrGLInterface* gl) {
+    GrGLSLVersion ver = GrGLGetGLSLVersion(gl);
+    switch (binding) {
+        case kDesktop_GrGLBinding:
+            GrAssert(ver >= GR_GLSL_VER(1,10));
+            if (ver >= GR_GLSL_VER(1,50)) {
+                return k150_GrGLSLGeneration;
+            } else if (ver >= GR_GLSL_VER(1,30)) {
+                return k130_GrGLSLGeneration;
+            } else {
+                return k110_GrGLSLGeneration;
+            }
+        case kES2_GrGLBinding:
+            // version 1.00 of ES GLSL based on ver 1.20 of desktop GLSL
+            GrAssert(ver >= GR_GL_VER(1,00));
+            return k110_GrGLSLGeneration;
+        default:
+            GrCrash("Unknown GL Binding");
+            return k110_GrGLSLGeneration; // suppress warning
+    }
+}
+
+const char* GrGetGLSLVersionDecl(GrGLBinding binding,
+                                   GrGLSLGeneration gen) {
+    switch (gen) {
+        case k110_GrGLSLGeneration:
+            if (kES2_GrGLBinding == binding) {
+                // ES2s shader language is based on version 1.20 but is version
+                // 1.00 of the ES language.
+                return "#version 100\n";
+            } else {
+                GrAssert(kDesktop_GrGLBinding == binding);
+                return "#version 110\n";
+            }
+        case k130_GrGLSLGeneration:
+            GrAssert(kDesktop_GrGLBinding == binding);
+            return "#version 130\n";
+        case k150_GrGLSLGeneration:
+            GrAssert(kDesktop_GrGLBinding == binding);
+            return "#version 150\n";
+        default:
+            GrCrash("Unknown GL version.");
+            return ""; // suppress warning
+    }
+}
+
+bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
+                             const char* nameIfDeclared,
+                             GrGLShaderVar* var) {
+    bool declaredOutput = k110_GrGLSLGeneration != gen;
+    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
new file mode 100644
index 0000000..cbff273
--- /dev/null
+++ b/src/gpu/gl/GrGLSL.h
@@ -0,0 +1,176 @@
+/*
+ * 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 GrGLSL_DEFINED
+#define GrGLSL_DEFINED
+
+#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)
+     */
+    k110_GrGLSLGeneration,
+    /**
+     * Desktop GLSL 1.30
+     */
+    k130_GrGLSLGeneration,
+    /**
+     * Dekstop 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
+ * langauges.)
+ */
+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 {
+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];
+}
+
+const char* GrGLSLOnesVecf(int count) {
+    static const char* kONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
+                                     "vec3(1,1,1)", "vec4(1,1,1,1)"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(kONESVEC));
+    return kONESVEC[count];
+}
+
+const char* GrGLSLZerosVecf(int count) {
+    static 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
+ * version.
+ */
+const char* GrGetGLSLVersionDecl(GrGLBinding binding,
+                                 GrGLSLGeneration v);
+
+/**
+ * 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
+ *      (using glBindFragDataLocation)
+ *    If the function returns false:
+ *    * Parameter var's name will be set to the GLSL built-in color output name.
+ *    * Do not declare the variable in the shader.
+ *    * Do not use glBindFragDataLocation to bind the variable
+ * In either case var is initialized to represent the color output in the
+ * shader.
+ */
+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 appeneded.
+  */
+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 appened (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 custom stage-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..473c7b4
--- /dev/null
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -0,0 +1,393 @@
+/*
+ * Copyright 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)
+    , fCurrentStage(kNonStageIdx)
+    , fTexCoordVaryingType(kVoid_GrSLType) {
+}
+
+void GrGLShaderBuilder::setupTextureAccess(const char* varyingFSName, GrSLType varyingType) {
+    // FIXME: We don't know how the custom stage will manipulate the coords. So we give up on using
+    // projective texturing and always give the stage 2D coords. This will be fixed when custom
+    // stages are repsonsible for setting up their own tex coords / tex matrices.
+    switch (varyingType) {
+        case kVec2f_GrSLType:
+            fDefaultTexCoordsName = varyingFSName;
+            fTexCoordVaryingType = kVec2f_GrSLType;
+            break;
+        case kVec3f_GrSLType: {
+            fDefaultTexCoordsName = "inCoord";
+            GrAssert(kNonStageIdx != fCurrentStage);
+            fDefaultTexCoordsName.appendS32(fCurrentStage);
+            fTexCoordVaryingType = kVec3f_GrSLType;
+            fFSCode.appendf("\t%s %s = %s.xy / %s.z;\n",
+                            GrGLShaderVar::TypeString(kVec2f_GrSLType),
+                            fDefaultTexCoordsName.c_str(),
+                            varyingFSName,
+                            varyingFSName);
+            break;
+        }
+        default:
+            GrCrash("Tex coords must either be Vec2f or Vec3f");
+    }
+}
+
+void GrGLShaderBuilder::appendTextureLookup(SkString* out,
+                                            const GrGLShaderBuilder::TextureSampler& sampler,
+                                            const char* coordName,
+                                            GrSLType varyingType) const {
+    GrAssert(NULL != sampler.textureAccess());
+
+    if (NULL == coordName) {
+        coordName = fDefaultTexCoordsName.c_str();
+        varyingType = kVec2f_GrSLType;
+    }
+    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());
+}
+
+GrCustomStage::StageKey GrGLShaderBuilder::KeyForTextureAccess(const GrTextureAccess& access,
+                                                               const GrGLCaps& caps) {
+    GrCustomStage::StageKey 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));
+    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 == fCurrentStage) {
+        uniName->printf("u%s", name);
+    } else {
+        uniName->printf("u%s%d", name, fCurrentStage);
+    }
+    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 == fCurrentStage) {
+        fVSOutputs.back().accessName()->printf("v%s", name);
+    } else {
+        fVSOutputs.back().accessName()->printf("v%s%d", name, fCurrentStage);
+    }
+    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 == fCurrentStage) {
+            fGSOutputs.back().accessName()->printf("g%s", name);
+        } else {
+            fGSOutputs.back().accessName()->printf("g%s%d", name, fCurrentStage);
+        }
+        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();
+    }
+}
+
+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 != fCurrentStage) {
+        outName->printf(" %s_%d", name, fCurrentStage);
+    } 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(fVSCode);
+            break;
+        case kGeometry_ShaderType:
+            if (fUsesGS) {
+                *shaderStr = fHeader;
+                shaderStr->append(fGSHeader);
+                this->appendDecls(fGSInputs, shaderStr);
+                this->appendDecls(fGSOutputs, shaderStr);
+                shaderStr->append(fGSCode);
+            } else {
+                shaderStr->reset();
+            }
+            break;
+        case kFragment_ShaderType:
+            *shaderStr = fHeader;
+            append_default_precision_qualifier(kDefaultFragmentPrecision,
+                                               fContext.binding(),
+                                               shaderStr);
+            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(fFSCode);
+            break;
+    }
+ }
+
+void GrGLShaderBuilder::finished(GrGLuint programID) {
+    fUniformManager.getUniformLocations(programID, fUniforms);
+}
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
new file mode 100644
index 0000000..f143af3
--- /dev/null
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -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.
+ */
+
+#ifndef GrGLShaderBuilder_DEFINED
+#define GrGLShaderBuilder_DEFINED
+
+#include "GrAllocator.h"
+#include "GrCustomStage.h"
+#include "gl/GrGLShaderVar.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:
+    /**
+     * Used by GrGLProgramStages 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:
+        void init(GrGLShaderBuilder* builder, const GrTextureAccess* access) {
+            GrAssert(NULL == fTextureAccess);
+            GrAssert(GrGLUniformManager::kInvalidUniformHandle == fSamplerUniform);
+
+            GrAssert(NULL != builder);
+            GrAssert(NULL != access);
+            fSamplerUniform = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                  kSampler2D_GrSLType,
+                                                  "Sampler");
+            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&);
+
+    /** Determines whether we should use texture2D() or texture2Dproj(), and if an explicit divide
+        is required for the sample coordinates, creates the new variable and emits the code to
+        initialize it. This should only be called by GrGLProgram.*/
+    void setupTextureAccess(const char* varyingFSName, GrSLType varyingType);
+
+    /** Appends a texture sample with projection if necessary; if coordName is not
+        specified, uses fSampleCoords. 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 = NULL,
+                             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 = NULL,
+                                        GrSLType coordType = kVec2f_GrSLType) const;
+
+    /** Gets the name of the default texture coords which are always kVec2f */
+    const char* defaultTexCoordsName() const { return fDefaultTexCoordsName.c_str(); }
+
+    /* Returns true if the texture matrix from which the default texture coords are computed has
+       perspective. */
+    bool defaultTextureMatrixIsPerspective() const {
+        return fTexCoordVaryingType == kVec3f_GrSLType;
+    }
+
+    /** 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 StageKey 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 GrCustomStage::StageKey KeyForTextureAccess(const GrTextureAccess& access,
+                                                       const GrGLCaps& caps);
+
+    /** 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 visibilty 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;
+
+    /**
+     * Shorcut 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);
+
+    /** Called after building is complete to get the final shader string. */
+    void getShader(ShaderType, SkString*) const;
+
+    /**
+     * TODO: Make this do all the compiling, linking, etc. Hide from the custom stages
+     */
+    void finished(GrGLuint programID);
+
+    /**
+     * Sets the current stage (used to make variable names unique).
+     * TODO: Hide from the custom stages
+     */
+    void setCurrentStage(int stage) { fCurrentStage = stage; }
+    void setNonStage() { fCurrentStage = kNonStageIdx; }
+
+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                     fCurrentStage;
+    SkString                fFSFunctions;
+
+    /// Per-stage settings - only valid while we're inside GrGLProgram::genStageCode().
+    //@{
+    GrSLType         fTexCoordVaryingType;  // the type, either Vec2f or Vec3f, of the coords passed
+                                            // as a varying from the VS to the FS.
+    SkString         fDefaultTexCoordsName; // the name of the default 2D coords value.
+    //@}
+
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLShaderVar.h b/src/gpu/gl/GrGLShaderVar.h
new file mode 100644
index 0000000..e8f491c
--- /dev/null
+++ b/src/gpu/gl/GrGLShaderVar.h
@@ -0,0 +1,339 @@
+/*
+ * 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 GrGLShaderVar_DEFINED
+#define GrGLShaderVar_DEFINED
+
+#include "GrGLContextInfo.h"
+#include "GrGLSL.h"
+#include "SkString.h"
+
+#define USE_UNIFORM_FLOAT_ARRAYS true
+
+/**
+ * Represents a variable in a shader
+ */
+class GrGLShaderVar {
+public:
+
+    /**
+     * Early versions of GLSL have Varying and Attribute; those are later
+     * deprecated, but we still need to know whether a Varying variable
+     * should be treated as In or Out.
+     */
+    enum TypeModifier {
+        kNone_TypeModifier,
+        kOut_TypeModifier,
+        kIn_TypeModifier,
+        kUniform_TypeModifier,
+        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.
+    };
+
+    /**
+     * Defaults to a float with no precision specifier
+     */
+    GrGLShaderVar() {
+        fType = kFloat_GrSLType;
+        fTypeModifier = kNone_TypeModifier;
+        fCount = kNonArray;
+        fPrecision = kDefault_Precision;
+        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;
+        fUseUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS;
+        fName = name;
+    }
+
+    GrGLShaderVar(const GrGLShaderVar& var)
+        : fType(var.fType)
+        , fTypeModifier(var.fTypeModifier)
+        , fName(var.fName)
+        , fCount(var.fCount)
+        , fPrecision(var.fPrecision)
+        , fUseUniformFloatArrays(var.fUseUniformFloatArrays) {
+        GrAssert(kVoid_GrSLType != var.fType);
+    }
+
+    /**
+     * Values for array count that have special meaning. We allow 1-sized arrays.
+     */
+    enum {
+        kNonArray     =  0, // not an array
+        kUnsizedArray = -1, // an unsized array (declared with [])
+    };
+
+    /**
+     * Sets as a non-array.
+     */
+    void set(GrSLType type,
+             TypeModifier typeModifier,
+             const SkString& name,
+             Precision precision = kDefault_Precision,
+             bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        GrAssert(kVoid_GrSLType != type);
+        fType = type;
+        fTypeModifier = typeModifier;
+        fName = name;
+        fCount = kNonArray;
+        fPrecision = precision;
+        fUseUniformFloatArrays = useUniformFloatArrays;
+    }
+
+    /**
+     * Sets as a non-array.
+     */
+    void set(GrSLType type,
+             TypeModifier typeModifier,
+             const char* name,
+             Precision precision = kDefault_Precision,
+             bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        GrAssert(kVoid_GrSLType != type);
+        fType = type;
+        fTypeModifier = typeModifier;
+        fName = name;
+        fCount = kNonArray;
+        fPrecision = precision;
+        fUseUniformFloatArrays = useUniformFloatArrays;
+    }
+
+    /**
+     * Set all var options
+     */
+    void set(GrSLType type,
+             TypeModifier typeModifier,
+             const SkString& name,
+             int count,
+             Precision precision = kDefault_Precision,
+             bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        GrAssert(kVoid_GrSLType != type);
+        fType = type;
+        fTypeModifier = typeModifier;
+        fName = name;
+        fCount = count;
+        fPrecision = precision;
+        fUseUniformFloatArrays = useUniformFloatArrays;
+    }
+
+    /**
+     * Set all var options
+     */
+    void set(GrSLType type,
+             TypeModifier typeModifier,
+             const char* name,
+             int count,
+             Precision precision = kDefault_Precision,
+             bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        GrAssert(kVoid_GrSLType != type);
+        fType = type;
+        fTypeModifier = typeModifier;
+        fName = name;
+        fCount = count;
+        fPrecision = precision;
+        fUseUniformFloatArrays = useUniformFloatArrays;
+    }
+
+    /**
+     * Is the var an array.
+     */
+    bool isArray() const { return kNonArray != fCount; }
+    /**
+     * Is this an unsized array, (i.e. declared with []).
+     */
+    bool isUnsizedArray() const { return kUnsizedArray == fCount; }
+    /**
+     * Get the array length of the var.
+     */
+    int getArrayCount() const { return fCount; }
+    /**
+     * Set the array length of the var
+     */
+    void setArrayCount(int count) { fCount = count; }
+    /**
+     * Set to be a non-array.
+     */
+    void setNonArray() { fCount = kNonArray; }
+    /**
+     * Set to be an unsized array.
+     */
+    void setUnsizedArray() { fCount = kUnsizedArray; }
+
+    /**
+     * Access the var name as a writable string
+     */
+    SkString* accessName() { return &fName; }
+    /**
+     * Set the var name
+     */
+    void setName(const SkString& n) { fName = n; }
+    void setName(const char* n) { fName = n; }
+
+    /**
+     * Get the var name.
+     */
+    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
+     */
+    GrSLType getType() const { return fType; }
+    /**
+     * Set the type of the var
+     */
+    void setType(GrSLType type) { fType = type; }
+
+    TypeModifier getTypeModifier() const { return fTypeModifier; }
+    void setTypeModifier(TypeModifier type) { fTypeModifier = type; }
+
+    /**
+     * Get the precision of the var
+     */
+    Precision getPrecision() const { return fPrecision; }
+
+    /**
+     * Set the precision of the var
+     */
+    void setPrecision(Precision p) { fPrecision = p; }
+
+    /**
+     * Write a declaration of this variable to out.
+     */
+    void appendDecl(const GrGLContextInfo& gl, SkString* out) const {
+        if (this->getTypeModifier() != kNone_TypeModifier) {
+           out->append(TypeModifierString(this->getTypeModifier(),
+                                          gl.glslGeneration()));
+           out->append(" ");
+        }
+        out->append(PrecisionString(fPrecision, gl.binding()));
+        GrSLType effectiveType = this->getType();
+        if (this->isArray()) {
+            if (this->isUnsizedArray()) {
+                out->appendf("%s %s[]",
+                             TypeString(effectiveType),
+                             this->getName().c_str());
+            } else {
+                GrAssert(this->getArrayCount() > 0);
+                out->appendf("%s %s[%d]",
+                             TypeString(effectiveType),
+                             this->getName().c_str(),
+                             this->getArrayCount());
+            }
+        } else {
+            out->appendf("%s %s",
+                         TypeString(effectiveType),
+                         this->getName().c_str());
+        }
+    }
+
+    static const char* TypeString(GrSLType t) {
+        switch (t) {
+            case kVoid_GrSLType:
+                return "void";
+            case kFloat_GrSLType:
+                return "float";
+            case kVec2f_GrSLType:
+                return "vec2";
+            case kVec3f_GrSLType:
+                return "vec3";
+            case kVec4f_GrSLType:
+                return "vec4";
+            case kMat33f_GrSLType:
+                return "mat3";
+            case kMat44f_GrSLType:
+                return "mat4";
+            case kSampler2D_GrSLType:
+                return "sampler2D";
+            default:
+                GrCrash("Unknown shader var type.");
+                return ""; // suppress warning
+        }
+    }
+
+    void appendArrayAccess(int index, SkString* out) const {
+        out->appendf("%s[%d]%s",
+                     this->getName().c_str(),
+                     index,
+                     fUseUniformFloatArrays ? "" : ".x");
+    }
+
+    void appendArrayAccess(const char* indexName, SkString* out) const {
+        out->appendf("%s[%s]%s",
+                     this->getName().c_str(),
+                     indexName,
+                     fUseUniformFloatArrays ? "" : ".x");
+    }
+
+private:
+    static const char* TypeModifierString(TypeModifier t, GrGLSLGeneration gen) {
+        switch (t) {
+            case kNone_TypeModifier:
+                return "";
+            case kOut_TypeModifier:
+                return k110_GrGLSLGeneration == gen ? "varying" : "out";
+            case kIn_TypeModifier:
+                return k110_GrGLSLGeneration == gen ? "varying" : "in";
+            case kUniform_TypeModifier:
+                return "uniform";
+            case kAttribute_TypeModifier:
+                return k110_GrGLSLGeneration == gen ? "attribute" : "in";
+            default:
+                GrCrash("Unknown shader variable type modifier.");
+                return ""; // suppress warning
+        }
+    }
+
+    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;
+    SkString        fName;
+    int             fCount;
+    Precision       fPrecision;
+    /// Work around driver bugs on some hardware that don't correctly
+    /// support uniform float []
+    bool            fUseUniformFloatArrays;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLStencilBuffer.cpp b/src/gpu/gl/GrGLStencilBuffer.cpp
new file mode 100644
index 0000000..030b54e
--- /dev/null
+++ b/src/gpu/gl/GrGLStencilBuffer.cpp
@@ -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.
+ */
+
+
+#include "GrGLStencilBuffer.h"
+#include "GrGpuGL.h"
+
+GrGLStencilBuffer::~GrGLStencilBuffer() {
+    this->release();
+}
+
+size_t GrGLStencilBuffer::sizeInBytes() const {
+    uint64_t size = this->width();
+    size *= this->height();
+    size *= fFormat.fTotalBits;
+    size *= GrMax(1,this->numSamples());
+    return static_cast<size_t>(size / 8);
+}
+
+void GrGLStencilBuffer::onRelease() {
+    if (0 != fRenderbufferID) {
+        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
new file mode 100644
index 0000000..2d175f6
--- /dev/null
+++ b/src/gpu/gl/GrGLStencilBuffer.h
@@ -0,0 +1,60 @@
+/*
+ * 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 GrGLStencilBuffer_DEFINED
+#define GrGLStencilBuffer_DEFINED
+
+#include "gl/GrGLInterface.h"
+#include "GrStencilBuffer.h"
+
+class GrGLStencilBuffer : public GrStencilBuffer {
+public:
+    static const GrGLenum kUnknownInternalFormat = ~0U;
+    static const GrGLuint kUnknownBitCount = ~0U;
+    struct Format {
+        GrGLenum  fInternalFormat;
+        GrGLuint  fStencilBits;
+        GrGLuint  fTotalBits;
+        bool      fPacked;
+    };
+
+    GrGLStencilBuffer(GrGpu* gpu, GrGLint rbid,
+                      int width, int height,
+                      int sampleCnt,
+                      const Format& format)
+        : GrStencilBuffer(gpu, width, height, format.fStencilBits, sampleCnt)
+        , fFormat(format)
+        , fRenderbufferID(rbid) {
+    }
+
+    virtual ~GrGLStencilBuffer();
+
+    virtual size_t sizeInBytes() const SK_OVERRIDE;
+
+    GrGLuint renderbufferID() const {
+        return fRenderbufferID;
+    }
+
+    const Format& format() const { return fFormat; }
+
+protected:
+    // overrides of GrResource
+    virtual void onRelease() SK_OVERRIDE;
+    virtual void onAbandon() SK_OVERRIDE;
+
+private:
+    Format fFormat;
+    // may be zero for external SBs associated with external RTs
+    // (we don't require the client to give us the id, just tell
+    // us how many bits of stencil there are).
+    GrGLuint fRenderbufferID;
+
+    typedef GrStencilBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
new file mode 100644
index 0000000..1e34fe5
--- /dev/null
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * 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)
+
+void GrGLTexture::init(GrGpuGL* gpu,
+                       const Desc& textureDesc,
+                       const GrGLRenderTarget::Desc* rtDesc) {
+
+    GrAssert(0 != textureDesc.fTextureID);
+
+    fTexParams.invalidate();
+    fTexParamsTimestamp = GrGpu::kExpiredTimestamp;
+    fTexIDObj           = SkNEW_ARGS(GrGLTexID,
+                                     (GPUGL->glInterface(),
+                                      textureDesc.fTextureID,
+                                      textureDesc.fOwnsID));
+    fOrientation        = textureDesc.fOrientation;
+
+    if (NULL != rtDesc) {
+        // we render to the top left
+        GrGLIRect vp;
+        vp.fLeft   = 0;
+        vp.fWidth  = textureDesc.fWidth;
+        vp.fBottom = 0;
+        vp.fHeight = textureDesc.fHeight;
+
+        fRenderTarget = SkNEW_ARGS(GrGLRenderTarget,
+                                   (gpu, *rtDesc, vp, fTexIDObj, this));
+    }
+}
+
+GrGLTexture::GrGLTexture(GrGpuGL* gpu,
+                         const Desc& textureDesc)
+    : INHERITED(gpu, textureDesc) {
+    this->init(gpu, textureDesc, NULL);
+}
+
+GrGLTexture::GrGLTexture(GrGpuGL* gpu,
+                         const Desc& textureDesc,
+                         const GrGLRenderTarget::Desc& rtDesc)
+    : INHERITED(gpu, textureDesc) {
+    this->init(gpu, textureDesc, &rtDesc);
+}
+
+void GrGLTexture::onRelease() {
+    GPUGL->notifyTextureDelete(this);
+    if (NULL != fTexIDObj) {
+        fTexIDObj->unref();
+        fTexIDObj = NULL;
+    }
+
+    INHERITED::onRelease();
+}
+
+void GrGLTexture::onAbandon() {
+    if (NULL != fTexIDObj) {
+        fTexIDObj->abandon();
+    }
+
+    INHERITED::onAbandon();
+}
+
+intptr_t GrGLTexture::getTextureHandle() const {
+    return fTexIDObj->id();
+}
+
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
new file mode 100644
index 0000000..2e9ee17
--- /dev/null
+++ b/src/gpu/gl/GrGLTexture.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLTexture_DEFINED
+#define GrGLTexture_DEFINED
+
+#include "GrGpu.h"
+#include "GrGLRenderTarget.h"
+
+/**
+ * A ref counted tex id that deletes the texture in its destructor.
+ */
+class GrGLTexID : public GrRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrGLTexID)
+
+    GrGLTexID(const GrGLInterface* gl, GrGLuint texID, bool ownsID)
+        : fGL(gl)
+        , fTexID(texID)
+        , fOwnsID(ownsID) {
+    }
+
+    virtual ~GrGLTexID() {
+        if (0 != fTexID && fOwnsID) {
+            GR_GL_CALL(fGL, DeleteTextures(1, &fTexID));
+        }
+    }
+
+    void abandon() { fTexID = 0; }
+    GrGLuint id() const { return fTexID; }
+
+private:
+    const GrGLInterface* fGL;
+    GrGLuint             fTexID;
+    bool                 fOwnsID;
+
+    typedef GrRefCnt INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+class GrGLTexture : public GrTexture {
+
+public:
+    enum Orientation {
+        kBottomUp_Orientation,
+        kTopDown_Orientation,
+    };
+
+    struct TexParams {
+        GrGLenum fFilter;
+        GrGLenum fWrapS;
+        GrGLenum fWrapT;
+        GrGLenum fSwizzleRGBA[4];
+        void invalidate() { memset(this, 0xff, sizeof(TexParams)); }
+    };
+
+    struct Desc : public GrTextureDesc {
+        GrGLuint        fTextureID;
+        bool            fOwnsID;
+        Orientation     fOrientation;
+    };
+
+    // creates a texture that is also an RT
+    GrGLTexture(GrGpuGL* gpu,
+                const Desc& textureDesc,
+                const GrGLRenderTarget::Desc& rtDesc);
+
+    // creates a non-RT texture
+    GrGLTexture(GrGpuGL* gpu,
+                const Desc& textureDesc);
+
+
+    virtual ~GrGLTexture() { this->release(); }
+
+    virtual intptr_t getTextureHandle() const SK_OVERRIDE;
+
+    virtual void invalidateCachedState() SK_OVERRIDE { fTexParams.invalidate(); }
+
+    // these functions
+    const TexParams& getCachedTexParams(GrGpu::ResetTimestamp* timestamp) const {
+        *timestamp = fTexParamsTimestamp;
+        return fTexParams;
+    }
+    void setCachedTexParams(const TexParams& texParams,
+                            GrGpu::ResetTimestamp timestamp) {
+        fTexParams = texParams;
+        fTexParamsTimestamp = timestamp;
+    }
+    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; }
+
+protected:
+
+    // overrides of GrTexture
+    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,
+              const GrGLRenderTarget::Desc* rtDesc);
+
+    typedef GrTexture INHERITED;
+};
+
+#endif
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..06ce2db
--- /dev/null
+++ b/src/gpu/gl/GrGLUniformManager.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright 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"
+
+#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 custom 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::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..e9856c6
--- /dev/null
+++ b/src/gpu/gl/GrGLUniformManager.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 GrGLUniformManager_DEFINED
+#define GrGLUniformManager_DEFINED
+
+#include "gl/GrGLShaderVar.h"
+#include "gl/GrGLSL.h"
+#include "GrAllocator.h"
+
+#include "SkTArray.h"
+
+class GrGLContextInfo;
+
+/** 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;
+
+    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
new file mode 100644
index 0000000..0e9e21f
--- /dev/null
+++ b/src/gpu/gl/GrGLUtil.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#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 0x%x(%s)", err, get_error_string(err));
+        if (NULL != location) {
+            GrPrintf(" at\n\t%s", location);
+        }
+        if (NULL != call) {
+            GrPrintf("\n\t\t%s", call);
+        }
+        GrPrintf("\n");
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_GL_LOG_CALLS
+    bool gLogCallsGL = !!(GR_GL_LOG_CALLS_START);
+#endif
+
+#if GR_GL_CHECK_ERROR
+    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;
+}
+
+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);
+}
diff --git a/src/gpu/gl/GrGLUtil.h b/src/gpu/gl/GrGLUtil.h
new file mode 100644
index 0000000..17d5a63
--- /dev/null
+++ b/src/gpu/gl/GrGLUtil.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 GrGLUtil_DEFINED
+#define GrGLUtil_DEFINED
+
+#include "gl/GrGLInterface.h"
+#include "GrGLDefines.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+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))
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  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);
+
+// these variants call glGetString()
+bool GrGLHasExtension(const GrGLInterface*, const char* ext);
+GrGLBinding GrGLGetBindingInUse(const GrGLInterface*);
+GrGLVersion GrGLGetVersion(const GrGLInterface*);
+GrGLSLVersion GrGLGetGLSLVersion(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
new file mode 100644
index 0000000..7cee29e
--- /dev/null
+++ b/src/gpu/gl/GrGLVertexBuffer.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 "GrGLVertexBuffer.h"
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+GrGLVertexBuffer::GrGLVertexBuffer(GrGpuGL* gpu,
+                                   GrGLuint id,
+                                   size_t sizeInBytes,
+                                   bool dynamic)
+    : INHERITED(gpu, sizeInBytes, dynamic)
+    , fBufferID(id)
+    , fLockPtr(NULL) {
+}
+
+void GrGLVertexBuffer::onRelease() {
+    // make sure we've not been abandoned
+    if (fBufferID) {
+        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 {
+    GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, fBufferID));
+    GPUGL->notifyVertexBufferBind(this);
+}
+
+GrGLuint GrGLVertexBuffer::bufferID() const {
+    return fBufferID;
+}
+
+void* GrGLVertexBuffer::lock() {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    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,
+                           this->dynamic() ? GR_GL_DYNAMIC_DRAW :
+                                             GR_GL_STATIC_DRAW));
+        GR_GL_CALL_RET(GPUGL->glInterface(),
+                       fLockPtr,
+                       MapBuffer(GR_GL_ARRAY_BUFFER, GR_GL_WRITE_ONLY));
+        return fLockPtr;
+    }
+    return NULL;
+}
+
+void* GrGLVertexBuffer::lockPtr() const {
+    return fLockPtr;
+}
+
+void GrGLVertexBuffer::unlock() {
+
+    GrAssert(fBufferID);
+    GrAssert(isLocked());
+    GrAssert(this->getGpu()->getCaps().bufferLockSupport());
+
+    this->bind();
+    GL_CALL(UnmapBuffer(GR_GL_ARRAY_BUFFER));
+    fLockPtr = NULL;
+}
+
+bool GrGLVertexBuffer::isLocked() const {
+    GrAssert(!this->isValid() || fBufferID);
+    // 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,
+                                     GR_GL_BUFFER_MAPPED, &mapped));
+        GrAssert(!!mapped == !!fLockPtr);
+    }
+#endif
+    return NULL != fLockPtr;
+}
+
+bool GrGLVertexBuffer::updateData(const void* src, size_t srcSizeInBytes) {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    if (srcSizeInBytes > this->sizeInBytes()) {
+        return false;
+    }
+    this->bind();
+    GrGLenum usage = dynamic() ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW;
+
+#if GR_GL_USE_BUFFER_DATA_NULL_HINT
+    if (this->sizeInBytes() == srcSizeInBytes) {
+        GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes, src, usage));
+    } else {
+        // Before we call glBufferSubData we give the driver a hint using
+        // glBufferData with NULL. This makes the old buffer contents
+        // inaccessible to future draws. The GPU may still be processing
+        // 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,
+                           this->sizeInBytes(), NULL, usage));
+        GL_CALL(BufferSubData(GR_GL_ARRAY_BUFFER, 0, srcSizeInBytes, src));
+    }
+#else
+    // 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..))
+    bool doSubData = false;
+#if GR_GL_MAC_BUFFER_OBJECT_PERFOMANCE_WORKAROUND
+    static int N = 0;
+    // 128 was chosen experimentally. At 256 a slight hitchiness was noticed
+    // when dragging a Chromium window around with a canvas tab backgrounded.
+    doSubData = 0 == (N % 128);
+    ++N;
+#endif
+    if (doSubData) {
+        // The workaround is to do a glBufferData followed by glBufferSubData.
+        // Chromium's command buffer may turn a glBufferSubData where the size
+        // exactly matches the buffer size into a glBufferData. So we tack 1
+        // extra byte onto the glBufferData.
+        GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes + 1,
+                           NULL, usage));
+        GL_CALL(BufferSubData(GR_GL_ARRAY_BUFFER, 0, srcSizeInBytes, src));
+    } else {
+        GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes, src, usage));
+    }
+#endif
+    return true;
+}
+
diff --git a/src/gpu/gl/GrGLVertexBuffer.h b/src/gpu/gl/GrGLVertexBuffer.h
new file mode 100644
index 0000000..bb829d3
--- /dev/null
+++ b/src/gpu/gl/GrGLVertexBuffer.h
@@ -0,0 +1,51 @@
+/*
+ * 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 GrGLVertexBuffer_DEFINED
+#define GrGLVertexBuffer_DEFINED
+
+#include "GrVertexBuffer.h"
+#include "gl/GrGLInterface.h"
+
+class GrGpuGL;
+
+class GrGLVertexBuffer : public GrVertexBuffer {
+
+public:
+    virtual ~GrGLVertexBuffer() { this->release(); }
+    // overrides of GrVertexBuffer
+    virtual void* lock();
+    virtual void* lockPtr() const;
+    virtual void unlock();
+    virtual bool isLocked() const;
+    virtual bool updateData(const void* src, size_t srcSizeInBytes);
+    GrGLuint bufferID() const;
+
+protected:
+    GrGLVertexBuffer(GrGpuGL* gpu,
+                     GrGLuint id,
+                     size_t sizeInBytes,
+                     bool dynamic);
+
+    // overrides of GrResource
+    virtual void onAbandon() SK_OVERRIDE;
+    virtual void onRelease() SK_OVERRIDE;
+
+private:
+    void bind() const;
+
+    GrGLuint     fBufferID;
+    void*        fLockPtr;
+
+    friend class GrGpuGL;
+
+    typedef GrVertexBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
new file mode 100644
index 0000000..157d9bc
--- /dev/null
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -0,0 +1,2375 @@
+/*
+ * Copyright 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 "GrGLStencilBuffer.h"
+#include "GrGLPath.h"
+#include "GrGLShaderBuilder.h"
+#include "GrTemplates.h"
+#include "GrTypes.h"
+#include "SkTemplates.h"
+
+static const GrGLuint GR_MAX_GLUINT = ~0U;
+static const GrGLint  GR_INVAL_GLINT = ~0;
+
+#define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
+#define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->glInterface(), RET, X)
+
+// we use a spare texture unit to avoid
+// mucking with the state of any of the stages.
+static const int SPARE_TEX_UNIT = GrDrawState::kNumStages;
+
+#define SKIP_CACHE_CHECK    true
+
+#if GR_GL_CHECK_ALLOC_WITH_GET_ERROR
+    #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
+    #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
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const GrGLenum gXfermodeCoeff2Blend[] = {
+    GR_GL_ZERO,
+    GR_GL_ONE,
+    GR_GL_SRC_COLOR,
+    GR_GL_ONE_MINUS_SRC_COLOR,
+    GR_GL_DST_COLOR,
+    GR_GL_ONE_MINUS_DST_COLOR,
+    GR_GL_SRC_ALPHA,
+    GR_GL_ONE_MINUS_SRC_ALPHA,
+    GR_GL_DST_ALPHA,
+    GR_GL_ONE_MINUS_DST_ALPHA,
+    GR_GL_CONSTANT_COLOR,
+    GR_GL_ONE_MINUS_CONSTANT_COLOR,
+    GR_GL_CONSTANT_ALPHA,
+    GR_GL_ONE_MINUS_CONSTANT_ALPHA,
+
+    // extended blend coeffs
+    GR_GL_SRC1_COLOR,
+    GR_GL_ONE_MINUS_SRC1_COLOR,
+    GR_GL_SRC1_ALPHA,
+    GR_GL_ONE_MINUS_SRC1_ALPHA,
+};
+
+bool GrGpuGL::BlendCoeffReferencesConstant(GrBlendCoeff coeff) {
+    static const bool gCoeffReferencesBlendConst[] = {
+        false,
+        false,
+        false,
+        false,
+        false,
+        false,
+        false,
+        false,
+        false,
+        false,
+        true,
+        true,
+        true,
+        true,
+
+        // extended blend coeffs
+        false,
+        false,
+        false,
+        false,
+    };
+    return gCoeffReferencesBlendConst[coeff];
+    GR_STATIC_ASSERT(kTotalGrBlendCoeffCount ==
+                     GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
+
+    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_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(kTotalGrBlendCoeffCount ==
+                     GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool gPrintStartupSpew;
+
+static bool fbo_test(const GrGLInterface* gl, int w, int h) {
+
+    GR_GL_CALL(gl, ActiveTexture(GR_GL_TEXTURE0 + SPARE_TEX_UNIT));
+
+    GrGLuint testFBO;
+    GR_GL_CALL(gl, GenFramebuffers(1, &testFBO));
+    GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, testFBO));
+    GrGLuint testRTTex;
+    GR_GL_CALL(gl, GenTextures(1, &testRTTex));
+    GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, testRTTex));
+    // some implementations require texture to be mip-map complete before
+    // FBO with level 0 bound as color attachment will be framebuffer complete.
+    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+                                 GR_GL_TEXTURE_MIN_FILTER,
+                                 GR_GL_NEAREST));
+    GR_GL_CALL(gl, TexImage2D(GR_GL_TEXTURE_2D, 0, GR_GL_RGBA, w, h,
+                              0, GR_GL_RGBA, GR_GL_UNSIGNED_BYTE, NULL));
+    GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, 0));
+    GR_GL_CALL(gl, FramebufferTexture2D(GR_GL_FRAMEBUFFER,
+                                        GR_GL_COLOR_ATTACHMENT0,
+                                        GR_GL_TEXTURE_2D, testRTTex, 0));
+    GrGLenum status;
+    GR_GL_CALL_RET(gl, status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+    GR_GL_CALL(gl, DeleteFramebuffers(1, &testFBO));
+    GR_GL_CALL(gl, DeleteTextures(1, &testRTTex));
+
+    return status == GR_GL_FRAMEBUFFER_COMPLETE;
+}
+
+GrGpuGL::GrGpuGL(const GrGLContextInfo& ctxInfo) : fGLContextInfo(ctxInfo) {
+
+    GrAssert(ctxInfo.isInitialized());
+
+    fillInConfigRenderableTable();
+
+    fPrintedCaps = false;
+
+    GrGLClearErr(fGLContextInfo.interface());
+
+    if (gPrintStartupSpew) {
+        const GrGLubyte* ext;
+        GL_CALL_RET(ext, GetString(GR_GL_EXTENSIONS));
+        const GrGLubyte* vendor;
+        const GrGLubyte* renderer;
+        const GrGLubyte* version;
+        GL_CALL_RET(vendor, GetString(GR_GL_VENDOR));
+        GL_CALL_RET(renderer, GetString(GR_GL_RENDERER));
+        GL_CALL_RET(version, GetString(GR_GL_VERSION));
+        GrPrintf("------------------------- create GrGpuGL %p --------------\n",
+                 this);
+        GrPrintf("------ VENDOR %s\n", vendor);
+        GrPrintf("------ RENDERER %s\n", renderer);
+        GrPrintf("------ VERSION %s\n",  version);
+        GrPrintf("------ EXTENSIONS\n %s \n", ext);
+    }
+
+    this->initCaps();
+
+    fProgramCache = SkNEW_ARGS(ProgramCache, (this->glContextInfo()));
+
+    fLastSuccessfulStencilFmtIdx = 0;
+    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
+    // since we will unref the GrGLInterface.
+    this->releaseResources();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::initCaps() {
+    GrGLint maxTextureUnits;
+    // check FS and fixed-function texture unit limits
+    // we only use textures in the fragment stage currently.
+    // checks are > to make sure we have a spare unit.
+    const GrGLInterface* gl = this->glInterface();
+    GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+    GrAssert(maxTextureUnits > GrDrawState::kNumStages);
+
+    CapsInternals* caps = this->capsInternals();
+
+    GrGLint numFormats;
+    GR_GL_GetIntegerv(gl, GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats);
+    SkAutoSTMalloc<10, GrGLint> formats(numFormats);
+    GR_GL_GetIntegerv(gl, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats);
+    for (int i = 0; i < numFormats; ++i) {
+        if (formats[i] == GR_GL_PALETTE8_RGBA8) {
+            caps->f8BitPaletteSupport = true;
+            break;
+        }
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        // 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).
+        caps->fTwoSidedStencilSupport = (this->glVersion() >= GR_GL_VER(2,0));
+        // supported on GL 1.4 and higher or by extension
+        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
+        caps->fTwoSidedStencilSupport = true;
+        caps->fStencilWrapOpsSupport = true;
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        caps->fBufferLockSupport = true; // we require VBO support and the desktop VBO
+                                         // extension includes glMapBuffer.
+    } else {
+        caps->fBufferLockSupport = this->hasExtension("GL_OES_mapbuffer");
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        if (this->glVersion() >= GR_GL_VER(2,0) ||
+            this->hasExtension("GL_ARB_texture_non_power_of_two")) {
+            caps->fNPOTTextureTileSupport = true;
+        } else {
+            caps->fNPOTTextureTileSupport = false;
+        }
+    } else {
+        // Unextended ES2 supports NPOT textures with clamp_to_edge and non-mip filters only
+        caps->fNPOTTextureTileSupport = this->hasExtension("GL_OES_texture_npot");
+    }
+
+    caps->fHWAALineSupport = (kDesktop_GrGLBinding == this->glBinding());
+
+    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:
+    caps->fMaxRenderTargetSize = GrMin(caps->fMaxTextureSize, caps->fMaxRenderTargetSize);
+
+    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");
+    }
+}
+
+void GrGpuGL::fillInConfigRenderableTable() {
+
+    // 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.
+
+    // 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;
+        }
+    } else {
+        // On ES we can only hope for R8
+        fConfigRenderSupport[kAlpha_8_GrPixelConfig] =
+                                this->glCaps().textureRedSupport();
+    }
+
+    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;
+    }
+}
+
+GrPixelConfig GrGpuGL::preferredReadPixelsConfig(GrPixelConfig config) const {
+    if (GR_GL_RGBA_8888_PIXEL_OPS_SLOW && GrPixelConfigIsRGBA8888(config)) {
+        return GrPixelConfigSwapRAndB(config);
+    } else {
+        return config;
+    }
+}
+
+GrPixelConfig GrGpuGL::preferredWritePixelsConfig(GrPixelConfig config) const {
+    if (GR_GL_RGBA_8888_PIXEL_OPS_SLOW && GrPixelConfigIsRGBA8888(config)) {
+        return GrPixelConfigSwapRAndB(config);
+    } else {
+        return config;
+    }
+}
+
+bool GrGpuGL::fullReadPixelsIsFasterThanPartial() const {
+    return SkToBool(GR_GL_FULL_READPIXELS_FASTER_THAN_PARTIAL);
+}
+
+void GrGpuGL::onResetContext() {
+    if (gPrintStartupSpew && !fPrintedCaps) {
+        fPrintedCaps = true;
+        this->getCaps().print();
+        this->glCaps().print();
+    }
+
+    // we don't use the zb at all
+    GL_CALL(Disable(GR_GL_DEPTH_TEST));
+    GL_CALL(DepthMask(GR_GL_FALSE));
+
+    fHWDrawFace = GrDrawState::kInvalid_DrawFace;
+    fHWDitherEnabled = kUnknown_TriState;
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        // Desktop-only state that we never change
+        GL_CALL(Disable(GR_GL_POINT_SMOOTH));
+        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));
+
+        // 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
+    fHWActiveTextureUnitIdx = -1;
+
+    fHWBlendState.invalidate();
+
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        fHWBoundTextures[s] = NULL;
+    }
+
+    fHWScissorSettings.invalidate();
+
+    fHWViewport.invalidate();
+
+    fHWStencilSettings.invalidate();
+    fHWStencilTestEnabled = kUnknown_TriState;
+
+    fHWGeometryState.fIndexBuffer = NULL;
+    fHWGeometryState.fVertexBuffer = NULL;
+
+    fHWGeometryState.fArrayPtrsDirty = true;
+
+    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()) {
+        GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
+    }
+    if (this->glCaps().packRowLengthSupport()) {
+        GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, 0));
+    }
+    if (this->glCaps().unpackFlipYSupport()) {
+        GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_FALSE));
+    }
+    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)) {
+        return NULL;
+    }
+
+    // next line relies on PlatformTextureDesc'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;
+
+    GrGLTexture* texture = NULL;
+    if (desc.fFlags & kRenderTarget_GrPlatformTextureFlag) {
+        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,
+                                             glTexDesc.fHeight,
+                                             glTexDesc.fTextureID,
+                                             &glRTDesc)) {
+            return NULL;
+        }
+        texture = SkNEW_ARGS(GrGLTexture, (this, glTexDesc, glRTDesc));
+    } else {
+        texture = SkNEW_ARGS(GrGLTexture, (this, glTexDesc));
+    }
+    if (NULL == texture) {
+        return NULL;
+    }
+
+    this->setSpareTextureUnit();
+    return texture;
+}
+
+GrRenderTarget* GrGpuGL::onCreatePlatformRenderTarget(const GrPlatformRenderTargetDesc& 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;
+    GrGLIRect viewport;
+    viewport.fLeft   = 0;
+    viewport.fBottom = 0;
+    viewport.fWidth  = desc.fWidth;
+    viewport.fHeight = desc.fHeight;
+
+    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 = SkNEW_ARGS(GrGLStencilBuffer,
+                                           (this,
+                                            0,
+                                            desc.fWidth,
+                                            desc.fHeight,
+                                            desc.fSampleCnt,
+                                            format));
+        tgt->setStencilBuffer(sb);
+        sb->unref();
+    }
+    return tgt;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::onWriteTexturePixels(GrTexture* texture,
+                                   int left, int top, int width, int height,
+                                   GrPixelConfig config, const void* buffer,
+                                   size_t rowBytes) {
+    if (NULL == buffer) {
+        return;
+    }
+    GrGLTexture* glTex = static_cast<GrGLTexture*>(texture);
+
+    this->setSpareTextureUnit();
+    GL_CALL(BindTexture(GR_GL_TEXTURE_2D, glTex->textureID()));
+    GrGLTexture::Desc desc;
+    desc.fFlags = glTex->desc().fFlags;
+    desc.fWidth = glTex->width();
+    desc.fHeight = glTex->height();
+    desc.fConfig = glTex->config();
+    desc.fSampleCnt = glTex->desc().fSampleCnt;
+    desc.fTextureID = glTex->textureID();
+    desc.fOrientation = glTex->orientation();
+
+    this->uploadTexData(desc, false,
+                        left, top, width, height,
+                        config, buffer, rowBytes);
+}
+
+namespace {
+bool adjust_pixel_ops_params(int surfaceWidth,
+                             int surfaceHeight,
+                             size_t bpp,
+                             int* left, int* top, int* width, int* height,
+                             const void** data,
+                             size_t* rowBytes) {
+    if (!*rowBytes) {
+        *rowBytes = *width * bpp;
+    }
+
+    GrIRect subRect = GrIRect::MakeXYWH(*left, *top, *width, *height);
+    GrIRect bounds = GrIRect::MakeWH(surfaceWidth, surfaceHeight);
+
+    if (!subRect.intersect(bounds)) {
+        return false;
+    }
+    *data = reinterpret_cast<const void*>(reinterpret_cast<intptr_t>(*data) +
+          (subRect.fTop - *top) * *rowBytes + (subRect.fLeft - *left) * bpp);
+
+    *left = subRect.fLeft;
+    *top = subRect.fTop;
+    *width = subRect.width();
+    *height = subRect.height();
+    return true;
+}
+}
+
+bool GrGpuGL::uploadTexData(const GrGLTexture::Desc& desc,
+                            bool isNewTexture,
+                            int left, int top, int width, int height,
+                            GrPixelConfig dataConfig,
+                            const void* data,
+                            size_t rowBytes) {
+    GrAssert(NULL != data || isNewTexture);
+
+    size_t bpp = GrBytesPerPixel(dataConfig);
+    if (!adjust_pixel_ops_params(desc.fWidth, desc.fHeight, bpp, &left, &top,
+                                 &width, &height, &data, &rowBytes)) {
+        return false;
+    }
+    size_t trimRowBytes = width * bpp;
+
+    // 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 && 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;
+    GrGLenum externalFormat;
+    GrGLenum externalType;
+    // glTexStorage requires sized internal formats on both desktop and ES. ES
+    // glTexImage requires an unsized format.
+    if (!this->configToGLFormats(dataConfig, useTexStorage, &internalFormat,
+                                 &externalFormat, &externalType)) {
+        return false;
+    }
+
+    if (!isNewTexture && GR_GL_PALETTE8_RGBA8 == internalFormat) {
+        // paletted textures cannot be updated
+        return false;
+    }
+
+    /*
+     *  check whether to allocate a temporary buffer for flipping y or
+     *  because our srcData has extra bytes past each row. If so, we need
+     *  to trim those off here, since GL ES may not let us specify
+     *  GL_UNPACK_ROW_LENGTH.
+     */
+    bool restoreGLRowLength = false;
+    bool swFlipY = false;
+    bool glFlipY = false;
+    if (NULL != data) {
+        if (GrGLTexture::kBottomUp_Orientation == desc.fOrientation) {
+            if (this->glCaps().unpackFlipYSupport()) {
+                glFlipY = true;
+            } else {
+                swFlipY = true;
+            }
+        }
+        if (this->glCaps().unpackRowLengthSupport() && !swFlipY) {
+            // can't use this for flipping, only non-neg values allowed. :(
+            if (rowBytes != trimRowBytes) {
+                GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp);
+                GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength));
+                restoreGLRowLength = true;
+            }
+        } else {
+            if (trimRowBytes != rowBytes || swFlipY) {
+                // copy data into our new storage, skipping the trailing bytes
+                size_t trimSize = height * trimRowBytes;
+                const char* src = (const char*)data;
+                if (swFlipY) {
+                    src += (height - 1) * rowBytes;
+                }
+                char* dst = (char*)tempStorage.reset(trimSize);
+                for (int y = 0; y < height; y++) {
+                    memcpy(dst, src, trimRowBytes);
+                    if (swFlipY) {
+                        src -= rowBytes;
+                    } else {
+                        src += rowBytes;
+                    }
+                    dst += trimRowBytes;
+                }
+                // now point data to our copied version
+                data = tempStorage.get();
+            }
+        }
+        if (glFlipY) {
+            GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_TRUE));
+        }
+        GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, static_cast<GrGLint>(bpp)));
+    }
+    bool succeeded = true;
+    if (isNewTexture &&
+        0 == left && 0 == top &&
+        desc.fWidth == width && desc.fHeight == height) {
+        CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+        if (useTexStorage) {
+            // We never resize  or change formats of textures. We don't use
+            // mipmaps currently.
+            GL_ALLOC_CALL(this->glInterface(),
+                          TexStorage2D(GR_GL_TEXTURE_2D,
+                                       1, // levels
+                                       internalFormat,
+                                       desc.fWidth, desc.fHeight));
+        } else {
+            if (GR_GL_PALETTE8_RGBA8 == internalFormat) {
+                GrGLsizei imageSize = desc.fWidth * desc.fHeight +
+                                      kGrColorTableSize;
+                GL_ALLOC_CALL(this->glInterface(),
+                              CompressedTexImage2D(GR_GL_TEXTURE_2D,
+                                                   0, // level
+                                                   internalFormat,
+                                                   desc.fWidth, desc.fHeight,
+                                                   0, // border
+                                                   imageSize,
+                                                   data));
+            } else {
+                GL_ALLOC_CALL(this->glInterface(),
+                              TexImage2D(GR_GL_TEXTURE_2D,
+                                         0, // level
+                                         internalFormat,
+                                         desc.fWidth, desc.fHeight,
+                                         0, // border
+                                         externalFormat, externalType,
+                                         data));
+            }
+        }
+        GrGLenum error = CHECK_ALLOC_ERROR(this->glInterface());
+        if (error != GR_GL_NO_ERROR) {
+            succeeded = false;
+        } else {
+            // if we have data and we used TexStorage to create the texture, we
+            // now upload with TexSubImage.
+            if (NULL != data && useTexStorage) {
+                GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D,
+                                      0, // level
+                                      left, top,
+                                      width, height,
+                                      externalFormat, externalType,
+                                      data));
+            }
+        }
+    } else {
+        if (swFlipY || glFlipY) {
+            top = desc.fHeight - (top + height);
+        }
+        GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D,
+                              0, // level
+                              left, top,
+                              width, height,
+                              externalFormat, externalType, data));
+    }
+
+    if (restoreGLRowLength) {
+        GrAssert(this->glCaps().unpackRowLengthSupport());
+        GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
+    }
+    if (glFlipY) {
+        GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_FALSE));
+    }
+    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;
+
+    GrGLenum status;
+
+    GrGLenum msColorFormat = 0; // suppress warning
+
+    GL_CALL(GenFramebuffers(1, &desc->fTexFBOID));
+    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.
+    if (desc->fSampleCnt > 0) {
+        if (GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType()) {
+            goto FAILED;
+        }
+        GL_CALL(GenFramebuffers(1, &desc->fRTFBOID));
+        GL_CALL(GenRenderbuffers(1, &desc->fMSColorRenderbufferID));
+        if (!desc->fRTFBOID ||
+            !desc->fMSColorRenderbufferID ||
+            !this->configToGLFormats(desc->fConfig,
+                                     // GLES requires sized internal formats
+                                     kES2_GrGLBinding == this->glBinding(),
+                                     &msColorFormat, NULL, NULL)) {
+            goto FAILED;
+        }
+    } else {
+        desc->fRTFBOID = desc->fTexFBOID;
+    }
+
+    // below here we may bind the FBO
+    fHWBoundRenderTarget = NULL;
+    if (desc->fRTFBOID != desc->fTexFBOID) {
+        GrAssert(desc->fSampleCnt > 1);
+        GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER,
+                               desc->fMSColorRenderbufferID));
+        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,
+                                      GR_GL_COLOR_ATTACHMENT0,
+                                      GR_GL_RENDERBUFFER,
+                                      desc->fMSColorRenderbufferID));
+        if (!this->glCaps().isConfigVerifiedColorAttachment(desc->fConfig)) {
+            GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+            if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+                goto FAILED;
+            }
+            fGLContextInfo.caps().markConfigAsValidColorAttachment(
+                                                                desc->fConfig);
+        }
+    }
+    GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, desc->fTexFBOID));
+
+    GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER,
+                                 GR_GL_COLOR_ATTACHMENT0,
+                                 GR_GL_TEXTURE_2D,
+                                 texID, 0));
+    if (!this->glCaps().isConfigVerifiedColorAttachment(desc->fConfig)) {
+        GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+        if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+            goto FAILED;
+        }
+        fGLContextInfo.caps().markConfigAsValidColorAttachment(desc->fConfig);
+    }
+
+    return true;
+
+FAILED:
+    if (desc->fMSColorRenderbufferID) {
+        GL_CALL(DeleteRenderbuffers(1, &desc->fMSColorRenderbufferID));
+    }
+    if (desc->fRTFBOID != desc->fTexFBOID) {
+        GL_CALL(DeleteFramebuffers(1, &desc->fRTFBOID));
+    }
+    if (desc->fTexFBOID) {
+        GL_CALL(DeleteFramebuffers(1, &desc->fTexFBOID));
+    }
+    return false;
+}
+
+// good to set a break-point here to know when createTexture fails
+static GrTexture* return_null_texture() {
+//    GrAssert(!"null texture");
+    return NULL;
+}
+
+#if 0 && GR_DEBUG
+static size_t as_size_t(int x) {
+    return x;
+}
+#endif
+
+GrTexture* GrGpuGL::onCreateTexture(const GrTextureDesc& desc,
+                                    const void* srcData,
+                                    size_t rowBytes) {
+
+    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.fSampleCnt = desc.fSampleCnt;
+
+    glTexDesc.fOwnsID = true;
+
+    glRTDesc.fMSColorRenderbufferID = 0;
+    glRTDesc.fRTFBOID = 0;
+    glRTDesc.fTexFBOID = 0;
+    glRTDesc.fOwnIDs = true;
+    glRTDesc.fConfig = glTexDesc.fConfig;
+
+    bool renderTarget = 0 != (desc.fFlags & kRenderTarget_GrTextureFlagBit);
+
+    const Caps& caps = this->getCaps();
+
+    // 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;
+
+    glRTDesc.fSampleCnt = desc.fSampleCnt;
+    if (GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType() &&
+        desc.fSampleCnt) {
+        //GrPrintf("MSAA RT requested but not supported on this platform.");
+        return return_null_texture();
+    }
+
+    if (renderTarget) {
+        if (glTexDesc.fWidth > caps.maxRenderTargetSize() ||
+            glTexDesc.fHeight > caps.maxRenderTargetSize()) {
+            return return_null_texture();
+        }
+    }
+
+    GL_CALL(GenTextures(1, &glTexDesc.fTextureID));
+    if (renderTarget && this->glCaps().textureUsageSupport()) {
+        // provides a hint about how this texture will be used
+        GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                              GR_GL_TEXTURE_USAGE,
+                              GR_GL_FRAMEBUFFER_ATTACHMENT));
+    }
+    if (!glTexDesc.fTextureID) {
+        return return_null_texture();
+    }
+
+    this->setSpareTextureUnit();
+    GL_CALL(BindTexture(GR_GL_TEXTURE_2D, glTexDesc.fTextureID));
+
+    // Some drivers like to know filter/wrap before seeing glTexImage2D. Some
+    // drivers have a bug where an FBO won't be complete if it includes a
+    // texture that is not mipmap complete (considering the filter in use).
+    GrGLTexture::TexParams initialTexParams;
+    // we only set a subset here so invalidate first
+    initialTexParams.invalidate();
+    initialTexParams.fFilter = GR_GL_NEAREST;
+    initialTexParams.fWrapS = GR_GL_CLAMP_TO_EDGE;
+    initialTexParams.fWrapT = GR_GL_CLAMP_TO_EDGE;
+    GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                          GR_GL_TEXTURE_MAG_FILTER,
+                          initialTexParams.fFilter));
+    GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                          GR_GL_TEXTURE_MIN_FILTER,
+                          initialTexParams.fFilter));
+    GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                          GR_GL_TEXTURE_WRAP_S,
+                          initialTexParams.fWrapS));
+    GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                          GR_GL_TEXTURE_WRAP_T,
+                          initialTexParams.fWrapT));
+    if (!this->uploadTexData(glTexDesc, true, 0, 0,
+                             glTexDesc.fWidth, glTexDesc.fHeight,
+                             desc.fConfig, srcData, rowBytes)) {
+        GL_CALL(DeleteTextures(1, &glTexDesc.fTextureID));
+        return return_null_texture();
+    }
+
+    GrGLTexture* tex;
+    if (renderTarget) {
+        // 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,
+                                             &glRTDesc)) {
+            GL_CALL(DeleteTextures(1, &glTexDesc.fTextureID));
+            return return_null_texture();
+        }
+        tex = SkNEW_ARGS(GrGLTexture, (this, glTexDesc, glRTDesc));
+    } else {
+        tex = SkNEW_ARGS(GrGLTexture, (this, glTexDesc));
+    }
+    tex->setCachedTexParams(initialTexParams, this->getResetTimestamp());
+#ifdef TRACE_TEXTURE_CREATION
+    GrPrintf("--- new texture [%d] size=(%d %d) config=%d\n",
+             glTexDesc.fTextureID, desc.fWidth, desc.fHeight, desc.fConfig);
+#endif
+    return tex;
+}
+
+namespace {
+
+const GrGLuint kUnknownBitCount = GrGLStencilBuffer::kUnknownBitCount;
+
+void inline get_stencil_rb_sizes(const GrGLInterface* gl,
+                                 GrGLuint rb,
+                                 GrGLStencilBuffer::Format* format) {
+    // we shouldn't ever know one size and not the other
+    GrAssert((kUnknownBitCount == format->fStencilBits) ==
+             (kUnknownBitCount == format->fTotalBits));
+    if (kUnknownBitCount == format->fStencilBits) {
+        GR_GL_GetRenderbufferParameteriv(gl, GR_GL_RENDERBUFFER,
+                                         GR_GL_RENDERBUFFER_STENCIL_SIZE,
+                                         (GrGLint*)&format->fStencilBits);
+        if (format->fPacked) {
+            GR_GL_GetRenderbufferParameteriv(gl, GR_GL_RENDERBUFFER,
+                                             GR_GL_RENDERBUFFER_DEPTH_SIZE,
+                                             (GrGLint*)&format->fTotalBits);
+            format->fTotalBits += format->fStencilBits;
+        } else {
+            format->fTotalBits = format->fStencilBits;
+        }
+    }
+}
+}
+
+bool GrGpuGL::createStencilBufferForRenderTarget(GrRenderTarget* rt,
+                                                 int width, int height) {
+
+    // All internally created RTs are also textures. We don't create
+    // SBs for a client's standalone RT (that is a RT that isn't also a texture).
+    GrAssert(rt->asTexture());
+    GrAssert(width >= rt->width());
+    GrAssert(height >= rt->height());
+
+    int samples = rt->numSamples();
+    GrGLuint sbID;
+    GL_CALL(GenRenderbuffers(1, &sbID));
+    if (!sbID) {
+        return false;
+    }
+
+    int stencilFmtCnt = this->glCaps().stencilFormats().count();
+    for (int i = 0; i < stencilFmtCnt; ++i) {
+        GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, sbID));
+        // we start with the last stencil format that succeeded in hopes
+        // that we won't go through this loop more than once after the
+        // first (painful) stencil creation.
+        int sIdx = (i + fLastSuccessfulStencilFmtIdx) % stencilFmtCnt;
+        const GrGLCaps::StencilFormat& sFmt =
+                this->glCaps().stencilFormats()[sIdx];
+        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.
+        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()));
+        }
+        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);
+            SkAutoTUnref<GrStencilBuffer> sb(SkNEW_ARGS(GrGLStencilBuffer,
+                                                  (this, sbID, width, height,
+                                                  samples, format)));
+            if (this->attachStencilBufferToRenderTarget(sb, rt)) {
+                fLastSuccessfulStencilFmtIdx = sIdx;
+                sb->transferToCache();
+                rt->setStencilBuffer(sb);
+                return true;
+           }
+           sb->abandon(); // otherwise we lose sbID
+        }
+    }
+    GL_CALL(DeleteRenderbuffers(1, &sbID));
+    return false;
+}
+
+bool GrGpuGL::attachStencilBufferToRenderTarget(GrStencilBuffer* sb,
+                                                GrRenderTarget* rt) {
+    GrGLRenderTarget* glrt = (GrGLRenderTarget*) rt;
+
+    GrGLuint fbo = glrt->renderFBOID();
+
+    if (NULL == sb) {
+        if (NULL != rt->getStencilBuffer()) {
+            GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                          GR_GL_STENCIL_ATTACHMENT,
+                                          GR_GL_RENDERBUFFER, 0));
+            GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                          GR_GL_DEPTH_ATTACHMENT,
+                                          GR_GL_RENDERBUFFER, 0));
+#if GR_DEBUG
+            GrGLenum status;
+            GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+            GrAssert(GR_GL_FRAMEBUFFER_COMPLETE == status);
+#endif
+        }
+        return true;
+    } else {
+        GrGLStencilBuffer* glsb = (GrGLStencilBuffer*) sb;
+        GrGLuint rb = glsb->renderbufferID();
+
+        fHWBoundRenderTarget = NULL;
+        GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fbo));
+        GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                      GR_GL_STENCIL_ATTACHMENT,
+                                      GR_GL_RENDERBUFFER, rb));
+        if (glsb->format().fPacked) {
+            GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                          GR_GL_DEPTH_ATTACHMENT,
+                                          GR_GL_RENDERBUFFER, rb));
+        } else {
+            GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                          GR_GL_DEPTH_ATTACHMENT,
+                                          GR_GL_RENDERBUFFER, 0));
+        }
+
+        GrGLenum status;
+        if (!this->glCaps().isColorConfigAndStencilFormatVerified(rt->config(),
+                                                           glsb->format())) {
+            GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+            if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+                GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                              GR_GL_STENCIL_ATTACHMENT,
+                                              GR_GL_RENDERBUFFER, 0));
+                if (glsb->format().fPacked) {
+                    GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                                  GR_GL_DEPTH_ATTACHMENT,
+                                                  GR_GL_RENDERBUFFER, 0));
+                }
+                return false;
+            } else {
+                fGLContextInfo.caps().markColorConfigAndStencilFormatAsVerified(
+                    rt->config(),
+                    glsb->format());
+            }
+        }
+        return true;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrVertexBuffer* GrGpuGL::onCreateVertexBuffer(uint32_t size, bool dynamic) {
+    GrGLuint id;
+    GL_CALL(GenBuffers(1, &id));
+    if (id) {
+        GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, id));
+        fHWGeometryState.fArrayPtrsDirty = true;
+        CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+        // make sure driver can allocate memory for this buffer
+        GL_ALLOC_CALL(this->glInterface(),
+                      BufferData(GR_GL_ARRAY_BUFFER,
+                                 size,
+                                 NULL,   // data ptr
+                                 dynamic ? GR_GL_DYNAMIC_DRAW :
+                                           GR_GL_STATIC_DRAW));
+        if (CHECK_ALLOC_ERROR(this->glInterface()) != GR_GL_NO_ERROR) {
+            GL_CALL(DeleteBuffers(1, &id));
+            // deleting bound buffer does implicit bind to 0
+            fHWGeometryState.fVertexBuffer = NULL;
+            return NULL;
+        }
+        GrGLVertexBuffer* vertexBuffer = SkNEW_ARGS(GrGLVertexBuffer,
+                                                    (this, id,
+                                                     size, dynamic));
+        fHWGeometryState.fVertexBuffer = vertexBuffer;
+        return vertexBuffer;
+    }
+    return NULL;
+}
+
+GrIndexBuffer* GrGpuGL::onCreateIndexBuffer(uint32_t size, bool dynamic) {
+    GrGLuint id;
+    GL_CALL(GenBuffers(1, &id));
+    if (id) {
+        GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, id));
+        CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+        // make sure driver can allocate memory for this buffer
+        GL_ALLOC_CALL(this->glInterface(),
+                      BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+                                 size,
+                                 NULL,  // data ptr
+                                 dynamic ? GR_GL_DYNAMIC_DRAW :
+                                           GR_GL_STATIC_DRAW));
+        if (CHECK_ALLOC_ERROR(this->glInterface()) != GR_GL_NO_ERROR) {
+            GL_CALL(DeleteBuffers(1, &id));
+            // deleting bound buffer does implicit bind to 0
+            fHWGeometryState.fIndexBuffer = NULL;
+            return NULL;
+        }
+        GrIndexBuffer* indexBuffer = SkNEW_ARGS(GrGLIndexBuffer,
+                                                (this, id, size, dynamic));
+        fHWGeometryState.fIndexBuffer = indexBuffer;
+        return indexBuffer;
+    }
+    return NULL;
+}
+
+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());
+
+    GrAssert(NULL != rt);
+    const GrGLIRect& vp = rt->getViewport();
+
+    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 (kNo_TriState != fHWScissorSettings.fEnabled) {
+        GL_CALL(Disable(GR_GL_SCISSOR_TEST));
+        fHWScissorSettings.fEnabled = kNo_TriState;
+        return;
+    }
+}
+
+void GrGpuGL::onClear(const GrIRect* rect, GrColor color) {
+    const GrDrawState& drawState = this->getDrawState();
+    const GrRenderTarget* rt = drawState.getRenderTarget();
+    // parent class should never let us get here with no RT
+    GrAssert(NULL != rt);
+
+    GrIRect clippedRect;
+    if (NULL != rect) {
+        // flushScissor expects rect to be clipped to the target.
+        clippedRect = *rect;
+        GrIRect rtRect = SkIRect::MakeWH(rt->width(), rt->height());
+        if (clippedRect.intersect(rtRect)) {
+            rect = &clippedRect;
+        } else {
+            return;
+        }
+    }
+    this->flushRenderTarget(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;
+    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));
+    fHWWriteToColor = kYes_TriState;
+    GL_CALL(ClearColor(r, g, b, a));
+    GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
+}
+
+void GrGpuGL::clearStencil() {
+    if (NULL == this->getDrawState().getRenderTarget()) {
+        return;
+    }
+
+    this->flushRenderTarget(&GrIRect::EmptyIRect());
+
+    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));
+    fHWStencilSettings.invalidate();
+}
+
+void GrGpuGL::clearStencilClip(const GrIRect& rect, bool insideClip) {
+    const GrDrawState& drawState = this->getDrawState();
+    const GrRenderTarget* rt = drawState.getRenderTarget();
+    GrAssert(NULL != rt);
+
+    // this should only be called internally when we know we have a
+    // stencil buffer.
+    GrAssert(NULL != rt->getStencilBuffer());
+    GrGLint stencilBitCount =  rt->getStencilBuffer()->bits();
+#if 0
+    GrAssert(stencilBitCount > 0);
+    GrGLint clipStencilMask  = (1 << (stencilBitCount - 1));
+#else
+    // we could just clear the clip bit but when we go through
+    // ANGLE a partial stencil mask will cause clears to be
+    // turned into draws. Our contract on GrDrawTarget says that
+    // changing the clip between stencil passes may or may not
+    // zero the client's clip bits. So we just clear the whole thing.
+    static const GrGLint clipStencilMask  = ~0;
+#endif
+    GrGLint value;
+    if (insideClip) {
+        value = (1 << (stencilBitCount - 1));
+    } else {
+        value = 0;
+    }
+    this->flushRenderTarget(&GrIRect::EmptyIRect());
+
+    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));
+    fHWStencilSettings.invalidate();
+}
+
+void GrGpuGL::onForceRenderTargetFlush() {
+    this->flushRenderTarget(&GrIRect::EmptyIRect());
+}
+
+bool GrGpuGL::readPixelsWillPayForYFlip(GrRenderTarget* renderTarget,
+                                        int left, int top,
+                                        int width, int height,
+                                        GrPixelConfig config,
+                                        size_t rowBytes) const {
+    // if GL can do the flip then we'll never pay for it.
+    if (this->glCaps().packFlipYSupport()) {
+        return false;
+    }
+
+    // If we have to do memcpy to handle non-trim rowBytes then we
+    // get the flip for free. Otherwise it costs.
+    if (this->glCaps().packRowLengthSupport()) {
+        return true;
+    }
+    // If we have to do memcpys to handle rowBytes then y-flip is free
+    // Note the rowBytes might be tight to the passed in data, but if data
+    // gets clipped in x to the target the rowBytes will no longer be tight.
+    if (left >= 0 && (left + width) < renderTarget->width()) {
+           return 0 == rowBytes ||
+                  GrBytesPerPixel(config) * width == rowBytes;
+    } else {
+        return false;
+    }
+}
+
+bool GrGpuGL::onReadPixels(GrRenderTarget* target,
+                           int left, int top,
+                           int width, int height,
+                           GrPixelConfig config,
+                           void* buffer,
+                           size_t rowBytes,
+                           bool invertY) {
+    GrGLenum format;
+    GrGLenum type;
+    if (!this->configToGLFormats(config, false, NULL, &format, &type)) {
+        return false;
+    }
+    size_t bpp = GrBytesPerPixel(config);
+    if (!adjust_pixel_ops_params(target->width(), target->height(), bpp,
+                                 &left, &top, &width, &height,
+                                 const_cast<const void**>(&buffer),
+                                 &rowBytes)) {
+        return false;
+    }
+
+    // resolve the render target if necessary
+    GrGLRenderTarget* tgt = static_cast<GrGLRenderTarget*>(target);
+    GrDrawState::AutoRenderTargetRestore artr;
+    switch (tgt->getResolveType()) {
+        case GrGLRenderTarget::kCantResolve_ResolveType:
+            return false;
+        case GrGLRenderTarget::kAutoResolves_ResolveType:
+            artr.set(this->drawState(), target);
+            this->flushRenderTarget(&GrIRect::EmptyIRect());
+            break;
+        case GrGLRenderTarget::kCanResolve_ResolveType:
+            this->onResolveRenderTarget(tgt);
+            // we don't track the state of the READ FBO ID.
+            GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER,
+                                    tgt->textureFBOID()));
+            break;
+        default:
+            GrCrash("Unknown resolve type");
+    }
+
+    const GrGLIRect& glvp = tgt->getViewport();
+
+    // 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;
+    if (rowBytes != tightRowBytes) {
+        if (this->glCaps().packRowLengthSupport()) {
+            GrAssert(!(rowBytes % sizeof(GrColor)));
+            GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, rowBytes / sizeof(GrColor)));
+            readDstRowBytes = rowBytes;
+        } else {
+            scratch.reset(tightRowBytes * height);
+            readDst = scratch.get();
+        }
+    }
+    if (!invertY && this->glCaps().packFlipYSupport()) {
+        GL_CALL(PixelStorei(GR_GL_PACK_REVERSE_ROW_ORDER, 1));
+    }
+    GL_CALL(ReadPixels(readRect.fLeft, readRect.fBottom,
+                       readRect.fWidth, readRect.fHeight,
+                       format, type, readDst));
+    if (readDstRowBytes != tightRowBytes) {
+        GrAssert(this->glCaps().packRowLengthSupport());
+        GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, 0));
+    }
+    if (!invertY && this->glCaps().packFlipYSupport()) {
+        GL_CALL(PixelStorei(GR_GL_PACK_REVERSE_ROW_ORDER, 0));
+        invertY = true;
+    }
+
+    // now reverse the order of the rows, since GL's are bottom-to-top, but our
+    // API presents top-to-bottom. We must preserve the padding contents. Note
+    // that the above readPixels did not overwrite the padding.
+    if (readDst == buffer) {
+        GrAssert(rowBytes == readDstRowBytes);
+        if (!invertY) {
+            scratch.reset(tightRowBytes);
+            void* tmpRow = scratch.get();
+            // flip y in-place by rows
+            const int halfY = height >> 1;
+            char* top = reinterpret_cast<char*>(buffer);
+            char* bottom = top + (height - 1) * rowBytes;
+            for (int y = 0; y < halfY; y++) {
+                memcpy(tmpRow, top, tightRowBytes);
+                memcpy(top, bottom, tightRowBytes);
+                memcpy(bottom, tmpRow, tightRowBytes);
+                top += rowBytes;
+                bottom -= rowBytes;
+            }
+        }
+    } else {
+        GrAssert(readDst != buffer);        GrAssert(rowBytes != tightRowBytes);
+        // copy from readDst to buffer while flipping y
+        // const int halfY = height >> 1;
+        const char* src = reinterpret_cast<const char*>(readDst);
+        char* dst = reinterpret_cast<char*>(buffer);
+        if (!invertY) {
+            dst += (height-1) * rowBytes;
+        }
+        for (int y = 0; y < height; y++) {
+            memcpy(dst, src, tightRowBytes);
+            src += readDstRowBytes;
+            if (invertY) {
+                dst += rowBytes;
+            } else {
+                dst -= rowBytes;
+            }
+        }
+    }
+    return true;
+}
+
+void GrGpuGL::flushRenderTarget(const GrIRect* bound) {
+
+    GrGLRenderTarget* rt =
+        static_cast<GrGLRenderTarget*>(this->drawState()->getRenderTarget());
+    GrAssert(NULL != rt);
+
+    if (fHWBoundRenderTarget != rt) {
+        GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, rt->renderFBOID()));
+    #if GR_DEBUG
+        GrGLenum status;
+        GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+        if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+            GrPrintf("GrGpuGL::flushRenderTarget glCheckFramebufferStatus %x\n", status);
+        }
+    #endif
+        fHWBoundRenderTarget = rt;
+        const GrGLIRect& vp = rt->getViewport();
+        if (fHWViewport != vp) {
+            vp.pushToGLViewport(this->glInterface());
+            fHWViewport = vp;
+        }
+    }
+    if (NULL == bound || !bound->isEmpty()) {
+        rt->flagAsNeedingResolve(bound);
+    }
+}
+
+GrGLenum gPrimitiveType2GLMode[] = {
+    GR_GL_TRIANGLES,
+    GR_GL_TRIANGLE_STRIP,
+    GR_GL_TRIANGLE_FAN,
+    GR_GL_POINTS,
+    GR_GL_LINES,
+    GR_GL_LINE_STRIP
+};
+
+#define SWAP_PER_DRAW 0
+
+#if SWAP_PER_DRAW
+    #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());
+            while(hwnd) {
+                DWORD wndProcID = 0;
+                GetWindowThreadProcessId(hwnd, &wndProcID);
+                if(wndProcID == procID) {
+                    SwapBuffers(GetDC(hwnd));
+                }
+                hwnd = GetNextWindow(hwnd, GW_HWNDNEXT);
+            }
+         }
+    #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));
+
+    GrGLvoid* indices = (GrGLvoid*)(sizeof(uint16_t) * startIndex);
+
+    GrAssert(NULL != fHWGeometryState.fIndexBuffer);
+    GrAssert(NULL != fHWGeometryState.fVertexBuffer);
+
+    // our setupGeometry better have adjusted this to zero since
+    // DrawElements always draws from the begining of the arrays for idx 0.
+    GrAssert(0 == startVertex);
+
+    GL_CALL(DrawElements(gPrimitiveType2GLMode[type], indexCount,
+                         GR_GL_UNSIGNED_SHORT, indices));
+#if SWAP_PER_DRAW
+    glFlush();
+    #if GR_MAC_BUILD
+        aglSwapBuffers(aglGetCurrentContext());
+        int set_a_break_pt_here = 9;
+        aglSwapBuffers(aglGetCurrentContext());
+    #elif GR_WIN32_BUILD
+        SwapBuf();
+        int set_a_break_pt_here = 9;
+        SwapBuf();
+    #endif
+#endif
+}
+
+void GrGpuGL::onGpuDrawNonIndexed(GrPrimitiveType type,
+                                  uint32_t startVertex,
+                                  uint32_t vertexCount) {
+    GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
+
+    GrAssert(NULL != fHWGeometryState.fVertexBuffer);
+
+    // our setupGeometry better have adjusted this to zero.
+    // DrawElements doesn't take an offset so we always adjus the startVertex.
+    GrAssert(0 == startVertex);
+
+    // pass 0 for parameter first. We have to adjust gl*Pointer() to
+    // account for startVertex in the DrawElements case. So we always
+    // rely on setupGeometry to have accounted for startVertex.
+    GL_CALL(DrawArrays(gPrimitiveType2GLMode[type], 0, vertexCount));
+#if 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
+}
+
+namespace {
+
+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);
+}
+}
+
+
+void GrGpuGL::setStencilPathSettings(const GrPath&,
+                                     GrPathFill fill,
+                                     GrStencilSettings* settings) {
+    switch (fill) {
+        case kEvenOdd_GrPathFill:
+            *settings = even_odd_nv_path_stencil_settings();
+            return;
+        case kWinding_GrPathFill:
+            *settings = winding_nv_path_stencil_settings();
+            return;
+        default:
+            GrCrash("Unexpected path fill.");
+    }
+}
+
+void GrGpuGL::onGpuStencilPath(const GrPath* path, GrPathFill 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 kWinding_GrPathFill:
+            fillMode = GR_GL_COUNT_UP;
+            GrAssert(kIncClamp_StencilOp ==
+                     fStencilSettings.passOp(GrStencilSettings::kFront_Face));
+            GrAssert(kIncClamp_StencilOp ==
+                     fStencilSettings.failOp(GrStencilSettings::kFront_Face));
+            break;
+        case kEvenOdd_GrPathFill:
+            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) {
+
+    GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(target);
+
+    if (rt->needsResolve()) {
+        GrAssert(GrGLCaps::kNone_MSFBOType != this->glCaps().msFBOType());
+        GrAssert(rt->textureFBOID() != rt->renderFBOID());
+        GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER,
+                                rt->renderFBOID()));
+        GL_CALL(BindFramebuffer(GR_GL_DRAW_FRAMEBUFFER,
+                                rt->textureFBOID()));
+        // make sure we go through flushRenderTarget() since we've modified
+        // the bound DRAW FBO ID.
+        fHWBoundRenderTarget = NULL;
+        const GrGLIRect& vp = rt->getViewport();
+        const GrIRect dirtyRect = rt->getResolveRect();
+        GrGLIRect r;
+        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.
+            asr.reset(&fScissorState);
+            fScissorState.fEnabled = true;
+            fScissorState.fRect = dirtyRect;
+            this->flushScissor();
+            GL_CALL(ResolveMultisampleFramebuffer());
+        } 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());
+                asr.reset(&fScissorState);
+                fScissorState.fEnabled = false;
+                this->flushScissor();
+            }
+            int right = r.fLeft + r.fWidth;
+            int top = r.fBottom + r.fHeight;
+            GL_CALL(BlitFramebuffer(r.fLeft, r.fBottom, right, top,
+                                    r.fLeft, r.fBottom, right, top,
+                                    GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST));
+        }
+        rt->flagAsResolved();
+    }
+}
+
+namespace {
+
+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);
+
+    return gTable[basicFunc];
+}
+
+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];
+}
+
+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));
+
+    GrGLint ref = settings.funcRef(grFace);
+    GrGLint mask = settings.funcMask(grFace);
+    GrGLint writeMask = settings.writeMask(grFace);
+
+    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));
+    }
+}
+}
+
+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;
+            }
+        } else {
+            if (kYes_TriState != fHWStencilTestEnabled) {
+                GL_CALL(Enable(GR_GL_STENCIL_TEST));
+                fHWStencilTestEnabled = kYes_TriState;
+            }
+        }
+        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(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
+        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 {
+                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(bool isLines,
+                         GrBlendCoeff srcCoeff,
+                         GrBlendCoeff dstCoeff) {
+    if (isLines && this->willUseHWAALines()) {
+        if (kYes_TriState != fHWBlendState.fEnabled) {
+            GL_CALL(Enable(GR_GL_BLEND));
+            fHWBlendState.fEnabled = kYes_TriState;
+        }
+        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_GrBlendCoeff == srcCoeff &&
+                        kZero_GrBlendCoeff == dstCoeff;
+        if (blendOff) {
+            if (kNo_TriState != fHWBlendState.fEnabled) {
+                GL_CALL(Disable(GR_GL_BLEND));
+                fHWBlendState.fEnabled = kNo_TriState;
+            }
+        } else {
+            if (kYes_TriState != fHWBlendState.fEnabled) {
+                GL_CALL(Enable(GR_GL_BLEND));
+                fHWBlendState.fEnabled = kYes_TriState;
+            }
+            if (fHWBlendState.fSrcCoeff != srcCoeff ||
+                fHWBlendState.fDstCoeff != dstCoeff) {
+                GL_CALL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
+                                  gXfermodeCoeff2Blend[dstCoeff]));
+                fHWBlendState.fSrcCoeff = srcCoeff;
+                fHWBlendState.fDstCoeff = dstCoeff;
+            }
+            GrColor blendConst = this->getDrawState().getBlendConstant();
+            if ((BlendCoeffReferencesConstant(srcCoeff) ||
+                 BlendCoeffReferencesConstant(dstCoeff)) &&
+                (!fHWBlendState.fConstColorValid ||
+                 fHWBlendState.fConstColor != blendConst)) {
+                GrGLfloat c[4];
+                GrColorToRGBAFloat(blendConst, c);
+                GL_CALL(BlendColor(c[0], c[1], c[2], c[3]));
+                fHWBlendState.fConstColor = blendConst;
+                fHWBlendState.fConstColorValid = true;
+            }
+        }
+    }
+}
+namespace {
+
+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)));
+}
+
+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 GrGpuGL::flushBoundTextureAndParams(int stage) {
+    GrDrawState* drawState = this->drawState();
+    // FIXME: Assuming at most one texture per custom stage
+    const GrCustomStage* customStage = drawState->sampler(stage)->getCustomStage();
+    GrGLTexture* nextTexture =  static_cast<GrGLTexture*>(customStage->texture(0));
+    if (NULL != nextTexture) {
+        const GrTextureParams& texParams = customStage->textureAccess(0).getParams();
+        this->flushBoundTextureAndParams(stage, texParams, nextTexture);
+    }
+}
+
+void GrGpuGL::flushBoundTextureAndParams(int stage,
+                                         const GrTextureParams& params,
+                                         GrGLTexture* nextTexture) {
+
+    // true for now, but maybe not with GrEffect.
+    GrAssert(NULL != nextTexture);
+    // If we created a rt/tex and rendered to it without using a texture and now we're texturing
+    // from the rt it will still be the last bound texture, but it needs resolving. So keep this
+    // out of the "last != next" check.
+    GrGLRenderTarget* texRT =  static_cast<GrGLRenderTarget*>(nextTexture->asRenderTarget());
+    if (NULL != texRT) {
+        this->onResolveRenderTarget(texRT);
+    }
+
+    if (fHWBoundTextures[stage] != nextTexture) {
+        this->setTextureUnit(stage);
+        GL_CALL(BindTexture(GR_GL_TEXTURE_2D, nextTexture->textureID()));
+        //GrPrintf("---- bindtexture %d\n", nextTexture->textureID());
+        fHWBoundTextures[stage] = nextTexture;
+    }
+
+    ResetTimestamp timestamp;
+    const GrGLTexture::TexParams& oldTexParams =
+                            nextTexture->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(nextTexture->config(), this->glCaps()),
+           sizeof(newTexParams.fSwizzleRGBA));
+    if (setAll || newTexParams.fFilter != oldTexParams.fFilter) {
+        this->setTextureUnit(stage);
+        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(stage);
+        GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                              GR_GL_TEXTURE_WRAP_S,
+                              newTexParams.fWrapS));
+    }
+    if (setAll || newTexParams.fWrapT != oldTexParams.fWrapT) {
+        this->setTextureUnit(stage);
+        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(stage);
+        set_tex_swizzle(newTexParams.fSwizzleRGBA,
+                        this->glInterface());
+    }
+    nextTexture->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));
+            fHWDitherEnabled = kYes_TriState;
+        }
+    } else {
+        if (kNo_TriState != fHWDitherEnabled) {
+            GL_CALL(Disable(GR_GL_DITHER));
+            fHWDitherEnabled = kNo_TriState;
+        }
+    }
+
+    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;
+        }
+    } else {
+        if (kYes_TriState != fHWWriteToColor) {
+            GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
+            fHWWriteToColor = kYes_TriState;
+        }
+    }
+
+    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));
+                break;
+            case GrDrawState::kCW_DrawFace:
+                GL_CALL(Enable(GR_GL_CULL_FACE));
+                GL_CALL(CullFace(GR_GL_FRONT));
+                break;
+            case GrDrawState::kBoth_DrawFace:
+                GL_CALL(Disable(GR_GL_CULL_FACE));
+                break;
+            default:
+                GrCrash("Unknown draw face.");
+        }
+        fHWDrawFace = drawState.getDrawFace();
+    }
+}
+
+void GrGpuGL::notifyVertexBufferBind(const GrGLVertexBuffer* buffer) {
+    if (fHWGeometryState.fVertexBuffer != buffer) {
+        fHWGeometryState.fArrayPtrsDirty = true;
+        fHWGeometryState.fVertexBuffer = buffer;
+    }
+}
+
+void GrGpuGL::notifyVertexBufferDelete(const GrGLVertexBuffer* buffer) {
+    if (fHWGeometryState.fVertexBuffer == buffer) {
+        // deleting bound buffer does implied bind to 0
+        fHWGeometryState.fVertexBuffer = NULL;
+        fHWGeometryState.fArrayPtrsDirty = true;
+    }
+}
+
+void GrGpuGL::notifyIndexBufferBind(const GrGLIndexBuffer* buffer) {
+    fHWGeometryState.fIndexBuffer = buffer;
+}
+
+void GrGpuGL::notifyIndexBufferDelete(const GrGLIndexBuffer* buffer) {
+    if (fHWGeometryState.fIndexBuffer == buffer) {
+        // deleting bound buffer does implied bind to 0
+        fHWGeometryState.fIndexBuffer = NULL;
+    }
+}
+
+void GrGpuGL::notifyRenderTargetDelete(GrRenderTarget* renderTarget) {
+    GrAssert(NULL != renderTarget);
+    if (fHWBoundRenderTarget == renderTarget) {
+        fHWBoundRenderTarget = NULL;
+    }
+}
+
+void GrGpuGL::notifyTextureDelete(GrGLTexture* texture) {
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        if (fHWBoundTextures[s] == texture) {
+            // deleting bound texture does implied bind to 0
+            fHWBoundTextures[s] = NULL;
+       }
+    }
+}
+
+bool GrGpuGL::configToGLFormats(GrPixelConfig config,
+                                bool getSizedInternalFormat,
+                                GrGLenum* internalFormat,
+                                GrGLenum* externalFormat,
+                                GrGLenum* externalType) {
+    GrGLenum dontCare;
+    if (NULL == internalFormat) {
+        internalFormat = &dontCare;
+    }
+    if (NULL == externalFormat) {
+        externalFormat = &dontCare;
+    }
+    if (NULL == externalType) {
+        externalType = &dontCare;
+    }
+
+    switch (config) {
+        case kRGBA_8888_GrPixelConfig:
+            *internalFormat = GR_GL_RGBA;
+            *externalFormat = GR_GL_RGBA;
+            if (getSizedInternalFormat) {
+                *internalFormat = GR_GL_RGBA8;
+            } else {
+                *internalFormat = GR_GL_RGBA;
+            }
+            *externalType = GR_GL_UNSIGNED_BYTE;
+            break;
+        case kBGRA_8888_GrPixelConfig:
+            if (!this->glCaps().bgraFormatSupport()) {
+                return false;
+            }
+            if (this->glCaps().bgraIsInternalFormat()) {
+                if (getSizedInternalFormat) {
+                    *internalFormat = GR_GL_BGRA8;
+                } else {
+                    *internalFormat = GR_GL_BGRA;
+                }
+            } else {
+                if (getSizedInternalFormat) {
+                    *internalFormat = GR_GL_RGBA8;
+                } else {
+                    *internalFormat = GR_GL_RGBA;
+                }
+            }
+            *externalFormat = GR_GL_BGRA;
+            *externalType = GR_GL_UNSIGNED_BYTE;
+            break;
+        case kRGB_565_GrPixelConfig:
+            *internalFormat = GR_GL_RGB;
+            *externalFormat = GR_GL_RGB;
+            if (getSizedInternalFormat) {
+                if (this->glBinding() == kDesktop_GrGLBinding) {
+                    return false;
+                } else {
+                    *internalFormat = GR_GL_RGB565;
+                }
+            } else {
+                *internalFormat = GR_GL_RGB;
+            }
+            *externalType = GR_GL_UNSIGNED_SHORT_5_6_5;
+            break;
+        case kRGBA_4444_GrPixelConfig:
+            *internalFormat = GR_GL_RGBA;
+            *externalFormat = GR_GL_RGBA;
+            if (getSizedInternalFormat) {
+                *internalFormat = GR_GL_RGBA4;
+            } else {
+                *internalFormat = GR_GL_RGBA;
+            }
+            *externalType = GR_GL_UNSIGNED_SHORT_4_4_4_4;
+            break;
+        case kIndex_8_GrPixelConfig:
+            if (this->getCaps().eightBitPaletteSupport()) {
+                *internalFormat = GR_GL_PALETTE8_RGBA8;
+                // glCompressedTexImage doesn't take external params
+                *externalFormat = GR_GL_PALETTE8_RGBA8;
+                // no sized/unsized internal format distinction here
+                *internalFormat = GR_GL_PALETTE8_RGBA8;
+                // unused with CompressedTexImage
+                *externalType = GR_GL_UNSIGNED_BYTE;
+            } else {
+                return false;
+            }
+            break;
+        case kAlpha_8_GrPixelConfig:
+            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;
+            }
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+
+void GrGpuGL::setTextureUnit(int unit) {
+    GrAssert(unit >= 0 && unit < GrDrawState::kNumStages);
+    if (fHWActiveTextureUnitIdx != unit) {
+        GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + unit));
+        fHWActiveTextureUnitIdx = unit;
+    }
+}
+
+void GrGpuGL::setSpareTextureUnit() {
+    if (fHWActiveTextureUnitIdx != (GR_GL_TEXTURE0 + SPARE_TEX_UNIT)) {
+        GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + SPARE_TEX_UNIT));
+        fHWActiveTextureUnitIdx = SPARE_TEX_UNIT;
+    }
+}
+
+void GrGpuGL::setBuffers(bool indexed,
+                         int* extraVertexOffset,
+                         int* extraIndexOffset) {
+
+    GrAssert(NULL != extraVertexOffset);
+
+    const GeometryPoolState& geoPoolState = this->getGeomPoolState();
+
+    GrGLVertexBuffer* vbuf;
+    switch (this->getGeomSrc().fVertexSrc) {
+    case kBuffer_GeometrySrcType:
+        *extraVertexOffset = 0;
+        vbuf = (GrGLVertexBuffer*) this->getGeomSrc().fVertexBuffer;
+        break;
+    case kArray_GeometrySrcType:
+    case kReserved_GeometrySrcType:
+        this->finalizeReservedVertices();
+        *extraVertexOffset = geoPoolState.fPoolStartVertex;
+        vbuf = (GrGLVertexBuffer*) geoPoolState.fPoolVertexBuffer;
+        break;
+    default:
+        vbuf = NULL; // suppress warning
+        GrCrash("Unknown geometry src type!");
+    }
+
+    GrAssert(NULL != vbuf);
+    GrAssert(!vbuf->isLocked());
+    if (fHWGeometryState.fVertexBuffer != vbuf) {
+        GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, vbuf->bufferID()));
+        fHWGeometryState.fArrayPtrsDirty = true;
+        fHWGeometryState.fVertexBuffer = vbuf;
+    }
+
+    if (indexed) {
+        GrAssert(NULL != extraIndexOffset);
+
+        GrGLIndexBuffer* ibuf;
+        switch (this->getGeomSrc().fIndexSrc) {
+        case kBuffer_GeometrySrcType:
+            *extraIndexOffset = 0;
+            ibuf = (GrGLIndexBuffer*)this->getGeomSrc().fIndexBuffer;
+            break;
+        case kArray_GeometrySrcType:
+        case kReserved_GeometrySrcType:
+            this->finalizeReservedIndices();
+            *extraIndexOffset = geoPoolState.fPoolStartIndex;
+            ibuf = (GrGLIndexBuffer*) geoPoolState.fPoolIndexBuffer;
+            break;
+        default:
+            ibuf = NULL; // suppress warning
+            GrCrash("Unknown geometry src type!");
+        }
+
+        GrAssert(NULL != ibuf);
+        GrAssert(!ibuf->isLocked());
+        if (fHWGeometryState.fIndexBuffer != ibuf) {
+            GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, ibuf->bufferID()));
+            fHWGeometryState.fIndexBuffer = ibuf;
+        }
+    }
+}
+
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
new file mode 100644
index 0000000..d2a22d0
--- /dev/null
+++ b/src/gpu/gl/GrGpuGL.h
@@ -0,0 +1,398 @@
+/*
+ * 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 GrGpuGL_DEFINED
+#define GrGpuGL_DEFINED
+
+#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 {
+        return fGLContextInfo.interface();
+    }
+    GrGLBinding glBinding() const { return fGLContextInfo.binding(); }
+    GrGLVersion glVersion() const { return fGLContextInfo.version(); }
+    GrGLSLGeneration glslGeneration() const {
+        return fGLContextInfo.glslGeneration();
+    }
+
+    // GrGpu overrides
+    virtual GrPixelConfig preferredReadPixelsConfig(GrPixelConfig config)
+                                                            const SK_OVERRIDE;
+    virtual GrPixelConfig preferredWritePixelsConfig(GrPixelConfig config)
+                                                            const SK_OVERRIDE;
+    virtual bool readPixelsWillPayForYFlip(
+                                    GrRenderTarget* renderTarget,
+                                    int left, int top,
+                                    int width, int height,
+                                    GrPixelConfig config,
+                                    size_t rowBytes) const SK_OVERRIDE;
+    virtual bool fullReadPixelsIsFasterThanPartial() const SK_OVERRIDE;
+
+    virtual void abandonResources() SK_OVERRIDE;
+
+    bool programUnitTest();
+
+
+protected:
+    // GrGpu overrides
+    virtual void onResetContext() SK_OVERRIDE;
+
+    virtual GrTexture* onCreateTexture(const GrTextureDesc& desc,
+                                       const void* srcData,
+                                       size_t rowBytes) SK_OVERRIDE;
+    virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size,
+                                                 bool dynamic) SK_OVERRIDE;
+    virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size,
+                                               bool dynamic) SK_OVERRIDE;
+    virtual GrPath* onCreatePath(const SkPath&) SK_OVERRIDE;
+    virtual GrTexture* onCreatePlatformTexture(
+        const GrPlatformTextureDesc& desc) SK_OVERRIDE;
+    virtual GrRenderTarget* onCreatePlatformRenderTarget(
+        const GrPlatformRenderTargetDesc& desc) SK_OVERRIDE;
+    virtual bool createStencilBufferForRenderTarget(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) SK_OVERRIDE;
+
+    virtual void onForceRenderTargetFlush() SK_OVERRIDE;
+
+    virtual bool onReadPixels(GrRenderTarget* target,
+                              int left, int top,
+                              int width, int height,
+                              GrPixelConfig,
+                              void* buffer,
+                              size_t rowBytes,
+                              bool invertY) SK_OVERRIDE;
+
+    virtual void onWriteTexturePixels(GrTexture* texture,
+                                      int left, int top, int width, int height,
+                                      GrPixelConfig config, const void* buffer,
+                                      size_t rowBytes) SK_OVERRIDE;
+
+    virtual void onResolveRenderTarget(GrRenderTarget* target) SK_OVERRIDE;
+
+    virtual void onGpuDrawIndexed(GrPrimitiveType type,
+                                  uint32_t startVertex,
+                                  uint32_t startIndex,
+                                  uint32_t vertexCount,
+                                  uint32_t indexCount) SK_OVERRIDE;
+    virtual void onGpuDrawNonIndexed(GrPrimitiveType type,
+                                     uint32_t vertexCount,
+                                     uint32_t numVertices) SK_OVERRIDE;
+
+    virtual void setStencilPathSettings(const GrPath&,
+                                        GrPathFill,
+                                        GrStencilSettings* settings)
+                                        SK_OVERRIDE;
+    virtual void onGpuStencilPath(const GrPath*, GrPathFill) SK_OVERRIDE;
+
+    virtual void clearStencil() SK_OVERRIDE;
+    virtual void clearStencilClip(const GrIRect& rect,
+                                  bool insideClip) SK_OVERRIDE;
+    virtual bool flushGraphicsState(DrawType) SK_OVERRIDE;
+    virtual void setupGeometry(int* startVertex,
+                               int* startIndex,
+                               int vertexCount,
+                               int indexCount) SK_OVERRIDE;
+
+private:
+
+    const GrGLCaps& glCaps() const { return fGLContextInfo.caps(); }
+
+    // binds texture unit in GL
+    void setTextureUnit(int unitIdx);
+
+    // binds appropriate vertex and index buffers, also returns any extra
+    // extra verts or indices to offset by.
+    void setBuffers(bool indexed,
+                    int* extraVertexOffset,
+                    int* extraIndexOffset);
+
+    // Subclasses should call this to flush the blend state.
+    // The params should be the final coeffecients to apply
+    // (after any blending optimizations or dual source blending considerations
+    // have been accounted for).
+    void flushBlend(bool isLines,
+                    GrBlendCoeff srcCoeff,
+                    GrBlendCoeff dstCoeff);
+
+    bool hasExtension(const char* ext) const {
+        return fGLContextInfo.hasExtension(ext);
+    }
+
+    const GrGLContextInfo& glContextInfo() const { return fGLContextInfo; }
+
+    // adjusts texture matrix to account for orientation
+    static void AdjustTextureMatrix(const GrGLTexture* texture,
+                                    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);
+
+    // for readability of function impls
+    typedef GrGLProgram::Desc        ProgramDesc;
+    typedef ProgramDesc::StageDesc   StageDesc;
+
+    class ProgramCache : public ::GrNoncopyable {
+    public:
+        ProgramCache(const GrGLContextInfo& gl);
+
+        void abandon();
+        GrGLProgram* getProgram(const GrGLProgram::Desc& desc, const GrCustomStage** 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;
+    };
+
+    // binds the texture and sets its texture params
+    // This may also perform a downsample on the src texture which may or may
+    // not modify the scissor test and rect. So in flushGraphicsState a
+    // call to flushScissor must occur after all textures have been flushed via
+    // this function.
+    void flushBoundTextureAndParams(int stage);
+    void flushBoundTextureAndParams(int stage,
+                                    const GrTextureParams& params,
+                                    GrGLTexture* nextTexture);
+
+    // sets the texture matrix for the currently bound program
+    void flushTextureMatrix(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(DrawType type);
+
+    // flushes the parameters to two point radial gradient
+    void flushRadial2(int stage);
+
+    // flushes the parameters for convolution
+    void flushConvolution(int stage);
+
+    // flushes the color matrix
+    void flushColorMatrix();
+
+    // 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,
+                      const GrCustomStage** customStages,
+                      ProgramDesc* desc);
+
+    // Inits GrDrawTarget::Caps, sublcass may enable additional caps.
+    void initCaps();
+
+    void initFSAASupport();
+
+    // determines valid stencil formats
+    void initStencilFormats();
+
+    // notify callbacks to update state tracking when related
+    // objects are bound to GL or deleted outside of the class
+    void notifyVertexBufferBind(const GrGLVertexBuffer* buffer);
+    void notifyVertexBufferDelete(const GrGLVertexBuffer* buffer);
+    void notifyIndexBufferBind(const GrGLIndexBuffer* buffer);
+    void notifyIndexBufferDelete(const GrGLIndexBuffer* buffer);
+    void notifyTextureDelete(GrGLTexture* texture);
+    void notifyRenderTargetDelete(GrRenderTarget* renderTarget);
+
+    void setSpareTextureUnit();
+
+    // 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(DrawType);
+    void flushAAState(DrawType);
+
+    bool configToGLFormats(GrPixelConfig config,
+                           bool getSizedInternal,
+                           GrGLenum* internalFormat,
+                           GrGLenum* externalFormat,
+                           GrGLenum* externalType);
+    // helper for onCreateTexture and writeTexturePixels
+    bool uploadTexData(const GrGLTexture::Desc& desc,
+                       bool isNewTexture,
+                       int left, int top, int width, int height,
+                       GrPixelConfig dataConfig,
+                       const void* data,
+                       size_t rowBytes);
+
+    bool createRenderTargetObjects(int width, int height,
+                                   GrGLuint texID,
+                                   GrGLRenderTarget::Desc* desc);
+
+    void fillInConfigRenderableTable();
+
+    friend class GrGLVertexBuffer;
+    friend class GrGLIndexBuffer;
+    friend class GrGLTexture;
+    friend class GrGLRenderTarget;
+
+    GrGLContextInfo fGLContextInfo;
+
+    // GL program-related state
+    ProgramCache*               fProgramCache;
+    SkAutoTUnref<GrGLProgram>   fCurrentProgram;
+
+    ///////////////////////////////////////////////////////////////////////////
+    ///@name Caching of GL State
+    ///@{
+    int                         fHWActiveTextureUnitIdx;
+    GrGLuint                    fHWProgramID;
+    GrColor                     fHWConstAttribColor;
+    GrColor                     fHWConstAttribCoverage;
+
+    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 {
+        GrMatrix    fViewMatrix;
+        SkISize     fRTSize;
+        void invalidate() {
+            fViewMatrix = GrMatrix::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;
+
+    bool fPrintedCaps;
+
+    typedef GrGpu INHERITED;
+};
+
+#endif
+
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
new file mode 100644
index 0000000..9e3a5d8
--- /dev/null
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -0,0 +1,772 @@
+/*
+ * Copyright 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 "GrCustomStage.h"
+#include "GrGLProgramStage.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 GrCustomStage** 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 GrMatrix& 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.
+            GrMatrix m;
+            m.setScale(GrIntToScalar(2) / rt->width(), GrIntToScalar(-2) / rt->height());
+            m.postTranslate(-GR_Scalar1, GR_Scalar1);
+            m.preConcat(vm);
+
+            // GL wants a column-major 4x4.
+            GrGLfloat mv[]  = {
+                // col 0
+                GrScalarToFloat(m[GrMatrix::kMScaleX]),
+                GrScalarToFloat(m[GrMatrix::kMSkewY]),
+                0,
+                GrScalarToFloat(m[GrMatrix::kMPersp0]),
+
+                // col 1
+                GrScalarToFloat(m[GrMatrix::kMSkewX]),
+                GrScalarToFloat(m[GrMatrix::kMScaleY]),
+                0,
+                GrScalarToFloat(m[GrMatrix::kMPersp1]),
+
+                // col 2
+                0, 0, 0, 0,
+
+                // col3
+                GrScalarToFloat(m[GrMatrix::kMTransX]),
+                GrScalarToFloat(m[GrMatrix::kMTransY]),
+                0.0f,
+                GrScalarToFloat(m[GrMatrix::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) {
+        GrMatrix m;
+        m.setAll(
+            GrIntToScalar(2) / viewportSize.fWidth, 0, -GR_Scalar1,
+            0,-GrIntToScalar(2) / viewportSize.fHeight, 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])
+        };
+        fCurrentProgram->fUniformManager.setMatrix3f(fCurrentProgram->fUniforms.fViewMatrixUni, mt);
+        fCurrentProgram->fViewMatrix = vm;
+        fCurrentProgram->fViewportSize = viewportSize;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// helpers for texture matrices
+
+void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
+                                  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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::flushTextureMatrix(int s) {
+    const GrDrawState& drawState = this->getDrawState();
+
+    // FIXME: Still assuming only a single texture per custom stage
+    const GrCustomStage* stage = drawState.getSampler(s).getCustomStage();
+    const GrGLTexture* texture = static_cast<const GrGLTexture*>(stage->texture(0));
+    if (NULL != texture) {
+
+        bool orientationChange = fCurrentProgram->fTextureOrientation[s] !=
+                                 texture->orientation();
+
+        UniformHandle matrixUni = fCurrentProgram->fUniforms.fStages[s].fTextureMatrixUni;
+
+        const GrMatrix& hwMatrix = fCurrentProgram->fTextureMatrices[s];
+        const GrMatrix& samplerMatrix = drawState.getSampler(s).getMatrix();
+
+        if (kInvalidUniformHandle != matrixUni &&
+            (orientationChange || !hwMatrix.cheapEqualTo(samplerMatrix))) {
+
+            GrMatrix m = samplerMatrix;
+            AdjustTextureMatrix(texture, &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])
+            };
+
+            fCurrentProgram->fUniformManager.setMatrix3f(matrixUni, mt);
+            fCurrentProgram->fTextureMatrices[s] = samplerMatrix;
+        }
+
+        fCurrentProgram->fTextureOrientation[s] = texture->orientation();
+    }
+}
+
+
+void GrGpuGL::flushColorMatrix() {
+    UniformHandle matrixUni = fCurrentProgram->fUniforms.fColorMatrixUni;
+    UniformHandle vecUni = fCurrentProgram->fUniforms.fColorMatrixVecUni;
+    if (kInvalidUniformHandle != matrixUni && kInvalidUniformHandle != 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,
+        };
+        fCurrentProgram->fUniformManager.setMatrix4f(matrixUni, mt);
+        fCurrentProgram->fUniformManager.set4fv(vecUni, 0, 1, vec);
+    }
+}
+
+void GrGpuGL::flushColor(GrColor color) {
+    const ProgramDesc& desc = fCurrentProgram->getDesc();
+    const GrDrawState& drawState = this->getDrawState();
+
+    if (this->getVertexLayout() & 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->fUniforms.fColorUni);
+                    fCurrentProgram->fUniformManager.set4fv(fCurrentProgram->fUniforms.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->fUniforms.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() & 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->fUniforms.fCoverageUni);
+                    fCurrentProgram->fUniformManager.set4fv(fCurrentProgram->fUniforms.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 GrCustomStage* customStages [GrDrawState::kNumStages];
+        GrGLProgram::Desc desc;
+        this->buildProgram(kDrawPoints_DrawType == type, blendOpts, dstCoeff, customStages, &desc);
+
+        fCurrentProgram.reset(fProgramCache->getProgram(desc, customStages));
+        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);
+
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            if (this->isStageEnabled(s)) {
+                this->flushBoundTextureAndParams(s);
+
+                this->flushTextureMatrix(s);
+
+                if (NULL != fCurrentProgram->fProgramStage[s]) {
+                    const GrSamplerState& sampler = this->getDrawState().getSampler(s);
+                    fCurrentProgram->fProgramStage[s]->setData(fCurrentProgram->fUniformManager,
+                                                               *sampler.getCustomStage(),
+                                                               drawState.getRenderTarget(), s);
+                }
+            }
+        }
+        this->flushColorMatrix();
+    }
+    this->flushStencil(type);
+    this->flushViewMatrix(type);
+    this->flushScissor();
+    this->flushAAState(type);
+
+    GrIRect* devRect = NULL;
+    GrIRect devClipBounds;
+    if (drawState.isClipState()) {
+        fClip->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(int* startVertex,
+                            int* startIndex,
+                            int vertexCount,
+                            int indexCount) {
+
+    int newColorOffset;
+    int newCoverageOffset;
+    int newTexCoordOffsets[GrDrawState::kMaxTexCoords];
+    int newEdgeOffset;
+
+    GrVertexLayout currLayout = this->getVertexLayout();
+
+    GrGLsizei newStride = VertexSizeAndOffsetsByIdx(
+                                            currLayout,
+                                            newTexCoordOffsets,
+                                            &newColorOffset,
+                                            &newCoverageOffset,
+                                            &newEdgeOffset);
+    int oldColorOffset;
+    int oldCoverageOffset;
+    int oldTexCoordOffsets[GrDrawState::kMaxTexCoords];
+    int oldEdgeOffset;
+
+    GrGLsizei oldStride = VertexSizeAndOffsetsByIdx(
+                                            fHWGeometryState.fVertexLayout,
+                                            oldTexCoordOffsets,
+                                            &oldColorOffset,
+                                            &oldCoverageOffset,
+                                            &oldEdgeOffset);
+    bool indexed = NULL != startIndex;
+
+    int extraVertexOffset;
+    int extraIndexOffset;
+    this->setBuffers(indexed, &extraVertexOffset, &extraIndexOffset);
+
+    GrGLenum scalarType;
+    bool texCoordNorm;
+    if (currLayout & kTextFormat_VertexLayoutBit) {
+        scalarType = TEXT_COORDS_GL_TYPE;
+        texCoordNorm = SkToBool(TEXT_COORDS_ARE_NORMALIZED);
+    } else {
+        GR_STATIC_ASSERT(GR_SCALAR_IS_FLOAT);
+        scalarType = GR_GL_FLOAT;
+        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 ||
+                           (((TEXT_COORDS_GL_TYPE != GR_GL_FLOAT) || TEXT_COORDS_ARE_NORMALIZED) &&
+                                (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;
+}
+
+namespace {
+
+void setup_custom_stage(GrGLProgram::Desc::StageDesc* stage,
+                        const GrSamplerState& sampler,
+                        const GrGLCaps& caps,
+                        const GrCustomStage** customStages,
+                        GrGLProgram* program, int index) {
+    const GrCustomStage* customStage = sampler.getCustomStage();
+    if (customStage) {
+        const GrProgramStageFactory& factory = customStage->getFactory();
+        stage->fCustomStageKey = factory.glStageKey(*customStage, caps);
+        customStages[index] = customStage;
+    } else {
+        stage->fCustomStageKey = 0;
+        customStages[index] = NULL;
+    }
+}
+
+}
+
+void GrGpuGL::buildProgram(bool isPoints,
+                           BlendOptFlags blendOpts,
+                           GrBlendCoeff dstCoeff,
+                           const GrCustomStage** customStages,
+                           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 & 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;
+    }
+
+    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 GrSamplerState& sampler = drawState.getSampler(s);
+            // FIXME: Still assuming one texture per custom stage
+            const GrCustomStage* customStage = drawState.getSampler(s).getCustomStage();
+            const GrGLTexture* texture = static_cast<const GrGLTexture*>(customStage->texture(0));
+            if (NULL != texture) {
+                // We call this helper function rather then simply checking the client-specified
+                // texture matrix. This is because we may have to concat a y-inversion to account
+                // for texture orientation.
+                if (TextureMatrixIsIdentity(texture, sampler)) {
+                    stage.fOptFlags |= StageDesc::kIdentityMatrix_OptFlagBit;
+                } else if (!sampler.getMatrix().hasPerspective()) {
+                    stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+                }
+            }
+
+            setup_custom_stage(&stage, sampler, this->glCaps(), customStages,
+                               fCurrentProgram.get(), s);
+
+        } else {
+            stage.fOptFlags         = 0;
+            stage.fCustomStageKey   = 0;
+            customStages[s] = NULL;
+        }
+    }
+
+    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 fromthe 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 & 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().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
new file mode 100644
index 0000000..c5069b2
--- /dev/null
+++ b/src/gpu/gl/SkGLContext.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 "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);
+}
+
+bool SkGLContext::hasExtension(const char* extensionName) const {
+    return GrGLHasExtensionFromString(extensionName, fExtensionString.c_str());
+}
+
+bool SkGLContext::init(int width, int height) {
+    if (fGL) {
+        fGL->unref();
+        this->destroyGLContext();
+    }
+
+    fGL = this->createGLContext();
+    if (fGL) {
+        fExtensionString =
+            reinterpret_cast<const char*>(SK_GL(*this,
+                                                 GetString(GR_GL_EXTENSIONS)));
+        const char* versionStr =
+            reinterpret_cast<const char*>(SK_GL(*this,
+                                                GetString(GR_GL_VERSION)));
+        GrGLVersion version = GrGLGetVersionFromString(versionStr);
+
+        // clear any existing GL erorrs
+        GrGLenum error;
+        do {
+            error = SK_GL(*this, GetError());
+        } while (GR_GL_NO_ERROR != error);
+
+        GrGLBinding bindingInUse = GrGLGetBindingInUse(this->gl());
+
+        SK_GL(*this, GenFramebuffers(1, &fFBO));
+        SK_GL(*this, BindFramebuffer(GR_GL_FRAMEBUFFER, fFBO));
+        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,
+                                             width, height));
+        } else {
+            SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+                                             GR_GL_RGBA,
+                                             width, height));
+        }
+        SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                             GR_GL_COLOR_ATTACHMENT0,
+                                             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 =
+                    this->hasExtension("GL_OES_packed_depth_stencil");
+        } else {
+            supportsPackedDepthStencil = version >= GR_GL_VER(3,0) ||
+                    this->hasExtension("GL_EXT_packed_depth_stencil") ||
+                    this->hasExtension("GL_ARB_framebuffer_object");
+        }
+
+        if (supportsPackedDepthStencil) {
+            // ES2 requires sized internal formats for RenderbufferStorage
+            // On Desktop we let the driver decide.
+            GrGLenum format = kES2_GrGLBinding == bindingInUse ?
+                                    GR_GL_DEPTH24_STENCIL8 :
+                                    GR_GL_DEPTH_STENCIL;
+            SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+                                             format,
+                                             width, height));
+            SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                                 GR_GL_DEPTH_ATTACHMENT,
+                                                 GR_GL_RENDERBUFFER,
+                                                 fDepthStencilBufferID));
+        } else {
+            GrGLenum format = kES2_GrGLBinding == bindingInUse ?
+                                    GR_GL_STENCIL_INDEX8 :
+                                    GR_GL_STENCIL_INDEX;
+            SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+                                             format,
+                                             width, height));
+        }
+        SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                             GR_GL_STENCIL_ATTACHMENT,
+                                             GR_GL_RENDERBUFFER,
+                                             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));
+
+        if (GR_GL_FRAMEBUFFER_COMPLETE != status ||
+            GR_GL_NO_ERROR != error) {
+            fFBO = 0;
+            fColorBufferID = 0;
+            fDepthStencilBufferID = 0;
+            fGL->unref();
+            fGL = NULL;
+            this->destroyGLContext();
+            return false;
+        } else {
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/src/gpu/gl/SkNullGLContext.cpp b/src/gpu/gl/SkNullGLContext.cpp
new file mode 100644
index 0000000..86c09b2
--- /dev/null
+++ b/src/gpu/gl/SkNullGLContext.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 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/SkNullGLContext.h"
+
+const GrGLInterface* SkNullGLContext::createGLContext() {
+    return GrGLCreateNullInterface();
+};
diff --git a/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp b/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
new file mode 100644
index 0000000..688f00d
--- /dev/null
+++ b/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
@@ -0,0 +1,135 @@
+// 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;
+        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->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..19fe44f
--- /dev/null
+++ b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
@@ -0,0 +1,156 @@
+
+/*
+ * Copyright 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..4aff202
--- /dev/null
+++ b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp
@@ -0,0 +1,1457 @@
+
+/*
+ * Copyright 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,
+                                                 const char** str,
+                                                 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..b7c7b0c
--- /dev/null
+++ b/src/gpu/gl/debug/GrTextureUnitObj.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 "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..e0ecc38
--- /dev/null
+++ b/src/gpu/gl/mac/GrGLCreateNativeInterface_mac.cpp
@@ -0,0 +1,275 @@
+
+/*
+ * Copyright 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;
+        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;
+        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..2f712e3
--- /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 <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(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..cac021c
--- /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(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/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_unittests.cpp b/src/gpu/gr_unittests.cpp
new file mode 100644
index 0000000..a0f0c18
--- /dev/null
+++ b/src/gpu/gr_unittests.cpp
@@ -0,0 +1,114 @@
+
+/*
+ * 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 "GrBinHashKey.h"
+#include "GrDrawTarget.h"
+#include "GrMatrix.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
+// that relies on them.
+static bool LT(const int& elem, int value) {
+    return elem < value;
+}
+static bool EQ(const int& elem, int value) {
+    return elem == value;
+}
+#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
+    };
+
+    for (size_t n = 0; n < GR_ARRAY_COUNT(array); n++) {
+        for (size_t i = 0; i < n; i++) {
+            int index = GrTBSearch<int, int>(array, n, array[i]);
+            GrAssert(index == (int) i);
+            index = GrTBSearch<int, int>(array, n, -array[i]);
+            GrAssert(index < 0);
+        }
+    }
+}
+
+// bogus empty class for GrBinHashKey
+class BogusEntry {};
+
+static void test_binHashKey()
+{
+    const char* testStringA_ = "abcdABCD";
+    const char* testStringB_ = "abcdBBCD";
+    const uint32_t* testStringA = reinterpret_cast<const uint32_t*>(testStringA_);
+    const uint32_t* testStringB = reinterpret_cast<const uint32_t*>(testStringB_);
+    enum {
+        kDataLenUsedForKey = 8
+    };
+
+    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyA;
+    keyA.setKeyData(testStringA);
+    // test copy constructor and comparison
+    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyA2(keyA);
+    GrAssert(keyA.compare(keyA2) == 0);
+    GrAssert(keyA.getHash() == keyA2.getHash());
+    // test re-init
+    keyA2.setKeyData(testStringA);
+    GrAssert(keyA.compare(keyA2) == 0);
+    GrAssert(keyA.getHash() == keyA2.getHash());
+    // test sorting
+    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyB;
+    keyB.setKeyData(testStringB);
+    GrAssert(keyA.compare(keyB) < 0);
+    GrAssert(keyA.getHash() != keyB.getHash());
+}
+
+
+void gr_run_unittests() {
+    test_tdarray();
+    test_bsearch();
+    test_binHashKey();
+    GrRedBlackTree<int>::UnitTest();
+    GrDrawTarget::VertexLayoutUnitTest();
+}
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..b6fc489
--- /dev/null
+++ b/src/image/SkImage.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 "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..e7a244a
--- /dev/null
+++ b/src/image/SkImagePriv.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 "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, NULL, 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..9332abc
--- /dev/null
+++ b/src/image/SkImagePriv.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 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);
+
+#endif
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
new file mode 100644
index 0000000..ed74610
--- /dev/null
+++ b/src/image/SkImage_Base.h
@@ -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.
+ */
+
+#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, SkScalar, 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..b46ac2d
--- /dev/null
+++ b/src/image/SkImage_Codec.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 "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..e061e2d
--- /dev/null
+++ b/src/image/SkImage_Gpu.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 "SkImage_Base.h"
+#include "SkImagePriv.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkDataPixelRef.h"
+
+class SkImage_Gpu : public SkImage_Base {
+public:
+    static bool ValidArgs(GrContext* context,
+                          const GrPlatformTextureDesc& desc) {
+        if (0 == desc.fTextureHandle) {
+            return false;
+        }
+        if (desc.fWidth < 0 || desc.fHeight < 0) {
+            return false;
+        }
+        return true;
+    }
+
+    SkImage_Gpu(GrContext* context, const GrPlatformTextureDesc& desc);
+    virtual ~SkImage_Gpu();
+
+    virtual void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) SK_OVERRIDE;
+
+private:
+    SkBitmap    fBitmap;
+
+    typedef SkImage_Base INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage_Gpu::SkImage_Gpu(GrContext* context, const GrPlatformTextureDesc& desc)
+        : INHERITED(desc.fWidth, desc.fHeight) {
+#if 0
+    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();
+#endif
+}
+
+SkImage_Gpu::~SkImage_Gpu() {}
+
+void SkImage_Gpu::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) {
+    canvas->drawBitmap(fBitmap, x, y, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage* SkImage::NewRasterCopy(NewTexture(GrContext* context,
+                                           const GrPlatformTextureDesc& desc) {
+    if (NULL == context) {
+        return NULL;
+    }
+    if (!SkImage_Gpu::ValidArgs(context, desc)) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkImage_Gpu, (context, desc));
+}
+
diff --git a/src/image/SkImage_Picture.cpp b/src/image/SkImage_Picture.cpp
new file mode 100644
index 0000000..959e47b
--- /dev/null
+++ b/src/image/SkImage_Picture.cpp
@@ -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.
+ */
+
+#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..c9a132d
--- /dev/null
+++ b/src/image/SkImage_Raster.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 "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, SkColorSpace* cs, 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&, SkColorSpace*, 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, SkColorSpace* cs,
+                               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, SkColorSpace* cs,
+                                const void* pixels, size_t rowBytes) {
+    if (!SkImage_Raster::ValidArgs(info, cs, 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, cs, data, rowBytes));
+}
+
+
+SkImage* SkImage::NewRasterData(const SkImage::Info& info, SkColorSpace* cs,
+                                SkData* pixelData, size_t rowBytes) {
+    if (!SkImage_Raster::ValidArgs(info, cs, 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, cs, 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..2860a58
--- /dev/null
+++ b/src/image/SkSurface.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 "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();
+    }
+}
+
+void SkSurface_Base::onCopyOnWrite(SkImage*, SkCanvas*) {}
+
+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, SkColorSpace* cs) {
+    return asSB(this)->onNewSurface(info, cs);
+}
+
+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..52b494f
--- /dev/null
+++ b/src/image/SkSurface_Base.h
@@ -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.
+ */
+
+#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&, SkColorSpace*) = 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*);
+
+    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..d707d93
--- /dev/null
+++ b/src/image/SkSurface_Gpu.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright 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 "SkMallocPixelRef.h"
+
+static const size_t kIgnoreRowBytesValue = (size_t)~0;
+
+class SkSurface_Gpu : public SkSurface_Base {
+public:
+    static bool Valid(const SkImage::Info&, SkColorSpace*, size_t rb = kIgnoreRowBytesValue);
+
+    SkSurface_Gpu(const SkImage::Info&, SkColorSpace*, void*, size_t rb);
+    SkSurface_Gpu(const SkImage::Info&, SkColorSpace*, SkPixelRef*, size_t rb);
+
+    virtual SkCanvas* onNewCanvas() SK_OVERRIDE;
+    virtual SkSurface* onNewSurface(const SkImage::Info&, SkColorSpace*) SK_OVERRIDE;
+    virtual SkImage* onNewImageShapshot() SK_OVERRIDE;
+    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
+                        const SkPaint*) SK_OVERRIDE;
+
+private:
+    SkBitmap    fBitmap;
+    bool        fWeOwnThePixels;
+
+    typedef SkSurface_Base INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkSurface_Gpu::Valid(const SkImage::Info& info, SkColorSpace* cs,
+                             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_Gpu::SkSurface_Gpu(const SkImage::Info& info, SkColorSpace* cs,
+                                   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;
+}
+
+SkSurface_Gpu::SkSurface_Gpu(const SkImage::Info& info, SkColorSpace* cs,
+                                   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(0);
+    }
+}
+
+SkCanvas* SkSurface_Gpu::onNewCanvas() {
+    return SkNEW_ARGS(SkCanvas, (fBitmap));
+}
+
+SkSurface* SkSurface_Gpu::onNewSurface(const SkImage::Info& info,
+                                          SkColorSpace* cs) {
+    return SkSurface::NewRaster(info, cs);
+}
+
+SkImage* SkSurface_Gpu::onNewImageShapshot() {
+    // if we don't own the pixels, we need to make a deep-copy
+    // if we do, we need to perform a copy-on-write the next time
+    // we draw to this bitmap from our canvas...
+    return SkNewImageFromBitmap(fBitmap);
+}
+
+void SkSurface_Gpu::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                              const SkPaint* paint) {
+    canvas->drawBitmap(fBitmap, x, y, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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,
+                                      SkColorSpace*, int sampleCount) {
+    if (NULL == ctx) {
+        return NULL;
+    }
+    if (!SkSurface_Gpu::Valid(info, cs, sampleCount)) {
+        return NULL;
+    }
+
+//    return SkNEW_ARGS(SkSurface_Gpu, (info, cs, pr, rowBytes));
+}
+
diff --git a/src/image/SkSurface_Picture.cpp b/src/image/SkSurface_Picture.cpp
new file mode 100644
index 0000000..bac09c5
--- /dev/null
+++ b/src/image/SkSurface_Picture.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 "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&, SkColorSpace*) SK_OVERRIDE;
+    virtual SkImage* onNewImageShapshot() SK_OVERRIDE;
+    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
+                        const SkPaint*) 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, SkColorSpace*) {
+    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, NULL, 0);
+    }
+}
+
+void SkSurface_Picture::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                               const SkPaint* paint) {
+    if (!fPicture) {
+        return;
+    }
+    SkImagePrivDrawPicture(canvas, fPicture, x, y, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+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..8afab6b
--- /dev/null
+++ b/src/image/SkSurface_Raster.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright 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&, SkColorSpace*, size_t rb = kIgnoreRowBytesValue);
+
+    SkSurface_Raster(const SkImage::Info&, SkColorSpace*, void*, size_t rb);
+    SkSurface_Raster(const SkImage::Info&, SkColorSpace*, SkPixelRef*, size_t rb);
+
+    virtual SkCanvas* onNewCanvas() SK_OVERRIDE;
+    virtual SkSurface* onNewSurface(const SkImage::Info&, SkColorSpace*) 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, SkColorSpace* cs,
+                             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, SkColorSpace* cs,
+                                   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, SkColorSpace* cs,
+                                   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(0);
+    }
+}
+
+SkCanvas* SkSurface_Raster::onNewCanvas() {
+    return SkNEW_ARGS(SkCanvas, (fBitmap));
+}
+
+SkSurface* SkSurface_Raster::onNewSurface(const SkImage::Info& info,
+                                          SkColorSpace* cs) {
+    return SkSurface::NewRaster(info, cs);
+}
+
+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,
+                                      SkColorSpace* cs,
+                                      void* pixels, size_t rowBytes) {
+    if (!SkSurface_Raster::Valid(info, cs, rowBytes)) {
+        return NULL;
+    }
+    if (NULL == pixels) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkSurface_Raster, (info, cs, pixels, rowBytes));
+}
+
+SkSurface* SkSurface::NewRaster(const SkImage::Info& info, SkColorSpace* cs) {
+    if (!SkSurface_Raster::Valid(info, cs)) {
+        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, cs, pr, rowBytes));
+}
+
diff --git a/src/images/SkFDStream.cpp b/src/images/SkFDStream.cpp
new file mode 100644
index 0000000..5bf0850
--- /dev/null
+++ b/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_t) 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 (size_t) (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/src/images/SkFlipPixelRef.cpp b/src/images/SkFlipPixelRef.cpp
new file mode 100644
index 0000000..2e73ece
--- /dev/null
+++ b/src/images/SkFlipPixelRef.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 "SkFlipPixelRef.h"
+#include "SkFlattenableBuffers.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);
+    // only need to write page0
+    buffer.writeByteArray(fPage0, fSize);
+}
+
+SkFlipPixelRef::SkFlipPixelRef(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer, NULL) {
+    fSize = buffer.getArrayCount();
+    fStorage = sk_malloc_throw(fSize << 1);
+    fPage0 = fStorage;
+    fPage1 = (char*)fStorage + fSize;
+    buffer.readByteArray(fPage0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
new file mode 100644
index 0000000..2980896
--- /dev/null
+++ b/src/images/SkImageDecoder.cpp
@@ -0,0 +1,194 @@
+
+/*
+ * Copyright 2006 The Android 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"
+
+SK_DEFINE_INST_COUNT(SkImageDecoder::Peeker)
+SK_DEFINE_INST_COUNT(SkImageDecoder::Chooser)
+SK_DEFINE_INST_COUNT(SkImageDecoderFactory)
+
+static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
+
+SkBitmap::Config SkImageDecoder::GetDeviceConfig()
+{
+    return gDeviceConfig;
+}
+
+void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
+{
+    gDeviceConfig = config;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder::SkImageDecoder()
+    : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1),
+      fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true),
+      fUsePrefTable(false) {
+}
+
+SkImageDecoder::~SkImageDecoder() {
+    SkSafeUnref(fPeeker);
+    SkSafeUnref(fChooser);
+    SkSafeUnref(fAllocator);
+}
+
+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;
+}
+
+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) {
+    // 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 (!this->onDecode(stream, &tmp, mode)) {
+        return false;
+    }
+    bm->swap(tmp);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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/src/images/SkImageDecoder_Factory.cpp b/src/images/SkImageDecoder_Factory.cpp
new file mode 100644
index 0000000..f3cb120
--- /dev/null
+++ b/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/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp
new file mode 100644
index 0000000..2cbdcc3
--- /dev/null
+++ b/src/images/SkImageDecoder_libbmp.cpp
@@ -0,0 +1,144 @@
+
+/*
+ * 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);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(BMPImageDecoder);
+///////////////////////////////////////////////////////////////////////////////
+
+static SkImageDecoder* sk_libbmp_dfactory(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(sk_libbmp_dfactory);
+
+///////////////////////////////////////////////////////////////////////////////
+
+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());
+
+    bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
+    bm->setIsOpaque(true);
+    if (justBounds) {
+        return 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/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
new file mode 100644
index 0000000..3008130
--- /dev/null
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -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.
+ */
+
+
+#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");
+            }
+
+            bm->setConfig(SkBitmap::kIndex8_Config, width, height);
+            if (SkImageDecoder::kDecodeBounds_Mode == mode)
+                return true;
+
+            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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(GIFImageDecoder);
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+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 ||
+                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(sk_libgif_dfactory);
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
new file mode 100644
index 0000000..5167fe1
--- /dev/null
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -0,0 +1,393 @@
+
+/*
+ * Copyright 2006 The Android 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);
+};
+
+#if 0 // UNUSED
+SkImageDecoder* SkCreateICOImageDecoder() {
+    return new SkICOImageDecoder;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+//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
+
+    bm->setConfig(SkBitmap::kARGB_8888_Config, w, h, calculateRowBytesFor8888(w, bitCount));
+
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        delete[] colors;
+        return true;
+    }
+
+    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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(ICOImageDecoder);
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+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);
+    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(sk_libico_dfactory);
+
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
new file mode 100644
index 0000000..6bae965
--- /dev/null
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -0,0 +1,682 @@
+
+/*
+ * 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 <stdio.h>
+extern "C" {
+    #include "jpeglib.h"
+    #include "jerror.h"
+}
+
+// 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 SkJPEGImageDecoder : public SkImageDecoder {
+public:
+    virtual Format getFormat() const {
+        return kJPEG_Format;
+    }
+
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+#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
+
+/* For non-ndk builds we could look at the system's jpeg memory cap and use it
+ * if it is set. However, for now we will use the NDK compliant hardcoded values
+ */
+//#include <cutils/properties.h>
+//static const char KEY_MEM_CAP[] = "ro.media.dec.jpeg.memcap";
+
+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;
+}
+
+// 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[]) {
+#if 0
+    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
+}
+
+// Convert a scanline of CMYK samples to RGBX in place. Note that this
+// method moves the "scanline" pointer in its processing
+static void convert_CMYK_to_RGB(uint8_t* scanline, unsigned int width) {
+    // At this point we've received CMYK pixels from libjpeg. We
+    // perform a crude conversion to RGB (based on the formulae
+    // from easyrgb.com):
+    //  CMYK -> CMY
+    //    C = ( C * (1 - K) + K )      // for each CMY component
+    //  CMY -> RGB
+    //    R = ( 1 - C ) * 255          // for each RGB component
+    // Unfortunately we are seeing inverted CMYK so all the original terms
+    // are 1-. This yields:
+    //  CMYK -> CMY
+    //    C = ( (1-C) * (1 - (1-K) + (1-K) ) -> C = 1 - C*K
+    // The conversion from CMY->RGB remains the same
+    for (unsigned int x = 0; x < width; ++x, scanline += 4) {
+        scanline[0] = SkMulDiv255Round(scanline[0], scanline[3]);
+        scanline[1] = SkMulDiv255Round(scanline[1], scanline[3]);
+        scanline[2] = SkMulDiv255Round(scanline[2], scanline[3]);
+        scanline[3] = 255;
+    }
+}
+
+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();
+
+    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 */
+    if (cinfo.jpeg_color_space == JCS_CMYK) {
+        // libjpeg cannot convert from CMYK to RGB - here we set up
+        // so libjpeg will give us CMYK samples back and we will
+        // later manually convert them to RGB
+        cinfo.out_color_space = JCS_CMYK;
+    } else {
+        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 (SkBitmap::kARGB_8888_Config == config && JCS_CMYK != cinfo.out_color_space) {
+        cinfo.out_color_space = JCS_RGBA_8888;
+    } else if (SkBitmap::kRGB_565_Config == config && JCS_CMYK != cinfo.out_color_space) {
+        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->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");
+        }
+        SkAutoLockPixels alp(*bm);
+        JSAMPLE* 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;
+        }
+        jpeg_finish_decompress(&cinfo);
+        return true;
+    }
+#endif
+
+    // check for supported formats
+    SkScaledBitmapSampler::SrcConfig sc;
+    if (JCS_CMYK == cinfo.out_color_space) {
+        // In this case we will manually convert the CMYK values to RGB
+        sc = SkScaledBitmapSampler::kRGBX;
+    } else 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->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");
+    }
+
+    SkAutoLockPixels alp(*bm);
+    if (!sampler.begin(bm, sc, this->getDitherImage())) {
+        return return_false(cinfo, *bm, "sampler.begin");
+    }
+
+    // The CMYK work-around relies on 4 components per pixel here
+    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");
+        }
+
+        if (JCS_CMYK == cinfo.out_color_space) {
+            convert_CMYK_to_RGB(srcRow, cinfo.output_width);
+        }
+
+        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");
+    }
+    jpeg_finish_decompress(&cinfo);
+
+//    SkDebugf("------------------- bm2 size %d [%d %d] %d\n", bm->getSize(), bm->width(), bm->height(), bm->config());
+    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;
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(JPEGImageDecoder);
+DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+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];
+    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* sk_libjpeg_efactory(SkImageEncoder::Type t) {
+    return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
+}
+
+
+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
new file mode 100644
index 0000000..d48bce5
--- /dev/null
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -0,0 +1,880 @@
+
+/*
+ * Copyright 2006 The Android 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 SkPNGImageDecoder : public SkImageDecoder {
+public:
+    virtual Format getFormat() const {
+        return kPNG_Format;
+    }
+
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+};
+
+#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, 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_get_io_ptr(png_ptr);
+    size_t bytes = sk_stream->read(data, length);
+    if (bytes != length) {
+        png_error(png_ptr, "Read Error!");
+    }
+}
+
+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, 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::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,
+                                 Mode mode) {
+//    SkAutoTrace    apr("SkPNGImageDecoder::onDecode");
+
+    /* 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;
+    }
+
+    /* 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, NULL, NULL);
+        return false;
+    }
+
+    PNGAutoClean autoClean(png_ptr, 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);
+    /* 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, NULL, 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_expand_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);
+    }
+
+    SkBitmap::Config    config;
+    bool                hasAlpha = false;
+    bool                doDither = this->getDitherImage();
+    SkPMColor           theTranspColor = 0; // 0 tells us not to try to match
+
+    // check for sBIT chunk data, in case we should disable dithering because
+    // our data is not truely 8bits per component
+    if (doDither) {
+        png_color_8p sig_bit = NULL;
+        bool has_sbit = PNG_INFO_sBIT == png_get_sBIT(png_ptr, info_ptr,
+                                                      &sig_bit);
+#if 0
+        if (has_sbit) {
+            SkDebugf("----- sBIT %d %d %d %d\n", sig_bit->red, sig_bit->green,
+                     sig_bit->blue, sig_bit->alpha);
+        }
+#endif
+        // 0 seems to indicate no information available
+        if (has_sbit && pos_le(sig_bit->red, SK_R16_BITS) &&
+                pos_le(sig_bit->green, SK_G16_BITS) &&
+                pos_le(sig_bit->blue, SK_B16_BITS)) {
+            doDither = false;
+        }
+    }
+
+    if (color_type == PNG_COLOR_TYPE_PALETTE) {
+        bool paletteHasAlpha = hasTransparencyInPalette(png_ptr, info_ptr);
+        config = this->getPrefConfig(kIndex_SrcDepth, paletteHasAlpha);
+        // now see if we can upscale to their requested config
+        if (!canUpscalePaletteToConfig(config, paletteHasAlpha)) {
+            config = 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) {
+                    theTranspColor = SkPackARGB32(0xFF, transpColor->red >> 8,
+                              transpColor->green >> 8, transpColor->blue >> 8);
+                } else {
+                    theTranspColor = SkPackARGB32(0xFF, transpColor->red,
+                                      transpColor->green, transpColor->blue);
+                }
+            } else {    // gray
+                if (16 == bit_depth) {
+                    theTranspColor = SkPackARGB32(0xFF, transpColor->gray >> 8,
+                              transpColor->gray >> 8, transpColor->gray >> 8);
+                } else {
+                    theTranspColor = SkPackARGB32(0xFF, transpColor->gray,
+                                          transpColor->gray, transpColor->gray);
+                }
+            }
+        }
+
+        if (valid ||
+                PNG_COLOR_TYPE_RGB_ALPHA == color_type ||
+                PNG_COLOR_TYPE_GRAY_ALPHA == color_type) {
+            hasAlpha = true;
+        }
+        config = this->getPrefConfig(k32Bit_SrcDepth, hasAlpha);
+        // now match the request against our capabilities
+        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;
+            }
+        }
+    }
+
+    // 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(config, origWidth, origHeight)) {
+        return false;
+    }
+
+    const int sampleSize = this->getSampleSize();
+    SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);
+
+    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) {
+        int num_palette;
+        png_colorp palette;
+        png_bytep trans;
+        int num_trans;
+
+        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);
+            hasAlpha = (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);
+    }
+
+    SkAutoUnref aur(colorTable);
+
+    if (!this->allocPixelRef(decodedBitmap,
+                             SkBitmap::kIndex8_Config == config ?
+                                colorTable : NULL)) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(*decodedBitmap);
+
+    /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
+//  if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+//      ; // png_set_swap_alpha(png_ptr);
+
+    /* swap bytes of 16 bit files to least significant byte first */
+    //   png_set_swap(png_ptr);
+
+    /* 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, 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, 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, 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);
+    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_get_io_ptr(png_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,  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, 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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(PNGImageDecoder);
+DEFINE_ENCODER_CREATOR(PNGImageEncoder);
+///////////////////////////////////////////////////////////////////////////////
+
+#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/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp
new file mode 100644
index 0000000..175a444
--- /dev/null
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -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.
+ */
+
+
+#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);
+    }
+}
+
+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;
+
+    // assign these directly, in case we return kDimensions_Result
+    decodedBitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
+    decodedBitmap->setIsOpaque(true);
+
+    if (SkImageDecoder::kDecodeBounds_Mode == mode)
+        return 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;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(WBMPImageDecoder);
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* sk_wbmp_dfactory(SkStream* stream) {
+    wbmp_head   head;
+
+    if (head.init(stream)) {
+        return SkNEW(SkWBMPImageDecoder);
+    }
+    return NULL;
+}
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_wbmp_dfactory);
+
diff --git a/src/images/SkImageEncoder.cpp b/src/images/SkImageEncoder.cpp
new file mode 100644
index 0000000..e05a28c
--- /dev/null
+++ b/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/src/images/SkImageEncoder_Factory.cpp b/src/images/SkImageEncoder_Factory.cpp
new file mode 100644
index 0000000..2bd1113
--- /dev/null
+++ b/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/src/images/SkImageRef.cpp b/src/images/SkImageRef.cpp
new file mode 100644
index 0000000..39a1de9
--- /dev/null
+++ b/src/images/SkImageRef.cpp
@@ -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.
+ */
+#include "SkImageRef.h"
+#include "SkBitmap.h"
+#include "SkFlattenableBuffers.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.readUInt();
+    fSampleSize = buffer.readInt();
+    fDoDither = buffer.readBool();
+
+    size_t length = buffer.getArrayCount();
+    fStream = SkNEW_ARGS(SkMemoryStream, (length));
+    buffer.readByteArray((void*)fStream->getMemoryBase());
+
+    fPrev = fNext = NULL;
+    fFactory = NULL;
+}
+
+void SkImageRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.writeUInt(fConfig);
+    buffer.writeInt(fSampleSize);
+    buffer.writeBool(fDoDither);
+    fStream->rewind();
+    buffer.writeStream(fStream, fStream->getLength());
+}
+
diff --git a/src/images/SkImageRefPool.cpp b/src/images/SkImageRefPool.cpp
new file mode 100644
index 0000000..b8bbfd2
--- /dev/null
+++ b/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/src/images/SkImageRefPool.h b/src/images/SkImageRefPool.h
new file mode 100644
index 0000000..08633ee
--- /dev/null
+++ b/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/src/images/SkImageRef_GlobalPool.cpp b/src/images/SkImageRef_GlobalPool.cpp
new file mode 100644
index 0000000..e62816a
--- /dev/null
+++ b/src/images/SkImageRef_GlobalPool.cpp
@@ -0,0 +1,100 @@
+
+/*
+ * Copyright 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();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// 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/src/images/SkImages.cpp b/src/images/SkImages.cpp
new file mode 100644
index 0000000..28dfd7f
--- /dev/null
+++ b/src/images/SkImages.cpp
@@ -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.
+ */
+
+#include "SkFlattenable.h"
+#include "SkFlipPixelRef.h"
+#include "SkImageRef_GlobalPool.h"
+#include "SkImages.h"
+
+void SkImages::InitializeFlattenables() {
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkFlipPixelRef)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageRef_GlobalPool)
+}
diff --git a/src/images/SkJpegUtility.cpp b/src/images/SkJpegUtility.cpp
new file mode 100644
index 0000000..19db018
--- /dev/null
+++ b/src/images/SkJpegUtility.cpp
@@ -0,0 +1,185 @@
+
+/*
+ * 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->fStream->rewind();
+}
+
+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->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;
+            }
+            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*/) {}
+
+
+#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;
+    src->bytes_in_buffer = 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*/) {}
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder,
+                                     bool ownStream) : fStream(stream) {
+    fDecoder = decoder;
+    // const void* baseAddr = stream->getMemoryBase();
+    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;
+//    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/src/images/SkMovie.cpp b/src/images/SkMovie.cpp
new file mode 100644
index 0000000..e671950
--- /dev/null
+++ b/src/images/SkMovie.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 "SkMovie.h"
+#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)
+
+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/src/images/SkMovie_gif.cpp b/src/images/SkMovie_gif.cpp
new file mode 100644
index 0000000..9cebc0f
--- /dev/null
+++ b/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/src/images/SkPageFlipper.cpp b/src/images/SkPageFlipper.cpp
new file mode 100644
index 0000000..4fd8632
--- /dev/null
+++ b/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/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp
new file mode 100644
index 0000000..25b32fd
--- /dev/null
+++ b/src/images/SkScaledBitmapSampler.cpp
@@ -0,0 +1,432 @@
+
+/*
+ * 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) {
+    fCTable = NULL;
+    fDstRow = NULL;
+    fRowProc = NULL;
+
+    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);
+}
+
+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/src/images/SkScaledBitmapSampler.h b/src/images/SkScaledBitmapSampler.h
new file mode 100644
index 0000000..f6de4cc
--- /dev/null
+++ b/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/src/images/bmpdecoderhelper.cpp b/src/images/bmpdecoderhelper.cpp
new file mode 100644
index 0000000..7749664
--- /dev/null
+++ b/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/src/images/bmpdecoderhelper.h b/src/images/bmpdecoderhelper.h
new file mode 100644
index 0000000..f2f4109
--- /dev/null
+++ b/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/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
new file mode 100644
index 0000000..1e2dc93
--- /dev/null
+++ b/src/opts/SkBitmapProcState_opts_SSE2.cpp
@@ -0,0 +1,767 @@
+
+/*
+ * 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++ = pack_two_shorts(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;
+    }
+}
+
+/*  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();
+
+    __m128i _m_shift_5 = _mm_set1_epi32((1<<5)-1);
+    __m128i _m_shift_6 = _mm_set1_epi32((1<<6)-1);
+    do {
+        uint32_t XX = *xy++;    // x0:14 | 4 | x1:14
+        unsigned x0 = XX >> 18;
+        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
new file mode 100644
index 0000000..46e35a0
--- /dev/null
+++ b/src/opts/SkBitmapProcState_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 "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);
+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
new file mode 100644
index 0000000..59b2386
--- /dev/null
+++ b/src/opts/SkBitmapProcState_opts_SSSE3.cpp
@@ -0,0 +1,723 @@
+/*
+ * 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);
+}
+
+// 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
+//                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);
+}
+
+// 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.
+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 = _mm_setzero_si128();
+    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);
+        }
+    }
+}
+
+/*
+ * 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,
+                                    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);
+}
+
+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
new file mode 100644
index 0000000..176f2bf
--- /dev/null
+++ b/src/opts/SkBitmapProcState_opts_SSSE3.h
@@ -0,0 +1,21 @@
+/*
+ * 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);
+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
new file mode 100644
index 0000000..8b64773
--- /dev/null
+++ b/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 "SkTypes.h"
+#include "SkUtils.h"
+
+#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) SK_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) SK_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 // SK_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 SK_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/src/opts/SkBitmapProcState_opts_none.cpp b/src/opts/SkBitmapProcState_opts_none.cpp
new file mode 100644
index 0000000..82be4ea
--- /dev/null
+++ b/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/src/opts/SkBlitRect_opts_SSE2.cpp b/src/opts/SkBlitRect_opts_SSE2.cpp
new file mode 100644
index 0000000..6d8a296
--- /dev/null
+++ b/src/opts/SkBlitRect_opts_SSE2.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 "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..d3ec0e3
--- /dev/null
+++ b/src/opts/SkBlitRect_opts_SSE2.h
@@ -0,0 +1,24 @@
+/*
+ * 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
new file mode 100644
index 0000000..27ce1e5
--- /dev/null
+++ b/src/opts/SkBlitRow_opts_SSE2.cpp
@@ -0,0 +1,764 @@
+/*
+ * 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 "SkBitmapProcState_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);
+}
+
+// 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(SkPackedR16x5ToUnmaskedR32x5_SSE2(mask),
+                              _mm_set1_epi32(0x1F << SK_R32_SHIFT));
+
+    __m128i g = _mm_and_si128(SkPackedG16x5ToUnmaskedG32x5_SSE2(mask),
+                              _mm_set1_epi32(0x1F << SK_G32_SHIFT));
+
+    __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.
+    __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(SkPackedR16x5ToUnmaskedR32x5_SSE2(mask),
+                              _mm_set1_epi32(0x1F << SK_R32_SHIFT));
+
+    __m128i g = _mm_and_si128(SkPackedG16x5ToUnmaskedG32x5_SSE2(mask),
+                              _mm_set1_epi32(0x1F << SK_G32_SHIFT));
+
+    __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.
+    __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 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[],
+                         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/src/opts/SkBlitRow_opts_SSE2.h b/src/opts/SkBlitRow_opts_SSE2.h
new file mode 100644
index 0000000..b443ec7
--- /dev/null
+++ b/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/src/opts/SkBlitRow_opts_arm.cpp b/src/opts/SkBlitRow_opts_arm.cpp
new file mode 100644
index 0000000..f6e6ba2
--- /dev/null
+++ b/src/opts/SkBlitRow_opts_arm.cpp
@@ -0,0 +1,406 @@
+/*
+ * 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"
+
+#if USE_ARM_CODE
+
+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 (
+                  "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"
+#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"
+#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"
+                  "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"
+                  );
+}
+
+static void S32A_Opaque_BlitRow32_arm(SkPMColor* SK_RESTRICT dst,
+                                  const SkPMColor* SK_RESTRICT src,
+                                  int count, U8CPU alpha) {
+
+    SkASSERT(255 == alpha);
+
+    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"
+                  );
+}
+#endif // USE_ARM_CODE
+
+/*
+ * ARM asm version of S32A_Blend_BlitRow32
+ */
+// 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 */
+
+                  "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 */
+#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 */
+                  "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 */
+#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 */
+                  "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"
+                  );
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if USE_ARM_CODE
+const SkBlitRow::Proc sk_blitrow_platform_565_procs_arm[] = {
+    // no dither
+    // 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
+    NULL,   // S32_D565_Opaque_Dither
+    NULL,   // S32_D565_Blend_Dither
+    NULL,   // S32A_D565_Opaque_Dither
+    NULL,   // S32A_D565_Blend_Dither
+};
+
+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,
+    NULL,   // S32A_D4444_Opaque_Dither,
+    NULL,   // S32A_D4444_Blend_Dither
+};
+
+const SkBlitRow::Proc32 sk_blitrow_platform_32_procs_arm[] = {
+    NULL,   // S32_Opaque,
+    NULL,   // S32_Blend,
+    S32A_Opaque_BlitRow32_arm,   // S32A_Opaque,
+    S32A_Blend_BlitRow32_arm     // S32A_Blend
+};
+#endif
+
+SkBlitRow::Proc SkBlitRow::PlatformProcs4444(unsigned flags) {
+    return SK_ARM_NEON_WRAP(sk_blitrow_platform_4444_procs_arm)[flags];
+}
+
+SkBlitRow::Proc SkBlitRow::PlatformProcs565(unsigned flags) {
+    return SK_ARM_NEON_WRAP(sk_blitrow_platform_565_procs_arm)[flags];
+}
+
+SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) {
+    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,
+                                                     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/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
new file mode 100644
index 0000000..5f4598e
--- /dev/null
+++ b/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/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
new file mode 100644
index 0000000..08e4f66
--- /dev/null
+++ b/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/src/opts/SkUtils_opts_SSE2.h b/src/opts/SkUtils_opts_SSE2.h
new file mode 100644
index 0000000..ed24c1f
--- /dev/null
+++ b/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/src/opts/SkUtils_opts_none.cpp b/src/opts/SkUtils_opts_none.cpp
new file mode 100644
index 0000000..bb2558c
--- /dev/null
+++ b/src/opts/SkUtils_opts_none.cpp
@@ -0,0 +1,25 @@
+
+/*
+ * 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 "SkUtils.h"
+
+SkMemset16Proc SkMemset16GetPlatformProc() {
+    return NULL;
+}
+
+SkMemset32Proc SkMemset32GetPlatformProc() {
+    return NULL;
+}
+
+SkBlitRow::ColorRectProc PlatformColorRectProcFactory() {
+    return NULL;
+}
+
+
diff --git a/src/opts/memset.arm.S b/src/opts/memset.arm.S
new file mode 100644
index 0000000..44b75e3
--- /dev/null
+++ b/src/opts/memset.arm.S
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2010 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Changes:
+ * 2010-08-11 Steve McIntyre <steve.mcintyre@arm.com>
+ *    Added small changes to the two functions to make them work on the
+ *    specified number of 16- or 32-bit values rather than the original
+ *    code which was specified as a count of bytes. More verbose comments
+ *    to aid future maintenance.
+ */
+
+    .text
+    .align 4
+    .syntax unified
+
+    .global arm_memset32
+    .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, r1, lsl #16
+
+        /* align to 32 bits */
+        tst         r0, #2
+        strhne      r1, [r0], #2
+        subne       r2, r2, #2
+
+        /* Now jump into the main loop below. */
+        b           .Lwork_32
+        .fnend
+
+arm_memset32:
+        .fnstart
+        push        {lr}
+
+        /* if count is equal to zero then abort */
+        teq         r2, #0
+        ble         .Lfinish
+
+        /* Multiply count by 4 - go from the number of 32-bit words to
+         * the number of bytes desired. */
+        mov         r2, r2, lsl #2
+
+.Lwork_32:
+        /* Set up registers ready for writing them out. */
+        mov         ip, r1
+        mov         lr, r1
+
+        /* Try to align the destination to a cache line. Assume 32
+         * byte (8 word) cache lines, it's the common case. */
+        rsb         r3, r0, #0
+        ands        r3, r3, #0x1C
+        beq         .Laligned32
+        cmp         r3, r2
+        andhi       r3, r2, #0x1C
+        sub         r2, r2, r3
+
+        /* (Optionally) write any unaligned leading bytes.
+         * (0-28 bytes, length in r3) */
+        movs        r3, r3, lsl #28
+        stmiacs     r0!, {r1, lr}
+        stmiacs     r0!, {r1, lr}
+        stmiami     r0!, {r1, lr}
+        movs        r3, r3, lsl #2
+        strcs       r1, [r0], #4
+
+        /* Now quickly loop through the cache-aligned data. */
+.Laligned32:
+        mov         r3, r1
+1:      subs        r2, r2, #32
+        stmiahs     r0!, {r1,r3,ip,lr}
+        stmiahs     r0!, {r1,r3,ip,lr}
+        bhs         1b
+        add         r2, r2, #32
+
+        /* (Optionally) store any remaining trailing bytes.
+         * (0-30 bytes, length in r2) */
+        movs        r2, r2, lsl #28
+        stmiacs     r0!, {r1,r3,ip,lr}
+        stmiami     r0!, {r1,lr}
+        movs        r2, r2, lsl #2
+        strcs       r1, [r0], #4
+        strhmi      lr, [r0], #2
+
+.Lfinish:
+        pop         {pc}
+        .fnend
diff --git a/src/opts/memset16_neon.S b/src/opts/memset16_neon.S
new file mode 100644
index 0000000..b1719fa
--- /dev/null
+++ b/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/src/opts/memset32_neon.S b/src/opts/memset32_neon.S
new file mode 100644
index 0000000..a9eaa0e
--- /dev/null
+++ b/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/src/opts/opts_check_SSE2.cpp b/src/opts/opts_check_SSE2.cpp
new file mode 100644
index 0000000..96d0dea
--- /dev/null
+++ b/src/opts/opts_check_SSE2.cpp
@@ -0,0 +1,248 @@
+/*
+ * 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.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
+   in this directory should be compiled with -msse2. */
+
+
+#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
+        mov    edi, [info]
+        mov    [edi], eax
+        mov    [edi+4], ebx
+        mov    [edi+8], ecx
+        mov    [edi+12], edx
+    }
+#endif
+}
+#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) || 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;
+}
+#else
+
+static inline bool hasSSE2() {
+    int cpu_info[4] = { 0 };
+    getcpuid(1, cpu_info);
+    return (cpu_info[3] & (1<<26)) != 0;
+}
+#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();
+    return gHasSSE2;
+}
+
+static bool cachedHasSSSE3() {
+    static bool gHasSSSE3 = hasSSSE3();
+    return gHasSSSE3;
+}
+
+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()) {
+        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;
+    }
+}
+
+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
new file mode 100644
index 0000000..69cedb2
--- /dev/null
+++ b/src/opts/opts_check_arm.cpp
@@ -0,0 +1,68 @@
+/***************************************************************************
+ * 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 "SkBlitRow.h"
+#include "SkUtils.h"
+
+#include "SkUtilsArm.h"
+
+#if defined(SK_CPU_LENDIAN) && !SK_ARM_NEON_IS_NONE
+extern "C" void memset16_neon(uint16_t dst[], uint16_t value, int count);
+extern "C" void memset32_neon(uint32_t dst[], uint32_t value, int count);
+#endif
+
+#if defined(SK_CPU_LENDIAN)
+extern "C" void arm_memset16(uint16_t* dst, uint16_t value, int count);
+extern "C" void arm_memset32(uint32_t* dst, uint32_t value, int count);
+#endif
+
+SkMemset16Proc SkMemset16GetPlatformProc() {
+    // 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() {
+    // 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
new file mode 100644
index 0000000..c0f6fb0
--- /dev/null
+++ b/src/pdf/SkPDFCatalog.cpp
@@ -0,0 +1,208 @@
+
+/*
+ * Copyright 2010 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkPDFCatalog.h"
+#include "SkPDFTypes.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+
+SkPDFCatalog::SkPDFCatalog(SkPDFDocument::Flags flags)
+    : fFirstPageCount(0),
+      fNextObjNum(1),
+      fNextFirstPageObjNum(0),
+      fDocumentFlags(flags) {
+}
+
+SkPDFCatalog::~SkPDFCatalog() {
+    fSubstituteResourcesRemaining.safeUnrefAll();
+    fSubstituteResourcesFirstPage.safeUnrefAll();
+}
+
+SkPDFObject* SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) {
+    if (findObjectIndex(obj) != -1) {  // object already added
+        return obj;
+    }
+    SkASSERT(fNextFirstPageObjNum == 0);
+    if (onFirstPage) {
+        fFirstPageCount++;
+    }
+
+    struct Rec newEntry(obj, onFirstPage);
+    fCatalog.append(1, &newEntry);
+    return obj;
+}
+
+size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, off_t offset) {
+    int objIndex = assignObjNum(obj) - 1;
+    SkASSERT(fCatalog[objIndex].fObjNumAssigned);
+    SkASSERT(fCatalog[objIndex].fFileOffset == 0);
+    fCatalog[objIndex].fFileOffset = offset;
+
+    return getSubstituteObject(obj)->getOutputSize(this, true);
+}
+
+void SkPDFCatalog::emitObjectNumber(SkWStream* stream, SkPDFObject* obj) {
+    stream->writeDecAsText(assignObjNum(obj));
+    stream->writeText(" 0");  // Generation number is always 0.
+}
+
+size_t SkPDFCatalog::getObjectNumberSize(SkPDFObject* obj) {
+    SkDynamicMemoryWStream buffer;
+    emitObjectNumber(&buffer, obj);
+    return buffer.getOffset();
+}
+
+int SkPDFCatalog::findObjectIndex(SkPDFObject* obj) const {
+    for (int i = 0; i < fCatalog.count(); i++) {
+        if (fCatalog[i].fObject == obj) {
+            return i;
+        }
+    }
+    // If it's not in the main array, check if it's a substitute object.
+    for (int i = 0; i < fSubstituteMap.count(); ++i) {
+        if (fSubstituteMap[i].fSubstitute == obj) {
+            return findObjectIndex(fSubstituteMap[i].fOriginal);
+        }
+    }
+    return -1;
+}
+
+int SkPDFCatalog::assignObjNum(SkPDFObject* obj) {
+    int pos = findObjectIndex(obj);
+    // If this assert fails, it means you probably forgot to add an object
+    // to the resource list.
+    SkASSERT(pos >= 0);
+    uint32_t currentIndex = pos;
+    if (fCatalog[currentIndex].fObjNumAssigned) {
+        return currentIndex + 1;
+    }
+
+    // First assignment.
+    if (fNextFirstPageObjNum == 0) {
+        fNextFirstPageObjNum = fCatalog.count() - fFirstPageCount + 1;
+    }
+
+    uint32_t objNum;
+    if (fCatalog[currentIndex].fOnFirstPage) {
+        objNum = fNextFirstPageObjNum;
+        fNextFirstPageObjNum++;
+    } else {
+        objNum = fNextObjNum;
+        fNextObjNum++;
+    }
+
+    // When we assign an object an object number, we put it in that array
+    // offset (minus 1 because object number 0 is reserved).
+    SkASSERT(!fCatalog[objNum - 1].fObjNumAssigned);
+    if (objNum - 1 != currentIndex) {
+        SkTSwap(fCatalog[objNum - 1], fCatalog[currentIndex]);
+    }
+    fCatalog[objNum - 1].fObjNumAssigned = true;
+    return objNum;
+}
+
+int32_t SkPDFCatalog::emitXrefTable(SkWStream* stream, bool firstPage) {
+    int first = -1;
+    int last = fCatalog.count() - 1;
+    // TODO(vandebo): Support linearized format.
+    // int last = fCatalog.count() - fFirstPageCount - 1;
+    // if (firstPage) {
+    //     first = fCatalog.count() - fFirstPageCount;
+    //     last = fCatalog.count() - 1;
+    // }
+
+    stream->writeText("xref\n");
+    stream->writeDecAsText(first + 1);
+    stream->writeText(" ");
+    stream->writeDecAsText(last - first + 1);
+    stream->writeText("\n");
+
+    if (first == -1) {
+        stream->writeText("0000000000 65535 f \n");
+        first++;
+    }
+    for (int i = first; i <= last; i++) {
+        SkASSERT(fCatalog[i].fFileOffset > 0);
+        SkASSERT(fCatalog[i].fFileOffset <= 9999999999LL);
+        stream->writeBigDecAsText(fCatalog[i].fFileOffset, 10);
+        stream->writeText(" 00000 n \n");
+    }
+
+    return fCatalog.count() + 1;
+}
+
+void SkPDFCatalog::setSubstitute(SkPDFObject* original,
+                                 SkPDFObject* substitute) {
+#if defined(SK_DEBUG)
+    // Sanity check: is the original already in substitute list?
+    for (int i = 0; i < fSubstituteMap.count(); ++i) {
+        if (original == fSubstituteMap[i].fSubstitute ||
+            original == fSubstituteMap[i].fOriginal) {
+            SkASSERT(false);
+            return;
+        }
+    }
+#endif
+    // Check if the original is on first page.
+    bool onFirstPage = false;
+    for (int i = 0; i < fCatalog.count(); ++i) {
+        if (fCatalog[i].fObject == original) {
+            onFirstPage = fCatalog[i].fOnFirstPage;
+            break;
+        }
+#if defined(SK_DEBUG)
+        if (i == fCatalog.count() - 1) {
+            SkASSERT(false);  // original not in catalog
+            return;
+        }
+#endif
+    }
+
+    SubstituteMapping newMapping(original, substitute);
+    fSubstituteMap.append(1, &newMapping);
+
+    // Add resource objects of substitute object to catalog.
+    SkTDArray<SkPDFObject*>* targetList = getSubstituteList(onFirstPage);
+    int existingSize = targetList->count();
+    newMapping.fSubstitute->getResources(targetList);
+    for (int i = existingSize; i < targetList->count(); ++i) {
+        addObject((*targetList)[i], onFirstPage);
+    }
+}
+
+SkPDFObject* SkPDFCatalog::getSubstituteObject(SkPDFObject* object) {
+    for (int i = 0; i < fSubstituteMap.count(); ++i) {
+        if (object == fSubstituteMap[i].fOriginal) {
+            return fSubstituteMap[i].fSubstitute;
+        }
+    }
+    return object;
+}
+
+off_t SkPDFCatalog::setSubstituteResourcesOffsets(off_t fileOffset,
+                                                  bool firstPage) {
+    SkTDArray<SkPDFObject*>* targetList = getSubstituteList(firstPage);
+    off_t offsetSum = fileOffset;
+    for (int i = 0; i < targetList->count(); ++i) {
+        offsetSum += setFileOffset((*targetList)[i], offsetSum);
+    }
+    return offsetSum - fileOffset;
+}
+
+void SkPDFCatalog::emitSubstituteResources(SkWStream *stream, bool firstPage) {
+    SkTDArray<SkPDFObject*>* targetList = getSubstituteList(firstPage);
+    for (int i = 0; i < targetList->count(); ++i) {
+        (*targetList)[i]->emit(stream, this, true);
+    }
+}
+
+SkTDArray<SkPDFObject*>* SkPDFCatalog::getSubstituteList(bool firstPage) {
+    return firstPage ? &fSubstituteResourcesFirstPage :
+                       &fSubstituteResourcesRemaining;
+}
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
new file mode 100644
index 0000000..929ca10
--- /dev/null
+++ b/src/pdf/SkPDFDevice.cpp
@@ -0,0 +1,1627 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkPDFDevice.h"
+
+#include "SkAnnotation.h"
+#include "SkColor.h"
+#include "SkClipStack.h"
+#include "SkData.h"
+#include "SkDraw.h"
+#include "SkGlyphCache.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPDFFont.h"
+#include "SkPDFFormXObject.h"
+#include "SkPDFGraphicState.h"
+#include "SkPDFImage.h"
+#include "SkPDFShader.h"
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkPDFUtils.h"
+#include "SkRect.h"
+#include "SkString.h"
+#include "SkTextFormatParams.h"
+#include "SkTypeface.h"
+#include "SkTypes.h"
+
+// Utility functions
+
+static void emit_pdf_color(SkColor color, SkWStream* result) {
+    SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere.
+    SkScalar colorMax = SkIntToScalar(0xFF);
+    SkPDFScalar::Append(
+            SkScalarDiv(SkIntToScalar(SkColorGetR(color)), colorMax), result);
+    result->writeText(" ");
+    SkPDFScalar::Append(
+            SkScalarDiv(SkIntToScalar(SkColorGetG(color)), colorMax), result);
+    result->writeText(" ");
+    SkPDFScalar::Append(
+            SkScalarDiv(SkIntToScalar(SkColorGetB(color)), colorMax), result);
+    result->writeText(" ");
+}
+
+static SkPaint calculate_text_paint(const SkPaint& paint) {
+    SkPaint result = paint;
+    if (result.isFakeBoldText()) {
+        SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
+                                                    kStdFakeBoldInterpKeys,
+                                                    kStdFakeBoldInterpValues,
+                                                    kStdFakeBoldInterpLength);
+        SkScalar width = SkScalarMul(result.getTextSize(), fakeBoldScale);
+        if (result.getStyle() == SkPaint::kFill_Style) {
+            result.setStyle(SkPaint::kStrokeAndFill_Style);
+        } else {
+            width += result.getStrokeWidth();
+        }
+        result.setStrokeWidth(width);
+    }
+    return result;
+}
+
+// Stolen from measure_text in SkDraw.cpp and then tweaked.
+static void align_text(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
+                       const uint16_t* glyphs, size_t len,
+                       SkScalar* x, SkScalar* y) {
+    if (paint.getTextAlign() == SkPaint::kLeft_Align) {
+        return;
+    }
+
+    SkMatrix ident;
+    ident.reset();
+    SkAutoGlyphCache autoCache(paint, &ident);
+    SkGlyphCache* cache = autoCache.getCache();
+
+    const char* start = reinterpret_cast<const char*>(glyphs);
+    const char* stop = reinterpret_cast<const char*>(glyphs + len);
+    SkFixed xAdv = 0, yAdv = 0;
+
+    // TODO(vandebo): This probably needs to take kerning into account.
+    while (start < stop) {
+        const SkGlyph& glyph = glyphCacheProc(cache, &start, 0, 0);
+        xAdv += glyph.fAdvanceX;
+        yAdv += glyph.fAdvanceY;
+    };
+    if (paint.getTextAlign() == SkPaint::kLeft_Align) {
+        return;
+    }
+
+    SkScalar xAdj = SkFixedToScalar(xAdv);
+    SkScalar yAdj = SkFixedToScalar(yAdv);
+    if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+        xAdj = SkScalarHalf(xAdj);
+        yAdj = SkScalarHalf(yAdj);
+    }
+    *x = *x - xAdj;
+    *y = *y - yAdj;
+}
+
+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
+    // the passed parameters.
+    content->writeText("1 0 ");
+    SkPDFScalar::Append(0 - textSkewX, content);
+    content->writeText(" -1 ");
+    SkPDFScalar::Append(x, content);
+    content->writeText(" ");
+    SkPDFScalar::Append(y, content);
+    content->writeText(" Tm\n");
+}
+
+// It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
+// later being our representation of an object in the PDF file.
+struct GraphicStateEntry {
+    GraphicStateEntry();
+
+    // Compare the fields we care about when setting up a new content entry.
+    bool compareInitialState(const GraphicStateEntry& b);
+
+    SkMatrix fMatrix;
+    // We can't do set operations on Paths, though PDF natively supports
+    // intersect.  If the clip stack does anything other than intersect,
+    // we have to fall back to the region.  Treat fClipStack as authoritative.
+    // See http://code.google.com/p/skia/issues/detail?id=221
+    SkClipStack fClipStack;
+    SkRegion fClipRegion;
+
+    // When emitting the content entry, we will ensure the graphic state
+    // is set to these values first.
+    SkColor fColor;
+    SkScalar fTextScaleX;  // Zero means we don't care what the value is.
+    SkPaint::Style fTextFill;  // Only if TextScaleX is non-zero.
+    int fShaderIndex;
+    int fGraphicStateIndex;
+
+    // We may change the font (i.e. for Type1 support) within a
+    // ContentEntry.  This is the one currently in effect, or NULL if none.
+    SkPDFFont* fFont;
+    // In PDF, text size has no default value. It is only valid if fFont is
+    // not NULL.
+    SkScalar fTextSize;
+};
+
+GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK),
+                                         fTextScaleX(SK_Scalar1),
+                                         fTextFill(SkPaint::kFill_Style),
+                                         fShaderIndex(-1),
+                                         fGraphicStateIndex(-1),
+                                         fFont(NULL),
+                                         fTextSize(SK_ScalarNaN) {
+    fMatrix.reset();
+}
+
+bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& b) {
+    return fColor == b.fColor &&
+           fShaderIndex == b.fShaderIndex &&
+           fGraphicStateIndex == b.fGraphicStateIndex &&
+           fMatrix == b.fMatrix &&
+           fClipStack == b.fClipStack &&
+               (fTextScaleX == 0 ||
+                b.fTextScaleX == 0 ||
+                (fTextScaleX == b.fTextScaleX && fTextFill == b.fTextFill));
+}
+
+class GraphicStackState {
+public:
+    GraphicStackState(const SkClipStack& existingClipStack,
+                      const SkRegion& existingClipRegion,
+                      SkWStream* contentStream)
+            : fStackDepth(0),
+              fContentStream(contentStream) {
+        fEntries[0].fClipStack = existingClipStack;
+        fEntries[0].fClipRegion = existingClipRegion;
+    }
+
+    void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion,
+                    const SkPoint& translation);
+    void updateMatrix(const SkMatrix& matrix);
+    void updateDrawingState(const GraphicStateEntry& state);
+
+    void drainStack();
+
+private:
+    void push();
+    void pop();
+    GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
+
+    // Conservative limit on save depth, see impl. notes in PDF 1.4 spec.
+    static const int kMaxStackDepth = 12;
+    GraphicStateEntry fEntries[kMaxStackDepth + 1];
+    int fStackDepth;
+    SkWStream* fContentStream;
+};
+
+void GraphicStackState::drainStack() {
+    while (fStackDepth) {
+        pop();
+    }
+}
+
+void GraphicStackState::push() {
+    SkASSERT(fStackDepth < kMaxStackDepth);
+    fContentStream->writeText("q\n");
+    fStackDepth++;
+    fEntries[fStackDepth] = fEntries[fStackDepth - 1];
+}
+
+void GraphicStackState::pop() {
+    SkASSERT(fStackDepth > 0);
+    fContentStream->writeText("Q\n");
+    fStackDepth--;
+}
+
+// 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::Iter* iter) {
+    SkClipStack::B2TIter prefixIter(prefix);
+    iter->reset(stack, SkClipStack::Iter::kBottom_IterStart);
+
+    const SkClipStack::B2TIter::Clip* prefixEntry;
+    const SkClipStack::B2TIter::Clip* iterEntry;
+
+    for (prefixEntry = prefixIter.next(); prefixEntry;
+            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));
+            // back up the iterator by one
+            iter->prev();
+            prefixEntry = prefixIter.next();
+            break;
+        }
+    }
+
+    SkASSERT(prefixEntry == NULL);
+}
+
+static void emit_clip(SkPath* clipPath, SkRect* clipRect,
+                      SkWStream* contentStream) {
+    SkASSERT(clipPath || clipRect);
+
+    SkPath::FillType clipFill;
+    if (clipPath) {
+        SkPDFUtils::EmitPath(*clipPath, SkPaint::kFill_Style, contentStream);
+        clipFill = clipPath->getFillType();
+    } else {
+        SkPDFUtils::AppendRectangle(*clipRect, contentStream);
+        clipFill = SkPath::kWinding_FillType;
+    }
+
+    NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
+    NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
+    if (clipFill == SkPath::kEvenOdd_FillType) {
+        contentStream->writeText("W* n\n");
+    } else {
+        contentStream->writeText("W n\n");
+    }
+}
+
+// TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF
+// graphic state stack, and the fact that we can know all the clips used
+// on the page to optimize this.
+void GraphicStackState::updateClip(const SkClipStack& clipStack,
+                                   const SkRegion& clipRegion,
+                                   const SkPoint& translation) {
+    if (clipStack == currentEntry()->fClipStack) {
+        return;
+    }
+
+    while (fStackDepth > 0) {
+        pop();
+        if (clipStack == currentEntry()->fClipStack) {
+            return;
+        }
+    }
+    push();
+
+    // gsState->initialEntry()->fClipStack/Region specifies the clip that has
+    // already been applied.  (If this is a top level device, then it specifies
+    // a clip to the content area.  If this is a layer, then it specifies
+    // the clip in effect when the layer was created.)  There's no need to
+    // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the
+    // 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::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::B2TIter::Clip* clipEntry;
+    for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
+        if (clipEntry->fOp != SkRegion::kIntersect_Op ||
+                (clipEntry->fPath && clipEntry->fPath->isInverseFillType())) {
+            needRegion = true;
+            break;
+        }
+    }
+
+    if (needRegion) {
+        SkPath clipPath;
+        SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
+        emit_clip(&clipPath, NULL, fContentStream);
+    } else {
+        skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
+        SkMatrix transform;
+        transform.setTranslate(translation.fX, translation.fY);
+        const SkClipStack::B2TIter::Clip* 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);
+            }
+        }
+    }
+    currentEntry()->fClipStack = clipStack;
+    currentEntry()->fClipRegion = clipRegion;
+}
+
+void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
+    if (matrix == currentEntry()->fMatrix) {
+        return;
+    }
+
+    if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) {
+        SkASSERT(fStackDepth > 0);
+        SkASSERT(fEntries[fStackDepth].fClipStack ==
+                 fEntries[fStackDepth -1].fClipStack);
+        pop();
+
+        SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask);
+    }
+    if (matrix.getType() == SkMatrix::kIdentity_Mask) {
+        return;
+    }
+
+    push();
+    SkPDFUtils::AppendTransform(matrix, fContentStream);
+    currentEntry()->fMatrix = matrix;
+}
+
+void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) {
+    // PDF treats a shader as a color, so we only set one or the other.
+    if (state.fShaderIndex >= 0) {
+        if (state.fShaderIndex != currentEntry()->fShaderIndex) {
+            fContentStream->writeText("/Pattern CS /Pattern cs /P");
+            fContentStream->writeDecAsText(state.fShaderIndex);
+            fContentStream->writeText(" SCN /P");
+            fContentStream->writeDecAsText(state.fShaderIndex);
+            fContentStream->writeText(" scn\n");
+            currentEntry()->fShaderIndex = state.fShaderIndex;
+        }
+    } else {
+        if (state.fColor != currentEntry()->fColor ||
+                currentEntry()->fShaderIndex >= 0) {
+            emit_pdf_color(state.fColor, fContentStream);
+            fContentStream->writeText("RG ");
+            emit_pdf_color(state.fColor, fContentStream);
+            fContentStream->writeText("rg\n");
+            currentEntry()->fColor = state.fColor;
+            currentEntry()->fShaderIndex = -1;
+        }
+    }
+
+    if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
+        SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream);
+        currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
+    }
+
+    if (state.fTextScaleX) {
+        if (state.fTextScaleX != currentEntry()->fTextScaleX) {
+            SkScalar pdfScale = SkScalarMul(state.fTextScaleX,
+                                            SkIntToScalar(100));
+            SkPDFScalar::Append(pdfScale, fContentStream);
+            fContentStream->writeText(" Tz\n");
+            currentEntry()->fTextScaleX = state.fTextScaleX;
+        }
+        if (state.fTextFill != currentEntry()->fTextFill) {
+            SK_COMPILE_ASSERT(SkPaint::kFill_Style == 0, enum_must_match_value);
+            SK_COMPILE_ASSERT(SkPaint::kStroke_Style == 1,
+                              enum_must_match_value);
+            SK_COMPILE_ASSERT(SkPaint::kStrokeAndFill_Style == 2,
+                              enum_must_match_value);
+            fContentStream->writeDecAsText(state.fTextFill);
+            fContentStream->writeText(" Tr\n");
+            currentEntry()->fTextFill = state.fTextFill;
+        }
+    }
+}
+
+SkDevice* SkPDFDevice::onCreateCompatibleDevice(SkBitmap::Config config,
+                                                int width, int height,
+                                                bool isOpaque,
+                                                Usage usage) {
+    SkMatrix initialTransform;
+    initialTransform.reset();
+    SkISize size = SkISize::Make(width, height);
+    return SkNEW_ARGS(SkPDFDevice, (size, size, initialTransform));
+}
+
+
+struct ContentEntry {
+    GraphicStateEntry fState;
+    SkDynamicMemoryWStream fContent;
+    SkTScopedPtr<ContentEntry> fNext;
+};
+
+// A helper class to automatically finish a ContentEntry at the end of a
+// drawing method and maintain the state needed between set up and finish.
+class ScopedContentEntry {
+public:
+    ScopedContentEntry(SkPDFDevice* device, const SkDraw& draw,
+                       const SkPaint& paint, bool hasText = false)
+        : fDevice(device),
+          fContentEntry(NULL),
+          fXfermode(SkXfermode::kSrcOver_Mode) {
+        init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText);
+    }
+    ScopedContentEntry(SkPDFDevice* device, const SkClipStack* clipStack,
+                       const SkRegion& clipRegion, const SkMatrix& matrix,
+                       const SkPaint& paint, bool hasText = false)
+        : fDevice(device),
+          fContentEntry(NULL),
+          fXfermode(SkXfermode::kSrcOver_Mode) {
+        init(clipStack, clipRegion, matrix, paint, hasText);
+    }
+
+    ~ScopedContentEntry() {
+        if (fContentEntry) {
+            fDevice->finishContentEntry(fXfermode, fDstFormXObject);
+        }
+        SkSafeUnref(fDstFormXObject);
+    }
+
+    ContentEntry* entry() { return fContentEntry; }
+private:
+    SkPDFDevice* fDevice;
+    ContentEntry* fContentEntry;
+    SkXfermode::Mode fXfermode;
+    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);
+        }
+        fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion,
+                                                   matrix, paint, hasText,
+                                                   &fDstFormXObject);
+    }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+static inline SkBitmap makeContentBitmap(const SkISize& contentSize,
+                                         const SkMatrix* initialTransform) {
+    SkBitmap bitmap;
+    if (initialTransform) {
+        // Compute the size of the drawing area.
+        SkVector drawingSize;
+        SkMatrix inverse;
+        drawingSize.set(SkIntToScalar(contentSize.fWidth),
+                        SkIntToScalar(contentSize.fHeight));
+        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),
+                         abs(size.fHeight));
+    } else {
+        bitmap.setConfig(SkBitmap::kNo_Config, abs(contentSize.fWidth),
+                         abs(contentSize.fHeight));
+    }
+
+    return bitmap;
+}
+
+// TODO(vandebo) change pageSize to SkSize.
+SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
+                         const SkMatrix& initialTransform)
+    : SkDevice(makeContentBitmap(contentSize, &initialTransform)),
+      fPageSize(pageSize),
+      fContentSize(contentSize),
+      fLastContentEntry(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.
+    fInitialTransform.setTranslate(0, SkIntToScalar(pageSize.fHeight));
+    fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1);
+    fInitialTransform.preConcat(initialTransform);
+
+    SkIRect existingClip = SkIRect::MakeWH(this->width(), this->height());
+    fExistingClipRegion.setRect(existingClip);
+
+    this->init();
+}
+
+// TODO(vandebo) change layerSize to SkSize.
+SkPDFDevice::SkPDFDevice(const SkISize& layerSize,
+                         const SkClipStack& existingClipStack,
+                         const SkRegion& existingClipRegion)
+    : SkDevice(makeContentBitmap(layerSize, NULL)),
+      fPageSize(layerSize),
+      fContentSize(layerSize),
+      fExistingClipStack(existingClipStack),
+      fExistingClipRegion(existingClipRegion),
+      fLastContentEntry(NULL),
+      fLastMarginContentEntry(NULL),
+      fClipStack(NULL) {
+    fInitialTransform.reset();
+    this->init();
+}
+
+SkPDFDevice::~SkPDFDevice() {
+    this->cleanUp(true);
+}
+
+void SkPDFDevice::init() {
+    fAnnotations = NULL;
+    fResourceDict = NULL;
+    fContentEntries.reset();
+    fLastContentEntry = NULL;
+    fMarginContentEntries.reset();
+    fLastMarginContentEntry = NULL;
+    fDrawingArea = kContent_DrawingArea;
+    if (fFontGlyphUsage == NULL) {
+        fFontGlyphUsage.reset(new SkPDFGlyphSetMap());
+    }
+}
+
+void SkPDFDevice::cleanUp(bool clearFontUsage) {
+    fGraphicStateResources.unrefAll();
+    fXObjectResources.unrefAll();
+    fFontResources.unrefAll();
+    fShaderResources.unrefAll();
+    SkSafeUnref(fAnnotations);
+    SkSafeUnref(fResourceDict);
+
+    if (clearFontUsage) {
+        fFontGlyphUsage->reset();
+    }
+}
+
+uint32_t SkPDFDevice::getDeviceCapabilities() {
+    return kVector_Capability;
+}
+
+void SkPDFDevice::clear(SkColor color) {
+    this->cleanUp(true);
+    this->init();
+
+    SkPaint paint;
+    paint.setColor(color);
+    paint.setStyle(SkPaint::kFill_Style);
+    SkMatrix identity;
+    identity.reset();
+    ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
+                               identity, paint);
+    internalDrawPaint(paint, content.entry());
+}
+
+void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
+    SkPaint newPaint = paint;
+    newPaint.setStyle(SkPaint::kFill_Style);
+    ScopedContentEntry content(this, d, newPaint);
+    internalDrawPaint(newPaint, content.entry());
+}
+
+void SkPDFDevice::internalDrawPaint(const SkPaint& paint,
+                                    ContentEntry* contentEntry) {
+    if (!contentEntry) {
+        return;
+    }
+    SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()),
+                                 SkIntToScalar(this->height()));
+    SkMatrix totalTransform = fInitialTransform;
+    totalTransform.preConcat(contentEntry->fState.fMatrix);
+    SkMatrix inverse;
+    if (!totalTransform.invert(&inverse)) {
+        return;
+    }
+    inverse.mapRect(&bbox);
+
+    SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent);
+    SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
+                          &contentEntry->fContent);
+}
+
+void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
+                             size_t count, const SkPoint* points,
+                             const SkPaint& passedPaint) {
+    if (count == 0) {
+        return;
+    }
+
+    // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
+    // We only use this when there's a path effect because of the overhead
+    // of multiple calls to setUpContentEntry it causes.
+    if (passedPaint.getPathEffect()) {
+        if (d.fClip->isEmpty()) {
+            return;
+        }
+        SkDraw pointDraw(d);
+        pointDraw.fDevice = this;
+        pointDraw.drawPoints(mode, count, points, passedPaint, true);
+        return;
+    }
+
+    const SkPaint* paint = &passedPaint;
+    SkPaint modifiedPaint;
+
+    if (mode == SkCanvas::kPoints_PointMode &&
+            paint->getStrokeCap() != SkPaint::kRound_Cap) {
+        modifiedPaint = *paint;
+        paint = &modifiedPaint;
+        if (paint->getStrokeWidth()) {
+            // PDF won't draw a single point with square/butt caps because the
+            // orientation is ambiguous.  Draw a rectangle instead.
+            modifiedPaint.setStyle(SkPaint::kFill_Style);
+            SkScalar strokeWidth = paint->getStrokeWidth();
+            SkScalar halfStroke = SkScalarHalf(strokeWidth);
+            for (size_t i = 0; i < count; i++) {
+                SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0);
+                r.inset(-halfStroke, -halfStroke);
+                drawRect(d, r, modifiedPaint);
+            }
+            return;
+        } else {
+            modifiedPaint.setStrokeCap(SkPaint::kRound_Cap);
+        }
+    }
+
+    ScopedContentEntry content(this, d, *paint);
+    if (!content.entry()) {
+        return;
+    }
+
+    switch (mode) {
+        case SkCanvas::kPolygon_PointMode:
+            SkPDFUtils::MoveTo(points[0].fX, points[0].fY,
+                               &content.entry()->fContent);
+            for (size_t i = 1; i < count; i++) {
+                SkPDFUtils::AppendLine(points[i].fX, points[i].fY,
+                                       &content.entry()->fContent);
+            }
+            SkPDFUtils::StrokePath(&content.entry()->fContent);
+            break;
+        case SkCanvas::kLines_PointMode:
+            for (size_t i = 0; i < count/2; i++) {
+                SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY,
+                                   &content.entry()->fContent);
+                SkPDFUtils::AppendLine(points[i * 2 + 1].fX,
+                                       points[i * 2 + 1].fY,
+                                       &content.entry()->fContent);
+                SkPDFUtils::StrokePath(&content.entry()->fContent);
+            }
+            break;
+        case SkCanvas::kPoints_PointMode:
+            SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap);
+            for (size_t i = 0; i < count; i++) {
+                SkPDFUtils::MoveTo(points[i].fX, points[i].fY,
+                                   &content.entry()->fContent);
+                SkPDFUtils::ClosePath(&content.entry()->fContent);
+                SkPDFUtils::StrokePath(&content.entry()->fContent);
+            }
+            break;
+        default:
+            SkASSERT(false);
+    }
+}
+
+void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
+                           const SkPaint& paint) {
+    if (paint.getPathEffect()) {
+        if (d.fClip->isEmpty()) {
+            return;
+        }
+        SkPath path;
+        path.addRect(r);
+        drawPath(d, path, paint, NULL, true);
+        return;
+    }
+
+    if (handleAnnotations(r, *d.fMatrix, paint)) {
+        return;
+    }
+
+    ScopedContentEntry content(this, d, paint);
+    if (!content.entry()) {
+        return;
+    }
+    SkPDFUtils::AppendRectangle(r, &content.entry()->fContent);
+    SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
+                          &content.entry()->fContent);
+}
+
+void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath,
+                           const SkPaint& paint, const SkMatrix* prePathMatrix,
+                           bool pathIsMutable) {
+    SkPath modifiedPath;
+    SkPath* pathPtr = const_cast<SkPath*>(&origPath);
+
+    SkMatrix matrix = *d.fMatrix;
+    if (prePathMatrix) {
+        if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
+            if (!pathIsMutable) {
+                pathPtr = &modifiedPath;
+                pathIsMutable = true;
+            }
+            origPath.transform(*prePathMatrix, pathPtr);
+        } else {
+            if (!matrix.preConcat(*prePathMatrix)) {
+                return;
+            }
+        }
+    }
+
+    if (paint.getPathEffect()) {
+        if (d.fClip->isEmpty()) {
+            return;
+        }
+        if (!pathIsMutable) {
+            pathPtr = &modifiedPath;
+            pathIsMutable = true;
+        }
+        bool fill = paint.getFillPath(origPath, pathPtr);
+
+        SkPaint noEffectPaint(paint);
+        noEffectPaint.setPathEffect(NULL);
+        if (fill) {
+            noEffectPaint.setStyle(SkPaint::kFill_Style);
+        } else {
+            noEffectPaint.setStyle(SkPaint::kStroke_Style);
+            noEffectPaint.setStrokeWidth(0);
+        }
+        drawPath(d, *pathPtr, noEffectPaint, NULL, true);
+        return;
+    }
+
+    if (handleAnnotations(pathPtr->getBounds(), *d.fMatrix, paint)) {
+        return;
+    }
+
+    ScopedContentEntry content(this, d, paint);
+    if (!content.entry()) {
+        return;
+    }
+    SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(),
+                         &content.entry()->fContent);
+    SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(),
+                          &content.entry()->fContent);
+}
+
+void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
+                             const SkIRect* srcRect, const SkMatrix& matrix,
+                             const SkPaint& paint) {
+    if (d.fClip->isEmpty()) {
+        return;
+    }
+
+    SkMatrix transform = matrix;
+    transform.postConcat(*d.fMatrix);
+    internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, srcRect,
+                       paint);
+}
+
+void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap,
+                             int x, int y, const SkPaint& paint) {
+    if (d.fClip->isEmpty()) {
+        return;
+    }
+
+    SkMatrix matrix;
+    matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
+    internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL, paint);
+}
+
+void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
+                           SkScalar x, SkScalar y, const SkPaint& paint) {
+    SkPaint textPaint = calculate_text_paint(paint);
+    ScopedContentEntry content(this, d, textPaint, true);
+    if (!content.entry()) {
+        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);
+    }
+
+    SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
+    align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y);
+    content.entry()->fContent.writeText("BT\n");
+    set_text_transform(x, y, textPaint.getTextSkewX(),
+                       &content.entry()->fContent);
+    size_t consumedGlyphCount = 0;
+    while (numGlyphs > consumedGlyphCount) {
+        updateFont(textPaint, glyphIDs[consumedGlyphCount], content.entry());
+        SkPDFFont* font = content.entry()->fState.fFont;
+        size_t availableGlyphs =
+            font->glyphsToPDFFontEncoding(glyphIDs + consumedGlyphCount,
+                                          numGlyphs - consumedGlyphCount);
+        fFontGlyphUsage->noteGlyphUsage(font, glyphIDs + consumedGlyphCount,
+                                        availableGlyphs);
+        SkString encodedString =
+            SkPDFString::FormatString(glyphIDs + consumedGlyphCount,
+                                      availableGlyphs, font->multiByteGlyphs());
+        content.entry()->fContent.writeText(encodedString.c_str());
+        consumedGlyphCount += availableGlyphs;
+        content.entry()->fContent.writeText(" Tj\n");
+    }
+    content.entry()->fContent.writeText("ET\n");
+}
+
+void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
+                              const SkScalar pos[], SkScalar constY,
+                              int scalarsPerPos, const SkPaint& paint) {
+    SkASSERT(1 == scalarsPerPos || 2 == scalarsPerPos);
+    SkPaint textPaint = calculate_text_paint(paint);
+    ScopedContentEntry content(this, d, textPaint, true);
+    if (!content.entry()) {
+        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)));
+    }
+
+    SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
+    content.entry()->fContent.writeText("BT\n");
+    updateFont(textPaint, glyphIDs[0], content.entry());
+    for (size_t i = 0; i < numGlyphs; i++) {
+        SkPDFFont* font = content.entry()->fState.fFont;
+        uint16_t encodedValue = glyphIDs[i];
+        if (font->glyphsToPDFFontEncoding(&encodedValue, 1) != 1) {
+            updateFont(textPaint, glyphIDs[i], content.entry());
+            i--;
+            continue;
+        }
+        fFontGlyphUsage->noteGlyphUsage(font, &encodedValue, 1);
+        SkScalar x = pos[i * scalarsPerPos];
+        SkScalar y = scalarsPerPos == 1 ? constY : pos[i * scalarsPerPos + 1];
+        align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y);
+        set_text_transform(x, y, textPaint.getTextSkewX(),
+                           &content.entry()->fContent);
+        SkString encodedString =
+            SkPDFString::FormatString(&encodedValue, 1,
+                                      font->multiByteGlyphs());
+        content.entry()->fContent.writeText(encodedString.c_str());
+        content.entry()->fContent.writeText(" Tj\n");
+    }
+    content.entry()->fContent.writeText("ET\n");
+}
+
+void SkPDFDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
+                                 const SkPath& path, const SkMatrix* matrix,
+                                 const SkPaint& paint) {
+    if (d.fClip->isEmpty()) {
+        return;
+    }
+    d.drawTextOnPath((const char*)text, len, path, matrix, paint);
+}
+
+void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode,
+                               int vertexCount, const SkPoint verts[],
+                               const SkPoint texs[], const SkColor colors[],
+                               SkXfermode* xmode, const uint16_t indices[],
+                               int indexCount, const SkPaint& paint) {
+    if (d.fClip->isEmpty()) {
+        return;
+    }
+    NOT_IMPLEMENTED("drawVerticies", true);
+}
+
+void SkPDFDevice::drawDevice(const SkDraw& d, SkDevice* device, int x, int y,
+                             const SkPaint& paint) {
+    if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
+        // If we somehow get a raster device, do what our parent would do.
+        SkDevice::drawDevice(d, device, x, y, paint);
+        return;
+    }
+
+    // Assume that a vector capable device means that it's a PDF Device.
+    SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
+    if (pdfDevice->isContentEmpty()) {
+        return;
+    }
+
+    SkMatrix matrix;
+    matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
+    ScopedContentEntry content(this, d.fClipStack, *d.fClip, matrix, paint);
+    if (!content.entry()) {
+        return;
+    }
+
+    SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
+    fXObjectResources.push(xobject);  // Transfer reference.
+    SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+                                &content.entry()->fContent);
+
+    // Merge glyph sets from the drawn device.
+    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;
+    } else {
+        return fLastMarginContentEntry;
+    }
+}
+
+SkTScopedPtr<ContentEntry>* SkPDFDevice::getContentEntries() {
+    if (fDrawingArea == kContent_DrawingArea) {
+        return &fContentEntries;
+    } else {
+        return &fMarginContentEntries;
+    }
+}
+
+void SkPDFDevice::setLastContentEntry(ContentEntry* contentEntry) {
+    if (fDrawingArea == kContent_DrawingArea) {
+        fLastContentEntry = contentEntry;
+    } else {
+        fLastMarginContentEntry = contentEntry;
+    }
+}
+
+void SkPDFDevice::setDrawingArea(DrawingArea drawingArea) {
+    // A ScopedContentEntry only exists during the course of a draw call, so
+    // this can't be called while a ScopedContentEntry exists.
+    fDrawingArea = drawingArea;
+}
+
+SkPDFDict* SkPDFDevice::getResourceDict() {
+    if (NULL == fResourceDict) {
+        fResourceDict = SkNEW(SkPDFDict);
+
+        if (fGraphicStateResources.count()) {
+            SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
+            extGState->unref();  // SkRefPtr and new both took a reference.
+            for (int i = 0; i < fGraphicStateResources.count(); i++) {
+                SkString nameString("G");
+                nameString.appendS32(i);
+                extGState->insert(
+                        nameString.c_str(),
+                        new SkPDFObjRef(fGraphicStateResources[i]))->unref();
+            }
+            fResourceDict->insert("ExtGState", extGState.get());
+        }
+
+        if (fXObjectResources.count()) {
+            SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
+            xObjects->unref();  // SkRefPtr and new both took a reference.
+            for (int i = 0; i < fXObjectResources.count(); i++) {
+                SkString nameString("X");
+                nameString.appendS32(i);
+                xObjects->insert(
+                        nameString.c_str(),
+                        new SkPDFObjRef(fXObjectResources[i]))->unref();
+            }
+            fResourceDict->insert("XObject", xObjects.get());
+        }
+
+        if (fFontResources.count()) {
+            SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
+            fonts->unref();  // SkRefPtr and new both took a reference.
+            for (int i = 0; i < fFontResources.count(); i++) {
+                SkString nameString("F");
+                nameString.appendS32(i);
+                fonts->insert(nameString.c_str(),
+                              new SkPDFObjRef(fFontResources[i]))->unref();
+            }
+            fResourceDict->insert("Font", fonts.get());
+        }
+
+        if (fShaderResources.count()) {
+            SkRefPtr<SkPDFDict> patterns = new SkPDFDict();
+            patterns->unref();  // SkRefPtr and new both took a reference.
+            for (int i = 0; i < fShaderResources.count(); i++) {
+                SkString nameString("P");
+                nameString.appendS32(i);
+                patterns->insert(nameString.c_str(),
+                                 new SkPDFObjRef(fShaderResources[i]))->unref();
+            }
+            fResourceDict->insert("Pattern", patterns.get());
+        }
+
+        // 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.
+        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;
+}
+
+void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList,
+                               bool recursive) const {
+    resourceList->setReserve(resourceList->count() +
+                             fGraphicStateResources.count() +
+                             fXObjectResources.count() +
+                             fFontResources.count() +
+                             fShaderResources.count());
+    for (int i = 0; i < fGraphicStateResources.count(); i++) {
+        resourceList->push(fGraphicStateResources[i]);
+        fGraphicStateResources[i]->ref();
+        if (recursive) {
+            fGraphicStateResources[i]->getResources(resourceList);
+        }
+    }
+    for (int i = 0; i < fXObjectResources.count(); i++) {
+        resourceList->push(fXObjectResources[i]);
+        fXObjectResources[i]->ref();
+        if (recursive) {
+            fXObjectResources[i]->getResources(resourceList);
+        }
+    }
+    for (int i = 0; i < fFontResources.count(); i++) {
+        resourceList->push(fFontResources[i]);
+        fFontResources[i]->ref();
+        if (recursive) {
+            fFontResources[i]->getResources(resourceList);
+        }
+    }
+    for (int i = 0; i < fShaderResources.count(); i++) {
+        resourceList->push(fShaderResources[i]);
+        fShaderResources[i]->ref();
+        if (recursive) {
+            fShaderResources[i]->getResources(resourceList);
+        }
+    }
+}
+
+const SkTDArray<SkPDFFont*>& SkPDFDevice::getFontResources() const {
+    return fFontResources;
+}
+
+SkPDFArray* SkPDFDevice::copyMediaBox() const {
+    // should this be a singleton?
+    SkAutoTUnref<SkPDFInt> zero(SkNEW_ARGS(SkPDFInt, (0)));
+
+    SkPDFArray* mediaBox = SkNEW(SkPDFArray);
+    mediaBox->reserve(4);
+    mediaBox->append(zero.get());
+    mediaBox->append(zero.get());
+    mediaBox->appendInt(fPageSize.fWidth);
+    mediaBox->appendInt(fPageSize.fHeight);
+    return mediaBox;
+}
+
+SkStream* SkPDFDevice::content() const {
+    SkMemoryStream* result = new SkMemoryStream;
+    result->setData(this->copyContentToData())->unref();
+    return result;
+}
+
+void SkPDFDevice::copyContentEntriesToData(ContentEntry* entry,
+        SkWStream* data) const {
+    // TODO(ctguil): For margins, I'm not sure fExistingClipStack/Region is the
+    // right thing to pass here.
+    GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, data);
+    while (entry != NULL) {
+        SkPoint translation;
+        translation.iset(this->getOrigin());
+        translation.negate();
+        gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion,
+                           translation);
+        gsState.updateMatrix(entry->fState.fMatrix);
+        gsState.updateDrawingState(entry->fState);
+
+        SkAutoDataUnref copy(entry->fContent.copyToData());
+        data->write(copy->data(), copy->size());
+        entry = entry->fNext.get();
+    }
+    gsState.drainStack();
+}
+
+SkData* SkPDFDevice::copyContentToData() const {
+    SkDynamicMemoryWStream data;
+    if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
+        SkPDFUtils::AppendTransform(fInitialTransform, &data);
+    }
+
+    // TODO(aayushkumar): Apply clip along the margins.  Currently, webkit
+    // colors the contentArea white before it starts drawing into it and
+    // that currently acts as our clip.
+    // Also, think about adding a transform here (or assume that the values
+    // sent across account for that)
+    SkPDFDevice::copyContentEntriesToData(fMarginContentEntries.get(), &data);
+
+    // If the content area is the entire page, then we don't need to clip
+    // the content area (PDF area clips to the page size).  Otherwise,
+    // 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(SkIntToScalar(this->width()),
+                                  SkIntToScalar(this->height()));
+        emit_clip(NULL, &r, &data);
+    }
+
+    SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data);
+
+    // potentially we could cache this SkData, and only rebuild it if we
+    // see that our state has changed.
+    return data.copyToData();
+}
+
+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,
+                                       const SkRegion& clipRegion) {
+    if (clipRegion.isEmpty() || isContentEmpty()) {
+        return;
+    }
+    SkAutoTUnref<SkPDFFormXObject> curContent(createFormXObjectFromDevice());
+
+    // Redraw what we already had, but with the clip as a mask.
+    drawFormXObjectWithClip(curContent, clipStack, clipRegion, true);
+}
+
+void SkPDFDevice::drawFormXObjectWithClip(SkPDFFormXObject* xobject,
+                                          const SkClipStack* clipStack,
+                                          const SkRegion& clipRegion,
+                                          bool invertClip) {
+    if (clipRegion.isEmpty() && !invertClip) {
+        return;
+    }
+
+    // Create the mask.
+    SkMatrix identity;
+    identity.reset();
+    SkDraw draw;
+    draw.fMatrix = &identity;
+    draw.fClip = &clipRegion;
+    draw.fClipStack = clipStack;
+    SkPaint stockPaint;
+    this->drawPaint(draw, stockPaint);
+    SkAutoTUnref<SkPDFFormXObject> maskFormXObject(createFormXObjectFromDevice());
+    SkRefPtr<SkPDFGraphicState> sMaskGS =
+        SkPDFGraphicState::GetSMaskGraphicState(maskFormXObject, invertClip);
+    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
+
+    // Draw the xobject with the clip as a mask.
+    ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
+                                 identity, stockPaint);
+    if (!content.entry()) {
+        return;
+    }
+    SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+                                  &content.entry()->fContent);
+    SkPDFUtils::DrawFormXObject(fXObjectResources.count(),
+                                &content.entry()->fContent);
+    fXObjectResources.push(xobject);
+    xobject->ref();
+
+    sMaskGS = SkPDFGraphicState::GetNoSMaskGraphicState();
+    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
+    SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+                                  &content.entry()->fContent);
+}
+
+ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
+                                             const SkRegion& clipRegion,
+                                             const SkMatrix& matrix,
+                                             const SkPaint& paint,
+                                             bool hasText,
+                                             SkPDFFormXObject** dst) {
+    *dst = NULL;
+    if (clipRegion.isEmpty()) {
+        return NULL;
+    }
+
+    // The clip stack can come from an SkDraw where it is technically optional.
+    SkClipStack synthesizedClipStack;
+    if (clipStack == NULL) {
+        if (clipRegion == fExistingClipRegion) {
+            clipStack = &fExistingClipStack;
+        } else {
+            // GraphicStackState::updateClip expects the clip stack to have
+            // fExistingClip as a prefix, so start there, then set the clip
+            // to the passed region.
+            synthesizedClipStack = fExistingClipStack;
+            SkPath clipPath;
+            clipRegion.getBoundaryPath(&clipPath);
+            synthesizedClipStack.clipDevPath(clipPath, SkRegion::kReplace_Op,
+                                             false);
+            clipStack = &synthesizedClipStack;
+        }
+    }
+
+    SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
+    if (paint.getXfermode()) {
+        paint.getXfermode()->asMode(&xfermode);
+    }
+
+    if (xfermode == SkXfermode::kClear_Mode ||
+            xfermode == SkXfermode::kSrc_Mode) {
+        this->clearClipFromContent(clipStack, clipRegion);
+    } else if (xfermode == SkXfermode::kSrcIn_Mode ||
+               xfermode == SkXfermode::kDstIn_Mode ||
+               xfermode == SkXfermode::kSrcOut_Mode ||
+               xfermode == SkXfermode::kDstOut_Mode) {
+        // For the following modes, we use both source and destination, but
+        // we use one as a smask for the other, so we have to make form xobjects
+        // out of both of them: SrcIn, DstIn, SrcOut, DstOut.
+        if (isContentEmpty()) {
+            return NULL;
+        } else {
+            *dst = createFormXObjectFromDevice();
+        }
+    }
+    // TODO(vandebo): Figure out how/if we can handle the following modes:
+    // SrcAtop, DestAtop, Xor, Plus.
+
+    // These xfer modes don't draw source at all.
+    if (xfermode == SkXfermode::kClear_Mode ||
+            xfermode == SkXfermode::kDst_Mode) {
+        return NULL;
+    }
+
+    ContentEntry* entry;
+    SkTScopedPtr<ContentEntry> newEntry;
+
+    ContentEntry* lastContentEntry = getLastContentEntry();
+    if (lastContentEntry && lastContentEntry->fContent.getOffset() == 0) {
+        entry = lastContentEntry;
+    } else {
+        newEntry.reset(new ContentEntry);
+        entry = newEntry.get();
+    }
+
+    populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint,
+                                       hasText, &entry->fState);
+    if (lastContentEntry && xfermode != SkXfermode::kDstOver_Mode &&
+            entry->fState.compareInitialState(lastContentEntry->fState)) {
+        return lastContentEntry;
+    }
+
+    SkTScopedPtr<ContentEntry>* contentEntries = getContentEntries();
+    if (!lastContentEntry) {
+        contentEntries->reset(entry);
+        setLastContentEntry(entry);
+    } else if (xfermode == SkXfermode::kDstOver_Mode) {
+        entry->fNext.reset(contentEntries->release());
+        contentEntries->reset(entry);
+    } else {
+        lastContentEntry->fNext.reset(entry);
+        setLastContentEntry(entry);
+    }
+    newEntry.release();
+    return entry;
+}
+
+void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
+                                     SkPDFFormXObject* dst) {
+    if (xfermode != SkXfermode::kSrcIn_Mode &&
+            xfermode != SkXfermode::kDstIn_Mode &&
+            xfermode != SkXfermode::kSrcOut_Mode &&
+            xfermode != SkXfermode::kDstOut_Mode) {
+        SkASSERT(!dst);
+        return;
+    }
+
+    ContentEntry* contentEntries = getContentEntries()->get();
+    SkASSERT(dst);
+    SkASSERT(!contentEntries->fNext.get());
+    // We have to make a copy of these here because changing the current
+    // content into a form xobject will destroy them.
+    SkClipStack clipStack = contentEntries->fState.fClipStack;
+    SkRegion clipRegion = contentEntries->fState.fClipRegion;
+
+    SkAutoTUnref<SkPDFFormXObject> srcFormXObject;
+    if (!isContentEmpty()) {
+        srcFormXObject.reset(createFormXObjectFromDevice());
+    }
+
+    drawFormXObjectWithClip(dst, &clipStack, clipRegion, true);
+
+    // We've redrawn dst minus the clip area, if there's no src, we're done.
+    if (!srcFormXObject.get()) {
+        return;
+    }
+
+    SkMatrix identity;
+    identity.reset();
+    SkPaint stockPaint;
+    ScopedContentEntry inClipContentEntry(this, &fExistingClipStack,
+                                          fExistingClipRegion, identity,
+                                          stockPaint);
+    if (!inClipContentEntry.entry()) {
+        return;
+    }
+
+    SkRefPtr<SkPDFGraphicState> sMaskGS;
+    if (xfermode == SkXfermode::kSrcIn_Mode ||
+            xfermode == SkXfermode::kSrcOut_Mode) {
+        sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
+                dst, xfermode == SkXfermode::kSrcOut_Mode);
+        fXObjectResources.push(srcFormXObject.get());
+        srcFormXObject.get()->ref();
+    } else {
+        sMaskGS = 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.
+    SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
+                                  &inClipContentEntry.entry()->fContent);
+}
+
+bool SkPDFDevice::isContentEmpty() {
+    ContentEntry* contentEntries = getContentEntries()->get();
+    if (!contentEntries || contentEntries->fContent.getOffset() == 0) {
+        SkASSERT(!contentEntries || !contentEntries->fNext.get());
+        return true;
+    }
+    return false;
+}
+
+void SkPDFDevice::populateGraphicStateEntryFromPaint(
+        const SkMatrix& matrix,
+        const SkClipStack& clipStack,
+        const SkRegion& clipRegion,
+        const SkPaint& paint,
+        bool hasText,
+        GraphicStateEntry* entry) {
+    SkASSERT(paint.getPathEffect() == NULL);
+
+    NOT_IMPLEMENTED(paint.getMaskFilter() != NULL, false);
+    NOT_IMPLEMENTED(paint.getColorFilter() != NULL, false);
+
+    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;
+    const SkShader* shader = paint.getShader();
+    SkColor color = paint.getColor();
+    if (shader) {
+        // PDF positions patterns relative to the initial transform, so
+        // we need to apply the current transform to the shader parameters.
+        SkMatrix transform = matrix;
+        transform.postConcat(fInitialTransform);
+
+        // PDF doesn't support kClamp_TileMode, so we simulate it by making
+        // a pattern the size of the current clip.
+        SkIRect bounds = clipRegion.getBounds();
+
+        // 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 = SkPDFShader::GetPDFShader(*shader, transform, bounds);
+        SkSafeUnref(pdfShader.get());  // getShader and SkRefPtr both took a ref
+
+        if (pdfShader.get()) {
+            // pdfShader has been canonicalized so we can directly compare
+            // pointers.
+            int resourceIndex = fShaderResources.find(pdfShader.get());
+            if (resourceIndex < 0) {
+                resourceIndex = fShaderResources.count();
+                fShaderResources.push(pdfShader.get());
+                pdfShader->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.
+            SkShader::GradientInfo gradientInfo;
+            SkColor gradientColor;
+            gradientInfo.fColors = &gradientColor;
+            gradientInfo.fColorOffsets = NULL;
+            gradientInfo.fColorCount = 1;
+            if (shader->asAGradient(&gradientInfo) ==
+                    SkShader::kColor_GradientType) {
+                entry->fColor = SkColorSetA(gradientColor, 0xFF);
+                color = gradientColor;
+            }
+        }
+    }
+
+    SkRefPtr<SkPDFGraphicState> newGraphicState;
+    if (color == paint.getColor()) {
+        newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(paint);
+    } else {
+        SkPaint newPaint = paint;
+        newPaint.setColor(color);
+        newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(newPaint);
+    }
+    newGraphicState->unref();  // getGraphicState and SkRefPtr both took a ref.
+    int resourceIndex = addGraphicStateResource(newGraphicState.get());
+    entry->fGraphicStateIndex = resourceIndex;
+
+    if (hasText) {
+        entry->fTextScaleX = paint.getTextScaleX();
+        entry->fTextFill = paint.getStyle();
+    } else {
+        entry->fTextScaleX = 0;
+    }
+}
+
+int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) {
+    // Assumes that gs has been canonicalized (so we can directly compare
+    // pointers).
+    int result = fGraphicStateResources.find(gs);
+    if (result < 0) {
+        result = fGraphicStateResources.count();
+        fGraphicStateResources.push(gs);
+        gs->ref();
+    }
+    return result;
+}
+
+void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID,
+                             ContentEntry* contentEntry) {
+    SkTypeface* typeface = paint.getTypeface();
+    if (contentEntry->fState.fFont == NULL ||
+            contentEntry->fState.fTextSize != paint.getTextSize() ||
+            !contentEntry->fState.fFont->hasGlyph(glyphID)) {
+        int fontIndex = getFontResourceIndex(typeface, glyphID);
+        contentEntry->fContent.writeText("/F");
+        contentEntry->fContent.writeDecAsText(fontIndex);
+        contentEntry->fContent.writeText(" ");
+        SkPDFScalar::Append(paint.getTextSize(), &contentEntry->fContent);
+        contentEntry->fContent.writeText(" Tf\n");
+        contentEntry->fState.fFont = fFontResources[fontIndex];
+    }
+}
+
+int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
+    SkRefPtr<SkPDFFont> newFont = SkPDFFont::GetFontResource(typeface, glyphID);
+    newFont->unref();  // getFontResource and SkRefPtr both took a ref.
+    int resourceIndex = fFontResources.find(newFont.get());
+    if (resourceIndex < 0) {
+        resourceIndex = fFontResources.count();
+        fFontResources.push(newFont.get());
+        newFont->ref();
+    }
+    return resourceIndex;
+}
+
+void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
+                                     const SkClipStack* clipStack,
+                                     const SkRegion& clipRegion,
+                                     const SkBitmap& bitmap,
+                                     const SkIRect* srcRect,
+                                     const SkPaint& paint) {
+    SkMatrix scaled;
+    // Adjust for origin flip.
+    scaled.setScale(SK_Scalar1, -SK_Scalar1);
+    scaled.postTranslate(0, SK_Scalar1);
+    // Scale the image up from 1x1 to WxH.
+    SkIRect subset = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+    scaled.postScale(SkIntToScalar(subset.width()),
+                     SkIntToScalar(subset.height()));
+    scaled.postConcat(matrix);
+    ScopedContentEntry content(this, clipStack, clipRegion, scaled, paint);
+    if (!content.entry()) {
+        return;
+    }
+
+    if (srcRect && !subset.intersect(*srcRect)) {
+        return;
+    }
+
+    SkPDFImage* image = SkPDFImage::CreateImage(bitmap, subset, paint);
+    if (!image) {
+        return;
+    }
+
+    fXObjectResources.push(image);  // Transfer reference.
+    SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
+                                &content.entry()->fContent);
+}
+
+bool SkPDFDevice::onReadPixels(const SkBitmap& bitmap, int x, int y,
+                               SkCanvas::Config8888) {
+    return false;
+}
+
+bool SkPDFDevice::allowImageFilter(SkImageFilter*) {
+    return false;
+}
+
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
new file mode 100644
index 0000000..7e064ee
--- /dev/null
+++ b/src/pdf/SkPDFDocument.cpp
@@ -0,0 +1,278 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
+#include "SkPDFDocument.h"
+#include "SkPDFFont.h"
+#include "SkPDFPage.h"
+#include "SkPDFTypes.h"
+#include "SkStream.h"
+
+// Add the resources, starting at firstIndex to the catalog, removing any dupes.
+// A hash table would be really nice here.
+static void addResourcesToCatalog(int firstIndex, bool firstPage,
+                          SkTDArray<SkPDFObject*>* resourceList,
+                          SkPDFCatalog* catalog) {
+    for (int i = firstIndex; i < resourceList->count(); i++) {
+        int index = resourceList->find((*resourceList)[i]);
+        if (index != i) {
+            (*resourceList)[i]->unref();
+            resourceList->removeShuffle(i);
+            i--;
+        } else {
+            catalog->addObject((*resourceList)[i], firstPage);
+        }
+    }
+}
+
+static void perform_font_subsetting(SkPDFCatalog* catalog,
+                                    const SkTDArray<SkPDFPage*>& pages,
+                                    SkTDArray<SkPDFObject*>* substitutes) {
+    SkASSERT(catalog);
+    SkASSERT(substitutes);
+
+    SkPDFGlyphSetMap usage;
+    for (int i = 0; i < pages.count(); ++i) {
+        usage.merge(pages[i]->getFontGlyphUsage());
+    }
+    SkPDFGlyphSetMap::F2BIter iterator(usage);
+    SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
+    while (entry) {
+        SkPDFFont* subsetFont =
+            entry->fFont->getFontSubset(entry->fGlyphSet);
+        if (subsetFont) {
+            catalog->setSubstitute(entry->fFont, subsetFont);
+            substitutes->push(subsetFont);  // Transfer ownership to substitutes
+        }
+        entry = iterator.next();
+    }
+}
+
+SkPDFDocument::SkPDFDocument(Flags flags)
+        : fXRefFileOffset(0),
+          fSecondPageFirstResourceIndex(0),
+          fTrailerDict(NULL) {
+    fCatalog.reset(new SkPDFCatalog(flags));
+    fDocCatalog = SkNEW_ARGS(SkPDFDict, ("Catalog"));
+    fCatalog->addObject(fDocCatalog, true);
+}
+
+SkPDFDocument::~SkPDFDocument() {
+    fPages.safeUnrefAll();
+
+    // The page tree has both child and parent pointers, so it creates a
+    // reference cycle.  We must clear that cycle to properly reclaim memory.
+    for (int i = 0; i < fPageTree.count(); i++) {
+        fPageTree[i]->clear();
+    }
+    fPageTree.safeUnrefAll();
+    fPageResources.safeUnrefAll();
+    fSubstitutes.safeUnrefAll();
+
+    fDocCatalog->unref();
+    SkSafeUnref(fTrailerDict);
+}
+
+bool SkPDFDocument::emitPDF(SkWStream* stream) {
+    if (fPages.isEmpty()) {
+        return false;
+    }
+    for (int i = 0; i < fPages.count(); i++) {
+        if (fPages[i] == NULL) {
+            return false;
+        }
+    }
+
+    // We haven't emitted the document before if fPageTree is empty.
+    if (fPageTree.isEmpty()) {
+        SkPDFDict* pageTreeRoot;
+        SkPDFPage::GeneratePageTree(fPages, fCatalog.get(), &fPageTree,
+                                    &pageTreeRoot);
+        fDocCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref();
+
+        /* TODO(vandebo): output intent
+        SkRefPtr<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
+        outputIntent->unref();  // SkRefPtr and new both took a reference.
+        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.
+        intentArray->append(outputIntent.get());
+        fDocCatalog->insert("OutputIntent", intentArray.get());
+        */
+
+        bool firstPage = true;
+        for (int i = 0; i < fPages.count(); i++) {
+            int resourceCount = fPageResources.count();
+            fPages[i]->finalizePage(fCatalog.get(), firstPage, &fPageResources);
+            addResourcesToCatalog(resourceCount, firstPage, &fPageResources,
+                                  fCatalog.get());
+            if (i == 0) {
+                firstPage = false;
+                fSecondPageFirstResourceIndex = fPageResources.count();
+            }
+        }
+
+        // Build font subsetting info before proceeding.
+        perform_font_subsetting(fCatalog.get(), fPages, &fSubstitutes);
+
+        // Figure out the size of things and inform the catalog of file offsets.
+        off_t fileOffset = headerSize();
+        fileOffset += fCatalog->setFileOffset(fDocCatalog, fileOffset);
+        fileOffset += fCatalog->setFileOffset(fPages[0], fileOffset);
+        fileOffset += fPages[0]->getPageSize(fCatalog.get(),
+                (size_t) fileOffset);
+        for (int i = 0; i < fSecondPageFirstResourceIndex; i++) {
+            fileOffset += fCatalog->setFileOffset(fPageResources[i],
+                                                  fileOffset);
+        }
+        // Add the size of resources of substitute objects used on page 1.
+        fileOffset += fCatalog->setSubstituteResourcesOffsets(fileOffset, true);
+        if (fPages.count() > 1) {
+            // TODO(vandebo): For linearized format, save the start of the
+            // first page xref table and calculate the size.
+        }
+
+        for (int i = 0; i < fPageTree.count(); i++) {
+            fileOffset += fCatalog->setFileOffset(fPageTree[i], fileOffset);
+        }
+
+        for (int i = 1; i < fPages.count(); i++) {
+            fileOffset += fPages[i]->getPageSize(fCatalog.get(), fileOffset);
+        }
+
+        for (int i = fSecondPageFirstResourceIndex;
+                 i < fPageResources.count();
+                 i++) {
+            fileOffset += fCatalog->setFileOffset(fPageResources[i],
+                                                  fileOffset);
+        }
+
+        fileOffset += fCatalog->setSubstituteResourcesOffsets(fileOffset,
+                                                              false);
+        fXRefFileOffset = fileOffset;
+    }
+
+    emitHeader(stream);
+    fDocCatalog->emitObject(stream, fCatalog.get(), true);
+    fPages[0]->emitObject(stream, fCatalog.get(), true);
+    fPages[0]->emitPage(stream, fCatalog.get());
+    for (int i = 0; i < fSecondPageFirstResourceIndex; i++) {
+        fPageResources[i]->emit(stream, fCatalog.get(), true);
+    }
+    fCatalog->emitSubstituteResources(stream, true);
+    // TODO(vandebo): Support linearized format
+    // if (fPages.size() > 1) {
+    //     // TODO(vandebo): Save the file offset for the first page xref table.
+    //     fCatalog->emitXrefTable(stream, true);
+    // }
+
+    for (int i = 0; i < fPageTree.count(); i++) {
+        fPageTree[i]->emitObject(stream, fCatalog.get(), true);
+    }
+
+    for (int i = 1; i < fPages.count(); i++) {
+        fPages[i]->emitPage(stream, fCatalog.get());
+    }
+
+    for (int i = fSecondPageFirstResourceIndex;
+            i < fPageResources.count();
+            i++) {
+        fPageResources[i]->emit(stream, fCatalog.get(), true);
+    }
+
+    fCatalog->emitSubstituteResources(stream, false);
+    int64_t objCount = fCatalog->emitXrefTable(stream, fPages.count() > 1);
+    emitFooter(stream, objCount);
+    return true;
+}
+
+bool SkPDFDocument::setPage(int pageNumber, SkPDFDevice* pdfDevice) {
+    if (!fPageTree.isEmpty()) {
+        return false;
+    }
+
+    pageNumber--;
+    SkASSERT(pageNumber >= 0);
+
+    if (pageNumber >= fPages.count()) {
+        int oldSize = fPages.count();
+        fPages.setCount(pageNumber + 1);
+        for (int i = oldSize; i <= pageNumber; i++) {
+            fPages[i] = NULL;
+        }
+    }
+
+    SkPDFPage* page = new SkPDFPage(pdfDevice);
+    SkSafeUnref(fPages[pageNumber]);
+    fPages[pageNumber] = page;  // Reference from new passed to fPages.
+    return true;
+}
+
+bool SkPDFDocument::appendPage(SkPDFDevice* pdfDevice) {
+    if (!fPageTree.isEmpty()) {
+        return false;
+    }
+
+    SkPDFPage* page = new SkPDFPage(pdfDevice);
+    fPages.push(page);  // Reference from new passed to fPages.
+    return true;
+}
+
+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) {
+    stream->writeText("%PDF-1.4\n%");
+    // The PDF spec recommends including a comment with four bytes, all
+    // with their high bits set.  This is "Skia" with the high bits set.
+    stream->write32(0xD3EBE9E1);
+    stream->writeText("\n");
+}
+
+size_t SkPDFDocument::headerSize() {
+    SkDynamicMemoryWStream buffer;
+    emitHeader(&buffer);
+    return buffer.getOffset();
+}
+
+void SkPDFDocument::emitFooter(SkWStream* stream, int64_t objCount) {
+    if (NULL == fTrailerDict) {
+        fTrailerDict = SkNEW(SkPDFDict);
+
+        // TODO(vandebo): Linearized format will take a Prev entry too.
+        // TODO(vandebo): PDF/A requires an ID entry.
+        fTrailerDict->insertInt("Size", int(objCount));
+        fTrailerDict->insert("Root", new SkPDFObjRef(fDocCatalog))->unref();
+    }
+
+    stream->writeText("trailer\n");
+    fTrailerDict->emitObject(stream, fCatalog.get(), false);
+    stream->writeText("\nstartxref\n");
+    stream->writeBigDecAsText(fXRefFileOffset);
+    stream->writeText("\n%%EOF");
+}
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
new file mode 100644
index 0000000..5cb801f
--- /dev/null
+++ b/src/pdf/SkPDFFont.cpp
@@ -0,0 +1,1414 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include <ctype.h>
+
+#include "SkData.h"
+#include "SkFontHost.h"
+#include "SkGlyphCache.h"
+#include "SkPaint.h"
+#include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
+#include "SkPDFFont.h"
+#include "SkPDFFontImpl.h"
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkPDFUtils.h"
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
+#include "SkTypes.h"
+#include "SkUtils.h"
+
+#if defined (SK_SFNTLY_SUBSETTER)
+#include SK_SFNTLY_SUBSETTER
+#endif
+
+namespace {
+
+///////////////////////////////////////////////////////////////////////////////
+// File-Local Functions
+///////////////////////////////////////////////////////////////////////////////
+
+bool parsePFBSection(const uint8_t** src, size_t* len, int sectionType,
+                     size_t* size) {
+    // PFB sections have a two or six bytes header. 0x80 and a one byte
+    // section type followed by a four byte section length.  Type one is
+    // an ASCII section (includes a length), type two is a binary section
+    // (includes a length) and type three is an EOF marker with no length.
+    const uint8_t* buf = *src;
+    if (*len < 2 || buf[0] != 0x80 || buf[1] != sectionType) {
+        return false;
+    } else if (buf[1] == 3) {
+        return true;
+    } else if (*len < 6) {
+        return false;
+    }
+
+    *size = (size_t)buf[2] | ((size_t)buf[3] << 8) | ((size_t)buf[4] << 16) |
+            ((size_t)buf[5] << 24);
+    size_t consumed = *size + 6;
+    if (consumed > *len) {
+        return false;
+    }
+    *src = *src + consumed;
+    *len = *len - consumed;
+    return true;
+}
+
+bool parsePFB(const uint8_t* src, size_t size, size_t* headerLen,
+              size_t* dataLen, size_t* trailerLen) {
+    const uint8_t* srcPtr = src;
+    size_t remaining = size;
+
+    return parsePFBSection(&srcPtr, &remaining, 1, headerLen) &&
+           parsePFBSection(&srcPtr, &remaining, 2, dataLen) &&
+           parsePFBSection(&srcPtr, &remaining, 1, trailerLen) &&
+           parsePFBSection(&srcPtr, &remaining, 3, NULL);
+}
+
+/* The sections of a PFA file are implicitly defined.  The body starts
+ * after the line containing "eexec," and the trailer starts with 512
+ * literal 0's followed by "cleartomark" (plus arbitrary white space).
+ *
+ * This function assumes that src is NUL terminated, but the NUL
+ * termination is not included in size.
+ *
+ */
+bool parsePFA(const char* src, size_t size, size_t* headerLen,
+              size_t* hexDataLen, size_t* dataLen, size_t* trailerLen) {
+    const char* end = src + size;
+
+    const char* dataPos = strstr(src, "eexec");
+    if (!dataPos) {
+        return false;
+    }
+    dataPos += strlen("eexec");
+    while ((*dataPos == '\n' || *dataPos == '\r' || *dataPos == ' ') &&
+            dataPos < end) {
+        dataPos++;
+    }
+    *headerLen = dataPos - src;
+
+    const char* trailerPos = strstr(dataPos, "cleartomark");
+    if (!trailerPos) {
+        return false;
+    }
+    int zeroCount = 0;
+    for (trailerPos--; trailerPos > dataPos && zeroCount < 512; trailerPos--) {
+        if (*trailerPos == '\n' || *trailerPos == '\r' || *trailerPos == ' ') {
+            continue;
+        } else if (*trailerPos == '0') {
+            zeroCount++;
+        } else {
+            return false;
+        }
+    }
+    if (zeroCount != 512) {
+        return false;
+    }
+
+    *hexDataLen = trailerPos - src - *headerLen;
+    *trailerLen = size - *headerLen - *hexDataLen;
+
+    // Verify that the data section is hex encoded and count the bytes.
+    int nibbles = 0;
+    for (; dataPos < trailerPos; dataPos++) {
+        if (isspace(*dataPos)) {
+            continue;
+        }
+        if (!isxdigit(*dataPos)) {
+            return false;
+        }
+        nibbles++;
+    }
+    *dataLen = (nibbles + 1) / 2;
+
+    return true;
+}
+
+int8_t hexToBin(uint8_t c) {
+    if (!isxdigit(c)) {
+        return -1;
+    } else if (c <= '9') {
+        return c - '0';
+    } else if (c <= 'F') {
+        return c - 'A' + 10;
+    } else if (c <= 'f') {
+        return c - 'a' + 10;
+    }
+    return -1;
+}
+
+SkStream* handleType1Stream(SkStream* srcStream, size_t* headerLen,
+                            size_t* dataLen, size_t* trailerLen) {
+    // srcStream may be backed by a file or a unseekable fd, so we may not be
+    // able to use skip(), rewind(), or getMemoryBase().  read()ing through
+    // the input only once is doable, but very ugly. Furthermore, it'd be nice
+    // 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;
+    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.
+        src = (const uint8_t*)staticStream->getMemoryBase();
+        if (srcStream->getMemoryBase() != NULL) {
+            memcpy((void *)src, srcStream->getMemoryBase(), srcLen);
+        } else {
+            size_t read = 0;
+            while (read < srcLen) {
+                size_t got = srcStream->read((void *)staticStream->getAtPos(),
+                                             srcLen - read);
+                if (got == 0) {
+                    return NULL;
+                }
+                read += got;
+                staticStream->seek(read);
+            }
+        }
+        ((uint8_t *)src)[srcLen] = 0;
+    } else {
+        static const size_t kBufSize = 4096;
+        uint8_t buf[kBufSize];
+        size_t amount;
+        while ((amount = srcStream->read(buf, kBufSize)) > 0) {
+            dynamicStream.write(buf, amount);
+        }
+        amount = 0;
+        dynamicStream.write(&amount, 1);  // NULL terminator.
+        data = dynamicStream.copyToData();
+        src = data->bytes();
+        srcLen = data->size() - 1;
+    }
+
+    // this handles releasing the data we may have gotten from dynamicStream.
+    // if data is null, it is a no-op
+    SkAutoDataUnref aud(data);
+
+    if (parsePFB(src, srcLen, headerLen, dataLen, trailerLen)) {
+        SkMemoryStream* result =
+            new SkMemoryStream(*headerLen + *dataLen + *trailerLen);
+        memcpy((char*)result->getAtPos(), src + 6, *headerLen);
+        result->seek(*headerLen);
+        memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6, *dataLen);
+        result->seek(*headerLen + *dataLen);
+        memcpy((char*)result->getAtPos(), src + 6 + *headerLen + 6 + *dataLen,
+               *trailerLen);
+        result->rewind();
+        return result;
+    }
+
+    // A PFA has to be converted for PDF.
+    size_t hexDataLen;
+    if (parsePFA((const char*)src, srcLen, headerLen, &hexDataLen, dataLen,
+                 trailerLen)) {
+        SkMemoryStream* result =
+            new SkMemoryStream(*headerLen + *dataLen + *trailerLen);
+        memcpy((char*)result->getAtPos(), src, *headerLen);
+        result->seek(*headerLen);
+
+        const uint8_t* hexData = src + *headerLen;
+        const uint8_t* trailer = hexData + hexDataLen;
+        size_t outputOffset = 0;
+        uint8_t dataByte = 0;  // To hush compiler.
+        bool highNibble = true;
+        for (; hexData < trailer; hexData++) {
+            char curNibble = hexToBin(*hexData);
+            if (curNibble < 0) {
+                continue;
+            }
+            if (highNibble) {
+                dataByte = curNibble << 4;
+                highNibble = false;
+            } else {
+                dataByte |= curNibble;
+                highNibble = true;
+                ((char *)result->getAtPos())[outputOffset++] = dataByte;
+            }
+        }
+        if (!highNibble) {
+            ((char *)result->getAtPos())[outputOffset++] = dataByte;
+        }
+        SkASSERT(outputOffset == *dataLen);
+        result->seek(*headerLen + outputOffset);
+
+        memcpy((char *)result->getAtPos(), src + *headerLen + hexDataLen,
+               *trailerLen);
+        result->rewind();
+        return result;
+    }
+
+    return NULL;
+}
+
+// scale from em-units to base-1000, returning as a SkScalar
+SkScalar scaleFromFontUnits(int16_t val, uint16_t emSize) {
+    SkScalar scaled = SkIntToScalar(val);
+    if (emSize == 1000) {
+        return scaled;
+    } else {
+        return SkScalarMulDiv(scaled, 1000, emSize);
+    }
+}
+
+void setGlyphWidthAndBoundingBox(SkScalar width, SkIRect box,
+                                 SkWStream* content) {
+    // Specify width and bounding box for the glyph.
+    SkPDFScalar::Append(width, content);
+    content->writeText(" 0 ");
+    content->writeDecAsText(box.fLeft);
+    content->writeText(" ");
+    content->writeDecAsText(box.fTop);
+    content->writeText(" ");
+    content->writeDecAsText(box.fRight);
+    content->writeText(" ");
+    content->writeDecAsText(box.fBottom);
+    content->writeText(" d1\n");
+}
+
+SkPDFArray* makeFontBBox(SkIRect glyphBBox, uint16_t emSize) {
+    SkPDFArray* bbox = new SkPDFArray;
+    bbox->reserve(4);
+    bbox->appendScalar(scaleFromFontUnits(glyphBBox.fLeft, emSize));
+    bbox->appendScalar(scaleFromFontUnits(glyphBBox.fBottom, emSize));
+    bbox->appendScalar(scaleFromFontUnits(glyphBBox.fRight, emSize));
+    bbox->appendScalar(scaleFromFontUnits(glyphBBox.fTop, emSize));
+    return bbox;
+}
+
+SkPDFArray* appendWidth(const int16_t& width, uint16_t emSize,
+                        SkPDFArray* array) {
+    array->appendScalar(scaleFromFontUnits(width, emSize));
+    return array;
+}
+
+SkPDFArray* appendVerticalAdvance(
+        const SkAdvancedTypefaceMetrics::VerticalMetric& advance,
+        uint16_t emSize, SkPDFArray* array) {
+    appendWidth(advance.fVerticalAdvance, emSize, array);
+    appendWidth(advance.fOriginXDisp, emSize, array);
+    appendWidth(advance.fOriginYDisp, emSize, array);
+    return array;
+}
+
+template <typename Data>
+SkPDFArray* composeAdvanceData(
+        SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* advanceInfo,
+        uint16_t emSize,
+        SkPDFArray* (*appendAdvance)(const Data& advance, uint16_t emSize,
+                                     SkPDFArray* array),
+        Data* defaultAdvance) {
+    SkPDFArray* result = new SkPDFArray();
+    for (; advanceInfo != NULL; advanceInfo = advanceInfo->fNext.get()) {
+        switch (advanceInfo->fType) {
+            case SkAdvancedTypefaceMetrics::WidthRange::kDefault: {
+                SkASSERT(advanceInfo->fAdvance.count() == 1);
+                *defaultAdvance = advanceInfo->fAdvance[0];
+                break;
+            }
+            case SkAdvancedTypefaceMetrics::WidthRange::kRange: {
+                SkRefPtr<SkPDFArray> advanceArray = new SkPDFArray();
+                advanceArray->unref();  // SkRefPtr and new both took a ref.
+                for (int j = 0; j < advanceInfo->fAdvance.count(); j++)
+                    appendAdvance(advanceInfo->fAdvance[j], emSize,
+                                  advanceArray.get());
+                result->appendInt(advanceInfo->fStartId);
+                result->append(advanceArray.get());
+                break;
+            }
+            case SkAdvancedTypefaceMetrics::WidthRange::kRun: {
+                SkASSERT(advanceInfo->fAdvance.count() == 1);
+                result->appendInt(advanceInfo->fStartId);
+                result->appendInt(advanceInfo->fEndId);
+                appendAdvance(advanceInfo->fAdvance[0], emSize, result);
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+}  // namespace
+
+static void append_tounicode_header(SkDynamicMemoryWStream* cmap) {
+    // 12 dict begin: 12 is an Adobe-suggested value. Shall not change.
+    // It's there to prevent old version Adobe Readers from malfunctioning.
+    const char* kHeader =
+        "/CIDInit /ProcSet findresource begin\n"
+        "12 dict begin\n"
+        "begincmap\n";
+    cmap->writeText(kHeader);
+
+    // The /CIDSystemInfo must be consistent to the one in
+    // SkPDFFont::populateCIDFont().
+    // We can not pass over the system info object here because the format is
+    // different. This is not a reference object.
+    const char* kSysInfo =
+        "/CIDSystemInfo\n"
+        "<<  /Registry (Adobe)\n"
+        "/Ordering (UCS)\n"
+        "/Supplement 0\n"
+        ">> def\n";
+    cmap->writeText(kSysInfo);
+
+    // The CMapName must be consistent to /CIDSystemInfo above.
+    // /CMapType 2 means ToUnicode.
+    // We specify codespacerange from 0x0000 to 0xFFFF because we convert our
+    // code table from unsigned short (16-bits). Codespace range just tells the
+    // PDF processor the valid range. It does not matter whether a complete
+    // mapping is provided or not.
+    const char* kTypeInfo =
+        "/CMapName /Adobe-Identity-UCS def\n"
+        "/CMapType 2 def\n"
+        "1 begincodespacerange\n"
+        "<0000> <FFFF>\n"
+        "endcodespacerange\n";
+    cmap->writeText(kTypeInfo);
+}
+
+static void append_cmap_footer(SkDynamicMemoryWStream* cmap) {
+    const char* kFooter =
+        "endcmap\n"
+        "CMapName currentdict /CMap defineresource pop\n"
+        "end\n"
+        "end";
+    cmap->writeText(kFooter);
+}
+
+struct BFChar {
+    uint16_t fGlyphId;
+    SkUnichar fUnicode;
+};
+
+struct BFRange {
+    uint16_t fStart;
+    uint16_t fEnd;
+    SkUnichar fUnicode;
+};
+
+static void append_bfchar_section(const SkTDArray<BFChar>& bfchar,
+                                  SkDynamicMemoryWStream* cmap) {
+    // PDF spec defines that every bf* list can have at most 100 entries.
+    for (int i = 0; i < bfchar.count(); i += 100) {
+        int count = bfchar.count() - i;
+        count = SkMin32(count, 100);
+        cmap->writeDecAsText(count);
+        cmap->writeText(" beginbfchar\n");
+        for (int j = 0; j < count; ++j) {
+            cmap->writeText("<");
+            cmap->writeHexAsText(bfchar[i + j].fGlyphId, 4);
+            cmap->writeText("> <");
+            cmap->writeHexAsText(bfchar[i + j].fUnicode, 4);
+            cmap->writeText(">\n");
+        }
+        cmap->writeText("endbfchar\n");
+    }
+}
+
+static void append_bfrange_section(const SkTDArray<BFRange>& bfrange,
+                                   SkDynamicMemoryWStream* cmap) {
+    // PDF spec defines that every bf* list can have at most 100 entries.
+    for (int i = 0; i < bfrange.count(); i += 100) {
+        int count = bfrange.count() - i;
+        count = SkMin32(count, 100);
+        cmap->writeDecAsText(count);
+        cmap->writeText(" beginbfrange\n");
+        for (int j = 0; j < count; ++j) {
+            cmap->writeText("<");
+            cmap->writeHexAsText(bfrange[i + j].fStart, 4);
+            cmap->writeText("> <");
+            cmap->writeHexAsText(bfrange[i + j].fEnd, 4);
+            cmap->writeText("> <");
+            cmap->writeHexAsText(bfrange[i + j].fUnicode, 4);
+            cmap->writeText(">\n");
+        }
+        cmap->writeText("endbfrange\n");
+    }
+}
+
+// Generate <bfchar> and <bfrange> table according to PDF spec 1.4 and Adobe
+// Technote 5014.
+// The function is not static so we can test it in unit tests.
+//
+// Current implementation guarantees bfchar and bfrange entries do not overlap.
+//
+// Current implementation does not attempt aggresive optimizations against
+// following case because the specification is not clear.
+//
+// 4 beginbfchar          1 beginbfchar
+// <0003> <0013>          <0020> <0014>
+// <0005> <0015>    to    endbfchar
+// <0007> <0017>          1 beginbfrange
+// <0020> <0014>          <0003> <0007> <0013>
+// endbfchar              endbfrange
+//
+// Adobe Technote 5014 said: "Code mappings (unlike codespace ranges) may
+// overlap, but succeeding maps superceded preceding maps."
+//
+// In case of searching text in PDF, bfrange will have higher precedence so
+// typing char id 0x0014 in search box will get glyph id 0x0004 first.  However,
+// the spec does not mention how will this kind of conflict being resolved.
+//
+// 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) {
+    if (glyphToUnicode.isEmpty()) {
+        return;
+    }
+
+    SkTDArray<BFChar> bfcharEntries;
+    SkTDArray<BFRange> bfrangeEntries;
+
+    BFRange currentRangeEntry = {0, 0, 0};
+    bool rangeEmpty = true;
+    const int count = glyphToUnicode.count();
+
+    for (int i = 0; i < count + 1; ++i) {
+        bool inSubset = i < count && (subset == NULL || subset->has(i));
+        if (!rangeEmpty) {
+            // PDF spec requires bfrange not changing the higher byte,
+            // e.g. <1035> <10FF> <2222> is ok, but
+            //      <1035> <1100> <2222> is no good
+            bool inRange =
+                i == currentRangeEntry.fEnd + 1 &&
+                i >> 8 == currentRangeEntry.fStart >> 8 &&
+                i < count &&
+                glyphToUnicode[i] == currentRangeEntry.fUnicode + i -
+                                         currentRangeEntry.fStart;
+            if (!inSubset || !inRange) {
+                if (currentRangeEntry.fEnd > currentRangeEntry.fStart) {
+                    bfrangeEntries.push(currentRangeEntry);
+                } else {
+                    BFChar* entry = bfcharEntries.append();
+                    entry->fGlyphId = currentRangeEntry.fStart;
+                    entry->fUnicode = currentRangeEntry.fUnicode;
+                }
+                rangeEmpty = true;
+            }
+        }
+        if (inSubset) {
+            currentRangeEntry.fEnd = i;
+            if (rangeEmpty) {
+              currentRangeEntry.fStart = i;
+              currentRangeEntry.fUnicode = glyphToUnicode[i];
+              rangeEmpty = false;
+            }
+        }
+    }
+
+    // The spec requires all bfchar entries for a font must come before bfrange
+    // entries.
+    append_bfchar_section(bfcharEntries, cmap);
+    append_bfrange_section(bfrangeEntries, cmap);
+}
+
+static SkPDFStream* generate_tounicode_cmap(
+        const SkTDArray<SkUnichar>& glyphToUnicode,
+        const SkPDFGlyphSet* subset) {
+    SkDynamicMemoryWStream cmap;
+    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())->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.
+
+    int fontSize = fontData->getLength();
+
+#if defined (SK_SFNTLY_SUBSETTER)
+    // Read font into buffer.
+    SkPDFStream* subsetFontStream = NULL;
+    SkTDArray<unsigned char> originalFont;
+    originalFont.setCount(fontSize);
+    if (fontData->read(originalFont.begin(), fontSize) == (size_t)fontSize) {
+        unsigned char* subsetFont = NULL;
+        // sfntly requires unsigned int* to be passed in, as far as we know,
+        // unsigned int is equivalent to uint32_t on all platforms.
+        SK_COMPILE_ASSERT(sizeof(unsigned int) == sizeof(uint32_t),
+                          unsigned_int_not_32_bits);
+        int subsetFontSize = SfntlyWrapper::SubsetFont(fontName,
+                                                       originalFont.begin(),
+                                                       fontSize,
+                                                       subset.begin(),
+                                                       subset.count(),
+                                                       &subsetFont);
+        if (subsetFontSize > 0 && subsetFont != NULL) {
+            SkAutoDataUnref data(SkData::NewWithProc(subsetFont,
+                                                     subsetFontSize,
+                                                     sk_delete_array,
+                                                     NULL));
+            subsetFontStream = new SkPDFStream(data.get());
+            fontSize = subsetFontSize;
+        }
+    }
+    if (subsetFontStream) {
+        *fontStream = subsetFontStream;
+        return fontSize;
+    }
+#endif
+
+    // Fail over: just embed the whole font.
+    *fontStream = new SkPDFStream(fontData.get());
+    return fontSize;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFGlyphSet
+///////////////////////////////////////////////////////////////////////////////
+
+SkPDFGlyphSet::SkPDFGlyphSet() : fBitSet(SK_MaxU16 + 1) {
+}
+
+void SkPDFGlyphSet::set(const uint16_t* glyphIDs, int numGlyphs) {
+    for (int i = 0; i < numGlyphs; ++i) {
+        fBitSet.setBit(glyphIDs[i], true);
+    }
+}
+
+bool SkPDFGlyphSet::has(uint16_t glyphID) const {
+    return fBitSet.isBitSet(glyphID);
+}
+
+void SkPDFGlyphSet::merge(const SkPDFGlyphSet& usage) {
+    fBitSet.orBits(usage.fBitSet);
+}
+
+void SkPDFGlyphSet::exportTo(SkTDArray<unsigned int>* glyphIDs) const {
+    fBitSet.exportTo(glyphIDs);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFGlyphSetMap
+///////////////////////////////////////////////////////////////////////////////
+SkPDFGlyphSetMap::FontGlyphSetPair::FontGlyphSetPair(SkPDFFont* font,
+                                                     SkPDFGlyphSet* glyphSet)
+        : fFont(font),
+          fGlyphSet(glyphSet) {
+}
+
+SkPDFGlyphSetMap::F2BIter::F2BIter(const SkPDFGlyphSetMap& map) {
+    reset(map);
+}
+
+SkPDFGlyphSetMap::FontGlyphSetPair* SkPDFGlyphSetMap::F2BIter::next() const {
+    if (fIndex >= fMap->count()) {
+        return NULL;
+    }
+    return &((*fMap)[fIndex++]);
+}
+
+void SkPDFGlyphSetMap::F2BIter::reset(const SkPDFGlyphSetMap& map) {
+    fMap = &(map.fMap);
+    fIndex = 0;
+}
+
+SkPDFGlyphSetMap::SkPDFGlyphSetMap() {
+}
+
+SkPDFGlyphSetMap::~SkPDFGlyphSetMap() {
+    reset();
+}
+
+void SkPDFGlyphSetMap::merge(const SkPDFGlyphSetMap& usage) {
+    for (int i = 0; i < usage.fMap.count(); ++i) {
+        SkPDFGlyphSet* myUsage = getGlyphSetForFont(usage.fMap[i].fFont);
+        myUsage->merge(*(usage.fMap[i].fGlyphSet));
+    }
+}
+
+void SkPDFGlyphSetMap::reset() {
+    for (int i = 0; i < fMap.count(); ++i) {
+        delete fMap[i].fGlyphSet;  // Should not be NULL.
+    }
+    fMap.reset();
+}
+
+void SkPDFGlyphSetMap::noteGlyphUsage(SkPDFFont* font, const uint16_t* glyphIDs,
+                                      int numGlyphs) {
+    SkPDFGlyphSet* subset = getGlyphSetForFont(font);
+    if (subset) {
+        subset->set(glyphIDs, numGlyphs);
+    }
+}
+
+SkPDFGlyphSet* SkPDFGlyphSetMap::getGlyphSetForFont(SkPDFFont* font) {
+    int index = fMap.count();
+    for (int i = 0; i < index; ++i) {
+        if (fMap[i].fFont == font) {
+            return fMap[i].fGlyphSet;
+        }
+    }
+    fMap.append();
+    index = fMap.count() - 1;
+    fMap[index].fFont = font;
+    fMap[index].fGlyphSet = new SkPDFGlyphSet();
+    return fMap[index].fGlyphSet;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFFont
+///////////////////////////////////////////////////////////////////////////////
+
+/* Font subset design: It would be nice to be able to subset fonts
+ * (particularly type 3 fonts), but it's a lot of work and not a priority.
+ *
+ * Resources are canonicalized and uniqueified by pointer so there has to be
+ * some additional state indicating which subset of the font is used.  It
+ * must be maintained at the page granularity and then combined at the document
+ * granularity. a) change SkPDFFont to fill in its state on demand, kind of
+ * like SkPDFGraphicState.  b) maintain a per font glyph usage class in each
+ * page/pdf device. c) in the document, retrieve the per font glyph usage
+ * from each page and combine it and ask for a resource with that subset.
+ */
+
+SkPDFFont::~SkPDFFont() {
+    SkAutoMutexAcquire lock(CanonicalFontsMutex());
+    int index;
+    if (Find(SkTypeface::UniqueID(fTypeface.get()), fFirstGlyphID, &index) &&
+            CanonicalFonts()[index].fFont == this) {
+        CanonicalFonts().removeShuffle(index);
+    }
+    fResources.unrefAll();
+}
+
+void SkPDFFont::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+    GetResourcesHelper(&fResources, resourceList);
+}
+
+SkTypeface* SkPDFFont::typeface() {
+    return fTypeface.get();
+}
+
+SkAdvancedTypefaceMetrics::FontType SkPDFFont::getType() {
+    return fFontType;
+}
+
+bool SkPDFFont::hasGlyph(uint16_t id) {
+    return (id >= fFirstGlyphID && id <= fLastGlyphID) || id == 0;
+}
+
+size_t SkPDFFont::glyphsToPDFFontEncoding(uint16_t* glyphIDs,
+                                          size_t numGlyphs) {
+    // A font with multibyte glyphs will support all glyph IDs in a single font.
+    if (this->multiByteGlyphs()) {
+        return numGlyphs;
+    }
+
+    for (size_t i = 0; i < numGlyphs; i++) {
+        if (glyphIDs[i] == 0) {
+            continue;
+        }
+        if (glyphIDs[i] < fFirstGlyphID || glyphIDs[i] > fLastGlyphID) {
+            return i;
+        }
+        glyphIDs[i] -= (fFirstGlyphID - 1);
+    }
+
+    return numGlyphs;
+}
+
+// static
+SkPDFFont* SkPDFFont::GetFontResource(SkTypeface* typeface, uint16_t glyphID) {
+    SkAutoMutexAcquire lock(CanonicalFontsMutex());
+    const uint32_t fontID = SkTypeface::UniqueID(typeface);
+    int relatedFontIndex;
+    if (Find(fontID, glyphID, &relatedFontIndex)) {
+        CanonicalFonts()[relatedFontIndex].fFont->ref();
+        return CanonicalFonts()[relatedFontIndex].fFont;
+    }
+
+    SkRefPtr<SkAdvancedTypefaceMetrics> fontMetrics;
+    SkPDFDict* relatedFontDescriptor = NULL;
+    if (relatedFontIndex >= 0) {
+        SkPDFFont* relatedFont = CanonicalFonts()[relatedFontIndex].fFont;
+        fontMetrics = relatedFont->fontInfo();
+        relatedFontDescriptor = relatedFont->getFontDescriptor();
+    } else {
+        SkAdvancedTypefaceMetrics::PerGlyphInfo info;
+        info = SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo;
+        info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
+                  info, SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo);
+#if !defined (SK_SFNTLY_SUBSETTER)
+        info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
+                  info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo);
+#endif
+        fontMetrics =
+            SkFontHost::GetAdvancedTypefaceMetrics(fontID, info, NULL, 0);
+        SkSafeUnref(fontMetrics.get());  // SkRefPtr and Get both took a ref.
+#if defined (SK_SFNTLY_SUBSETTER)
+        if (fontMetrics &&
+            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
+        }
+#endif
+    }
+
+    SkPDFFont* font = Create(fontMetrics.get(), typeface, glyphID,
+                             relatedFontDescriptor);
+    FontRec newEntry(font, fontID, font->fFirstGlyphID);
+    CanonicalFonts().push(newEntry);
+    return font;  // Return the reference new SkPDFFont() created.
+}
+
+SkPDFFont* SkPDFFont::getFontSubset(const SkPDFGlyphSet* usage) {
+    return NULL;  // Default: no support.
+}
+
+// static
+SkTDArray<SkPDFFont::FontRec>& SkPDFFont::CanonicalFonts() {
+    // This initialization is only thread safe with gcc.
+    static SkTDArray<FontRec> gCanonicalFonts;
+    return gCanonicalFonts;
+}
+
+// static
+SkBaseMutex& SkPDFFont::CanonicalFontsMutex() {
+    // This initialization is only thread safe with gcc, or when
+    // POD-style mutex initialization is used.
+    SK_DECLARE_STATIC_MUTEX(gCanonicalFontsMutex);
+    return gCanonicalFontsMutex;
+}
+
+// static
+bool SkPDFFont::Find(uint32_t fontID, uint16_t glyphID, int* index) {
+    // TODO(vandebo): Optimize this, do only one search?
+    FontRec search(NULL, fontID, glyphID);
+    *index = CanonicalFonts().find(search);
+    if (*index >= 0) {
+        return true;
+    }
+    search.fGlyphID = 0;
+    *index = CanonicalFonts().find(search);
+    return false;
+}
+
+SkPDFFont::SkPDFFont(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface,
+                     uint16_t glyphID, bool descendantFont)
+        : SkPDFDict("Font"),
+          fTypeface(typeface),
+          fFirstGlyphID(1),
+          fLastGlyphID(info ? info->fLastGlyphID : 0),
+          fFontInfo(info) {
+    if (info == NULL) {
+        fFontType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+    } else if (info->fMultiMaster) {
+        fFontType = SkAdvancedTypefaceMetrics::kOther_Font;
+    } else {
+        fFontType = info->fType;
+    }
+}
+
+// static
+SkPDFFont* SkPDFFont::Create(SkAdvancedTypefaceMetrics* info,
+                             SkTypeface* typeface, uint16_t glyphID,
+                             SkPDFDict* relatedFontDescriptor) {
+    SkAdvancedTypefaceMetrics::FontType type =
+        info ? info->fType : SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+
+    if (info && info->fMultiMaster) {
+        NOT_IMPLEMENTED(true, true);
+        return new SkPDFType3Font(info,
+                                  typeface,
+                                  glyphID,
+                                  relatedFontDescriptor);
+    }
+    if (type == SkAdvancedTypefaceMetrics::kType1CID_Font ||
+        type == SkAdvancedTypefaceMetrics::kTrueType_Font) {
+        SkASSERT(relatedFontDescriptor == NULL);
+        return new SkPDFType0Font(info, typeface);
+    }
+    if (type == SkAdvancedTypefaceMetrics::kType1_Font) {
+        return new SkPDFType1Font(info,
+                                  typeface,
+                                  glyphID,
+                                  relatedFontDescriptor);
+    }
+
+    SkASSERT(type == SkAdvancedTypefaceMetrics::kCFF_Font ||
+             type == SkAdvancedTypefaceMetrics::kOther_Font ||
+             type == SkAdvancedTypefaceMetrics::kNotEmbeddable_Font);
+
+    return new SkPDFType3Font(info, typeface, glyphID, relatedFontDescriptor);
+}
+
+SkAdvancedTypefaceMetrics* SkPDFFont::fontInfo() {
+    return fFontInfo.get();
+}
+
+void SkPDFFont::setFontInfo(SkAdvancedTypefaceMetrics* info) {
+    if (info == NULL || info == fFontInfo.get()) {
+        return;
+    }
+    fFontInfo = info;
+}
+
+uint16_t SkPDFFont::firstGlyphID() const {
+    return fFirstGlyphID;
+}
+
+uint16_t SkPDFFont::lastGlyphID() const {
+    return fLastGlyphID;
+}
+
+void SkPDFFont::setLastGlyphID(uint16_t glyphID) {
+    fLastGlyphID = glyphID;
+}
+
+void SkPDFFont::addResource(SkPDFObject* object) {
+    SkASSERT(object != NULL);
+    fResources.push(object);
+}
+
+SkPDFDict* SkPDFFont::getFontDescriptor() {
+    return fDescriptor.get();
+}
+
+void SkPDFFont::setFontDescriptor(SkPDFDict* descriptor) {
+    fDescriptor = descriptor;
+}
+
+bool SkPDFFont::addCommonFontDescriptorEntries(int16_t defaultWidth) {
+    if (fDescriptor.get() == NULL) {
+        return false;
+    }
+
+    const uint16_t emSize = fFontInfo->fEmSize;
+
+    fDescriptor->insertName("FontName", fFontInfo->fFontName);
+    fDescriptor->insertInt("Flags", fFontInfo->fStyle);
+    fDescriptor->insertScalar("Ascent",
+            scaleFromFontUnits(fFontInfo->fAscent, emSize));
+    fDescriptor->insertScalar("Descent",
+            scaleFromFontUnits(fFontInfo->fDescent, emSize));
+    fDescriptor->insertScalar("StemV",
+            scaleFromFontUnits(fFontInfo->fStemV, emSize));
+    fDescriptor->insertScalar("CapHeight",
+            scaleFromFontUnits(fFontInfo->fCapHeight, emSize));
+    fDescriptor->insertInt("ItalicAngle", fFontInfo->fItalicAngle);
+    fDescriptor->insert("FontBBox", makeFontBBox(fFontInfo->fBBox,
+                                                 fFontInfo->fEmSize))->unref();
+
+    if (defaultWidth > 0) {
+        fDescriptor->insertScalar("MissingWidth",
+                scaleFromFontUnits(defaultWidth, emSize));
+    }
+    return true;
+}
+
+void SkPDFFont::adjustGlyphRangeForSingleByteEncoding(int16_t glyphID) {
+    // Single byte glyph encoding supports a max of 255 glyphs.
+    fFirstGlyphID = glyphID - (glyphID - 1) % 255;
+    if (fLastGlyphID > fFirstGlyphID + 255 - 1) {
+        fLastGlyphID = fFirstGlyphID + 255 - 1;
+    }
+}
+
+bool SkPDFFont::FontRec::operator==(const SkPDFFont::FontRec& b) const {
+    if (fFontID != b.fFontID) {
+        return false;
+    }
+    if (fFont != NULL && b.fFont != NULL) {
+        return fFont->fFirstGlyphID == b.fFont->fFirstGlyphID &&
+            fFont->fLastGlyphID == b.fFont->fLastGlyphID;
+    }
+    if (fGlyphID == 0 || b.fGlyphID == 0) {
+        return true;
+    }
+
+    if (fFont != NULL) {
+        return fFont->fFirstGlyphID <= b.fGlyphID &&
+            b.fGlyphID <= fFont->fLastGlyphID;
+    } else if (b.fFont != NULL) {
+        return b.fFont->fFirstGlyphID <= fGlyphID &&
+            fGlyphID <= b.fFont->fLastGlyphID;
+    }
+    return fGlyphID == b.fGlyphID;
+}
+
+SkPDFFont::FontRec::FontRec(SkPDFFont* font, uint32_t fontID, uint16_t glyphID)
+    : fFont(font),
+      fFontID(fontID),
+      fGlyphID(glyphID) {
+}
+
+void SkPDFFont::populateToUnicodeTable(const SkPDFGlyphSet* subset) {
+    if (fFontInfo == NULL || fFontInfo->fGlyphToUnicode.begin() == NULL) {
+        return;
+    }
+    SkRefPtr<SkPDFStream> pdfCmap =
+        generate_tounicode_cmap(fFontInfo->fGlyphToUnicode, subset);
+    addResource(pdfCmap.get());  // Pass reference from new.
+    insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFType0Font
+///////////////////////////////////////////////////////////////////////////////
+
+SkPDFType0Font::SkPDFType0Font(SkAdvancedTypefaceMetrics* info,
+                               SkTypeface* typeface)
+        : SkPDFFont(info, typeface, 0, false) {
+    SkDEBUGCODE(fPopulated = false);
+}
+
+SkPDFType0Font::~SkPDFType0Font() {}
+
+SkPDFFont* SkPDFType0Font::getFontSubset(const SkPDFGlyphSet* subset) {
+    SkPDFType0Font* newSubset = new SkPDFType0Font(fontInfo(), typeface());
+    newSubset->populate(subset);
+    return newSubset;
+}
+
+#ifdef SK_DEBUG
+void SkPDFType0Font::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                                bool indirect) {
+    SkASSERT(fPopulated);
+    return INHERITED::emitObject(stream, catalog, indirect);
+}
+#endif
+
+bool SkPDFType0Font::populate(const SkPDFGlyphSet* subset) {
+    insertName("Subtype", "Type0");
+    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();
+    insert("DescendantFonts", descendantFonts.get());
+
+    populateToUnicodeTable(subset);
+
+    SkDEBUGCODE(fPopulated = true);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFCIDFont
+///////////////////////////////////////////////////////////////////////////////
+
+SkPDFCIDFont::SkPDFCIDFont(SkAdvancedTypefaceMetrics* info,
+                           SkTypeface* typeface, const SkPDFGlyphSet* subset)
+        : SkPDFFont(info, typeface, 0, true) {
+    populate(subset);
+}
+
+SkPDFCIDFont::~SkPDFCIDFont() {}
+
+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.
+    setFontDescriptor(descriptor.get());
+
+    switch (getType()) {
+        case SkAdvancedTypefaceMetrics::kTrueType_Font: {
+            SkASSERT(subset);
+            // Font subsetting
+            SkPDFStream* rawStream = NULL;
+            int fontSize = get_subset_font_stream(fontInfo()->fFontName.c_str(),
+                                                  typeface(),
+                                                  *subset,
+                                                  &rawStream);
+            SkASSERT(fontSize);
+            SkASSERT(rawStream);
+            SkRefPtr<SkPDFStream> fontStream = rawStream;
+            // SkRefPtr and new both ref()'d fontStream, pass one.
+            addResource(fontStream.get());
+
+            fontStream->insertInt("Length1", fontSize);
+            descriptor->insert("FontFile2",
+                                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.
+            addResource(fontStream.get());
+
+            if (getType() == SkAdvancedTypefaceMetrics::kCFF_Font) {
+                fontStream->insertName("Subtype", "Type1C");
+            } else {
+                fontStream->insertName("Subtype", "CIDFontType0c");
+            }
+            descriptor->insert("FontFile3",
+                                new SkPDFObjRef(fontStream.get()))->unref();
+            break;
+        }
+        default:
+            SkASSERT(false);
+    }
+
+    addResource(descriptor.get());
+    descriptor->ref();
+
+    insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
+    return addCommonFontDescriptorEntries(defaultWidth);
+}
+
+bool SkPDFCIDFont::populate(const SkPDFGlyphSet* subset) {
+    // Generate new font metrics with advance info for true type fonts.
+    if (fontInfo()->fType == SkAdvancedTypefaceMetrics::kTrueType_Font) {
+        // Generate glyph id array.
+        SkTDArray<uint32_t> glyphIDs;
+        glyphIDs.push(0);  // Always include glyph 0.
+        if (subset) {
+            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 =
+            SkFontHost::GetAdvancedTypefaceMetrics(
+                    SkTypeface::UniqueID(typeface()),
+                    info,
+                    glyphs,
+                    glyphsCount);
+        SkSafeUnref(fontMetrics.get());  // SkRefPtr and Get both took a ref
+        setFontInfo(fontMetrics.get());
+        addFontDescriptor(0, &glyphIDs);
+    } else {
+        // Other CID fonts
+        addFontDescriptor(0, NULL);
+    }
+
+    insertName("BaseFont", fontInfo()->fFontName);
+
+    if (getType() == SkAdvancedTypefaceMetrics::kType1CID_Font) {
+        insertName("Subtype", "CIDFontType0");
+    } else if (getType() == SkAdvancedTypefaceMetrics::kTrueType_Font) {
+        insertName("Subtype", "CIDFontType2");
+        insertName("CIDToGIDMap", "Identity");
+    } else {
+        SkASSERT(false);
+    }
+
+    SkRefPtr<SkPDFDict> sysInfo = new SkPDFDict;
+    sysInfo->unref();  // SkRefPtr and new both took a reference.
+    sysInfo->insert("Registry", new SkPDFString("Adobe"))->unref();
+    sysInfo->insert("Ordering", new SkPDFString("Identity"))->unref();
+    sysInfo->insertInt("Supplement", 0);
+    insert("CIDSystemInfo", sysInfo.get());
+
+    if (fontInfo()->fGlyphWidths.get()) {
+        int16_t defaultWidth = 0;
+        SkRefPtr<SkPDFArray> widths =
+            composeAdvanceData(fontInfo()->fGlyphWidths.get(),
+                               fontInfo()->fEmSize, &appendWidth,
+                               &defaultWidth);
+        widths->unref();  // SkRefPtr and compose both took a reference.
+        if (widths->size())
+            insert("W", widths.get());
+        if (defaultWidth != 0) {
+            insertScalar("DW", scaleFromFontUnits(defaultWidth,
+                                                  fontInfo()->fEmSize));
+        }
+    }
+    if (fontInfo()->fVerticalMetrics.get()) {
+        struct SkAdvancedTypefaceMetrics::VerticalMetric defaultAdvance;
+        defaultAdvance.fVerticalAdvance = 0;
+        defaultAdvance.fOriginXDisp = 0;
+        defaultAdvance.fOriginYDisp = 0;
+        SkRefPtr<SkPDFArray> advances =
+            composeAdvanceData(fontInfo()->fVerticalMetrics.get(),
+                               fontInfo()->fEmSize, &appendVerticalAdvance,
+                               &defaultAdvance);
+        advances->unref();  // SkRefPtr and compose both took a ref.
+        if (advances->size())
+            insert("W2", advances.get());
+        if (defaultAdvance.fVerticalAdvance ||
+                defaultAdvance.fOriginXDisp ||
+                defaultAdvance.fOriginYDisp) {
+            insert("DW2", appendVerticalAdvance(defaultAdvance,
+                                                fontInfo()->fEmSize,
+                                                new SkPDFArray))->unref();
+        }
+    }
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFType1Font
+///////////////////////////////////////////////////////////////////////////////
+
+SkPDFType1Font::SkPDFType1Font(SkAdvancedTypefaceMetrics* info,
+                               SkTypeface* typeface,
+                               uint16_t glyphID,
+                               SkPDFDict* relatedFontDescriptor)
+        : SkPDFFont(info, typeface, glyphID, false) {
+    populate(glyphID);
+}
+
+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();
+        return true;
+    }
+
+    descriptor = new SkPDFDict("FontDescriptor");
+    descriptor->unref();  // SkRefPtr and new both took a ref.
+    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.
+    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.
+    addResource(fontStream.get());
+    fontStream->insertInt("Length1", header);
+    fontStream->insertInt("Length2", data);
+    fontStream->insertInt("Length3", trailer);
+    descriptor->insert("FontFile", new SkPDFObjRef(fontStream.get()))->unref();
+
+    addResource(descriptor.get());
+    descriptor->ref();
+    insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
+
+    return addCommonFontDescriptorEntries(defaultWidth);
+}
+
+bool SkPDFType1Font::populate(int16_t glyphID) {
+    SkASSERT(!fontInfo()->fVerticalMetrics.get());
+    SkASSERT(fontInfo()->fGlyphWidths.get());
+
+    adjustGlyphRangeForSingleByteEncoding(glyphID);
+
+    int16_t defaultWidth = 0;
+    const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry = NULL;
+    const SkAdvancedTypefaceMetrics::WidthRange* widthEntry;
+    for (widthEntry = fontInfo()->fGlyphWidths.get();
+            widthEntry != NULL;
+            widthEntry = widthEntry->fNext.get()) {
+        switch (widthEntry->fType) {
+            case SkAdvancedTypefaceMetrics::WidthRange::kDefault:
+                defaultWidth = widthEntry->fAdvance[0];
+                break;
+            case SkAdvancedTypefaceMetrics::WidthRange::kRun:
+                SkASSERT(false);
+                break;
+            case SkAdvancedTypefaceMetrics::WidthRange::kRange:
+                SkASSERT(widthRangeEntry == NULL);
+                widthRangeEntry = widthEntry;
+                break;
+        }
+    }
+
+    if (!addFontDescriptor(defaultWidth)) {
+        return false;
+    }
+
+    insertName("Subtype", "Type1");
+    insertName("BaseFont", fontInfo()->fFontName);
+
+    addWidthInfoFromRange(defaultWidth, widthRangeEntry);
+
+    SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding");
+    encoding->unref();  // SkRefPtr and new both took a reference.
+    insert("Encoding", encoding.get());
+
+    SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
+    encDiffs->unref();  // SkRefPtr and new both took a reference.
+    encoding->insert("Differences", encDiffs.get());
+
+    encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2);
+    encDiffs->appendInt(1);
+    for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) {
+        encDiffs->appendName(fontInfo()->fGlyphNames->get()[gID].c_str());
+    }
+
+    return true;
+}
+
+void SkPDFType1Font::addWidthInfoFromRange(
+        int16_t defaultWidth,
+        const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry) {
+    SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
+    widthArray->unref();  // SkRefPtr and new both took a ref.
+    int firstChar = 0;
+    if (widthRangeEntry) {
+        const uint16_t emSize = fontInfo()->fEmSize;
+        int startIndex = firstGlyphID() - widthRangeEntry->fStartId;
+        int endIndex = startIndex + lastGlyphID() - firstGlyphID() + 1;
+        if (startIndex < 0)
+            startIndex = 0;
+        if (endIndex > widthRangeEntry->fAdvance.count())
+            endIndex = widthRangeEntry->fAdvance.count();
+        if (widthRangeEntry->fStartId == 0) {
+            appendWidth(widthRangeEntry->fAdvance[0], emSize, widthArray.get());
+        } else {
+            firstChar = startIndex + widthRangeEntry->fStartId;
+        }
+        for (int i = startIndex; i < endIndex; i++) {
+            appendWidth(widthRangeEntry->fAdvance[i], emSize, widthArray.get());
+        }
+    } else {
+        appendWidth(defaultWidth, 1000, widthArray.get());
+    }
+    insertInt("FirstChar", firstChar);
+    insertInt("LastChar", firstChar + widthArray->size() - 1);
+    insert("Widths", widthArray.get());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// class SkPDFType3Font
+///////////////////////////////////////////////////////////////////////////////
+
+SkPDFType3Font::SkPDFType3Font(SkAdvancedTypefaceMetrics* info,
+                               SkTypeface* typeface,
+                               uint16_t glyphID,
+                               SkPDFDict* relatedFontDescriptor)
+        : SkPDFFont(info, typeface, glyphID, false) {
+    populate(glyphID);
+}
+
+SkPDFType3Font::~SkPDFType3Font() {}
+
+bool SkPDFType3Font::populate(int16_t glyphID) {
+    SkPaint paint;
+    paint.setTypeface(typeface());
+    paint.setTextSize(1000);
+    SkAutoGlyphCache autoCache(paint, NULL);
+    SkGlyphCache* cache = autoCache.getCache();
+    // If fLastGlyphID isn't set (because there is not fFontInfo), look it up.
+    if (lastGlyphID() == 0) {
+        setLastGlyphID(cache->getGlyphCount() - 1);
+    }
+
+    adjustGlyphRangeForSingleByteEncoding(glyphID);
+
+    insertName("Subtype", "Type3");
+    // Flip about the x-axis and scale by 1/1000.
+    SkMatrix fontMatrix;
+    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.
+    insert("CharProcs", charProcs.get());
+
+    SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding");
+    encoding->unref();  // SkRefPtr and new both took a reference.
+    insert("Encoding", encoding.get());
+
+    SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
+    encDiffs->unref();  // SkRefPtr and new both took a reference.
+    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.
+
+    SkIRect bbox = SkIRect::MakeEmpty();
+    for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) {
+        SkString characterName;
+        characterName.printf("gid%d", gID);
+        encDiffs->appendName(characterName.c_str());
+
+        const SkGlyph& glyph = cache->getGlyphIDMetrics(gID);
+        widthArray->appendScalar(SkFixedToScalar(glyph.fAdvanceX));
+        SkIRect glyphBBox = SkIRect::MakeXYWH(glyph.fLeft, glyph.fTop,
+                                              glyph.fWidth, glyph.fHeight);
+        bbox.join(glyphBBox);
+
+        SkDynamicMemoryWStream content;
+        setGlyphWidthAndBoundingBox(SkFixedToScalar(glyph.fAdvanceX), glyphBBox,
+                                    &content);
+        const SkPath* path = cache->findPath(glyph);
+        if (path) {
+            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.
+        glyphStream->setData(content.copyToData())->unref();
+
+        SkRefPtr<SkPDFStream> glyphDescription =
+            new SkPDFStream(glyphStream.get());
+        // SkRefPtr and new both ref()'d charProcs, pass one.
+        addResource(glyphDescription.get());
+        charProcs->insert(characterName.c_str(),
+                          new SkPDFObjRef(glyphDescription.get()))->unref();
+    }
+
+    insert("FontBBox", makeFontBBox(bbox, 1000))->unref();
+    insertInt("FirstChar", firstGlyphID());
+    insertInt("LastChar", lastGlyphID());
+    insert("Widths", widthArray.get());
+    insertName("CIDToGIDMap", "Identity");
+
+    populateToUnicodeTable(NULL);
+    return true;
+}
diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h
new file mode 100644
index 0000000..f463ed7
--- /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);
+    };
+
+    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/src/pdf/SkPDFFontImpl.h b/src/pdf/SkPDFFontImpl.h
new file mode 100755
index 0000000..d298a38
--- /dev/null
+++ b/src/pdf/SkPDFFontImpl.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 SkPDFFontImpl_DEFINED
+#define SkPDFFontImpl_DEFINED
+
+#include "SkPDFFont.h"
+
+class SkPDFType0Font : public SkPDFFont {
+public:
+    virtual ~SkPDFType0Font();
+    virtual bool multiByteGlyphs() const { return true; }
+    SK_API virtual SkPDFFont* getFontSubset(const SkPDFGlyphSet* usage);
+#ifdef SK_DEBUG
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+#endif
+
+private:
+    friend class SkPDFFont;  // to access the constructor
+#ifdef SK_DEBUG
+    bool fPopulated;
+    typedef SkPDFDict INHERITED;
+#endif
+
+    SkPDFType0Font(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface);
+
+    bool populate(const SkPDFGlyphSet* subset);
+};
+
+class SkPDFCIDFont : public SkPDFFont {
+public:
+    virtual ~SkPDFCIDFont();
+    virtual bool multiByteGlyphs() const { return true; }
+
+private:
+    friend class SkPDFType0Font;  // to access the constructor
+
+    SkPDFCIDFont(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface,
+                 const SkPDFGlyphSet* subset);
+
+    bool populate(const SkPDFGlyphSet* subset);
+    bool addFontDescriptor(int16_t defaultWidth,
+                           const SkTDArray<uint32_t>* subset);
+};
+
+class SkPDFType1Font : public SkPDFFont {
+public:
+    virtual ~SkPDFType1Font();
+    virtual bool multiByteGlyphs() const { return false; }
+
+private:
+    friend class SkPDFFont;  // to access the constructor
+
+    SkPDFType1Font(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface,
+                   uint16_t glyphID, SkPDFDict* relatedFontDescriptor);
+
+    bool populate(int16_t glyphID);
+    bool addFontDescriptor(int16_t defaultWidth);
+    void addWidthInfoFromRange(int16_t defaultWidth,
+        const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry);
+};
+
+class SkPDFType3Font : public SkPDFFont {
+public:
+    virtual ~SkPDFType3Font();
+    virtual bool multiByteGlyphs() const { return false; }
+
+private:
+    friend class SkPDFFont;  // to access the constructor
+
+    SkPDFType3Font(SkAdvancedTypefaceMetrics* info, SkTypeface* typeface,
+                   uint16_t glyphID, SkPDFDict* relatedFontDescriptor);
+
+    bool populate(int16_t glyphID);
+};
+
+#endif
diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp
new file mode 100644
index 0000000..c1e2192
--- /dev/null
+++ b/src/pdf/SkPDFFormXObject.cpp
@@ -0,0 +1,62 @@
+
+/*
+ * 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 "SkPDFFormXObject.h"
+
+#include "SkMatrix.h"
+#include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
+#include "SkPDFUtils.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+
+SkPDFFormXObject::SkPDFFormXObject(SkPDFDevice* device) {
+    // 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, false);
+
+    SkRefPtr<SkStream> content = device->content();
+    content->unref();  // SkRefPtr and content() both took a reference.
+    setData(content.get());
+
+    insertName("Type", "XObject");
+    insertName("Subtype", "Form");
+    SkSafeUnref(this->insert("BBox", device->copyMediaBox()));
+    insert("Resources", device->getResourceDict());
+
+    // We invert the initial transform and apply that to the xobject so that
+    // it doesn't get applied twice. We can't just undo it because it's
+    // embedded in things like shaders and images.
+    if (!device->initialTransform().isIdentity()) {
+        SkMatrix 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.
+    group->insertName("S", "Transparency");
+    group->insert("I", new SkPDFBool(true))->unref();  // Isolated.
+    insert("Group", group.get());
+}
+
+SkPDFFormXObject::~SkPDFFormXObject() {
+    fResources.unrefAll();
+}
+
+void SkPDFFormXObject::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+    GetResourcesHelper(&fResources, resourceList);
+}
diff --git a/src/pdf/SkPDFFormXObject.h b/src/pdf/SkPDFFormXObject.h
new file mode 100644
index 0000000..0c49152
--- /dev/null
+++ b/src/pdf/SkPDFFormXObject.h
@@ -0,0 +1,48 @@
+
+/*
+ * 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 SkPDFFormXObject_DEFINED
+#define SkPDFFormXObject_DEFINED
+
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+#include "SkString.h"
+
+class SkMatrix;
+class SkPDFDevice;
+class SkPDFCatalog;
+
+/** \class SkPDFFormXObject
+
+    A form XObject; a self contained description of graphics objects.  A form
+    XObject is basically a page object with slightly different syntax, that
+    can be drawn onto a page.
+*/
+
+// The caller could keep track of the form XObjects it creates and
+// canonicalize them, but the Skia API doesn't provide enough context to
+// automatically do it (trivially).
+class SkPDFFormXObject : public SkPDFStream {
+public:
+    /** Create a PDF form XObject. Entries for the dictionary entries are
+     *  automatically added.
+     *  @param device      The set of graphical elements on this form.
+     */
+    explicit SkPDFFormXObject(SkPDFDevice* device);
+    virtual ~SkPDFFormXObject();
+
+    // The SkPDFObject interface.
+    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+private:
+    SkTDArray<SkPDFObject*> fResources;
+};
+
+#endif
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
new file mode 100644
index 0000000..ec9b0e7
--- /dev/null
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -0,0 +1,283 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkPDFFormXObject.h"
+#include "SkPDFGraphicState.h"
+#include "SkPDFUtils.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+
+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::kScreen_Mode:     return "Screen";
+        case SkXfermode::kOverlay_Mode:    return "Overlay";
+        case SkXfermode::kDarken_Mode:     return "Darken";
+        case SkXfermode::kLighten_Mode:    return "Lighten";
+        case SkXfermode::kColorDodge_Mode: return "ColorDodge";
+        case SkXfermode::kColorBurn_Mode:  return "ColorBurn";
+        case SkXfermode::kHardLight_Mode:  return "HardLight";
+        case SkXfermode::kSoftLight_Mode:  return "SoftLight";
+        case SkXfermode::kDifference_Mode: return "Difference";
+        case SkXfermode::kExclusion_Mode:  return "Exclusion";
+
+        // These are handled in SkPDFDevice::setUpContentEntry.
+        case SkXfermode::kClear_Mode:
+        case SkXfermode::kSrc_Mode:
+        case SkXfermode::kDst_Mode:
+        case SkXfermode::kDstOver_Mode:
+        case SkXfermode::kSrcIn_Mode:
+        case SkXfermode::kDstIn_Mode:
+        case SkXfermode::kSrcOut_Mode:
+        case SkXfermode::kDstOut_Mode:
+            return "Normal";
+
+        // TODO(vandebo): Figure out if we can support more of these modes.
+        case SkXfermode::kSrcATop_Mode:
+        case SkXfermode::kDstATop_Mode:
+        case SkXfermode::kXor_Mode:
+        case SkXfermode::kPlus_Mode:
+            return NULL;
+    }
+    return NULL;
+}
+
+SkPDFGraphicState::~SkPDFGraphicState() {
+    SkAutoMutexAcquire lock(CanonicalPaintsMutex());
+    if (!fSMask) {
+        int index = Find(fPaint);
+        SkASSERT(index >= 0);
+        SkASSERT(CanonicalPaints()[index].fGraphicState == this);
+        CanonicalPaints().removeShuffle(index);
+    }
+    fResources.unrefAll();
+}
+
+void SkPDFGraphicState::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+    GetResourcesHelper(&fResources, resourceList);
+}
+
+void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                                   bool indirect) {
+    populateDict();
+    SkPDFDict::emitObject(stream, catalog, indirect);
+}
+
+// static
+size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    populateDict();
+    return SkPDFDict::getOutputSize(catalog, indirect);
+}
+
+// static
+SkTDArray<SkPDFGraphicState::GSCanonicalEntry>&
+SkPDFGraphicState::CanonicalPaints() {
+    // This initialization is only thread safe with gcc.
+    static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
+    return gCanonicalPaints;
+}
+
+// static
+SkBaseMutex& SkPDFGraphicState::CanonicalPaintsMutex() {
+    // This initialization is only thread safe with gcc or when
+    // POD-style mutex initialization is used.
+    SK_DECLARE_STATIC_MUTEX(gCanonicalPaintsMutex);
+    return gCanonicalPaintsMutex;
+}
+
+// static
+SkPDFGraphicState* SkPDFGraphicState::GetGraphicStateForPaint(
+        const SkPaint& paint) {
+    SkAutoMutexAcquire lock(CanonicalPaintsMutex());
+    int index = Find(paint);
+    if (index >= 0) {
+        CanonicalPaints()[index].fGraphicState->ref();
+        return CanonicalPaints()[index].fGraphicState;
+    }
+    GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
+    CanonicalPaints().push(newEntry);
+    return newEntry.fGraphicState;
+}
+
+// static
+SkPDFObject* SkPDFGraphicState::GetInvertFunction() {
+    // This assumes that canonicalPaintsMutex is held.
+    static SkPDFStream* invertFunction = NULL;
+    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.
+        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.
+
+        invertFunction = new SkPDFStream(psInvertStream.get());
+        invertFunction->insertInt("FunctionType", 4);
+        invertFunction->insert("Domain", domainAndRange.get());
+        invertFunction->insert("Range", domainAndRange.get());
+    }
+    return invertFunction;
+}
+
+// static
+SkPDFGraphicState* SkPDFGraphicState::GetSMaskGraphicState(
+        SkPDFFormXObject* sMask, bool invert) {
+    // The practical chances of using the same mask more than once are unlikely
+    // 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.
+    sMaskDict->insertName("S", "Alpha");
+    sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
+
+    SkPDFGraphicState* result = new SkPDFGraphicState;
+    result->fPopulated = true;
+    result->fSMask = true;
+    result->insertName("Type", "ExtGState");
+    result->insert("SMask", sMaskDict.get());
+    result->fResources.push(sMask);
+    sMask->ref();
+
+    if (invert) {
+        SkPDFObject* invertFunction = GetInvertFunction();
+        result->fResources.push(invertFunction);
+        invertFunction->ref();
+        sMaskDict->insert("TR", new SkPDFObjRef(invertFunction))->unref();
+    }
+
+    return result;
+}
+
+// static
+SkPDFGraphicState* SkPDFGraphicState::GetNoSMaskGraphicState() {
+    SkAutoMutexAcquire lock(CanonicalPaintsMutex());
+    static SkPDFGraphicState* noSMaskGS = NULL;
+    if (!noSMaskGS) {
+        noSMaskGS = new SkPDFGraphicState;
+        noSMaskGS->fPopulated = true;
+        noSMaskGS->fSMask = true;
+        noSMaskGS->insertName("Type", "ExtGState");
+        noSMaskGS->insertName("SMask", "None");
+    }
+    noSMaskGS->ref();
+    return noSMaskGS;
+}
+
+// static
+int SkPDFGraphicState::Find(const SkPaint& paint) {
+    GSCanonicalEntry search(&paint);
+    return CanonicalPaints().find(search);
+}
+
+SkPDFGraphicState::SkPDFGraphicState()
+    : fPopulated(false),
+      fSMask(false) {
+}
+
+SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
+    : fPaint(paint),
+      fPopulated(false),
+      fSMask(false) {
+}
+
+// populateDict and operator== have to stay in sync with each other.
+void SkPDFGraphicState::populateDict() {
+    if (!fPopulated) {
+        fPopulated = true;
+        insertName("Type", "ExtGState");
+
+        SkRefPtr<SkPDFScalar> alpha =
+            new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF));
+        alpha->unref();  // SkRefPtr and new both took a reference.
+        insert("CA", alpha.get());
+        insert("ca", alpha.get());
+
+        SK_COMPILE_ASSERT(SkPaint::kButt_Cap == 0, paint_cap_mismatch);
+        SK_COMPILE_ASSERT(SkPaint::kRound_Cap == 1, paint_cap_mismatch);
+        SK_COMPILE_ASSERT(SkPaint::kSquare_Cap == 2, paint_cap_mismatch);
+        SK_COMPILE_ASSERT(SkPaint::kCapCount == 3, paint_cap_mismatch);
+        SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
+        insertInt("LC", fPaint.getStrokeCap());
+
+        SK_COMPILE_ASSERT(SkPaint::kMiter_Join == 0, paint_join_mismatch);
+        SK_COMPILE_ASSERT(SkPaint::kRound_Join == 1, paint_join_mismatch);
+        SK_COMPILE_ASSERT(SkPaint::kBevel_Join == 2, paint_join_mismatch);
+        SK_COMPILE_ASSERT(SkPaint::kJoinCount == 3, paint_join_mismatch);
+        SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
+        insertInt("LJ", fPaint.getStrokeJoin());
+
+        insertScalar("LW", fPaint.getStrokeWidth());
+        insertScalar("ML", fPaint.getStrokeMiter());
+        insert("SA", new SkPDFBool(true))->unref();  // Auto stroke adjustment.
+
+        SkXfermode::Mode xfermode = SkXfermode::kSrcOver_Mode;
+        // If asMode fails, default to kSrcOver_Mode.
+        if (fPaint.getXfermode())
+            fPaint.getXfermode()->asMode(&xfermode);
+        // If we don't support the mode, just use kSrcOver_Mode.
+        if (xfermode < 0 || xfermode > SkXfermode::kLastMode ||
+                blend_mode_from_xfermode(xfermode) == NULL) {
+            xfermode = SkXfermode::kSrcOver_Mode;
+            NOT_IMPLEMENTED("unsupported xfermode", false);
+        }
+        insertName("BM", blend_mode_from_xfermode(xfermode));
+    }
+}
+
+// We're only interested in some fields of the SkPaint, so we have a custom
+// operator== function.
+bool SkPDFGraphicState::GSCanonicalEntry::operator==(
+        const SkPDFGraphicState::GSCanonicalEntry& gs) const {
+    const SkPaint* a = fPaint;
+    const SkPaint* b = gs.fPaint;
+    SkASSERT(a != NULL);
+    SkASSERT(b != NULL);
+
+    if (SkColorGetA(a->getColor()) != SkColorGetA(b->getColor()) ||
+           a->getStrokeCap() != b->getStrokeCap() ||
+           a->getStrokeJoin() != b->getStrokeJoin() ||
+           a->getStrokeWidth() != b->getStrokeWidth() ||
+           a->getStrokeMiter() != b->getStrokeMiter()) {
+        return false;
+    }
+
+    SkXfermode::Mode aXfermodeName = SkXfermode::kSrcOver_Mode;
+    SkXfermode* aXfermode = a->getXfermode();
+    if (aXfermode) {
+        aXfermode->asMode(&aXfermodeName);
+    }
+    if (aXfermodeName < 0 || aXfermodeName > SkXfermode::kLastMode ||
+            blend_mode_from_xfermode(aXfermodeName) == NULL) {
+        aXfermodeName = SkXfermode::kSrcOver_Mode;
+    }
+    const char* aXfermodeString = blend_mode_from_xfermode(aXfermodeName);
+    SkASSERT(aXfermodeString != NULL);
+
+    SkXfermode::Mode bXfermodeName = SkXfermode::kSrcOver_Mode;
+    SkXfermode* bXfermode = b->getXfermode();
+    if (bXfermode) {
+        bXfermode->asMode(&bXfermodeName);
+    }
+    if (bXfermodeName < 0 || bXfermodeName > SkXfermode::kLastMode ||
+            blend_mode_from_xfermode(bXfermodeName) == NULL) {
+        bXfermodeName = SkXfermode::kSrcOver_Mode;
+    }
+    const char* bXfermodeString = blend_mode_from_xfermode(bXfermodeName);
+    SkASSERT(bXfermodeString != NULL);
+
+    return strcmp(aXfermodeString, bXfermodeString) == 0;
+}
diff --git a/src/pdf/SkPDFGraphicState.h b/src/pdf/SkPDFGraphicState.h
new file mode 100644
index 0000000..af01737
--- /dev/null
+++ b/src/pdf/SkPDFGraphicState.h
@@ -0,0 +1,101 @@
+
+/*
+ * 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 SkPDFGraphicState_DEFINED
+#define SkPDFGraphicState_DEFINED
+
+#include "SkPaint.h"
+#include "SkPDFTypes.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+
+class SkPDFFormXObject;
+
+/** \class SkPDFGraphicState
+    SkPaint objects roughly correspond to graphic state dictionaries that can
+    be installed. So that a given dictionary is only output to the pdf file
+    once, we want to canonicalize them. Static methods in this class manage
+    a weakly referenced set of SkPDFGraphicState objects: when the last
+    reference to a SkPDFGraphicState is removed, it removes itself from the
+    static set of objects.
+
+*/
+class SkPDFGraphicState : public SkPDFDict {
+public:
+    virtual ~SkPDFGraphicState();
+
+    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+    // Override emitObject and getOutputSize so that we can populate
+    // the dictionary on demand.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+    /** Get the graphic state for the passed SkPaint. 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 paint  The SkPaint to emulate.
+     */
+    static SkPDFGraphicState* GetGraphicStateForPaint(const SkPaint& paint);
+
+    /** Make a graphic state that only sets the passed soft mask. The
+     *  reference count of the object is incremented and it is the caller's
+     *  responsibility to unreference it when done.
+     *  @param sMask  The form xobject to use as a soft mask.
+     *  @param invert Indicates if the alpha of the sMask should be inverted.
+     */
+    static SkPDFGraphicState* GetSMaskGraphicState(SkPDFFormXObject* sMask,
+                                                   bool invert);
+
+    /** Get a graphic state that only unsets the soft mask. The reference
+     *  count of the object is incremented and it is the caller's responsibility
+     *  to unreference it when done. This is needed to accommodate the weak
+     *  reference pattern used when the returned object is new and has no
+     *  other references.
+     */
+    static SkPDFGraphicState* GetNoSMaskGraphicState();
+
+private:
+    const SkPaint fPaint;
+    SkTDArray<SkPDFObject*> fResources;
+    bool fPopulated;
+    bool fSMask;
+
+    class GSCanonicalEntry {
+    public:
+        SkPDFGraphicState* fGraphicState;
+        const SkPaint* fPaint;
+
+        bool operator==(const GSCanonicalEntry& b) const;
+        explicit GSCanonicalEntry(SkPDFGraphicState* gs)
+            : fGraphicState(gs),
+              fPaint(&gs->fPaint) {}
+        explicit GSCanonicalEntry(const SkPaint* paint)
+            : fGraphicState(NULL),
+              fPaint(paint) {}
+    };
+
+    // This should be made a hash table if performance is a problem.
+    static SkTDArray<GSCanonicalEntry>& CanonicalPaints();
+    static SkBaseMutex& CanonicalPaintsMutex();
+
+    SkPDFGraphicState();
+    explicit SkPDFGraphicState(const SkPaint& paint);
+
+    void populateDict();
+
+    static SkPDFObject* GetInvertFunction();
+
+    static int Find(const SkPaint& paint);
+};
+
+#endif
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp
new file mode 100644
index 0000000..429667a
--- /dev/null
+++ b/src/pdf/SkPDFImage.cpp
@@ -0,0 +1,357 @@
+
+/*
+ * Copyright 2010 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkPDFImage.h"
+
+#include "SkBitmap.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkPaint.h"
+#include "SkPackBits.h"
+#include "SkPDFCatalog.h"
+#include "SkRect.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkUnPreMultiply.h"
+
+namespace {
+
+void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect,
+                      SkStream** imageData, SkStream** alphaData) {
+    SkMemoryStream* image = NULL;
+    SkMemoryStream* alpha = NULL;
+    bool hasAlpha = false;
+    bool isTransparent = false;
+
+    bitmap.lockPixels();
+    switch (bitmap.getConfig()) {
+        case SkBitmap::kIndex8_Config: {
+            const int rowBytes = srcRect.width();
+            image = new SkMemoryStream(rowBytes * srcRect.height());
+            uint8_t* dst = (uint8_t*)image->getMemoryBase();
+            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+                memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes);
+                dst += rowBytes;
+            }
+            break;
+        }
+        case SkBitmap::kRLE_Index8_Config: {
+            const int rowBytes = srcRect.width();
+            image = new SkMemoryStream(rowBytes * srcRect.height());
+            uint8_t* dst = (uint8_t*)image->getMemoryBase();
+            const SkBitmap::RLEPixels* rle =
+                (const SkBitmap::RLEPixels*)bitmap.getPixels();
+            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+                SkPackBits::Unpack8(dst, srcRect.fLeft, rowBytes,
+                                    rle->packedAtY(y));
+                dst += rowBytes;
+            }
+            break;
+        }
+        case SkBitmap::kARGB_4444_Config: {
+            isTransparent = true;
+            const int rowBytes = (srcRect.width() * 3 + 1) / 2;
+            const int alphaRowBytes = (srcRect.width() + 1) / 2;
+            image = new SkMemoryStream(rowBytes * srcRect.height());
+            alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
+            uint8_t* dst = (uint8_t*)image->getMemoryBase();
+            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
+            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+                uint16_t* src = bitmap.getAddr16(0, y);
+                int x;
+                for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) {
+                    dst[0] = (SkGetPackedR4444(src[x]) << 4) |
+                        SkGetPackedG4444(src[x]);
+                    dst[1] = (SkGetPackedB4444(src[x]) << 4) |
+                        SkGetPackedR4444(src[x + 1]);
+                    dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) |
+                        SkGetPackedB4444(src[x + 1]);
+                    dst += 3;
+                    alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) |
+                        SkGetPackedA4444(src[x + 1]);
+                    if (alphaDst[0] != 0xFF) {
+                        hasAlpha = true;
+                    }
+                    if (alphaDst[0]) {
+                        isTransparent = false;
+                    }
+                    alphaDst++;
+                }
+                if (srcRect.width() & 1) {
+                    dst[0] = (SkGetPackedR4444(src[x]) << 4) |
+                        SkGetPackedG4444(src[x]);
+                    dst[1] = (SkGetPackedB4444(src[x]) << 4);
+                    dst += 2;
+                    alphaDst[0] = (SkGetPackedA4444(src[x]) << 4);
+                    if (alphaDst[0] != 0xF0) {
+                        hasAlpha = true;
+                    }
+                    if (alphaDst[0] & 0xF0) {
+                        isTransparent = false;
+                    }
+                    alphaDst++;
+                }
+            }
+            break;
+        }
+        case SkBitmap::kRGB_565_Config: {
+            const int rowBytes = srcRect.width() * 3;
+            image = new SkMemoryStream(rowBytes * srcRect.height());
+            uint8_t* dst = (uint8_t*)image->getMemoryBase();
+            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+                uint16_t* src = bitmap.getAddr16(0, y);
+                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
+                    dst[0] = SkGetPackedR16(src[x]);
+                    dst[1] = SkGetPackedG16(src[x]);
+                    dst[2] = SkGetPackedB16(src[x]);
+                    dst += 3;
+                }
+            }
+            break;
+        }
+        case SkBitmap::kARGB_8888_Config: {
+            isTransparent = true;
+            const int rowBytes = srcRect.width() * 3;
+            image = new SkMemoryStream(rowBytes * srcRect.height());
+            alpha = new SkMemoryStream(srcRect.width() * srcRect.height());
+            uint8_t* dst = (uint8_t*)image->getMemoryBase();
+            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
+            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+                uint32_t* src = bitmap.getAddr32(0, y);
+                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
+                    dst[0] = SkGetPackedR32(src[x]);
+                    dst[1] = SkGetPackedG32(src[x]);
+                    dst[2] = SkGetPackedB32(src[x]);
+                    dst += 3;
+                    alphaDst[0] = SkGetPackedA32(src[x]);
+                    if (alphaDst[0] != 0xFF) {
+                        hasAlpha = true;
+                    }
+                    if (alphaDst[0]) {
+                        isTransparent = false;
+                    }
+                    alphaDst++;
+                }
+            }
+            break;
+        }
+        case SkBitmap::kA1_Config: {
+            isTransparent = true;
+            image = new SkMemoryStream(1);
+            ((uint8_t*)image->getMemoryBase())[0] = 0;
+
+            const int alphaRowBytes = (srcRect.width() + 7) / 8;
+            alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
+            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
+            int offset1 = srcRect.fLeft % 8;
+            int offset2 = 8 - offset1;
+            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+                uint8_t* src = bitmap.getAddr1(0, y);
+                // This may read up to one byte after src, but the potentially
+                // invalid bits are never used for computation.
+                for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8)  {
+                    if (offset1) {
+                        alphaDst[0] = src[x / 8] << offset1 |
+                            src[x / 8 + 1] >> offset2;
+                    } else {
+                        alphaDst[0] = src[x / 8];
+                    }
+                    if (x + 7 < srcRect.fRight && alphaDst[0] != 0xFF) {
+                        hasAlpha = true;
+                    }
+                    if (x + 7 < srcRect.fRight && alphaDst[0]) {
+                        isTransparent = false;
+                    }
+                    alphaDst++;
+                }
+                // Calculate the mask of bits we're interested in within the
+                // last byte of alphaDst.
+                // width mod 8  == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE
+                uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1);
+                if (srcRect.width() % 8 && (alphaDst[-1] & mask) != mask) {
+                    hasAlpha = true;
+                }
+                if (srcRect.width() % 8 && (alphaDst[-1] & mask)) {
+                    isTransparent = false;
+                }
+            }
+            break;
+        }
+        case SkBitmap::kA8_Config: {
+            isTransparent = true;
+            image = new SkMemoryStream(1);
+            ((uint8_t*)image->getMemoryBase())[0] = 0;
+
+            const int alphaRowBytes = srcRect.width();
+            alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
+            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
+            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+                uint8_t* src = bitmap.getAddr8(0, y);
+                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
+                    alphaDst[0] = src[x];
+                    if (alphaDst[0] != 0xFF) {
+                        hasAlpha = true;
+                    }
+                    if (alphaDst[0]) {
+                        isTransparent = false;
+                    }
+                    alphaDst++;
+                }
+            }
+            break;
+        }
+        default:
+            SkASSERT(false);
+    }
+    bitmap.unlockPixels();
+
+    if (isTransparent) {
+        SkSafeUnref(image);
+    } else {
+        *imageData = image;
+    }
+
+    if (isTransparent || !hasAlpha) {
+        SkSafeUnref(alpha);
+    } else {
+        *alphaData = alpha;
+    }
+}
+
+SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
+    SkPDFArray* result = new SkPDFArray();
+    result->reserve(4);
+    result->appendName("Indexed");
+    result->appendName("DeviceRGB");
+    result->appendInt(table->count() - 1);
+
+    // Potentially, this could be represented in fewer bytes with a stream.
+    // Max size as a string is 1.5k.
+    SkString index;
+    for (int i = 0; i < table->count(); i++) {
+        char buf[3];
+        SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
+        buf[0] = SkGetPackedR32(color);
+        buf[1] = SkGetPackedG32(color);
+        buf[2] = SkGetPackedB32(color);
+        index.append(buf, 3);
+    }
+    result->append(new SkPDFString(index))->unref();
+    return result;
+}
+
+};  // namespace
+
+// static
+SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
+                                    const SkIRect& srcRect,
+                                    const SkPaint& paint) {
+    if (bitmap.getConfig() == SkBitmap::kNo_Config) {
+        return NULL;
+    }
+
+    SkStream* imageData = NULL;
+    SkStream* alphaData = NULL;
+    extractImageData(bitmap, srcRect, &imageData, &alphaData);
+    SkAutoUnref unrefImageData(imageData);
+    SkAutoUnref unrefAlphaData(alphaData);
+    if (!imageData) {
+        SkASSERT(!alphaData);
+        return NULL;
+    }
+
+    SkPDFImage* image =
+        new SkPDFImage(imageData, bitmap, srcRect, false, paint);
+
+    if (alphaData != NULL) {
+        image->addSMask(new SkPDFImage(alphaData, bitmap, srcRect, true,
+                                       paint))->unref();
+    }
+    return image;
+}
+
+SkPDFImage::~SkPDFImage() {
+    fResources.unrefAll();
+}
+
+SkPDFImage* SkPDFImage::addSMask(SkPDFImage* mask) {
+    fResources.push(mask);
+    mask->ref();
+    insert("SMask", new SkPDFObjRef(mask))->unref();
+    return mask;
+}
+
+void SkPDFImage::getResources(SkTDArray<SkPDFObject*>* resourceList) {
+    GetResourcesHelper(&fResources, resourceList);
+}
+
+SkPDFImage::SkPDFImage(SkStream* imageData, const SkBitmap& bitmap,
+                       const SkIRect& srcRect, bool doingAlpha,
+                       const SkPaint& paint) {
+    this->setData(imageData);
+    SkBitmap::Config config = bitmap.getConfig();
+    bool alphaOnly = (config == SkBitmap::kA1_Config ||
+                      config == SkBitmap::kA8_Config);
+
+    insertName("Type", "XObject");
+    insertName("Subtype", "Image");
+
+    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.
+        insert("Width", one.get());
+        insert("Height", one.get());
+    } else {
+        insertInt("Width", srcRect.width());
+        insertInt("Height", srcRect.height());
+    }
+
+    // if (!image mask) {
+    if (doingAlpha || alphaOnly) {
+        insertName("ColorSpace", "DeviceGray");
+    } else if (config == SkBitmap::kIndex8_Config ||
+        config == SkBitmap::kRLE_Index8_Config) {
+        insert("ColorSpace",
+               makeIndexedColorSpace(bitmap.getColorTable()))->unref();
+    } else {
+        insertName("ColorSpace", "DeviceRGB");
+    }
+    // }
+
+    int bitsPerComp = 8;
+    if (config == SkBitmap::kARGB_4444_Config) {
+        bitsPerComp = 4;
+    } else if (doingAlpha && config == SkBitmap::kA1_Config) {
+        bitsPerComp = 1;
+    }
+    insertInt("BitsPerComponent", bitsPerComp);
+
+    if (config == SkBitmap::kRGB_565_Config) {
+        SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
+        zeroVal->unref();  // SkRefPtr and new both took a reference.
+        SkRefPtr<SkPDFScalar> scale5Val =
+                new SkPDFScalar(SkFloatToScalar(8.2258f));  // 255/2^5-1
+        scale5Val->unref();  // SkRefPtr and new both took a reference.
+        SkRefPtr<SkPDFScalar> scale6Val =
+                new SkPDFScalar(SkFloatToScalar(4.0476f));  // 255/2^6-1
+        scale6Val->unref();  // SkRefPtr and new both took a reference.
+        SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
+        decodeValue->unref();  // SkRefPtr and new both took a reference.
+        decodeValue->reserve(6);
+        decodeValue->append(zeroVal.get());
+        decodeValue->append(scale5Val.get());
+        decodeValue->append(zeroVal.get());
+        decodeValue->append(scale6Val.get());
+        decodeValue->append(zeroVal.get());
+        decodeValue->append(scale5Val.get());
+        insert("Decode", decodeValue.get());
+    }
+}
diff --git a/src/pdf/SkPDFImage.h b/src/pdf/SkPDFImage.h
new file mode 100644
index 0000000..48b0157
--- /dev/null
+++ b/src/pdf/SkPDFImage.h
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright 2010 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkPDFImage_DEFINED
+#define SkPDFImage_DEFINED
+
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+
+class SkBitmap;
+class SkPaint;
+class SkPDFCatalog;
+struct SkIRect;
+
+/** \class SkPDFImage
+
+    An image XObject.
+*/
+
+// We could play the same trick here as is done in SkPDFGraphicState, storing
+// a copy of the Bitmap object (not the pixels), the pixel generation number,
+// and settings used from the paint to canonicalize image objects.
+class SkPDFImage : public SkPDFStream {
+public:
+    /** Create a new Image XObject to represent the passed bitmap.
+     *  @param bitmap   The image to encode.
+     *  @param srcRect  The rectangle to cut out of bitmap.
+     *  @param paint    Used to calculate alpha, masks, etc.
+     *  @return  The image XObject or NUll if there is nothing to draw for
+     *           the given parameters.
+     */
+    static SkPDFImage* CreateImage(const SkBitmap& bitmap,
+                                   const SkIRect& srcRect,
+                                   const SkPaint& paint);
+
+    virtual ~SkPDFImage();
+
+    /** Add a Soft Mask (alpha or shape channel) to the image.  Refs mask.
+     *  @param mask A gray scale image representing the mask.
+     *  @return The mask argument is returned.
+     */
+    SkPDFImage* addSMask(SkPDFImage* mask);
+
+    // The SkPDFObject interface.
+    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+private:
+    SkTDArray<SkPDFObject*> fResources;
+
+    /** Create a PDF image XObject. Entries for the image properties are
+     *  automatically added to the stream dictionary.
+     *  @param imageData  The final raw bits representing the image.
+     *  @param bitmap     The image parameters to use (Config, etc).
+     *  @param srcRect    The clipping applied to bitmap before generating
+     *                    imageData.
+     *  @param alpha      Is this the alpha channel of the bitmap.
+     *  @param paint      Used to calculate alpha, masks, etc.
+     */
+    SkPDFImage(SkStream* imageData, const SkBitmap& bitmap,
+               const SkIRect& srcRect, bool alpha, const SkPaint& paint);
+};
+
+#endif
diff --git a/src/pdf/SkPDFPage.cpp b/src/pdf/SkPDFPage.cpp
new file mode 100644
index 0000000..717f435
--- /dev/null
+++ b/src/pdf/SkPDFPage.cpp
@@ -0,0 +1,148 @@
+
+/*
+ * Copyright 2010 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
+#include "SkPDFPage.h"
+#include "SkStream.h"
+
+SkPDFPage::SkPDFPage(SkPDFDevice* content)
+    : SkPDFDict("Page"),
+      fDevice(content) {
+}
+
+SkPDFPage::~SkPDFPage() {}
+
+void SkPDFPage::finalizePage(SkPDFCatalog* catalog, bool firstPage,
+                             SkTDArray<SkPDFObject*>* resourceObjects) {
+    if (fContentStream.get() == NULL) {
+        insert("Resources", fDevice->getResourceDict());
+        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.
+        insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref();
+    }
+    catalog->addObject(fContentStream.get(), firstPage);
+    fDevice->getResources(resourceObjects, true);
+}
+
+off_t SkPDFPage::getPageSize(SkPDFCatalog* catalog, off_t fileOffset) {
+    SkASSERT(fContentStream.get() != NULL);
+    catalog->setFileOffset(fContentStream.get(), fileOffset);
+    return fContentStream->getOutputSize(catalog, true);
+}
+
+void SkPDFPage::emitPage(SkWStream* stream, SkPDFCatalog* catalog) {
+    SkASSERT(fContentStream.get() != NULL);
+    fContentStream->emitObject(stream, catalog, true);
+}
+
+// static
+void SkPDFPage::GeneratePageTree(const SkTDArray<SkPDFPage*>& pages,
+                                 SkPDFCatalog* catalog,
+                                 SkTDArray<SkPDFDict*>* pageTree,
+                                 SkPDFDict** rootNode) {
+    // PDF wants a tree describing all the pages in the document.  We arbitrary
+    // choose 8 (kNodeSize) as the number of allowed children.  The internal
+    // nodes have type "Pages" with an array of children, a parent pointer, and
+    // the number of leaves below the node as "Count."  The leaves are passed
+    // into the method, have type "Page" and need a parent pointer. This method
+    // builds the tree bottom up, skipping internal nodes that would have only
+    // one child.
+    static const int kNodeSize = 8;
+
+    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.
+
+    // curNodes takes a reference to its items, which it passes to pageTree.
+    SkTDArray<SkPDFDict*> curNodes;
+    curNodes.setReserve(pages.count());
+    for (int i = 0; i < pages.count(); i++) {
+        SkSafeRef(pages[i]);
+        curNodes.push(pages[i]);
+    }
+
+    // nextRoundNodes passes its references to nodes on to curNodes.
+    SkTDArray<SkPDFDict*> nextRoundNodes;
+    nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
+
+    int treeCapacity = kNodeSize;
+    do {
+        for (int i = 0; i < curNodes.count(); ) {
+            if (i > 0 && i + 1 == curNodes.count()) {
+                nextRoundNodes.push(curNodes[i]);
+                break;
+            }
+
+            SkPDFDict* newNode = new SkPDFDict("Pages");
+            SkRefPtr<SkPDFObjRef> newNodeRef = new SkPDFObjRef(newNode);
+            newNodeRef->unref();  // SkRefPtr and new both took a reference.
+
+            SkRefPtr<SkPDFArray> kids = new SkPDFArray;
+            kids->unref();  // SkRefPtr and new both took a reference.
+            kids->reserve(kNodeSize);
+
+            int count = 0;
+            for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
+                curNodes[i]->insert(parentName.get(), newNodeRef.get());
+                kids->append(new SkPDFObjRef(curNodes[i]))->unref();
+
+                // TODO(vandebo): put the objects in strict access order.
+                // Probably doesn't matter because they are so small.
+                if (curNodes[i] != pages[0]) {
+                    pageTree->push(curNodes[i]);  // Transfer reference.
+                    catalog->addObject(curNodes[i], false);
+                } else {
+                    SkSafeUnref(curNodes[i]);
+                    catalog->addObject(curNodes[i], true);
+                }
+            }
+
+            newNode->insert(kidsName.get(), kids.get());
+            int pageCount = treeCapacity;
+            if (count < kNodeSize) {
+                pageCount = pages.count() % treeCapacity;
+            }
+            newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref();
+            nextRoundNodes.push(newNode);  // Transfer reference.
+        }
+
+        curNodes = nextRoundNodes;
+        nextRoundNodes.rewind();
+        treeCapacity *= kNodeSize;
+    } while (curNodes.count() > 1);
+
+    pageTree->push(curNodes[0]);  // Transfer reference.
+    catalog->addObject(curNodes[0], false);
+    if (rootNode) {
+        *rootNode = curNodes[0];
+    }
+}
+
+const SkTDArray<SkPDFFont*>& SkPDFPage::getFontResources() const {
+    return fDevice->getFontResources();
+}
+
+const SkPDFGlyphSetMap& SkPDFPage::getFontGlyphUsage() const {
+    return fDevice->getFontGlyphUsage();
+}
diff --git a/src/pdf/SkPDFPage.h b/src/pdf/SkPDFPage.h
new file mode 100644
index 0000000..8ef909e
--- /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.
+    SkRefPtr<SkPDFDevice> fDevice;
+
+    // Once the content is finalized, put it into a stream for output.
+    SkRefPtr<SkPDFStream> fContentStream;
+};
+
+#endif
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
new file mode 100644
index 0000000..e6f1d7f
--- /dev/null
+++ b/src/pdf/SkPDFShader.cpp
@@ -0,0 +1,962 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkPDFShader.h"
+
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
+#include "SkPDFTypes.h"
+#include "SkPDFUtils.h"
+#include "SkScalar.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkThread.h"
+#include "SkTypes.h"
+
+static bool transformBBox(const SkMatrix& matrix, SkRect* bbox) {
+    SkMatrix inverse;
+    if (!matrix.invert(&inverse)) {
+        return false;
+    }
+    inverse.mapRect(bbox);
+    return true;
+}
+
+static void unitToPointsMatrix(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);
+    matrix->preTranslate(pts[0].fX, pts[0].fY);
+    matrix->preScale(mag, mag);
+}
+
+/* Assumes t + startOffset is on the stack and does a linear interpolation on t
+   between startOffset and endOffset from prevColor to curColor (for each color
+   component), leaving the result in component order on the stack.
+   @param range                  endOffset - startOffset
+   @param curColor[components]   The current color components.
+   @param prevColor[components]  The previous color components.
+   @param result                 The result ps function.
+ */
+static void interpolateColorCode(SkScalar range, SkScalar* curColor,
+                                 SkScalar* prevColor, int components,
+                                 SkString* result) {
+    // Figure out how to scale each color component.
+    SkAutoSTMalloc<4, SkScalar> multiplierAlloc(components);
+    SkScalar *multiplier = multiplierAlloc.get();
+    for (int i = 0; i < components; i++) {
+        multiplier[i] = SkScalarDiv(curColor[i] - prevColor[i], range);
+    }
+
+    // Calculate when we no longer need to keep a copy of the input parameter t.
+    // If the last component to use t is i, then dupInput[0..i - 1] = true
+    // and dupInput[i .. components] = false.
+    SkAutoSTMalloc<4, bool> dupInputAlloc(components);
+    bool *dupInput = dupInputAlloc.get();
+    dupInput[components - 1] = false;
+    for (int i = components - 2; i >= 0; i--) {
+        dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
+    }
+
+    if (!dupInput[0] && multiplier[0] == 0) {
+        result->append("pop ");
+    }
+
+    for (int i = 0; i < components; i++) {
+        // If the next components needs t, make a copy.
+        if (dupInput[i]) {
+            result->append("dup ");
+        }
+
+        if (multiplier[i] == 0) {
+            result->appendScalar(prevColor[i]);
+            result->append(" ");
+        } else {
+            if (multiplier[i] != 1) {
+                result->appendScalar(multiplier[i]);
+                result->append(" mul ");
+            }
+            if (prevColor[i] != 0) {
+                result->appendScalar(prevColor[i]);
+                result->append(" add ");
+            }
+        }
+
+        if (dupInput[i]) {
+            result->append("exch\n");
+        }
+    }
+}
+
+/* Generate Type 4 function code to map t=[0,1) to the passed gradient,
+   clamping at the edges of the range.  The generated code will be of the form:
+       if (t < 0) {
+           return colorData[0][r,g,b];
+       } else {
+           if (t < info.fColorOffsets[1]) {
+               return linearinterpolation(colorData[0][r,g,b],
+                                          colorData[1][r,g,b]);
+           } else {
+               if (t < info.fColorOffsets[2]) {
+                   return linearinterpolation(colorData[1][r,g,b],
+                                              colorData[2][r,g,b]);
+               } else {
+
+                ...    } else {
+                           return colorData[info.fColorCount - 1][r,g,b];
+                       }
+                ...
+           }
+       }
+ */
+static void gradientFunctionCode(const SkShader::GradientInfo& info,
+                                 SkString* result) {
+    /* We want to linearly interpolate from the previous color to the next.
+       Scale the colors from 0..255 to 0..1 and determine the multipliers
+       for interpolation.
+       C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
+     */
+    static const int kColorComponents = 3;
+    typedef SkScalar ColorTuple[kColorComponents];
+    SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
+    ColorTuple *colorData = colorDataAlloc.get();
+    const SkScalar scale = SkScalarInvert(SkIntToScalar(255));
+    for (int i = 0; i < info.fColorCount; i++) {
+        colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale);
+        colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale);
+        colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale);
+    }
+
+    // Clamp the initial color.
+    result->append("dup 0 le {pop ");
+    result->appendScalar(colorData[0][0]);
+    result->append(" ");
+    result->appendScalar(colorData[0][1]);
+    result->append(" ");
+    result->appendScalar(colorData[0][2]);
+    result->append(" }\n");
+
+    // The gradient colors.
+    for (int i = 1 ; i < info.fColorCount; i++) {
+        result->append("{dup ");
+        result->appendScalar(info.fColorOffsets[i]);
+        result->append(" le {");
+        if (info.fColorOffsets[i - 1] != 0) {
+            result->appendScalar(info.fColorOffsets[i - 1]);
+            result->append(" sub\n");
+        }
+
+        interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1],
+                             colorData[i], colorData[i - 1], kColorComponents,
+                             result);
+        result->append("}\n");
+    }
+
+    // Clamp the final color.
+    result->append("{pop ");
+    result->appendScalar(colorData[info.fColorCount - 1][0]);
+    result->append(" ");
+    result->appendScalar(colorData[info.fColorCount - 1][1]);
+    result->append(" ");
+    result->appendScalar(colorData[info.fColorCount - 1][2]);
+
+    for (int i = 0 ; i < info.fColorCount; i++) {
+        result->append("} ifelse\n");
+    }
+}
+
+/* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
+static void tileModeCode(SkShader::TileMode mode, SkString* result) {
+    if (mode == SkShader::kRepeat_TileMode) {
+        result->append("dup truncate sub\n");  // Get the fractional part.
+        result->append("dup 0 le {1 add} if\n");  // Map (-1,0) => (0,1)
+        return;
+    }
+
+    if (mode == SkShader::kMirror_TileMode) {
+        // Map t mod 2 into [0, 1, 1, 0].
+        //               Code                     Stack
+        result->append("abs "                 // Map negative to positive.
+                       "dup "                 // t.s t.s
+                       "truncate "            // t.s t
+                       "dup "                 // t.s t t
+                       "cvi "                 // t.s t T
+                       "2 mod "               // t.s t (i mod 2)
+                       "1 eq "                // t.s t true|false
+                       "3 1 roll "            // true|false t.s t
+                       "sub "                 // true|false 0.s
+                       "exch "                // 0.s true|false
+                       "{1 exch sub} if\n");  // 1 - 0.s|0.s
+    }
+}
+
+static SkString linearCode(const SkShader::GradientInfo& info) {
+    SkString function("{pop\n");  // Just ditch the y value.
+    tileModeCode(info.fTileMode, &function);
+    gradientFunctionCode(info, &function);
+    function.append("}");
+    return function;
+}
+
+static SkString radialCode(const SkShader::GradientInfo& info) {
+    SkString function("{");
+    // Find the distance from the origin.
+    function.append("dup "      // x y y
+                    "mul "      // x y^2
+                    "exch "     // y^2 x
+                    "dup "      // y^2 x x
+                    "mul "      // y^2 x^2
+                    "add "      // y^2+x^2
+                    "sqrt\n");  // sqrt(y^2+x^2)
+
+    tileModeCode(info.fTileMode, &function);
+    gradientFunctionCode(info, &function);
+    function.append("}");
+    return function;
+}
+
+/* The math here is all based on the description in Two_Point_Radial_Gradient,
+   with one simplification, the coordinate space has been scaled so that
+   Dr = 1.  This means we don't need to scale the entire equation by 1/Dr^2.
+ */
+static SkString twoPointRadialCode(const SkShader::GradientInfo& info) {
+    SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX;
+    SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY;
+    SkScalar sr = info.fRadius[0];
+    SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1;
+    bool posRoot = info.fRadius[1] > info.fRadius[0];
+
+    // We start with a stack of (x y), copy it and then consume one copy in
+    // order to calculate b and the other to calculate c.
+    SkString function("{");
+    function.append("2 copy ");
+
+    // Calculate -b and b^2.
+    function.appendScalar(dy);
+    function.append(" mul exch ");
+    function.appendScalar(dx);
+    function.append(" mul add ");
+    function.appendScalar(sr);
+    function.append(" sub 2 mul neg dup dup mul\n");
+
+    // Calculate c
+    function.append("4 2 roll dup mul exch dup mul add ");
+    function.appendScalar(SkScalarMul(sr, sr));
+    function.append(" sub\n");
+
+    // Calculate the determinate
+    function.appendScalar(SkScalarMul(SkIntToScalar(4), a));
+    function.append(" mul sub abs sqrt\n");
+
+    // And then the final value of t.
+    if (posRoot) {
+        function.append("sub ");
+    } else {
+        function.append("add ");
+    }
+    function.appendScalar(SkScalarMul(SkIntToScalar(2), a));
+    function.append(" div\n");
+
+    tileModeCode(info.fTileMode, &function);
+    gradientFunctionCode(info, &function);
+    function.append("}");
+    return function;
+}
+
+/* Conical gradient shader, based on the Canvas spec for radial gradients
+   See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
+ */
+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);
+    gradientFunctionCode(info, &function);
+    function.append("}");
+    return function;
+}
+
+class SkPDFShader::State {
+public:
+    SkShader::GradientType fType;
+    SkShader::GradientInfo fInfo;
+    SkAutoFree fColorData;
+    SkMatrix fCanvasTransform;
+    SkMatrix fShaderTransform;
+    SkIRect fBBox;
+
+    SkBitmap fImage;
+    uint32_t fPixelGeneration;
+    SkShader::TileMode fImageTileModes[2];
+
+    explicit State(const SkShader& shader, const SkMatrix& canvasTransform,
+                   const SkIRect& bbox);
+    bool operator==(const State& b) const;
+};
+
+class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader {
+public:
+    explicit SkPDFFunctionShader(SkPDFShader::State* state);
+    virtual ~SkPDFFunctionShader() {
+        if (isValid()) {
+            RemoveShader(this);
+        }
+        fResources.unrefAll();
+    }
+
+    virtual bool isValid() { return fResources.count() > 0; }
+
+    void getResources(SkTDArray<SkPDFObject*>* resourceList) {
+        GetResourcesHelper(&fResources, resourceList);
+    }
+
+private:
+    static SkPDFObject* RangeObject();
+
+    SkTDArray<SkPDFObject*> fResources;
+    SkAutoTDelete<const SkPDFShader::State> fState;
+
+    SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain);
+};
+
+class SkPDFImageShader : public SkPDFStream, public SkPDFShader {
+public:
+    explicit SkPDFImageShader(SkPDFShader::State* state);
+    virtual ~SkPDFImageShader() {
+        RemoveShader(this);
+        fResources.unrefAll();
+    }
+
+    virtual bool isValid() { return size() > 0; }
+
+    void getResources(SkTDArray<SkPDFObject*>* resourceList) {
+        GetResourcesHelper(&fResources, resourceList);
+    }
+
+private:
+    SkTDArray<SkPDFObject*> fResources;
+    SkAutoTDelete<const SkPDFShader::State> fState;
+};
+
+SkPDFShader::SkPDFShader() {}
+
+// static
+void SkPDFShader::RemoveShader(SkPDFObject* shader) {
+    SkAutoMutexAcquire lock(CanonicalShadersMutex());
+    ShaderCanonicalEntry entry(shader, NULL);
+    int index = CanonicalShaders().find(entry);
+    SkASSERT(index >= 0);
+    CanonicalShaders().removeShuffle(index);
+}
+
+// static
+SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader,
+                                       const SkMatrix& matrix,
+                                       const SkIRect& surfaceBBox) {
+    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);
+    if (index >= 0) {
+        result = CanonicalShaders()[index].fPDFShader;
+        result->ref();
+        return result;
+    }
+
+    bool valid = false;
+    // The PDFShader takes ownership of the shaderSate.
+    if (shaderState.get()->fType == SkShader::kNone_GradientType) {
+        SkPDFImageShader* imageShader =
+            new SkPDFImageShader(shaderState.detach());
+        valid = imageShader->isValid();
+        result = imageShader;
+    } else {
+        SkPDFFunctionShader* functionShader =
+            new SkPDFFunctionShader(shaderState.detach());
+        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.
+}
+
+// static
+SkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::CanonicalShaders() {
+    // This initialization is only thread safe with gcc.
+    static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders;
+    return gCanonicalShaders;
+}
+
+// static
+SkBaseMutex& SkPDFShader::CanonicalShadersMutex() {
+    // This initialization is only thread safe with gcc or when
+    // POD-style mutex initialization is used.
+    SK_DECLARE_STATIC_MUTEX(gCanonicalShadersMutex);
+    return gCanonicalShadersMutex;
+}
+
+// static
+SkPDFObject* SkPDFFunctionShader::RangeObject() {
+    // This initialization is only thread safe with gcc.
+    static SkPDFArray* range = NULL;
+    // This method is only used with CanonicalShadersMutex, so it's safe to
+    // populate domain.
+    if (range == NULL) {
+        range = new SkPDFArray;
+        range->reserve(6);
+        range->appendInt(0);
+        range->appendInt(1);
+        range->appendInt(0);
+        range->appendInt(1);
+        range->appendInt(0);
+        range->appendInt(1);
+    }
+    return range;
+}
+
+SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
+        : SkPDFDict("Pattern"),
+          fState(state) {
+    SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL;
+    SkPoint transformPoints[2];
+
+    // Depending on the type of the gradient, we want to transform the
+    // coordinate space in different ways.
+    const SkShader::GradientInfo* info = &fState.get()->fInfo;
+    transformPoints[0] = info->fPoint[0];
+    transformPoints[1] = info->fPoint[1];
+    switch (fState.get()->fType) {
+        case SkShader::kLinear_GradientType:
+            codeFunction = &linearCode;
+            break;
+        case SkShader::kRadial_GradientType:
+            transformPoints[1] = transformPoints[0];
+            transformPoints[1].fX += info->fRadius[0];
+            codeFunction = &radialCode;
+            break;
+        case SkShader::kRadial2_GradientType: {
+            // Bail out if the radii are the same.  Empty fResources signals
+            // an error and isValid will return false.
+            if (info->fRadius[0] == info->fRadius[1]) {
+                return;
+            }
+            transformPoints[1] = transformPoints[0];
+            SkScalar dr = info->fRadius[1] - info->fRadius[0];
+            transformPoints[1].fX += dr;
+            codeFunction = &twoPointRadialCode;
+            break;
+        }
+        case SkShader::kConical_GradientType: {
+            transformPoints[1] = transformPoints[0];
+            transformPoints[1].fX += SK_Scalar1;
+            codeFunction = &twoPointConicalCode;
+            break;
+        }
+        case SkShader::kSweep_GradientType:
+            transformPoints[1] = transformPoints[0];
+            transformPoints[1].fX += SK_Scalar1;
+            codeFunction = &sweepCode;
+            break;
+        case SkShader::kColor_GradientType:
+        case SkShader::kNone_GradientType:
+        default:
+            return;
+    }
+
+    // Move any scaling (assuming a unit gradient) or translation
+    // (and rotation for linear gradient), of the final gradient from
+    // info->fPoints to the matrix (updating bbox appropriately).  Now
+    // the gradient can be drawn on on the unit segment.
+    SkMatrix mapperMatrix;
+    unitToPointsMatrix(transformPoints, &mapperMatrix);
+    SkMatrix finalMatrix = fState.get()->fCanvasTransform;
+    finalMatrix.preConcat(mapperMatrix);
+    finalMatrix.preConcat(fState.get()->fShaderTransform);
+    SkRect bbox;
+    bbox.set(fState.get()->fBBox);
+    if (!transformBBox(finalMatrix, &bbox)) {
+        return;
+    }
+
+    SkRefPtr<SkPDFArray> domain = new SkPDFArray;
+    domain->unref();  // SkRefPtr and new both took a reference.
+    domain->reserve(4);
+    domain->appendScalar(bbox.fLeft);
+    domain->appendScalar(bbox.fRight);
+    domain->appendScalar(bbox.fTop);
+    domain->appendScalar(bbox.fBottom);
+
+    SkString functionCode;
+    // The two point radial gradient further references fState.get()->fInfo
+    // in translating from x, y coordinates to the t parameter. So, we have
+    // to transform the points and radii according to the calculated matrix.
+    if (fState.get()->fType == SkShader::kRadial2_GradientType) {
+        SkShader::GradientInfo twoPointRadialInfo = *info;
+        SkMatrix inverseMapperMatrix;
+        if (!mapperMatrix.invert(&inverseMapperMatrix)) {
+            return;
+        }
+        inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
+        twoPointRadialInfo.fRadius[0] =
+            inverseMapperMatrix.mapRadius(info->fRadius[0]);
+        twoPointRadialInfo.fRadius[1] =
+            inverseMapperMatrix.mapRadius(info->fRadius[1]);
+        functionCode = codeFunction(twoPointRadialInfo);
+    } else {
+        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.
+    pdfShader->insertInt("ShadingType", 1);
+    pdfShader->insertName("ColorSpace", "DeviceRGB");
+    pdfShader->insert("Domain", domain.get());
+    pdfShader->insert("Function", new SkPDFObjRef(function.get()))->unref();
+
+    insertInt("PatternType", 2);
+    insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
+    insert("Shading", pdfShader.get());
+}
+
+SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) {
+    fState.get()->fImage.lockPixels();
+
+    SkMatrix finalMatrix = fState.get()->fCanvasTransform;
+    finalMatrix.preConcat(fState.get()->fShaderTransform);
+    SkRect surfaceBBox;
+    surfaceBBox.set(fState.get()->fBBox);
+    if (!transformBBox(finalMatrix, &surfaceBBox)) {
+        return;
+    }
+
+    SkMatrix unflip;
+    unflip.setTranslate(0, SkScalarRoundToScalar(surfaceBBox.height()));
+    unflip.preScale(SK_Scalar1, -SK_Scalar1);
+    SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()),
+                                 SkScalarRound(surfaceBBox.height()));
+    SkPDFDevice pattern(size, size, unflip);
+    SkCanvas canvas(&pattern);
+    canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop);
+    finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop);
+
+    const SkBitmap* image = &fState.get()->fImage;
+    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];
+
+    canvas.drawBitmap(*image, 0, 0);
+    SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop,
+                                          width, height);
+
+    // Tiling is implied.  First we handle mirroring.
+    if (tileModes[0] == SkShader::kMirror_TileMode) {
+        SkMatrix xMirror;
+        xMirror.setScale(-1, 1);
+        xMirror.postTranslate(2 * width, 0);
+        canvas.drawBitmapMatrix(*image, xMirror);
+        patternBBox.fRight += width;
+    }
+    if (tileModes[1] == SkShader::kMirror_TileMode) {
+        SkMatrix yMirror;
+        yMirror.setScale(SK_Scalar1, -SK_Scalar1);
+        yMirror.postTranslate(0, 2 * height);
+        canvas.drawBitmapMatrix(*image, yMirror);
+        patternBBox.fBottom += height;
+    }
+    if (tileModes[0] == SkShader::kMirror_TileMode &&
+            tileModes[1] == SkShader::kMirror_TileMode) {
+        SkMatrix mirror;
+        mirror.setScale(-1, -1);
+        mirror.postTranslate(2 * width, 2 * height);
+        canvas.drawBitmapMatrix(*image, mirror);
+    }
+
+    // Then handle Clamping, which requires expanding the pattern canvas to
+    // cover the entire surfaceBBox.
+
+    // If both x and y are in clamp mode, we start by filling in the corners.
+    // (Which are just a rectangles of the corner colors.)
+    if (tileModes[0] == SkShader::kClamp_TileMode &&
+            tileModes[1] == SkShader::kClamp_TileMode) {
+        SkPaint paint;
+        SkRect rect;
+        rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0);
+        if (!rect.isEmpty()) {
+            paint.setColor(image->getColor(0, 0));
+            canvas.drawRect(rect, paint);
+        }
+
+        rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0);
+        if (!rect.isEmpty()) {
+            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(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, 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, image->height());
+        if (surfaceBBox.fLeft < 0) {
+            SkBitmap left;
+            SkAssertResult(image->extractSubset(&left, subset));
+
+            SkMatrix leftMatrix;
+            leftMatrix.setScale(-surfaceBBox.fLeft, 1);
+            leftMatrix.postTranslate(surfaceBBox.fLeft, 0);
+            canvas.drawBitmapMatrix(left, leftMatrix);
+
+            if (tileModes[1] == SkShader::kMirror_TileMode) {
+                leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
+                leftMatrix.postTranslate(0, 2 * height);
+                canvas.drawBitmapMatrix(left, leftMatrix);
+            }
+            patternBBox.fLeft = 0;
+        }
+
+        if (surfaceBBox.fRight > width) {
+            SkBitmap right;
+            subset.offset(image->width() - 1, 0);
+            SkAssertResult(image->extractSubset(&right, subset));
+
+            SkMatrix rightMatrix;
+            rightMatrix.setScale(surfaceBBox.fRight - width, 1);
+            rightMatrix.postTranslate(width, 0);
+            canvas.drawBitmapMatrix(right, rightMatrix);
+
+            if (tileModes[1] == SkShader::kMirror_TileMode) {
+                rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
+                rightMatrix.postTranslate(0, 2 * height);
+                canvas.drawBitmapMatrix(right, rightMatrix);
+            }
+            patternBBox.fRight = surfaceBBox.width();
+        }
+    }
+
+    if (tileModes[1] == SkShader::kClamp_TileMode) {
+        SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1);
+        if (surfaceBBox.fTop < 0) {
+            SkBitmap top;
+            SkAssertResult(image->extractSubset(&top, subset));
+
+            SkMatrix topMatrix;
+            topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop);
+            topMatrix.postTranslate(0, surfaceBBox.fTop);
+            canvas.drawBitmapMatrix(top, topMatrix);
+
+            if (tileModes[0] == SkShader::kMirror_TileMode) {
+                topMatrix.postScale(-1, 1);
+                topMatrix.postTranslate(2 * width, 0);
+                canvas.drawBitmapMatrix(top, topMatrix);
+            }
+            patternBBox.fTop = 0;
+        }
+
+        if (surfaceBBox.fBottom > height) {
+            SkBitmap bottom;
+            subset.offset(0, image->height() - 1);
+            SkAssertResult(image->extractSubset(&bottom, subset));
+
+            SkMatrix bottomMatrix;
+            bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height);
+            bottomMatrix.postTranslate(0, height);
+            canvas.drawBitmapMatrix(bottom, bottomMatrix);
+
+            if (tileModes[0] == SkShader::kMirror_TileMode) {
+                bottomMatrix.postScale(-1, 1);
+                bottomMatrix.postTranslate(2 * width, 0);
+                canvas.drawBitmapMatrix(bottom, bottomMatrix);
+            }
+            patternBBox.fBottom = surfaceBBox.height();
+        }
+    }
+
+    SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray;
+    patternBBoxArray->unref();  // SkRefPtr and new both took a reference.
+    patternBBoxArray->reserve(4);
+    patternBBoxArray->appendScalar(patternBBox.fLeft);
+    patternBBoxArray->appendScalar(patternBBox.fTop);
+    patternBBoxArray->appendScalar(patternBBox.fRight);
+    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, false);
+
+    setData(content.get());
+    insertName("Type", "Pattern");
+    insertInt("PatternType", 1);
+    insertInt("PaintType", 1);
+    insertInt("TilingType", 1);
+    insert("BBox", patternBBoxArray.get());
+    insertScalar("XStep", patternBBox.width());
+    insertScalar("YStep", patternBBox.height());
+    insert("Resources", pattern.getResourceDict());
+    insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
+
+    fState.get()->fImage.unlockPixels();
+}
+
+SkPDFStream* SkPDFFunctionShader::makePSFunction(const SkString& psCode,
+                                                 SkPDFArray* domain) {
+    SkAutoDataUnref funcData(SkData::NewWithCopy(psCode.c_str(),
+                                                 psCode.size()));
+    SkPDFStream* result = new SkPDFStream(funcData.get());
+    result->insertInt("FunctionType", 4);
+    result->insert("Domain", domain);
+    result->insert("Range", RangeObject());
+    return result;
+}
+
+SkPDFShader::ShaderCanonicalEntry::ShaderCanonicalEntry(SkPDFObject* pdfShader,
+                                                        const State* state)
+    : fPDFShader(pdfShader),
+      fState(state) {
+}
+
+bool SkPDFShader::ShaderCanonicalEntry::operator==(
+        const ShaderCanonicalEntry& b) const {
+    return fPDFShader == b.fPDFShader ||
+           (fState != NULL && b.fState != NULL && *fState == *b.fState);
+}
+
+bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const {
+    if (fType != b.fType ||
+            fCanvasTransform != b.fCanvasTransform ||
+            fShaderTransform != b.fShaderTransform ||
+            fBBox != b.fBBox) {
+        return false;
+    }
+
+    if (fType == SkShader::kNone_GradientType) {
+        if (fPixelGeneration != b.fPixelGeneration ||
+                fPixelGeneration == 0 ||
+                fImageTileModes[0] != b.fImageTileModes[0] ||
+                fImageTileModes[1] != b.fImageTileModes[1]) {
+            return false;
+        }
+    } else {
+        if (fInfo.fColorCount != b.fInfo.fColorCount ||
+                memcmp(fInfo.fColors, b.fInfo.fColors,
+                       sizeof(SkColor) * fInfo.fColorCount) != 0 ||
+                memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets,
+                       sizeof(SkScalar) * fInfo.fColorCount) != 0 ||
+                fInfo.fPoint[0] != b.fInfo.fPoint[0] ||
+                fInfo.fTileMode != b.fInfo.fTileMode) {
+            return false;
+        }
+
+        switch (fType) {
+            case SkShader::kLinear_GradientType:
+                if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) {
+                    return false;
+                }
+                break;
+            case SkShader::kRadial_GradientType:
+                if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) {
+                    return false;
+                }
+                break;
+            case SkShader::kRadial2_GradientType:
+            case SkShader::kConical_GradientType:
+                if (fInfo.fPoint[1] != b.fInfo.fPoint[1] ||
+                        fInfo.fRadius[0] != b.fInfo.fRadius[0] ||
+                        fInfo.fRadius[1] != b.fInfo.fRadius[1]) {
+                    return false;
+                }
+                break;
+            case SkShader::kSweep_GradientType:
+            case SkShader::kNone_GradientType:
+            case SkShader::kColor_GradientType:
+                break;
+        }
+    }
+    return true;
+}
+
+SkPDFShader::State::State(const SkShader& shader,
+                          const SkMatrix& canvasTransform, const SkIRect& bbox)
+        : fCanvasTransform(canvasTransform),
+          fBBox(bbox),
+          fPixelGeneration(0) {
+    fInfo.fColorCount = 0;
+    fInfo.fColors = NULL;
+    fInfo.fColorOffsets = NULL;
+    shader.getLocalMatrix(&fShaderTransform);
+    fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode;
+
+    fType = shader.asAGradient(&fInfo);
+
+    if (fType == SkShader::kNone_GradientType) {
+        SkShader::BitmapType bitmapType;
+        SkMatrix matrix;
+        bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes);
+        if (bitmapType != SkShader::kDefault_BitmapType) {
+            fImage.reset();
+            return;
+        }
+        SkASSERT(matrix.isIdentity());
+        fPixelGeneration = fImage.getGenerationID();
+    } else {
+        fColorData.set(sk_malloc_throw(
+                    fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar))));
+        fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get());
+        fInfo.fColorOffsets =
+            reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount);
+        shader.asAGradient(&fInfo);
+    }
+}
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
new file mode 100644
index 0000000..d113a0b
--- /dev/null
+++ b/src/pdf/SkPDFStream.cpp
@@ -0,0 +1,118 @@
+
+/*
+ * 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 "SkData.h"
+#include "SkFlate.h"
+#include "SkPDFCatalog.h"
+#include "SkPDFStream.h"
+#include "SkStream.h"
+
+static bool skip_compression(SkPDFCatalog* catalog) {
+    return SkToBool(catalog->getDocumentFlags() &
+                    SkPDFDocument::kNoCompression_Flags);
+}
+
+SkPDFStream::SkPDFStream(SkStream* stream)
+    : fState(kUnused_State),
+      fData(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.
+}
+
+SkPDFStream::SkPDFStream(const SkPDFStream& pdfStream)
+        : SkPDFDict(),
+          fState(kUnused_State),
+          fData(pdfStream.fData) {
+    bool removeLength = true;
+    // Don't uncompress an already compressed stream, but we could.
+    if (pdfStream.fState == kCompressed_State) {
+        fState = kCompressed_State;
+        removeLength = false;
+    }
+    SkPDFDict::Iter dict(pdfStream);
+    SkPDFName* key;
+    SkPDFObject* value;
+    SkPDFName lengthName("Length");
+    for (key = dict.next(&value); key != NULL; key = dict.next(&value)) {
+        if (removeLength && *key == lengthName) {
+            continue;
+        }
+        this->insert(key, value);
+    }
+}
+
+SkPDFStream::~SkPDFStream() {}
+
+void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                             bool indirect) {
+    if (indirect) {
+        return emitIndirectObject(stream, catalog);
+    }
+    if (!this->populate(catalog)) {
+        return fSubstitute->emitObject(stream, catalog, indirect);
+    }
+
+    this->INHERITED::emitObject(stream, catalog, false);
+    stream->writeText(" stream\n");
+    stream->write(fData->getMemoryBase(), fData->getLength());
+    stream->writeText("\nendstream");
+}
+
+size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    if (indirect) {
+        return getIndirectOutputSize(catalog);
+    }
+    if (!this->populate(catalog)) {
+        return fSubstitute->getOutputSize(catalog, indirect);
+    }
+
+    return this->INHERITED::getOutputSize(catalog, false) +
+        strlen(" stream\n\nendstream") + fData->getLength();
+}
+
+SkPDFStream::SkPDFStream() : fState(kUnused_State) {}
+
+void SkPDFStream::setData(SkStream* stream) {
+    fData = stream;
+}
+
+bool SkPDFStream::populate(SkPDFCatalog* catalog) {
+    if (fState == kUnused_State) {
+        if (!skip_compression(catalog) && SkFlate::HaveFlate()) {
+            SkDynamicMemoryWStream compressedData;
+
+            SkAssertResult(SkFlate::Deflate(fData.get(), &compressedData));
+            if (compressedData.getOffset() < fData->getLength()) {
+                SkMemoryStream* stream = new SkMemoryStream;
+                stream->setData(compressedData.copyToData())->unref();
+                fData = stream;
+                fData->unref();  // SkRefPtr and new both took a reference.
+                insertName("Filter", "FlateDecode");
+            }
+            fState = kCompressed_State;
+        } else {
+            fState = kNoCompression_State;
+        }
+        insertInt("Length", fData->getLength());
+    } 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.
+            catalog->setSubstitute(this, fSubstitute.get());
+        }
+        return false;
+    }
+    return true;
+}
diff --git a/src/pdf/SkPDFStream.h b/src/pdf/SkPDFStream.h
new file mode 100644
index 0000000..b3a7ad3
--- /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).
+    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/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
new file mode 100644
index 0000000..7fb1e95
--- /dev/null
+++ b/src/pdf/SkPDFTypes.cpp
@@ -0,0 +1,490 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkPDFCatalog.h"
+#include "SkPDFTypes.h"
+#include "SkStream.h"
+
+#ifdef SK_BUILD_FOR_WIN
+    #define SNPRINTF    _snprintf
+#else
+    #define SNPRINTF    snprintf
+#endif
+
+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) {
+    SkPDFObject* realObject = catalog->getSubstituteObject(this);
+    return realObject->emitObject(stream, catalog, indirect);
+}
+
+size_t SkPDFObject::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    SkDynamicMemoryWStream buffer;
+    emit(&buffer, catalog, indirect);
+    return buffer.getOffset();
+}
+
+void SkPDFObject::getResources(SkTDArray<SkPDFObject*>* resourceList) {}
+
+void SkPDFObject::emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog) {
+    catalog->emitObjectNumber(stream, this);
+    stream->writeText(" obj\n");
+    emit(stream, catalog, false);
+    stream->writeText("\nendobj\n");
+}
+
+size_t SkPDFObject::getIndirectOutputSize(SkPDFCatalog* catalog) {
+    return catalog->getObjectNumberSize(this) + strlen(" obj\n") +
+        this->getOutputSize(catalog, false) + strlen("\nendobj\n");
+}
+
+void SkPDFObject::AddResourceHelper(SkPDFObject* resource,
+                                    SkTDArray<SkPDFObject*>* list) {
+    list->push(resource);
+    resource->ref();
+}
+
+void SkPDFObject::GetResourcesHelper(SkTDArray<SkPDFObject*>* resources,
+                                     SkTDArray<SkPDFObject*>* result) {
+    if (resources->count()) {
+        result->setReserve(result->count() + resources->count());
+        for (int i = 0; i < resources->count(); i++) {
+            result->push((*resources)[i]);
+            (*resources)[i]->ref();
+            (*resources)[i]->getResources(result);
+        }
+    }
+}
+
+SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {}
+SkPDFObjRef::~SkPDFObjRef() {}
+
+void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                             bool indirect) {
+    SkASSERT(!indirect);
+    catalog->emitObjectNumber(stream, fObj.get());
+    stream->writeText(" R");
+}
+
+size_t SkPDFObjRef::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    SkASSERT(!indirect);
+    return catalog->getObjectNumberSize(fObj.get()) + strlen(" R");
+}
+
+SkPDFInt::SkPDFInt(int32_t value) : fValue(value) {}
+SkPDFInt::~SkPDFInt() {}
+
+void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                          bool indirect) {
+    if (indirect) {
+        return emitIndirectObject(stream, catalog);
+    }
+    stream->writeDecAsText(fValue);
+}
+
+SkPDFBool::SkPDFBool(bool value) : fValue(value) {}
+SkPDFBool::~SkPDFBool() {}
+
+void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                          bool indirect) {
+    SkASSERT(!indirect);
+    if (fValue) {
+        stream->writeText("true");
+    } else {
+        stream->writeText("false");
+    }
+}
+
+size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    SkASSERT(!indirect);
+    if (fValue) {
+        return strlen("true");
+    }
+    return strlen("false");
+}
+
+SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
+SkPDFScalar::~SkPDFScalar() {}
+
+void SkPDFScalar::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                             bool indirect) {
+    if (indirect) {
+        return emitIndirectObject(stream, catalog);
+    }
+
+    Append(fValue, stream);
+}
+
+// static
+void SkPDFScalar::Append(SkScalar value, SkWStream* stream) {
+    // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and
+    // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31).
+    // When using floats that are outside the whole value range, we can use
+    // integers instead.
+
+
+#if defined(SK_SCALAR_IS_FIXED)
+    stream->writeScalarAsText(value);
+    return;
+#endif  // SK_SCALAR_IS_FIXED
+
+#if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
+    if (value > 32767 || value < -32767) {
+        stream->writeDecAsText(SkScalarRound(value));
+        return;
+    }
+
+    char buffer[SkStrAppendScalar_MaxSize];
+    char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value));
+    stream->write(buffer, end - buffer);
+    return;
+#endif  // !SK_ALLOW_LARGE_PDF_SCALARS
+
+#if defined(SK_SCALAR_IS_FLOAT) && defined(SK_ALLOW_LARGE_PDF_SCALARS)
+    // Floats have 24bits of significance, so anything outside that range is
+    // no more precise than an int. (Plus PDF doesn't support scientific
+    // notation, so this clamps to SK_Max/MinS32).
+    if (value > (1 << 24) || value < -(1 << 24)) {
+        stream->writeDecAsText(value);
+        return;
+    }
+    // Continue to enforce the PDF limits for small floats.
+    if (value < 1.0f/65536 && value > -1.0f/65536) {
+        stream->writeDecAsText(0);
+        return;
+    }
+    // SkStrAppendFloat might still use scientific notation, so use snprintf
+    // directly..
+    static const int kFloat_MaxSize = 19;
+    char buffer[kFloat_MaxSize];
+    int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value);
+    // %f always prints trailing 0s, so strip them.
+    for (; buffer[len - 1] == '0' && len > 0; len--) {
+        buffer[len - 1] = '\0';
+    }
+    if (buffer[len - 1] == '.') {
+        buffer[len - 1] = '\0';
+    }
+    stream->writeText(buffer);
+    return;
+#endif  // SK_SCALAR_IS_FLOAT && SK_ALLOW_LARGE_PDF_SCALARS
+}
+
+SkPDFString::SkPDFString(const char value[])
+    : fValue(FormatString(value, strlen(value))) {
+}
+
+SkPDFString::SkPDFString(const SkString& value)
+    : fValue(FormatString(value.c_str(), value.size())) {
+}
+
+SkPDFString::SkPDFString(const uint16_t* value, size_t len, bool wideChars)
+    : fValue(FormatString(value, len, wideChars)) {
+}
+
+SkPDFString::~SkPDFString() {}
+
+void SkPDFString::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                             bool indirect) {
+    if (indirect)
+        return emitIndirectObject(stream, catalog);
+    stream->write(fValue.c_str(), fValue.size());
+}
+
+size_t SkPDFString::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    if (indirect)
+        return getIndirectOutputSize(catalog);
+    return fValue.size();
+}
+
+// static
+SkString SkPDFString::FormatString(const char* input, size_t len) {
+    return DoFormatString(input, len, false, false);
+}
+
+SkString SkPDFString::FormatString(const uint16_t* input, size_t len,
+                                   bool wideChars) {
+    return DoFormatString(input, len, true, wideChars);
+}
+
+// static
+SkString SkPDFString::DoFormatString(const void* input, size_t len,
+                                     bool wideInput, bool wideOutput) {
+    SkASSERT(len <= kMaxLen);
+    const uint16_t* win = (const uint16_t*) input;
+    const char* cin = (const char*) input;
+
+    if (wideOutput) {
+        SkASSERT(wideInput);
+        SkString result;
+        result.append("<");
+        for (size_t i = 0; i < len; i++) {
+            result.appendHex(win[i], 4);
+        }
+        result.append(">");
+        return result;
+    }
+
+    // 7-bit clean is a heuristic to decide what string format to use;
+    // a 7-bit clean string should require little escaping.
+    bool sevenBitClean = true;
+    for (size_t i = 0; i < len; i++) {
+        SkASSERT(!wideInput || !(win[i] & ~0xFF));
+        char val = wideInput ? win[i] : cin[i];
+        if (val > '~' || val < ' ') {
+            sevenBitClean = false;
+            break;
+        }
+    }
+
+    SkString result;
+    if (sevenBitClean) {
+        result.append("(");
+        for (size_t i = 0; i < len; i++) {
+            SkASSERT(!wideInput || !(win[i] & ~0xFF));
+            char val = wideInput ? win[i] : cin[i];
+            if (val == '\\' || val == '(' || val == ')') {
+                result.append("\\");
+            }
+            result.append(&val, 1);
+        }
+        result.append(")");
+    } else {
+        result.append("<");
+        for (size_t i = 0; i < len; i++) {
+            SkASSERT(!wideInput || !(win[i] & ~0xFF));
+            unsigned char val = wideInput ? win[i] : cin[i];
+            result.appendHex(val, 2);
+        }
+        result.append(">");
+    }
+
+    return result;
+}
+
+SkPDFName::SkPDFName(const char name[]) : fValue(FormatName(SkString(name))) {}
+SkPDFName::SkPDFName(const SkString& name) : fValue(FormatName(name)) {}
+SkPDFName::~SkPDFName() {}
+
+bool SkPDFName::operator==(const SkPDFName& b) const {
+    return fValue == b.fValue;
+}
+
+void SkPDFName::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                           bool indirect) {
+    SkASSERT(!indirect);
+    stream->write(fValue.c_str(), fValue.size());
+}
+
+size_t SkPDFName::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    SkASSERT(!indirect);
+    return fValue.size();
+}
+
+// static
+SkString SkPDFName::FormatName(const SkString& input) {
+    SkASSERT(input.size() <= kMaxLen);
+    // TODO(vandebo) If more escaping is needed, improve the linear scan.
+    static const char escaped[] = "#/%()<>[]{}";
+
+    SkString result("/");
+    for (size_t i = 0; i < input.size(); i++) {
+        if (input[i] & 0x80 || input[i] < '!' || strchr(escaped, input[i])) {
+            result.append("#");
+            // Mask with 0xFF to avoid sign extension. i.e. #FFFFFF81
+            result.appendHex(input[i] & 0xFF, 2);
+        } else {
+            result.append(input.c_str() + i, 1);
+        }
+    }
+
+    return result;
+}
+
+SkPDFArray::SkPDFArray() {}
+SkPDFArray::~SkPDFArray() {
+    fValue.unrefAll();
+}
+
+void SkPDFArray::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect) {
+    if (indirect) {
+        return emitIndirectObject(stream, catalog);
+    }
+
+    stream->writeText("[");
+    for (int i = 0; i < fValue.count(); i++) {
+        fValue[i]->emit(stream, catalog, false);
+        if (i + 1 < fValue.count()) {
+            stream->writeText(" ");
+        }
+    }
+    stream->writeText("]");
+}
+
+size_t SkPDFArray::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    if (indirect) {
+        return getIndirectOutputSize(catalog);
+    }
+
+    size_t result = strlen("[]");
+    if (fValue.count()) {
+        result += fValue.count() - 1;
+    }
+    for (int i = 0; i < fValue.count(); i++) {
+        result += fValue[i]->getOutputSize(catalog, false);
+    }
+    return result;
+}
+
+void SkPDFArray::reserve(int length) {
+    SkASSERT(length <= kMaxLen);
+    fValue.setReserve(length);
+}
+
+SkPDFObject* SkPDFArray::setAt(int offset, SkPDFObject* value) {
+    SkASSERT(offset < fValue.count());
+    value->ref();
+    fValue[offset]->unref();
+    fValue[offset] = value;
+    return value;
+}
+
+SkPDFObject* SkPDFArray::append(SkPDFObject* value) {
+    SkASSERT(fValue.count() < kMaxLen);
+    value->ref();
+    fValue.push(value);
+    return value;
+}
+
+void SkPDFArray::appendInt(int32_t value) {
+    SkASSERT(fValue.count() < kMaxLen);
+    fValue.push(new SkPDFInt(value));
+}
+
+void SkPDFArray::appendScalar(SkScalar value) {
+    SkASSERT(fValue.count() < kMaxLen);
+    fValue.push(new SkPDFScalar(value));
+}
+
+void SkPDFArray::appendName(const char name[]) {
+    SkASSERT(fValue.count() < kMaxLen);
+    fValue.push(new SkPDFName(name));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPDFDict::SkPDFDict() {}
+
+SkPDFDict::SkPDFDict(const char type[]) {
+    insertName("Type", type);
+}
+
+SkPDFDict::~SkPDFDict() {
+    clear();
+}
+
+void SkPDFDict::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                           bool indirect) {
+    if (indirect) {
+        return emitIndirectObject(stream, catalog);
+    }
+
+    stream->writeText("<<");
+    for (int i = 0; i < fValue.count(); i++) {
+        fValue[i].key->emitObject(stream, catalog, false);
+        stream->writeText(" ");
+        fValue[i].value->emit(stream, catalog, false);
+        stream->writeText("\n");
+    }
+    stream->writeText(">>");
+}
+
+size_t SkPDFDict::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
+    if (indirect) {
+        return getIndirectOutputSize(catalog);
+    }
+
+    size_t result = strlen("<<>>") + (fValue.count() * 2);
+    for (int i = 0; i < fValue.count(); i++) {
+        result += fValue[i].key->getOutputSize(catalog, false);
+        result += fValue[i].value->getOutputSize(catalog, false);
+    }
+    return result;
+}
+
+SkPDFObject* SkPDFDict::insert(SkPDFName* key, SkPDFObject* value) {
+    key->ref();
+    value->ref();
+    struct Rec* newEntry = fValue.append();
+    newEntry->key = key;
+    newEntry->value = value;
+    return value;
+}
+
+SkPDFObject* SkPDFDict::insert(const char key[], SkPDFObject* value) {
+    value->ref();
+    struct Rec* newEntry = fValue.append();
+    newEntry->key = new SkPDFName(key);
+    newEntry->value = value;
+    return value;
+}
+
+void SkPDFDict::insertInt(const char key[], int32_t value) {
+    struct Rec* newEntry = fValue.append();
+    newEntry->key = new SkPDFName(key);
+    newEntry->value = new SkPDFInt(value);
+}
+
+void SkPDFDict::insertScalar(const char key[], SkScalar value) {
+    struct Rec* newEntry = fValue.append();
+    newEntry->key = new SkPDFName(key);
+    newEntry->value = new SkPDFScalar(value);
+}
+
+void SkPDFDict::insertName(const char key[], const char name[]) {
+    struct Rec* newEntry = fValue.append();
+    newEntry->key = new SkPDFName(key);
+    newEntry->value = new SkPDFName(name);
+}
+
+void SkPDFDict::clear() {
+    for (int i = 0; i < fValue.count(); i++) {
+        fValue[i].key->unref();
+        fValue[i].value->unref();
+    }
+    fValue.reset();
+}
+
+SkPDFDict::Iter::Iter(const SkPDFDict& dict)
+    : fIter(dict.fValue.begin()),
+      fStop(dict.fValue.end()) {
+}
+
+SkPDFName* SkPDFDict::Iter::next(SkPDFObject** value) {
+    if (fIter != fStop) {
+        Rec* cur = fIter;
+        fIter++;
+        *value = cur->value;
+        return cur->key;
+    }
+    *value = NULL;
+    return NULL;
+}
diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h
new file mode 100644
index 0000000..28034ef
--- /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:
+    SkRefPtr<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
new file mode 100644
index 0000000..8cd3a90
--- /dev/null
+++ b/src/pdf/SkPDFUtils.cpp
@@ -0,0 +1,219 @@
+
+/*
+ * Copyright 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"
+#include "SkGeometry.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPDFUtils.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkPDFTypes.h"
+
+// static
+SkPDFArray* SkPDFUtils::MatrixToArray(const SkMatrix& matrix) {
+    SkScalar values[6];
+    if (!matrix.asAffine(values)) {
+        SkMatrix::SetAffineIdentity(values);
+    }
+
+    SkPDFArray* result = new SkPDFArray;
+    result->reserve(6);
+    for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
+        result->appendScalar(values[i]);
+    }
+    return result;
+}
+
+// static
+void SkPDFUtils::AppendTransform(const SkMatrix& matrix, SkWStream* content) {
+    SkScalar values[6];
+    if (!matrix.asAffine(values)) {
+        SkMatrix::SetAffineIdentity(values);
+    }
+    for (size_t i = 0; i < SK_ARRAY_COUNT(values); i++) {
+        SkPDFScalar::Append(values[i], content);
+        content->writeText(" ");
+    }
+    content->writeText("cm\n");
+}
+
+// static
+void SkPDFUtils::MoveTo(SkScalar x, SkScalar y, SkWStream* content) {
+    SkPDFScalar::Append(x, content);
+    content->writeText(" ");
+    SkPDFScalar::Append(y, content);
+    content->writeText(" m\n");
+}
+
+// static
+void SkPDFUtils::AppendLine(SkScalar x, SkScalar y, SkWStream* content) {
+    SkPDFScalar::Append(x, content);
+    content->writeText(" ");
+    SkPDFScalar::Append(y, content);
+    content->writeText(" l\n");
+}
+
+// static
+void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
+                             SkScalar ctl2X, SkScalar ctl2Y,
+                             SkScalar dstX, SkScalar dstY, SkWStream* content) {
+    SkString cmd("y\n");
+    SkPDFScalar::Append(ctl1X, content);
+    content->writeText(" ");
+    SkPDFScalar::Append(ctl1Y, content);
+    content->writeText(" ");
+    if (ctl2X != dstX || ctl2Y != dstY) {
+        cmd.set("c\n");
+        SkPDFScalar::Append(ctl2X, content);
+        content->writeText(" ");
+        SkPDFScalar::Append(ctl2Y, content);
+        content->writeText(" ");
+    }
+    SkPDFScalar::Append(dstX, content);
+    content->writeText(" ");
+    SkPDFScalar::Append(dstY, content);
+    content->writeText(" ");
+    content->writeText(cmd.c_str());
+}
+
+// static
+void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
+    // Skia has 0,0 at top left, pdf at bottom left.  Do the right thing.
+    SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
+
+    SkPDFScalar::Append(rect.fLeft, content);
+    content->writeText(" ");
+    SkPDFScalar::Append(bottom, content);
+    content->writeText(" ");
+    SkPDFScalar::Append(rect.width(), content);
+    content->writeText(" ");
+    SkPDFScalar::Append(rect.height(), content);
+    content->writeText(" re\n");
+}
+
+// static
+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);
+         verb != SkPath::kDone_Verb;
+         verb = iter.next(args)) {
+        // args gets all the points, even the implicit first point.
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                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, &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, &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, &currentSegment);
+                fillState = kNonSingleLine_SkipFillState;
+                break;
+            case SkPath::kClose_Verb:
+                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
+void SkPDFUtils::ClosePath(SkWStream* content) {
+    content->writeText("h\n");
+}
+
+// static
+void SkPDFUtils::PaintPath(SkPaint::Style style, SkPath::FillType fill,
+                           SkWStream* content) {
+    if (style == SkPaint::kFill_Style) {
+        content->writeText("f");
+    } else if (style == SkPaint::kStrokeAndFill_Style) {
+        content->writeText("B");
+    } else if (style == SkPaint::kStroke_Style) {
+        content->writeText("S");
+    }
+
+    if (style != SkPaint::kStroke_Style) {
+        NOT_IMPLEMENTED(fill == SkPath::kInverseEvenOdd_FillType, false);
+        NOT_IMPLEMENTED(fill == SkPath::kInverseWinding_FillType, false);
+        if (fill == SkPath::kEvenOdd_FillType) {
+            content->writeText("*");
+        }
+    }
+    content->writeText("\n");
+}
+
+// static
+void SkPDFUtils::StrokePath(SkWStream* content) {
+    SkPDFUtils::PaintPath(
+        SkPaint::kStroke_Style, SkPath::kWinding_FillType, content);
+}
+
+// static
+void SkPDFUtils::DrawFormXObject(int objectIndex, SkWStream* content) {
+    content->writeText("/X");
+    content->writeDecAsText(objectIndex);
+    content->writeText(" Do\n");
+}
+
+// static
+void SkPDFUtils::ApplyGraphicState(int objectIndex, SkWStream* content) {
+    content->writeText("/G");
+    content->writeDecAsText(objectIndex);
+    content->writeText(" gs\n");
+}
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
new file mode 100644
index 0000000..31803ea
--- /dev/null
+++ b/src/pipe/SkGPipePriv.h
@@ -0,0 +1,278 @@
+
+/*
+ * 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,
+    kImageFilter_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,
+    kDrawBitmapNine_DrawOp,
+    kDrawBitmapRectToRect_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,
+    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,
+};
+
+/**
+ *  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 inline unsigned DrawOp_unpackOp(uint32_t op32) {
+    return (op32 >> (DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS));
+}
+
+static inline unsigned DrawOp_unpackFlags(uint32_t op32) {
+    return (op32 >> DRAWOPS_DATA_BITS) & DRAWOPS_FLAG_MASK;
+}
+
+static inline unsigned DrawOp_unpackData(uint32_t op32) {
+    return op32 & DRAWOPS_DATA_MASK;
+}
+
+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));
+
+    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 {
+    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
+    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 inline unsigned PaintOp_unpackOp(uint32_t op32) {
+    return (op32 >> (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS));
+}
+
+static inline unsigned PaintOp_unpackFlags(uint32_t op32) {
+    return (op32 >> PAINTOPS_DATA_BITS) & PAINTOPS_FLAG_MASK;
+}
+
+static inline unsigned PaintOp_unpackData(uint32_t op32) {
+    return op32 & PAINTOPS_DATA_MASK;
+}
+
+static inline uint32_t PaintOp_packOp(PaintOps op) {
+    SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
+
+    return op << (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS);
+}
+
+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 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;
+}
+
+#endif
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
new file mode 100644
index 0000000..1f13daf
--- /dev/null
+++ b/src/pipe/SkGPipeRead.cpp
@@ -0,0 +1,812 @@
+
+/*
+ * Copyright 2011 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 "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 "SkOrderedReadBuffer.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 kImageFilter_PaintFlat:
+            paint->setImageFilter((SkImageFilter*)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 SkBitmapHeapReader {
+public:
+    SkGPipeState();
+    ~SkGPipeState();
+
+    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;
+        this->updateReader();
+    }
+
+    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) {
+        index--;
+        SkFlattenable* obj = fReader->readFlattenable();
+        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->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);
+    }
+
+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;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+    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;
+    reader->readRegion(&rgn);
+    canvas->clipRegion(rgn, (SkRegion::Op)DrawOp_unpackData(op32));
+}
+
+static void clipRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    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 setMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    SkMatrix matrix;
+    reader->readMatrix(&matrix);
+    canvas->setMatrix(matrix);
+}
+
+static void concat_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                      SkGPipeState* state) {
+    SkMatrix matrix;
+    reader->readMatrix(&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) {
+    if (state->shouldDraw()) {
+        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);
+    if (state->shouldDraw()) {
+        canvas->drawPoints(mode, count, pts, state->paint());
+    }
+}
+
+static void drawRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    const SkRect* rect = skip<SkRect>(reader);
+    if (state->shouldDraw()) {
+        canvas->drawRect(*rect, state->paint());
+    }
+}
+
+static void drawPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    SkPath path;
+    reader->readPath(&path);
+    if (state->shouldDraw()) {
+        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);
+    }
+    if (state->shouldDraw()) {
+        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);
+    if (state->shouldDraw()) {
+        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);
+    if (state->shouldDraw()) {
+        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();
+    if (state->shouldDraw()) {
+        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;
+    reader->readPath(&path);
+
+    SkMatrix matrixStorage;
+    const SkMatrix* matrix = NULL;
+    if (DrawOp_unpackFlags(op32) & kDrawTextOnPath_HasMatrix_DrawOpFlag) {
+        reader->readMatrix(&matrixStorage);
+        matrix = &matrixStorage;
+    }
+    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) {
+    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) {
+    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 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) {
+    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);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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));
+    if (state->shouldDraw()) {
+        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:
+                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) {
+    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 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*) {
+    size_t bytes = DrawOp_unpackData(op32);
+    (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*);
+
+static const ReadProc gReadTable[] = {
+    skip_rp,
+    clipPath_rp,
+    clipRegion_rp,
+    clipRect_rp,
+    concat_rp,
+    drawBitmap_rp,
+    drawBitmapMatrix_rp,
+    drawBitmapNine_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,
+    typeface_rp,
+    def_Typeface_rp,
+    def_PaintFlat_rp,
+    def_Bitmap_rp,
+    def_Factory_rp,
+
+    reportFlags_rp,
+    shareBitmapHeap_rp,
+    done_rp
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+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() {
+    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,
+                                              uint32_t playbackFlags, size_t* bytesRead) {
+    if (NULL == fCanvas) {
+        return kError_Status;
+    }
+
+    if (NULL == fState) {
+        fState = new SkGPipeState;
+    }
+
+    fState->setSilent(playbackFlags & kSilent_PlaybackFlag);
+
+    SkASSERT(SK_ARRAY_COUNT(gReadTable) == (kDone_DrawOp + 1));
+
+    const ReadProc* table = gReadTable;
+    SkOrderedReadBuffer reader(data, length);
+    SkCanvas* canvas = fCanvas;
+    Status status = kEOF_Status;
+
+    fState->setReader(&reader);
+    while (!reader.eof()) {
+        uint32_t op32 = reader.readUInt();
+        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.getReader32(), op32, fState);
+        if ((playbackFlags & kReadAtom_PlaybackFlag) &&
+            (table[op] != paintOp_rp &&
+             table[op] != def_Typeface_rp &&
+             table[op] != def_PaintFlat_rp &&
+             table[op] != def_Bitmap_rp
+             )) {
+                status = kReadAtom_Status;
+                break;
+            }
+    }
+
+    if (bytesRead) {
+        *bytesRead = reader.offset();
+    }
+    return status;
+}
+
+
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
new file mode 100644
index 0000000..f0b4e0a
--- /dev/null
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -0,0 +1,1142 @@
+
+/*
+ * Copyright 2011 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 "SkCanvas.h"
+#include "SkColorFilter.h"
+#include "SkData.h"
+#include "SkDrawLooper.h"
+#include "SkDevice.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 "SkShader.h"
+#include "SkStream.h"
+#include "SkTSearch.h"
+#include "SkTypeface.h"
+#include "SkWriter32.h"
+
+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);
+    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 kImageFilter_PaintFlat:    return paint.getImageFilter();
+        case kXfermode_PaintFlat:       return paint.getXfermode();
+    }
+    SkDEBUGFAIL("never gets here");
+    return NULL;
+}
+
+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->writePad(data->data(), 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*, uint32_t flags,
+                  uint32_t width, uint32_t height);
+    virtual ~SkGPipeCanvas();
+
+    void finish() {
+        if (!fDone) {
+            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) 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& rect, 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&) 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 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,
+                                const SkRect& dst, const SkPaint* paint = NULL) 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;
+
+    /**
+     * Flatten an SkBitmap to send to the reader, where it will be referenced
+     * according to slot.
+     */
+    bool shuttleBitmap(const SkBitmap&, int32_t slot);
+private:
+    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;
+    const uint32_t     fFlags;
+
+    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;
+            if (bytes > 0) {
+                fController->notifyWritten(bytes);
+                fBytesNotified += bytes;
+            }
+        }
+    }
+
+    // Should be called after any calls to an SkFlatDictionary::findAndReplace
+    // if a new SkFlatData was added when in cross process mode
+    void flattenFactoryNames();
+
+    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&);
+
+    class AutoPipeNotify {
+    public:
+        AutoPipeNotify(SkGPipeCanvas* canvas) : fCanvas(canvas) {}
+        ~AutoPipeNotify() { fCanvas->doNotify(); }
+    private:
+        SkGPipeCanvas* fCanvas;
+    };
+    friend class AutoPipeNotify;
+
+    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;
+    }
+
+    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);
+        }
+    }
+    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, 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
+    // We don't allocate pixels for the bitmap
+    SkBitmap bitmap;
+    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();
+    SkSafeUnref(fFactorySet);
+    SkSafeUnref(fBitmapHeap);
+}
+
+bool SkGPipeCanvas::needOpBytes(size_t needed) {
+    if (fDone) {
+        return false;
+    }
+
+    needed += 4;  // size of DrawOp atom
+    if (fWriter.size() + needed > 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;
+    }
+    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);
+        }
+    }
+
+    if (kNoSaveLayer == fFirstSaveLayerStackLevel){
+        fFirstSaveLayerStackLevel = this->getSaveCount();
+    }
+    // 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();
+
+    if (this->getSaveCount() == fFirstSaveLayerStackLevel){
+        fFirstSaveLayerStackLevel = kNoSaveLayer;
+    }
+}
+
+bool SkGPipeCanvas::isDrawingToLayer() const {
+    return kNoSaveLayer != fFirstSaveLayerStackLevel;
+}
+
+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.writeToMemory(NULL))) {
+            this->writeOp(kConcat_DrawOp);
+            fWriter.writeMatrix(matrix);
+        }
+    }
+    return this->INHERITED::concat(matrix);
+}
+
+void SkGPipeCanvas::setMatrix(const SkMatrix& matrix) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(matrix.writeToMemory(NULL))) {
+        this->writeOp(kSetMatrix_DrawOp);
+        fWriter.writeMatrix(matrix);
+    }
+    this->INHERITED::setMatrix(matrix);
+}
+
+bool SkGPipeCanvas::clipRect(const SkRect& rect, SkRegion::Op rgnOp,
+                             bool doAntiAlias) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(sizeof(SkRect))) {
+        unsigned flags = doAntiAlias & kClip_HasAntiAlias_DrawOpFlag;
+        this->writeOp(kClipRect_DrawOp, flags, rgnOp);
+        fWriter.writeRect(rect);
+    }
+    return this->INHERITED::clipRect(rect, 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, doAntiAlias);
+}
+
+bool SkGPipeCanvas::clipRegion(const SkRegion& region, SkRegion::Op rgnOp) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(region.writeToMemory(NULL))) {
+        this->writeOp(kClipRegion_DrawOp, 0, rgnOp);
+        fWriter.writeRegion(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(path.writeToMemory(NULL))) {
+        this->writeOp(kDrawPath_DrawOp);
+        fWriter.writePath(path);
+    }
+}
+
+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::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::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,
+                                 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) + path.writeToMemory(NULL);
+        if (matrix) {
+            flags |= kDrawTextOnPath_HasMatrix_DrawOpFlag;
+            size += matrix->writeToMemory(NULL);
+        }
+        this->writePaint(paint);
+        if (this->needOpBytes(size)) {
+            this->writeOp(kDrawTextOnPath_DrawOp, flags, 0);
+
+            fWriter.write32(byteLength);
+            fWriter.writePad(text, byteLength);
+
+            fWriter.writePath(path);
+            if (matrix) {
+                fWriter.writeMatrix(*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);
+        }
+    }
+}
+
+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) {
+    union {
+        T           fSrc;
+        uint32_t    fDst;
+    } data;
+    data.fSrc = value;
+    return data.fDst;
+}
+
+void SkGPipeCanvas::writePaint(const SkPaint& paint) {
+    if (fDone) {
+        return;
+    }
+    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())) {
+        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);
+        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;
+        }
+    }
+
+    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"
+
+SkGPipeController::~SkGPipeController() {
+    SkSafeUnref(fCanvas);
+}
+
+void SkGPipeController::setCanvas(SkGPipeCanvas* canvas) {
+    SkRefCnt_SafeAssign(fCanvas, canvas);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGPipeWriter::SkGPipeWriter()
+: fWriter(0) {
+    fCanvas = NULL;
+}
+
+SkGPipeWriter::~SkGPipeWriter() {
+    this->endRecording();
+}
+
+SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller, uint32_t flags,
+                                        uint32_t width, uint32_t height) {
+    if (NULL == fCanvas) {
+        fWriter.reset(NULL, 0);
+        fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter, flags, width, height));
+    }
+    controller->setCanvas(fCanvas);
+    return fCanvas;
+}
+
+void SkGPipeWriter::endRecording() {
+    if (fCanvas) {
+        fCanvas->finish();
+        fCanvas->unref();
+        fCanvas = NULL;
+    }
+}
+
+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..59f612b
--- /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;
+
+        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
new file mode 100644
index 0000000..ce98c48
--- /dev/null
+++ b/src/ports/FontHostConfiguration_android.cpp
@@ -0,0 +1,279 @@
+/* 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 "SkString.h"
+#include "SkTDArray.h"
+#include <expat.h>
+#include <sys/system_properties.h>
+
+#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
+    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 fFileNames 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:
+            *(familyData->currentFamily->fFileNames.append()) = buff;
+            break;
+        default:
+            // Noop - don't care about any text that's not in the Fonts or Names list
+            break;
+        }
+    }
+}
+
+/**
+ * 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) ||
+            (strncmp(tag, "file", len) == 0 && familyData->currentTag == FILESET_TAG)) {
+        // If it's a Name, parse the text inside
+        XML_SetCharacterDataHandler(*familyData->parser, textHandler);
+    }
+}
+
+/**
+ * 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);
+    }
+}
+
+/**
+ * Read the persistent locale.
+ */
+void getLocale(AndroidLocale &locale)
+{
+    char propLang[PROP_VALUE_MAX], propRegn[PROP_VALUE_MAX];
+    __system_property_get("persist.sys.language", propLang);
+    __system_property_get("persist.sys.country", propRegn);
+
+    if (*propLang == 0 && *propRegn == 0) {
+        /* Set to ro properties, default is en_US */
+        __system_property_get("ro.product.locale.language", propLang);
+        __system_property_get("ro.product.locale.region", propRegn);
+        if (*propLang == 0 && *propRegn == 0) {
+            strcpy(propLang, "en");
+            strcpy(propRegn, "US");
+        }
+    }
+    strncpy(locale.language, propLang, 2);
+    locale.language[2] = '\0';
+    strncpy(locale.region, propRegn, 2);
+    locale.region[2] = '\0';
+}
+
+/**
+ * Use the current system locale (language and region) to open the best matching
+ * customization. For example, when the language is Japanese, the sequence might be:
+ *      /system/etc/fallback_fonts-ja-JP.xml
+ *      /system/etc/fallback_fonts-ja.xml
+ *      /system/etc/fallback_fonts.xml
+ */
+FILE* openLocalizedFile(const char* origname) {
+    FILE* file = 0;
+    SkString basename;
+    SkString filename;
+    AndroidLocale locale;
+
+    basename.set(origname);
+    // Remove the .xml suffix. We'll add it back in a moment.
+    if (basename.endsWith(".xml")) {
+        basename.resize(basename.size()-4);
+    }
+    getLocale(locale);
+    // Try first with language and region
+    filename.printf("%s-%s-%s.xml", basename.c_str(), locale.language, locale.region);
+    file = fopen(filename.c_str(), "r");
+    if (!file) {
+        // If not found, try next with just language
+        filename.printf("%s-%s.xml", basename.c_str(), locale.language);
+        file = fopen(filename.c_str(), "r");
+
+        if (!file) {
+            // If still not found, try just the original name
+            file = fopen(origname, "r");
+        }
+    }
+    return file;
+}
+
+/**
+ * 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 = openLocalizedFile(filename);
+    // 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);
+    }
+}
+
+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];
+    }
+}
+
+void getTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
+                         const char* testMainConfigFile,
+                         const char* testFallbackConfigFile) {
+    parseConfigFile(testMainConfigFile, fontFamilies);
+
+    SkTDArray<FontFamily*> fallbackFonts;
+    parseConfigFile(testFallbackConfigFile, fallbackFonts);
+
+    // Append all fallback fonts to system fonts
+    for (int i = 0; i < fallbackFonts.count(); ++i) {
+        *fontFamilies.append() = fallbackFonts[i];
+    }
+}
diff --git a/src/ports/FontHostConfiguration_android.h b/src/ports/FontHostConfiguration_android.h
new file mode 100644
index 0000000..809594a
--- /dev/null
+++ b/src/ports/FontHostConfiguration_android.h
@@ -0,0 +1,63 @@
+/* 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 "SkTDArray.h"
+
+/**
+ * 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. fFileNames is the list of font
+ * filenames for the family. 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<const char*>  fFileNames;
+    int order;
+};
+
+/**
+ * Parses all system font configuration files and returns the results in an
+ * array of FontFamily structures.
+ */
+void getFontFamilies(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);
+
+/**
+ * Parses all test font configuration files and returns the results in an
+ * array of FontFamily structures.
+ */
+void getTestFontFamilies(SkTDArray<FontFamily*> &fontFamilies,
+                         const char* testMainConfigFile,
+                         const char* testFallbackConfigFile);
+
+struct AndroidLocale {
+    char language[3];
+    char region[3];
+};
+
+void getLocale(AndroidLocale &locale);
+
+#endif /* FONTHOSTCONFIGURATION_ANDROID_H_ */
diff --git a/src/ports/SkDebug_android.cpp b/src/ports/SkDebug_android.cpp
new file mode 100644
index 0000000..8e7d1d4
--- /dev/null
+++ b/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/src/ports/SkDebug_brew.cpp b/src/ports/SkDebug_brew.cpp
new file mode 100644
index 0000000..b7ad3ef
--- /dev/null
+++ b/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/src/ports/SkDebug_stdio.cpp b/src/ports/SkDebug_stdio.cpp
new file mode 100644
index 0000000..bc3d98b
--- /dev/null
+++ b/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/src/ports/SkDebug_win.cpp b/src/ports/SkDebug_win.cpp
new file mode 100644
index 0000000..b3077f1
--- /dev/null
+++ b/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/src/ports/SkFontDescriptor.cpp b/src/ports/SkFontDescriptor.cpp
new file mode 100644
index 0000000..7679d92
--- /dev/null
+++ b/src/ports/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/ports/SkFontDescriptor.h b/src/ports/SkFontDescriptor.h
new file mode 100644
index 0000000..5febfd8
--- /dev/null
+++ b/src/ports/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/ports/SkFontHost_FONTPATH.cpp b/src/ports/SkFontHost_FONTPATH.cpp
new file mode 100644
index 0000000..69e73f5
--- /dev/null
+++ b/src/ports/SkFontHost_FONTPATH.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 "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[],
+                                       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,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
+    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/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
new file mode 100644
index 0000000..dc7666e
--- /dev/null
+++ b/src/ports/SkFontHost_FreeType.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 "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"
+#include "SkString.h"
+#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
+#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
+
+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.
+
+/////////////////////////////////////////////////////////////////////////
+
+// 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*);
+
+// 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 color fringes for LCD smoothed glyphs.
+#ifdef FT_LCD_FILTER_H
+    // 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;
+#endif
+    gLCDSupportValid = true;
+
+    return true;
+}
+
+class SkScalerContext_FreeType : public SkScalerContext_FreeType_Base {
+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, SkMaskGamma::PreBlend* maskPreBlend);
+    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        fLCDIsVert;
+
+    FT_Error setupSize();
+    void getBBoxForCurrentGlyph(SkGlyph* glyph, FT_BBox* bbox,
+                                bool snapToPixelBoundary = false);
+    // Caller must lock gFTMutex before calling this function.
+    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)
+        : fNext(NULL), fSkStream(strm), fRefCnt(1), 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
+// Caller must lock gFTMutex before calling this function.
+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;
+        return rec;
+    }
+}
+
+// Caller must lock gFTMutex before calling this function.
+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, 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);
+    }
+
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    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)) {
+        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_GAMMA_APPLY_TO_A8
+    if (!isLCD(*rec)) {
+      rec->ignorePreBlend();
+    }
+#endif
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+uint32_t SkFontHost::GetUnitsPerEm(SkFontID fontID) {
+    SkAutoMutexAcquire ac(gFTMutex);
+    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_FreeType_Base(desc) {
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    if (gFTCount == 0) {
+        if (!InitFreetype()) {
+            sk_throw();
+        }
+    }
+    ++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;
+    }
+
+#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
+    {
+        FT_Int32 loadFlags = FT_LOAD_DEFAULT;
+        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
+            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
+                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 (fLCDIsVert) {
+                        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.
+        if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
+            loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
+        }
+
+        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() {
+    SkAutoMutexAcquire  ac(gFTMutex);
+
+    if (fFTSize != NULL) {
+        FT_Done_Size(fFTSize);
+    }
+
+    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;
+}
+
+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;
+}
+
+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);
+            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 = 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;
+        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 (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);
+        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)) {
+        if (fLCDIsVert) {
+            glyph->fHeight += gLCDExtra;
+            glyph->fTop -= gLCDExtra >> 1;
+        } else {
+            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;
+    }
+
+    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) {
+            emboldenOutline(fFace, &fFace->glyph->outline);
+        }
+
+        getBBoxForCurrentGlyph(glyph, &bbox, true);
+
+        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);
+
+        break;
+      }
+
+      case FT_GLYPH_FORMAT_BITMAP:
+        if (fRec.fFlags & kEmbolden_Flag) {
+            FT_GlyphSlot_Own_Bitmap(fFace->glyph);
+            FT_Bitmap_Embolden(gFTLibrary, &fFace->glyph->bitmap, kBitmapEmboldenStrength, 0);
+        }
+
+        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);
+            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::kVertical_Flag) {
+        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 (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);
+
+            if (fRec.fFlags & kDevKernText_Flag) {
+                glyph->fRsbDelta = SkToS8(fFace->glyph->rsb_delta);
+                glyph->fLsbDelta = SkToS8(fFace->glyph->lsb_delta);
+            }
+        }
+    }
+
+
+#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
+}
+
+
+void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) {
+    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;
+    }
+
+    generateGlyphImage(fFace, glyph, maskPreBlend);
+}
+
+
+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;
+    }
+
+    generateGlyphPath(fFace, glyph, path);
+
+    // 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);
+        path->offset(SkFDot6ToScalar(vector.x), -SkFDot6ToScalar(vector.y));
+    }
+}
+
+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) {
+                emboldenOutline(fFace, &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/src/ports/SkFontHost_FreeType_common.cpp b/src/ports/SkFontHost_FreeType_common.cpp
new file mode 100644
index 0000000..3a827d7
--- /dev/null
+++ b/src/ports/SkFontHost_FreeType_common.cpp
@@ -0,0 +1,341 @@
+/*
+ * 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;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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,
+                                                       SkMaskGamma::PreBlend* maskPreBlend)
+{
+    //Must be careful not to use these if maskPreBlend == NULL
+    const uint8_t* tableR = NULL;
+    const uint8_t* tableG = NULL;
+    const uint8_t* tableB = NULL;
+    if (maskPreBlend) {
+        tableR = maskPreBlend->fR;
+        tableG = maskPreBlend->fG;
+        tableB = maskPreBlend->fB;
+    }
+
+    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) {
+                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 (maskPreBlend) {
+                    copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert,
+                                       tableR, tableG, tableB);
+                } else {
+                    copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert,
+                                        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(face->glyph->library, outline, &target);
+            }
+        } break;
+
+        case FT_GLYPH_FORMAT_BITMAP: {
+            if (fRec.fFlags & SkScalerContext::kEmbolden_Flag) {
+                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 (maskPreBlend) {
+                    copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert,
+                                       tableR, tableG, tableB);
+                } else {
+                    copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert,
+                                        tableR, tableG, tableB);
+                }
+            } 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 && maskPreBlend) {
+        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] = tableG[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) {
+        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)
+               / 24;
+    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..d5f2c08
--- /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, SkMaskGamma::PreBlend* maskPreBlend);
+    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
new file mode 100644
index 0000000..d46c897
--- /dev/null
+++ b/src/ports/SkFontHost_android.cpp
@@ -0,0 +1,1159 @@
+/* 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 "SkFontDescriptor.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 "SkTypeface_android.h"
+#include "FontHostConfiguration_android.h"
+
+#ifndef SK_FONT_FILE_PREFIX
+    #define SK_FONT_FILE_PREFIX          "/fonts/"
+#endif
+
+#ifndef SK_DEBUG_FONTS
+    #define SK_DEBUG_FONTS 0
+#endif
+
+// For test only.
+static const char* gTestMainConfigFile = NULL;
+static const char* gTestFallbackConfigFile = NULL;
+static const char* gTestFontFilePrefix = NULL;
+
+bool find_name_and_attributes(SkStream* stream, SkString* name,
+                              SkTypeface::Style* style, bool* isFixedWidth);
+
+static void GetFullPathForSysFonts(SkString* full, const char name[]) {
+    if (gTestFontFilePrefix) {
+        full->set(gTestFontFilePrefix);
+    } else {
+        full->set(getenv("ANDROID_ROOT"));
+        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
+    }
+};
+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 gFamilyHead and GetNameList()
+SK_DECLARE_STATIC_MUTEX(gFamilyHeadAndNameListMutex);
+static FamilyRec* gFamilyHead;
+
+static NameFamilyPairList& GetNameList() {
+    /*
+     *  It is assumed that the caller has already acquired a lock on
+     *  gFamilyHeadAndNameListMutex before calling this.
+     */
+    static NameFamilyPairList* gNameList;
+    if (NULL == gNameList) {
+        gNameList = SkNEW(NameFamilyPairList);
+        // register a delete proc with sk_atexit(..) when available
+    }
+    return *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;
+}
+
+//  gFamilyHeadAndNameListMutex must already be acquired
+static const char* find_family_name(const SkTypeface* member) {
+    FamilyRec* family = find_family(member);
+    if (NULL == family) {
+        return NULL;
+    }
+
+    NameFamilyPairList& namelist = GetNameList();
+    NameFamilyPair* list = namelist.begin();
+    int             count = namelist.count();
+
+    for (int i = 0; i < count; i++) {
+        NameFamilyPair* pair = &list[i];
+        if (pair->fFamily == family) {
+            return pair->fName;
+        }
+    }
+
+    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);
+    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("remove_from_family(%p) face not found", face);
+    }
+    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;
+    }
+    SkASSERT(!"Yikes, couldn't find family in our list to remove/delete");
+}
+
+//  gFamilyHeadAndNameListMutex must already be acquired
+static SkTypeface* find_typeface(const char name[], SkTypeface::Style style) {
+    NameFamilyPairList& namelist = GetNameList();
+    NameFamilyPair* list = namelist.begin();
+    int             count = namelist.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;
+}
+
+//  gFamilyHeadAndNameListMutex must already be acquired
+static SkTypeface* find_typeface(const SkTypeface* familyMember,
+                                 SkTypeface::Style style) {
+    const FamilyRec* family = find_family(familyMember);
+    return family ? find_best_face(family, style) : NULL;
+}
+
+//  gFamilyHeadAndNameListMutex must already be acquired
+static void add_name(const char name[], FamilyRec* family) {
+    SkAutoAsciiToLC tolc(name);
+    name = tolc.lc();
+
+    NameFamilyPairList& namelist = GetNameList();
+    NameFamilyPair* list = namelist.begin();
+    int             count = namelist.count();
+
+    int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
+
+    if (index < 0) {
+        list = namelist.insert(~index);
+        list->construct(name, family);
+    }
+}
+
+//  gFamilyHeadAndNameListMutex must already be acquired
+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 = GetNameList();
+
+    // 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,
+                   bool isFixedWidth)
+    : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1, isFixedWidth) {
+        fIsSysFont = sysFont;
+
+        // our caller has acquired the gFamilyHeadAndNameListMutex so this is safe
+        FamilyRec* rec = NULL;
+        if (familyMember) {
+            rec = find_family(familyMember);
+            SkASSERT(rec);
+        } else {
+            rec = SkNEW(FamilyRec);
+        }
+        rec->fFaces[style] = this;
+    }
+
+    virtual ~FamilyTypeface() {
+        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 = 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, bool isFixedWidth)
+    : INHERITED(style, sysFont, familyMember, 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, SkTypeface* familyMember,
+                 const char path[], bool isFixedWidth)
+    : INHERITED(style, sysFont, familyMember, isFixedWidth) {
+        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* 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", 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
+};
+
+// 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 FontInitRec *gSystemFonts;
+static size_t gNumSystemFonts = 0;
+
+// these globals are assigned (once) by load_system_fonts()
+static FamilyRec* gDefaultFamily;
+static SkTypeface* gDefaultNormal;
+static char** gDefaultNames = NULL;
+static uint32_t *gFallbackFonts;
+
+static void dump_globals() {
+    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);
+    }
+
+    SkDebugf("gNumSystemFonts=%d gSystemFonts=%p gFallbackFonts=%p",
+             gNumSystemFonts, gSystemFonts, gFallbackFonts);
+
+    for (size_t i = 0; i < gNumSystemFonts; ++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);
+    }
+
+}
+
+
+/*  Load info from a configuration file that populates the system/fallback font structures
+*/
+static void load_font_info() {
+    SkTDArray<FontFamily*> fontFamilies;
+    if (gTestMainConfigFile) {
+        getTestFontFamilies(fontFamilies, gTestMainConfigFile, gTestFallbackConfigFile);
+    } else {
+        getFontFamilies(fontFamilies);
+    }
+
+    SkTDArray<FontInitRec> fontInfo;
+    bool firstInFamily = false;
+    for (int i = 0; i < fontFamilies.count(); ++i) {
+        FontFamily *family = fontFamilies[i];
+        firstInFamily = true;
+        for (int j = 0; j < family->fFileNames.count(); ++j) {
+            FontInitRec fontInfoRecord;
+            fontInfoRecord.fFileName = family->fFileNames[j];
+            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
+                        SkDEBUGFAIL("Failed to allocate nameList");
+                        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;
+            }
+            *fontInfo.append() = fontInfoRecord;
+        }
+    }
+    gNumSystemFonts = fontInfo.count();
+    gSystemFonts = (FontInitRec*) malloc(gNumSystemFonts * sizeof(FontInitRec));
+    gFallbackFonts = (uint32_t*) malloc((gNumSystemFonts + 1) * sizeof(uint32_t));
+    if (gSystemFonts == NULL) {
+        // shouldn't get here
+        SkDEBUGFAIL("No system fonts were found");
+        gNumSystemFonts = 0;
+    }
+
+#if SK_DEBUG_FONTS
+    SkDebugf("---- We have %d system fonts", gNumSystemFonts);
+#endif
+    for (size_t i = 0; i < gNumSystemFonts; ++i) {
+        gSystemFonts[i].fFileName = fontInfo[i].fFileName;
+        gSystemFonts[i].fNames = fontInfo[i].fNames;
+#if SK_DEBUG_FONTS
+        SkDebugf("---- gSystemFonts[%d] fileName=%s", i, fontInfo[i].fFileName);
+#endif
+    }
+    fontFamilies.deleteAll();
+}
+
+/*
+ *  Called once (ensured by the sentinel check at the beginning of our body).
+ *  Initializes all the globals, and register the system fonts.
+ *
+ *  gFamilyHeadAndNameListMutex must already be acquired.
+ */
+static void init_system_fonts() {
+    // check if we've already been called
+    if (gDefaultNormal) {
+        return;
+    }
+
+    SkASSERT(gUniqueFontID == 0);
+
+    load_font_info();
+
+    FontInitRec* rec = gSystemFonts;
+    SkTypeface* firstInFamily = NULL;
+    int fallbackCount = 0;
+
+    for (size_t i = 0; i < gNumSystemFonts; i++) {
+        // if we're the first in a new family, clear firstInFamily
+        if (rec[i].fNames != NULL) {
+            firstInFamily = NULL;
+        }
+
+        bool isFixedWidth;
+        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,
+                                &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 find_uniqueID.
+            sk_atomic_inc(&gUniqueFontID);
+            continue;
+        }
+
+        SkTypeface* tf = SkNEW_ARGS(FileTypeface,
+                                    (style,
+                                     true,  // system-font (cannot delete)
+                                     firstInFamily, // what family to join
+                                     rec[i].fFileName,
+                                     isFixedWidth) // filename
+                                    );
+#if SK_DEBUG_FONTS
+        SkDebugf("---- SkTypeface[%d] %s fontID %d",
+                  i, rec[i].fFileName, tf->uniqueID());
+#endif
+
+        if (rec[i].fNames != NULL) {
+            // see if this is one of our fallback fonts
+            if (rec[i].fNames == gFBNames) {
+#if SK_DEBUG_FONTS
+                SkDebugf("---- adding %s as fallback[%d] fontID %d",
+                          rec[i].fFileName, fallbackCount, tf->uniqueID());
+#endif
+                gFallbackFonts[fallbackCount++] = tf->uniqueID();
+
+                // Use the font file name as the name of the typeface.
+                const char **nameList = (const char**)malloc(2 * sizeof(char*));
+                if (nameList == NULL) {
+                    // shouldn't get here
+                    SkDEBUGFAIL("Failed to allocate nameList");
+                    break;
+                }
+                nameList[0] = rec[i].fFileName;
+                nameList[1] = NULL;
+                rec[i].fNames = nameList;
+            }
+
+            firstInFamily = tf;
+            FamilyRec* family = find_family(tf);
+            const char* const* names = rec[i].fNames;
+
+            // record the default family if this is it
+            if (names == gDefaultNames) {
+                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;
+
+//    SkDEBUGCODE(dump_globals());
+}
+
+static size_t find_uniqueID(const char* filename) {
+    // uniqueID is the index, offset by one, of the associated element in
+    // gSystemFonts[] (assumes system fonts are loaded before external fonts)
+    // return 0 if not found
+    const FontInitRec* rec = gSystemFonts;
+    for (size_t i = 0; i < gNumSystemFonts; i++) {
+        if (strcmp(rec[i].fFileName, filename) == 0) {
+            return i+1;
+        }
+    }
+    return 0;
+}
+
+static void reload_fallback_fonts() {
+    if (gTestFallbackConfigFile) {
+        // No need to reload fallback fonts in test environment.
+        return;
+    }
+
+    SkGraphics::PurgeFontCache();
+
+    SkTDArray<FontFamily*> fallbackFamilies;
+    getFallbackFontFamilies(fallbackFamilies);
+
+    int fallbackCount = 0;
+    for (int i = 0; i < fallbackFamilies.count(); ++i) {
+        FontFamily *family = fallbackFamilies[i];
+
+        for (int j = 0; j < family->fFileNames.count(); ++j) {
+            if (family->fFileNames[j]) {
+
+                // ensure the fallback font exists before adding it to the list
+                bool isFixedWidth;
+                SkString name;
+                SkTypeface::Style style;
+                if (!get_name_and_style(family->fFileNames[j], &name, &style,
+                                        &isFixedWidth, false)) {
+                    continue;
+                }
+
+                size_t uniqueID = find_uniqueID(family->fFileNames[j]);
+                SkASSERT(uniqueID != 0);
+#if SK_DEBUG_FONTS
+                SkDebugf("---- reload %s as fallback[%d] fontID %d oldFontID %d",
+                          family->fFileNames[j], fallbackCount, uniqueID,
+                          gFallbackFonts[fallbackCount]);
+#endif
+                gFallbackFonts[fallbackCount++] = uniqueID;
+                break;  // The fallback set contains only the first font of each family
+            }
+        }
+    }
+    // reset the sentinel the end of the newly ordered array
+    gFallbackFonts[fallbackCount] = 0;
+}
+
+static void load_system_fonts() {
+    static AndroidLocale prevLocale;
+    AndroidLocale locale;
+
+    getLocale(locale);
+
+    if (!gDefaultNormal) {
+        prevLocale = locale;
+        init_system_fonts();
+    } else if (strncmp(locale.language, prevLocale.language, 2) ||
+            strncmp(locale.region, prevLocale.region, 2)) {
+        prevLocale = locale;
+        reload_fallback_fonts();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+
+    SkFontDescriptor descriptor;
+    {
+        SkAutoMutexAcquire ac(gFamilyHeadAndNameListMutex);
+        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 {
+        stream->writePackedUInt(0);
+    }
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    {
+        SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+        load_system_fonts();
+    }
+
+    SkFontDescriptor descriptor(stream);
+    const char* familyName = descriptor.getFamilyName();
+    const char* fontFileName = descriptor.getFontFileName();
+    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;
+    }
+
+    if (NULL != fontFileName && 0 != *fontFileName) {
+        const FontInitRec* rec = gSystemFonts;
+        for (size_t i = 0; i < gNumSystemFonts; i++) {
+            if (strcmp(rec[i].fFileName, fontFileName) == 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], style);
+                    }
+                }
+            }
+        }
+    }
+
+    return SkFontHost::CreateTypeface(NULL, familyName, style);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+                                       const char familyName[],
+                                       SkTypeface::Style style) {
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+
+    load_system_fonts();
+
+    // 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 semantic is to return a new instance
+    tf->ref();
+    return tf;
+}
+
+SkStream* SkFontHost::OpenStream(uint32_t fontID) {
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+
+    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) {
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+
+    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) {
+#ifdef SK_BUILD_FOR_ANDROID_NDK
+    // Skia does not support font fallback for ndk applications in order to
+    // enable clients such as WebKit to customize their font selection.
+    // Clients can use GetFallbackFamilyNameForChar() to get the fallback
+    // font for individual characters.
+    return 0;
+#else
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+
+    load_system_fonts();
+
+    const SkTypeface* origTypeface = find_from_uniqueID(origFontID);
+    const SkTypeface* currTypeface = find_from_uniqueID(currFontID);
+
+    SkASSERT(origTypeface != 0);
+    SkASSERT(currTypeface != 0);
+    SkASSERT(gFallbackFonts);
+
+    // Our fallback list always stores the id of the plain in each fallback
+    // family, so we transform currFontID to its plain equivalent.
+    currFontID = find_typeface(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.
+     */
+    const uint32_t* list = gFallbackFonts;
+    for (int i = 0; list[i] != 0; i++) {
+        if (list[i] == currFontID) {
+            if (list[i+1] == 0)
+                return 0;
+            const SkTypeface* nextTypeface = find_from_uniqueID(list[i+1]);
+            return find_typeface(nextTypeface, origTypeface->style())->uniqueID();
+        }
+    }
+
+    // If we get here, currFontID was not a fallback, so we start at the
+    // beginning of our list. Assuming there is at least one fallback font,
+    // i.e. gFallbackFonts[0] != 0.
+    const SkTypeface* firstTypeface = find_from_uniqueID(list[0]);
+    return find_typeface(firstTypeface, origTypeface->style())->uniqueID();
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    if (NULL == stream || stream->getLength() <= 0) {
+        return NULL;
+    }
+
+    bool isFixedWidth;
+    SkTypeface::Style style;
+
+    if (find_name_and_attributes(stream, NULL, &style, &isFixedWidth)) {
+        SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+        // Make sure system fonts are loaded to comply with the assumption of
+        // unique id offset by one in find_uniqueID.
+        load_system_fonts();
+        return SkNEW_ARGS(StreamTypeface, (style, false, NULL, stream, isFixedWidth));
+    } 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
+///////////////////////////////////////////////////////////////////////////////
+
+struct FBScriptInfo {
+    const FallbackScripts fScript;
+    const char* fScriptID;
+    const SkTypeface::Style fStyle;
+    const SkUnichar fChar; // representative character for that script type
+    SkFontID fFontID;
+};
+
+#define SK_DEFINE_SCRIPT_ENTRY(script, style, unichar) \
+    { script, #script, style, unichar, 0 }
+
+static FBScriptInfo gFBScriptInfo[] = {
+    SK_DEFINE_SCRIPT_ENTRY(kArabic_FallbackScript,        SkTypeface::kNormal, 0x0627),
+    SK_DEFINE_SCRIPT_ENTRY(kArmenian_FallbackScript,      SkTypeface::kNormal, 0x0531),
+    SK_DEFINE_SCRIPT_ENTRY(kBengali_FallbackScript,       SkTypeface::kNormal, 0x0981),
+    SK_DEFINE_SCRIPT_ENTRY(kDevanagari_FallbackScript,    SkTypeface::kNormal, 0x0901),
+    SK_DEFINE_SCRIPT_ENTRY(kEthiopic_FallbackScript,      SkTypeface::kNormal, 0x1200),
+    SK_DEFINE_SCRIPT_ENTRY(kGeorgian_FallbackScript,      SkTypeface::kNormal, 0x10A0),
+    SK_DEFINE_SCRIPT_ENTRY(kHebrewRegular_FallbackScript, SkTypeface::kNormal, 0x0591),
+    SK_DEFINE_SCRIPT_ENTRY(kHebrewBold_FallbackScript,    SkTypeface::kBold,   0x0591),
+    SK_DEFINE_SCRIPT_ENTRY(kKannada_FallbackScript,       SkTypeface::kNormal, 0x0C90),
+    SK_DEFINE_SCRIPT_ENTRY(kMalayalam_FallbackScript,     SkTypeface::kNormal, 0x0D10),
+    SK_DEFINE_SCRIPT_ENTRY(kTamilRegular_FallbackScript,  SkTypeface::kNormal, 0x0B82),
+    SK_DEFINE_SCRIPT_ENTRY(kTamilBold_FallbackScript,     SkTypeface::kBold,   0x0B82),
+    SK_DEFINE_SCRIPT_ENTRY(kThai_FallbackScript,          SkTypeface::kNormal, 0x0E01),
+    SK_DEFINE_SCRIPT_ENTRY(kTelugu_FallbackScript,        SkTypeface::kNormal, 0x0C10),
+};
+
+static bool gFBScriptInitialized = false;
+static const int gFBScriptInfoCount = sizeof(gFBScriptInfo) / sizeof(FBScriptInfo);
+
+// ensure that if any value is added to the public enum it is also added here
+SK_COMPILE_ASSERT(gFBScriptInfoCount == kFallbackScriptNumber, FBScript_count_mismatch);
+
+// this function can't be called if the gFamilyHeadAndNameListMutex is already locked
+static bool typefaceContainsChar(SkTypeface* face, SkUnichar uni) {
+    SkPaint paint;
+    paint.setTypeface(face);
+    paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
+
+    uint16_t glyphID;
+    paint.textToGlyphs(&uni, sizeof(uni), &glyphID);
+    return glyphID != 0;
+}
+
+// this function can't be called if the gFamilyHeadAndNameListMutex is already locked
+static SkTypeface* findFallbackTypefaceForChar(SkUnichar uni) {
+    SkASSERT(gFallbackFonts);
+    const uint32_t* list = gFallbackFonts;
+    for (int i = 0; list[i] != 0; i++) {
+        SkTypeface* face;
+        {
+            SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+            face = find_from_uniqueID(list[i]);
+        }
+        if (typefaceContainsChar(face, uni)) {
+            return face;
+        }
+    }
+    return 0;
+}
+
+// this function can't be called if the gFamilyHeadAndNameListMutex is already locked
+static SkFontID findFallbackFontIDForChar(SkUnichar uni, SkTypeface::Style style) {
+    const SkTypeface* tf = findFallbackTypefaceForChar(uni);
+    if (!tf) {
+        return 0;
+    }
+    return find_typeface(tf, style)->uniqueID();
+}
+
+// this function can't be called if the gFamilyHeadAndNameListMutex is already locked
+static void initFBScriptInfo() {
+    if (gFBScriptInitialized) {
+        return;
+    }
+
+    // ensure the system fonts are loaded
+    gFamilyHeadAndNameListMutex.acquire();
+    load_system_fonts();
+    gFamilyHeadAndNameListMutex.release();
+
+    for (int i = 0; i < gFBScriptInfoCount; i++) {
+        FBScriptInfo& scriptInfo = gFBScriptInfo[i];
+        // selects the best available style for the desired font. However, if
+        // bold is requested and no bold font exists for the typeface containing
+        // the character the next best style is chosen (e.g. normal).
+        scriptInfo.fFontID = findFallbackFontIDForChar(scriptInfo.fChar, scriptInfo.fStyle);
+#if SK_DEBUG_FONTS
+        SkDebugf("gFBScriptInfo[%s] --> %d", scriptInfo.fScriptID, scriptInfo.fFontID);
+#endif
+    }
+    // mark the value as initialized so we don't repeat our work unnecessarily
+    gFBScriptInitialized = true;
+}
+
+SkTypeface* SkCreateTypefaceForScript(FallbackScripts script) {
+    if (!SkTypeface_ValidScript(script)) {
+        return NULL;
+    }
+
+    // ensure that our table is populated
+    initFBScriptInfo();
+
+    FBScriptInfo& scriptInfo = gFBScriptInfo[script];
+
+    // ensure the element with that index actually maps to the correct script
+    SkASSERT(scriptInfo.fScript == script);
+
+    // if a suitable script could not be found then return NULL
+    if (scriptInfo.fFontID == 0) {
+        return NULL;
+    }
+
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+
+    // retrieve the typeface the corresponds to this fontID
+    SkTypeface* tf = find_from_uniqueID(scriptInfo.fFontID);
+    // we ref(), since the semantic is to return a new instance
+    tf->ref();
+    return tf;
+}
+
+const char* SkGetFallbackScriptID(FallbackScripts script) {
+    for (int i = 0; i < gFBScriptInfoCount; i++) {
+        if (gFBScriptInfo[i].fScript == script) {
+            return gFBScriptInfo[i].fScriptID;
+        }
+    }
+    return NULL;
+}
+
+FallbackScripts SkGetFallbackScriptFromID(const char* id) {
+    for (int i = 0; i < gFBScriptInfoCount; i++) {
+        if (strcmp(gFBScriptInfo[i].fScriptID, id) == 0) {
+            return gFBScriptInfo[i].fScript;
+        }
+    }
+    return kFallbackScriptNumber; // Use kFallbackScriptNumber as an invalid value.
+}
+
+SkTypeface* SkCreateFallbackTypefaceForChar(SkUnichar uni,
+                                            SkTypeface::Style style) {
+    {
+        SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+        load_system_fonts();
+    }
+
+    SkTypeface* tf = findFallbackTypefaceForChar(uni);
+    if (!tf) {
+        return NULL;
+    }
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+    tf = find_typeface(tf, style);
+    // we ref(), since the semantic is to return a new instance
+    tf->ref();
+    return tf;
+}
+
+bool SkGetFallbackFamilyNameForChar(SkUnichar uni, SkString* name) {
+    SkASSERT(name);
+    {
+        SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+        load_system_fonts();
+    }
+
+    const SkTypeface* tf = findFallbackTypefaceForChar(uni);
+    if (!tf) {
+        return false;
+    }
+    SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
+    name->set(find_family_name(tf));
+    return true;
+}
+
+void SkUseTestFontConfigFile(const char* mainconf, const char* fallbackconf,
+                             const char* fontsdir) {
+    gTestMainConfigFile = mainconf;
+    gTestFallbackConfigFile = fallbackconf;
+    gTestFontFilePrefix = fontsdir;
+    SkASSERT(gTestMainConfigFile);
+    SkASSERT(gTestFallbackConfigFile);
+    SkASSERT(gTestFontFilePrefix);
+    SkDEBUGF(("Use Test Config File Main %s, Fallback %s, Font Dir %s",
+              gTestMainConfigFile, gTestFallbackConfigFile, gTestFontFilePrefix));
+}
diff --git a/src/ports/SkFontHost_ascender.cpp b/src/ports/SkFontHost_ascender.cpp
new file mode 100644
index 0000000..067d474
--- /dev/null
+++ b/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/src/ports/SkFontHost_fontconfig.cpp b/src/ports/SkFontHost_fontconfig.cpp
new file mode 100644
index 0000000..e028a01
--- /dev/null
+++ b/src/ports/SkFontHost_fontconfig.cpp
@@ -0,0 +1,353 @@
+
+/*
+ * 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[],
+                                       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/src/ports/SkFontHost_freetype_mac.cpp b/src/ports/SkFontHost_freetype_mac.cpp
new file mode 100644
index 0000000..652fb46
--- /dev/null
+++ b/src/ports/SkFontHost_freetype_mac.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 "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[],
+                                       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_linux.cpp b/src/ports/SkFontHost_linux.cpp
new file mode 100644
index 0000000..adcee49
--- /dev/null
+++ b/src/ports/SkFontHost_linux.cpp
@@ -0,0 +1,603 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkDescriptor.h"
+#include "SkMMapStream.h"
+#include "SkOSFile.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkStream.h"
+#include "SkThread.h"
+#include "SkTSearch.h"
+
+#ifndef SK_FONT_FILE_PREFIX
+    #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);
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+}
+
+/*  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 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();
+    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_directory_fonts(const SkString& directory, unsigned int* count) {
+    SkOSFile::Iter  iter(directory.c_str(), ".ttf");
+    SkString        name;
+
+    while (iter.next(&name, false)) {
+        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;
+        }
+
+        FamilyRec* family = find_familyrec(realname.c_str());
+        if (family && family->fFaces[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;
+    }
+
+    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);
+    }
+
+    // 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);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+
+    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 {
+        stream->writePackedUInt(0);
+    }
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    load_system_fonts();
+
+    SkFontDescriptor descriptor(stream);
+    const char* familyName = descriptor.getFamilyName();
+    const char* typefaceName = descriptor.getFontFileName();
+    const SkTypeface::Style style = descriptor.getStyle();
+
+    const uint32_t customFontDataLength = stream->readPackedUInt();
+    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, familyName, style);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+                                       const char familyName[],
+                                       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/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
new file mode 100755
index 0000000..7d12f53
--- /dev/null
+++ b/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/src/ports/SkFontHost_mac_atsui.cpp b/src/ports/SkFontHost_mac_atsui.cpp
new file mode 100644
index 0000000..84f7f0b
--- /dev/null
+++ b/src/ports/SkFontHost_mac_atsui.cpp
@@ -0,0 +1,593 @@
+
+/*
+ * Copyright 2006 The Android 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, SkTypeface*) {
+    // 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[],
+                            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/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp
new file mode 100644
index 0000000..e385337
--- /dev/null
+++ b/src/ports/SkFontHost_mac_coretext.cpp
@@ -0,0 +1,1954 @@
+
+/*
+ * Copyright 2006 The Android 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 "SkColorPriv.h"
+#include "SkDescriptor.h"
+#include "SkEndian.h"
+#include "SkFontDescriptor.h"
+#include "SkFloatingPoint.h"
+#include "SkGlyph.h"
+#include "SkMaskGamma.h"
+#include "SkPaint.h"
+#include "SkPath.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 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
+//
+#if 0 // UNUSED
+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;
+    }
+}
+#endif
+
+#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 isMountainLion() {
+    return darwinVersion() == 12;
+}
+
+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 (CGFloat) 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 SkScalar getFontScale(CGFontRef cgFont) {
+    int unitsPerEm = CGFontGetUnitsPerEm(cgFont);
+    return SkScalarInvert(SkIntToScalar(unitsPerEm));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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;
+    }
+    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);
+    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;
+}
+
+class Offscreen {
+public:
+    Offscreen();
+    ~Offscreen();
+
+    CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
+                      CGGlyph glyphID, size_t* rowBytesPtr,
+                      bool generateA8FromLCD);
+
+private:
+    enum {
+        kSize = 32 * 32 * sizeof(CGRGBPixel)
+    };
+    SkAutoSMalloc<kSize> fImageStorage;
+    CGColorSpaceRef fRGBSpace;
+
+    // cached state
+    CGContextRef    fCG;
+    SkISize         fSize;
+    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) {
+    SkFontID id = 0;
+// CTFontGetPlatformFont and ATSFontRef are not supported on iOS, so we have to
+// bracket this to be Mac only.
+#ifdef SK_BUILD_FOR_MAC
+    ATSFontRef ats = CTFontGetPlatformFont(fontRef, NULL);
+    id = (SkFontID)ats;
+    if (id != 0) {
+        id &= 0x3FFFFFFF; // make top two bits 00
+        return id;
+    }
+#endif
+    // CTFontGetPlatformFont returns NULL if the font is local
+    // (e.g., was created by a CSS3 @font-face rule).
+    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 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;
+    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[],
+                                       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) 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, SkMaskGamma::PreBlend* maskPreBlend) 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();
+
+private:
+    CGAffineTransform                   fTransform;
+    SkMatrix                            fUnitMatrix; // without font size
+    SkMatrix                            fVerticalMatrix; // unit rotated
+    SkMatrix                            fMatrix; // with font size
+    SkMatrix                            fAdjustBadMatrix; // lion-specific fix
+    Offscreen                           fOffscreen;
+    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,
+                             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();
+    }
+
+    // default to kBW_Format
+    bool doAA = false;
+    bool doLCD = false;
+
+    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);
+        }
+        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);
+
+        // 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;
+    }
+
+    if (fDoAA != doAA) {
+        CGContextSetShouldAntialias(fCG, doAA);
+        fDoAA = doAA;
+    }
+    if (fDoLCD != doLCD) {
+        CGContextSetShouldSmoothFonts(fCG, doLCD);
+        fDoLCD = doLCD;
+    }
+
+    CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
+    // skip rows based on the glyph's height
+    image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
+
+    // erase to black
+    sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
+
+    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 = {CGToScalar(vertOffset.width),
+                           CGToScalar(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_Check(theAdvance.width);
+    glyph->fAdvanceY = -SkFloatToFixed_Check(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() || isMountainLion()) {
+        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 = SkToU16(sk_float_ceil2int(theBounds.size.width));
+        glyph->fHeight = SkToU16(sk_float_ceil2int(theBounds.size.height));
+    } else {
+        glyph->fWidth = SkToU16(sk_float_round2int(theBounds.size.width));
+        glyph->fHeight = SkToU16(sk_float_round2int(theBounds.size.height));
+    }
+    glyph->fTop      = SkToS16(-sk_float_round2int(CGRectGetMaxY_inline(theBounds)));
+    glyph->fLeft     = SkToS16(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 = sk_float_pow(x, ee);
+        int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
+        table[i] = SkToU8(xx);
+    }
+}
+
+/**
+ *  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(gTableCoreGraphicsSmoothing, 2.0f);
+        gInited = true;
+    }
+    return gTableCoreGraphicsSmoothing;
+}
+
+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;
+    }
+}
+
+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;
+
+    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);
+        }
+        cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+        dst += dstRB;
+    }
+}
+
+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);
+}
+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;
+
+    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);
+    }
+}
+
+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);
+    }
+}
+
+template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
+    return (T*)((char*)ptr + byteOffset);
+}
+
+void SkScalerContext_Mac::generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) {
+    CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
+
+    // FIXME: lcd smoothed un-hinted rasterization unsupported.
+    bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
+
+    // Draw the glyph
+    size_t cgRowBytes;
+    CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
+    if (cgPixels == NULL) {
+        return;
+    }
+
+    //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.
+
+    // 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);
+        }
+    }
+
+    // Must be careful not to use these if maskPreBlend == NULL
+    const uint8_t* tableR = NULL;
+    const uint8_t* tableG = NULL;
+    const uint8_t* tableB = NULL;
+    if (maskPreBlend) {
+        tableR = maskPreBlend->fR;
+        tableG = maskPreBlend->fG;
+        tableB = maskPreBlend->fB;
+    }
+
+    // Convert glyph to mask
+    switch (glyph.fMaskFormat) {
+        case SkMask::kLCD32_Format: {
+            if (maskPreBlend) {
+                rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph, tableR, tableG, tableB);
+            } else {
+                rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph, tableR, tableG, tableB);
+            }
+        } break;
+        case SkMask::kLCD16_Format: {
+            if (maskPreBlend) {
+                rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph, tableR, tableG, tableB);
+            } else {
+                rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph, tableR, tableG, tableB);
+            }
+        } break;
+        case SkMask::kA8_Format: {
+            if (maskPreBlend) {
+                rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, tableG);
+            } else {
+                rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, tableG);
+            }
+        } 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;
+    }
+}
+
+/*
+ *  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);
+    AutoCFRelease autoSetRelease(charSet);
+    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;
+}
+
+// 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.
+    int 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);
+    SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
+
+    {
+        CFStringRef fontName = CTFontCopyPostScriptName(ctFont);
+        CFStringToSkString(fontName, &info->fFontName);
+        CFRelease(fontName);
+    }
+
+    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 = (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::MakeLTRB(
+            CGToScalar(CGRectGetMinX_inline(bbox)),   // Left
+            CGToScalar(CGRectGetMaxY_inline(bbox)),   // Top
+            CGToScalar(CGRectGetMaxX_inline(bbox)),   // Right
+            CGToScalar(CGRectGetMinY_inline(bbox)));  // Bottom
+
+    // 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 = (int16_t) 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"
+
+// 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) {
+    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) {
+    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());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface*) {
+    unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
+                                  SkScalerContext::kAutohinting_Flag;
+
+    rec->fFlags &= ~flagsWeDontSupport;
+
+    // Only two levels of hinting are supported.
+    // kNo_Hinting means avoid CoreGraphics outline dilation.
+    // kNormal_Hinting means CoreGraphics outline dilation is allowed.
+    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);
+
+    // 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.
+
+    bool lcdSupport = supports_LCD();
+    if (isLCDFormat(rec->fMaskFormat)) {
+        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;
+        }
+    }
+
+    if (lcdSupport) {
+        //CoreGraphics dialates smoothed text as needed.
+        rec->setContrast(0);
+    } else {
+#ifndef SK_GAMMA_APPLY_TO_A8
+        rec->ignorePreBlend();
+#endif
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+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) {
+            uintptr_t fontTag = reinterpret_cast<uintptr_t>(
+                    CFArrayGetValueAtIndex(cfArray, i));
+            tags[i] = static_cast<SkFontTableTag>(fontTag);
+        }
+    }
+    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/src/ports/SkFontHost_none.cpp b/src/ports/SkFontHost_none.cpp
new file mode 100644
index 0000000..21eda8e
--- /dev/null
+++ b/src/ports/SkFontHost_none.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 "SkFontHost.h"
+#include "SkScalerContext.h"
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+                                     const char famillyName[],
+                                     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,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
+    SkDEBUGFAIL("SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
+    return NULL;
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface*) {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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/src/ports/SkFontHost_sandbox_none.cpp b/src/ports/SkFontHost_sandbox_none.cpp
new file mode 100644
index 0000000..a52bbff
--- /dev/null
+++ b/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/src/ports/SkFontHost_simple.cpp b/src/ports/SkFontHost_simple.cpp
new file mode 100644
index 0000000..ac3c713
--- /dev/null
+++ b/src/ports/SkFontHost_simple.cpp
@@ -0,0 +1,645 @@
+
+/*
+ * 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], (SkTypeface::Style)style);
+                    }
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+                                       const char familyName[],
+                                       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,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
+    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/src/ports/SkFontHost_tables.cpp b/src/ports/SkFontHost_tables.cpp
new file mode 100644
index 0000000..9878119
--- /dev/null
+++ b/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/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
new file mode 100755
index 0000000..f2603e9
--- /dev/null
+++ b/src/ports/SkFontHost_win.cpp
@@ -0,0 +1,1748 @@
+
+/*
+ * Copyright 2006 The Android 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 "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"
+
+#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;
+
+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 SkFixed SkFIXEDToFixed(FIXED x) {
+    return *(SkFixed*)(&x);
+}
+
+static inline FIXED SkScalarToFIXED(SkScalar x) {
+    return SkFixedToFIXED(SkScalarToFixed(x));
+}
+
+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'));
+    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, 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);
+        SkFontID fontID = SkTypefaceCache::NewFontID();
+        return new LogFontTypeface(style, fontID, lf);
+    }
+};
+
+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 = static_cast<LogFontTypeface*>(face);
+    const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
+
+    return lface &&
+           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;
+}
+
+/**
+ *  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 = static_cast<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 = static_cast<LogFontTypeface*>(SkTypefaceCache::FindByID(fontID));
+    if (face) {
+        SkFontHost::EnsureTypefaceAccessible(*face);
+    }
+}
+
+static void GetLogFontByID(SkFontID fontID, LOGFONT* lf) {
+    LogFontTypeface* face = static_cast<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;
+    }
+
+    ~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, size_t* srcRBPtr);
+
+private:
+    HDC     fDC;
+    HBITMAP fBM;
+    HFONT   fFont;
+    XFORM   fXform;
+    void*   fBits;  // points into fBM
+    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,
+                               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);
+
+        COLORREF color = 0x00FFFFFF;
+        COLORREF prev = SetTextColor(fDC, color);
+        SkASSERT(prev != CLR_INVALID);
+    }
+
+    if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
+        DeleteObject(fBM);
+        fBM = 0;
+    }
+    fIsBW = isBW;
+
+    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;
+    memset(fBits, 0, 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() 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, SkMaskGamma::PreBlend* maskPreBlend) 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;
+    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;
+
+    /**
+     *  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) {
+    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);
+
+    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 (!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)) {
+        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 (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);
+}
+
+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) {
+        if (fType == SkScalerContext_Windows::kBitmap_Type) {
+           return fTM.tmLastChar;
+        }
+        fGlyphCount = calculateOutlineGlyphCount(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);
+
+    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));
+
+    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;
+
+            apply_outset(glyph, fOutset);
+        }
+
+        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->zeroMetrics();
+    }
+}
+
+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);
+
+    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);
+    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 = sk_float_pow(x, ee);
+        int xx = SkScalarRound(SkFloatToScalar(x * 255));
+        table[i] = SkToU8(xx);
+    }
+}
+
+/**
+ *  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 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(gTableClearType, level / 1000.0f);
+        gInited = true;
+    }
+    return gTableClearType;
+}
+
+#include "SkColorPriv.h"
+
+//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);
+}
+
+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);
+}
+
+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
+// 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;
+}
+
+// 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) {
+    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] & (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 = SkTAddByteOffset(src, srcRB);
+        dst -= dstRB;
+    }
+}
+
+template<bool APPLY_PREBLEND>
+static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
+                      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<APPLY_PREBLEND>(src[i], table8);
+        }
+        src = SkTAddByteOffset(src, srcRB);
+        dst -= dstRB;
+    }
+}
+
+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<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
+        }
+        src = SkTAddByteOffset(src, srcRB);
+        dst = (uint16_t*)((char*)dst - dstRB);
+    }
+}
+
+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;
+    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<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
+        }
+        src = SkTAddByteOffset(src, srcRB);
+        dst = (uint32_t*)((char*)dst - dstRB);
+    }
+}
+
+static inline unsigned clamp255(unsigned x) {
+    SkASSERT(x <= 256);
+    return x - (x >> 8);
+}
+
+void SkScalerContext_Windows::generateImage(const SkGlyph& glyph, SkMaskGamma::PreBlend* maskPreBlend) {
+    SkAutoMutexAcquire ac(gFTMutex);
+    SkASSERT(fDDC);
+
+    //Must be careful not to use these if maskPreBlend == NULL
+    const uint8_t* tableR = NULL;
+    const uint8_t* tableG = NULL;
+    const uint8_t* tableB = NULL;
+    if (maskPreBlend) {
+        tableR = maskPreBlend->fR;
+        tableG = maskPreBlend->fG;
+        tableB = maskPreBlend->fB;
+    }
+
+    const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
+    const bool isAA = !isLCD(fRec);
+
+    size_t srcRB;
+    const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
+    if (NULL == bits) {
+        ensure_typeface_accessible(fRec.fFontID);
+        bits = fOffscreen.draw(glyph, isBW, &srcRB);
+        if (NULL == bits) {
+            sk_bzero(glyph.fImage, glyph.computeImageSize());
+            return;
+        }
+    }
+
+    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) {
+                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;
+        if (maskPreBlend) {
+            rgb_to_a8<true>(src, srcRB, glyph, tableG);
+        } else {
+            rgb_to_a8<false>(src, srcRB, glyph, tableG);
+        }
+    } else {    // LCD16
+        const SkGdiRGB* src = (const SkGdiRGB*)bits;
+        if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
+            rgb_to_bw(src, srcRB, glyph);
+            ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
+        } else {
+            if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+                if (maskPreBlend) {
+                    rgb_to_lcd16<true>(src, srcRB, glyph, tableR, tableG, tableB);
+                } else {
+                    rgb_to_lcd16<false>(src, srcRB, glyph, tableR, tableG, tableB);
+                }
+            } else {
+                SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
+                if (maskPreBlend) {
+                    rgb_to_lcd32<true>(src, srcRB, glyph, tableR, tableG, tableB);
+                } else {
+                    rgb_to_lcd32<false>(src, srcRB, glyph, tableR, tableG, tableB);
+                }
+            }
+        }
+    }
+}
+
+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);
+}
+
+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 logfont_to_name(const LOGFONT& lf, SkString* s) {
+#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).
+    s->resize(str_len);
+    // Now actually convert the string.
+    WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1,
+                        s->writable_str(), str_len,
+                        NULL, NULL);
+#else
+    s->set(lf.lfFaceName);
+#endif
+}
+
+void SkFontHost::Serialize(const SkTypeface* rawFace, SkWStream* stream) {
+    const LogFontTypeface* face = static_cast<const LogFontTypeface*>(rawFace);
+    SkFontDescriptor descriptor(face->style());
+
+    SkString familyName;
+    logfont_to_name(face->fLogFont, &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) {
+    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) {
+    // 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 = calculateOutlineGlyphCount(hdc);
+
+    info = new SkAdvancedTypefaceMetrics;
+    info->fEmSize = otm.otmEMSquare;
+    info->fMultiMaster = false;
+    info->fLastGlyphID = SkToU16(glyphCount - 1);
+    info->fStyle = 0;
+    logfont_to_name(lf, &info->fFontName);
+
+    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
+        populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
+    }
+
+    if (glyphCount > 0 &&
+        (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;
+}
+
+//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)
+
+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) {
+    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[],
+                                       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 {
+        logfont_for_name(familyName, lf);
+    }
+    setStyle(&lf, style);
+    return SkCreateTypefaceFromLOGFONT(lf);
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    SkTypeface* face = NULL;
+    SkAutoTUnref<SkFILEStream> stream(SkNEW_ARGS(SkFILEStream, (path)));
+
+    if (stream->isValid()) {
+        face = CreateTypefaceFromStream(stream);
+    }
+    return face;
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface* typeface) {
+    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);
+
+// 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
+
+    LogFontTypeface* logfontTypeface = static_cast<LogFontTypeface*>(typeface);
+    if (!logfontTypeface->fCanBeLCD && isLCD(*rec)) {
+        rec->fMaskFormat = SkMask::kA8_Format;
+        rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag;
+    }
+}
diff --git a/src/ports/SkFontHost_win_dw.cpp b/src/ports/SkFontHost_win_dw.cpp
new file mode 100644
index 0000000..82d44ee
--- /dev/null
+++ b/src/ports/SkFontHost_win_dw.cpp
@@ -0,0 +1,1569 @@
+/*
+ * Copyright 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,
+                               SkMaskGamma::PreBlend* maskPreBlend) 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;
+    mat.setAll(fRec.fPost2x2[0][0], fRec.fPost2x2[0][1], 0,
+               fRec.fPost2x2[1][0], fRec.fPost2x2[1][1], 0,
+               0, 0, SkScalarToPersp(SK_Scalar1));
+    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,
+                                            SkMaskGamma::PreBlend* maskPreBlend) {
+    SkAutoMutexAcquire ac(gFTMutex);
+
+    //Must be careful not to use these if maskPreBlend == NULL
+    const uint8_t* tableR = NULL;
+    const uint8_t* tableG = NULL;
+    const uint8_t* tableB = NULL;
+    if (maskPreBlend) {
+        tableR = maskPreBlend->fR;
+        tableG = maskPreBlend->fG;
+        tableB = maskPreBlend->fB;
+    }
+
+    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 (maskPreBlend) {
+            rgb_to_a8<true>(src, glyph, tableG);
+        } else {
+            rgb_to_a8<false>(src, glyph, tableG);
+        }
+    } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+        if (maskPreBlend) {
+            rgb_to_lcd16<true>(src, glyph, tableR, tableG, tableB);
+        } else {
+            rgb_to_lcd16<false>(src, glyph, tableR, tableG, tableB);
+        }
+    } else {
+        SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
+        if (maskPreBlend) {
+            rgb_to_lcd32<true>(src, glyph, tableR, tableG, tableB);
+        } else {
+            rgb_to_lcd32<false>(src, glyph, tableR, tableG, tableB);
+        }
+    }
+}
+
+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.");
+}
+
+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
new file mode 100644
index 0000000..d05af70
--- /dev/null
+++ b/src/ports/SkGlobalInitialization_chromium.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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"
+#include "SkMagnifierImageFilter.h"
+
+void SkFlattenable::InitializeFlattenables() {
+
+    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
new file mode 100644
index 0000000..a764dd1
--- /dev/null
+++ b/src/ports/SkGlobalInitialization_default.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 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 "SkBitmapProcShader.h"
+#include "SkImageRef_ashmem.h"
+#include "SkMallocPixelRef.h"
+#include "SkPathEffect.h"
+#include "SkPixelRef.h"
+#include "SkXfermode.h"
+
+#include "Sk1DPathEffect.h"
+#include "Sk2DPathEffect.h"
+#include "SkAvoidXfermode.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 "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 "SkMorphologyImageFilter.h"
+#include "SkPixelXorXfermode.h"
+#include "SkStippleMaskFilter.h"
+#include "SkTableColorFilter.h"
+#include "SkTestImageFilters.h"
+
+void SkFlattenable::InitializeFlattenables() {
+
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAvoidXfermode)
+    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(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)
+
+    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
new file mode 100644
index 0000000..12f37f5
--- /dev/null
+++ b/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/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp
new file mode 100644
index 0000000..bcd3e37
--- /dev/null
+++ b/src/ports/SkImageDecoder_CG.cpp
@@ -0,0 +1,204 @@
+
+/*
+ * 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>
+#include <ImageIO/ImageIO.h>
+#include <MobileCoreServices/MobileCoreServices.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) {
+    // 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(*bmPtr);
+    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/src/ports/SkImageDecoder_WIC.cpp b/src/ports/SkImageDecoder_WIC.cpp
new file mode 100644
index 0000000..79d107a
--- /dev/null
+++ b/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/src/ports/SkImageDecoder_empty.cpp b/src/ports/SkImageDecoder_empty.cpp
new file mode 100644
index 0000000..410eef1
--- /dev/null
+++ b/src/ports/SkImageDecoder_empty.cpp
@@ -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.
+ */
+
+
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkMovie.h"
+
+class SkBitmap;
+class SkStream;
+
+SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
+    return NULL;
+}
+
+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;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+#include "SkImages.h"
+
+void SkImages::InitializeFlattenables() {}
diff --git a/src/ports/SkImageRef_ashmem.cpp b/src/ports/SkImageRef_ashmem.cpp
new file mode 100644
index 0000000..cdd56c8
--- /dev/null
+++ b/src/ports/SkImageRef_ashmem.cpp
@@ -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 "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);
+    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;
+    const char* uri = buffer.readString();
+    if (uri) {
+        setURI(uri);
+        sk_free(uri);
+    }
+    this->useDefaultMutex();   // we don't need/want the shared imageref mutex
+}
diff --git a/src/ports/SkImageRef_ashmem.h b/src/ports/SkImageRef_ashmem.h
new file mode 100644
index 0000000..f98507a
--- /dev/null
+++ b/src/ports/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/ports/SkMemory_brew.cpp b/src/ports/SkMemory_brew.cpp
new file mode 100644
index 0000000..96af702
--- /dev/null
+++ b/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/src/ports/SkMemory_malloc.cpp b/src/ports/SkMemory_malloc.cpp
new file mode 100644
index 0000000..44e43ad
--- /dev/null
+++ b/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/src/ports/SkMemory_mozalloc.cpp b/src/ports/SkMemory_mozalloc.cpp
new file mode 100644
index 0000000..1f16ee5
--- /dev/null
+++ b/src/ports/SkMemory_mozalloc.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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_brew.cpp b/src/ports/SkOSFile_brew.cpp
new file mode 100644
index 0000000..50e133f
--- /dev/null
+++ b/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/src/ports/SkOSFile_stdio.cpp b/src/ports/SkOSFile_stdio.cpp
new file mode 100644
index 0000000..1254394
--- /dev/null
+++ b/src/ports/SkOSFile_stdio.cpp
@@ -0,0 +1,100 @@
+
+/*
+ * Copyright 2006 The Android 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);
+
+    long curr = ::ftell((FILE*)f);       // remember where we are
+    if (curr < 0) {
+        return 0;
+    }
+    ::fseek((FILE*)f, 0, SEEK_END);         // go to the end
+    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;
+}
+
+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/src/ports/SkThread_none.cpp b/src/ports/SkThread_none.cpp
new file mode 100644
index 0000000..1122c95
--- /dev/null
+++ b/src/ports/SkThread_none.cpp
@@ -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.
+ */
+
+
+#include "SkThread.h"
+#include "SkTLS.h"
+
+int32_t sk_atomic_inc(int32_t* addr) {
+    int32_t value = *addr;
+    *addr = value + 1;
+    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
new file mode 100644
index 0000000..b430dd9
--- /dev/null
+++ b/src/ports/SkThread_pthread.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 "SkThread.h"
+#include "SkTLS.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_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
+
+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_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);
+
+    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)
+{
+    SkAutoMutexAcquire ac(gAtomicMutex);
+
+    int32_t value = *addr;
+    if (value != 0) ++*addr;
+    return value;
+}
+void sk_membar_aquire__after_atomic_conditional_inc() { }
+
+#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
+
+///////////////////////////////////////////////////////////////////////////////
+
+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
new file mode 100644
index 0000000..91a5ceb
--- /dev/null
+++ b/src/ports/SkThread_win.cpp
@@ -0,0 +1,95 @@
+
+/*
+ * 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"
+#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, _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),
+                      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));
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+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);
+}
+
diff --git a/src/ports/SkTime_Unix.cpp b/src/ports/SkTime_Unix.cpp
new file mode 100644
index 0000000..f519a69
--- /dev/null
+++ b/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/src/ports/SkTime_win.cpp b/src/ports/SkTime_win.cpp
new file mode 100644
index 0000000..37af9f2
--- /dev/null
+++ b/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/src/ports/SkXMLParser_empty.cpp b/src/ports/SkXMLParser_empty.cpp
new file mode 100644
index 0000000..09b222e
--- /dev/null
+++ b/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/src/ports/SkXMLParser_expat.cpp b/src/ports/SkXMLParser_expat.cpp
new file mode 100644
index 0000000..8c1c2bf
--- /dev/null
+++ b/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/src/ports/SkXMLParser_tinyxml.cpp b/src/ports/SkXMLParser_tinyxml.cpp
new file mode 100644
index 0000000..de1fbe5
--- /dev/null
+++ b/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/src/ports/SkXMLPullParser_expat.cpp b/src/ports/SkXMLPullParser_expat.cpp
new file mode 100644
index 0000000..5fcdff3
--- /dev/null
+++ b/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.reset();
+
+    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/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..5167e3c
--- /dev/null
+++ b/src/sfnt/SkOTTableTypes.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 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 int16_t SK_OT_SHORT;
+typedef uint16_t SK_OT_USHORT;
+typedef uint32_t SK_OT_ULONG;
+typedef int32_t SK_OT_LONG;
+//16.16 Fixed point representation.
+typedef int32_t SK_OT_Fixed;
+//F units are the units of measurement in em space.
+typedef int16_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..3f74426
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2.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 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 {
+        //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..25e8c9c
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_V0.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 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;
+    //The only way to differentiate versionA and version0 is by size.
+    static const SK_OT_USHORT version0 = 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..47d9921
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_V1.h
@@ -0,0 +1,517 @@
+/*
+ * Copyright 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 version1 = 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..ee1a2dd
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_V2.h
@@ -0,0 +1,539 @@
+/*
+ * Copyright 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 version2 = 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..ffd6d6a
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_V3.h
@@ -0,0 +1,549 @@
+/*
+ * Copyright 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 version3 = 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..64c6e8c
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_V4.h
@@ -0,0 +1,584 @@
+/*
+ * Copyright 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 version4 = 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..22a022c
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_VA.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright 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;
+    //The only way to differentiate versionA and version0 is by size.
+    static const SK_OT_USHORT version0 = 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_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_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..6e94437
--- /dev/null
+++ b/src/sfnt/SkOTUtils.cpp
@@ -0,0 +1,152 @@
+/*
+ * Copyright 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"
+
+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;
+    SkSFNTTableDirectoryEntry 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.
+    SkSFNTTableDirectoryEntry* currentEntry = reinterpret_cast<SkSFNTTableDirectoryEntry*>(data + sizeof(SkSFNTHeader));
+    SkSFNTTableDirectoryEntry* endEntry = currentEntry + numTables;
+    SkSFNTTableDirectoryEntry* 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 == tableEntry.tag) {
+            headTableEntry = currentEntry;
+        }
+    }
+
+    // Make the table directory entry point to the new 'name' table.
+    SkSFNTTableDirectoryEntry* nameTableEntry = reinterpret_cast<SkSFNTTableDirectoryEntry*>(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..1398de6
--- /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 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..c220656
--- /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 SkSFNTTableDirectoryEntry {
+    SK_SFNT_ULONG tag;
+    SK_SFNT_ULONG checksum;
+    SK_SFNT_ULONG offset; //From beginning of header.
+    SK_SFNT_ULONG logicalLength;
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkSFNTHeader) == 12, sizeof_SkSFNTHeader_not_12);
+SK_COMPILE_ASSERT(sizeof(SkSFNTTableDirectoryEntry) == 16, sizeof_SkSFNTTableDirectoryEntry_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/SkSVG.cpp b/src/svg/SkSVG.cpp
new file mode 100644
index 0000000..fdfc13a
--- /dev/null
+++ b/src/svg/SkSVG.cpp
@@ -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.
+ */
+
+
+#include "SkSVG.h"
+#include 'SkSVGParser.h"
+
+SkSVG::SkSVG() {
+}
+
+SkSVG::~SkSVG() {
+}
+
+bool SkSVG::decodeStream(SkStream* stream);
+{
+    size_t size = stream->read(nil, 0);
+    SkAutoMalloc    storage(size);
+    char* data = (char*)storage.get();
+    size_t actual = stream->read(data, size);
+    SkASSERT(size == actual);
+    SkSVGParser parser(*fMaker);
+    return parser.parse(data, actual, &fErrorCode, &fErrorLineNumber);
+}
diff --git a/src/svg/SkSVGCircle.cpp b/src/svg/SkSVGCircle.cpp
new file mode 100644
index 0000000..e34e179
--- /dev/null
+++ b/src/svg/SkSVGCircle.cpp
@@ -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.
+ */
+
+
+#include "SkSVGCircle.h"
+#include "SkSVGParser.h"
+#include "SkParse.h"
+#include <stdio.h>
+
+const SkSVGAttribute SkSVGCircle::gAttributes[] = {
+    SVG_ATTRIBUTE(cx),
+    SVG_ATTRIBUTE(cy),
+    SVG_ATTRIBUTE(r)
+};
+
+DEFINE_SVG_INFO(Circle)
+
+void SkSVGCircle::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("oval");
+    INHERITED::translate(parser, defState);
+    SkScalar cx, cy, r;
+    SkParse::FindScalar(f_cx.c_str(), &cx);
+    SkParse::FindScalar(f_cy.c_str(), &cy);
+    SkParse::FindScalar(f_r.c_str(), &r);
+    SkScalar left, top, right, bottom;
+    left = cx - r;
+    top = cy - r;
+    right = cx + r;
+    bottom = cy + r;
+    char scratch[16];
+    sprintf(scratch, "%g", SkScalarToDouble(left));
+    parser._addAttribute("left", scratch);
+    sprintf(scratch, "%g", SkScalarToDouble(top));
+    parser._addAttribute("top", scratch);
+    sprintf(scratch, "%g", SkScalarToDouble(right));
+    parser._addAttribute("right", scratch);
+    sprintf(scratch, "%g", SkScalarToDouble(bottom));
+    parser._addAttribute("bottom", scratch);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGCircle.h b/src/svg/SkSVGCircle.h
new file mode 100644
index 0000000..1185162
--- /dev/null
+++ b/src/svg/SkSVGCircle.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 SkSVGCircle_DEFINED
+#define SkSVGCircle_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGCircle : public SkSVGElement {
+    DECLARE_SVG_INFO(Circle);
+private:
+    SkString f_cx;
+    SkString f_cy;
+    SkString f_r;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGCircle_DEFINED
diff --git a/src/svg/SkSVGClipPath.cpp b/src/svg/SkSVGClipPath.cpp
new file mode 100644
index 0000000..fa3a799
--- /dev/null
+++ b/src/svg/SkSVGClipPath.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 "SkSVGClipPath.h"
+#include "SkSVGParser.h"
+#include "SkSVGUse.h"
+
+DEFINE_SVG_NO_INFO(ClipPath)
+
+bool SkSVGClipPath::isDef() {
+    return true;
+}
+
+bool SkSVGClipPath::isNotDef() {
+    return false;
+}
+
+void SkSVGClipPath::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("clip");
+    INHERITED::translate(parser, defState);
+    SkASSERT(fChildren.count() == 1);
+    SkSVGElement* child = *fChildren.begin();
+    SkASSERT(child->getType() == SkSVGType_Use);
+    SkSVGUse* use = (SkSVGUse*) child;
+    SkSVGElement* ref = NULL;
+    const char* refStr = &use->f_xlink_href.c_str()[1];
+    SkASSERT(parser.getIDs().find(refStr, &ref));
+    SkASSERT(ref);
+    if (ref->getType() == SkSVGType_Rect)
+        parser._addAttribute("rectangle", refStr);
+    else
+        parser._addAttribute("path", refStr);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGClipPath.h b/src/svg/SkSVGClipPath.h
new file mode 100644
index 0000000..9ba4fbc
--- /dev/null
+++ b/src/svg/SkSVGClipPath.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 SkSVGClipPath_DEFINED
+#define SkSVGClipPath_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGClipPath : public SkSVGElement {
+    DECLARE_SVG_INFO(ClipPath);
+    virtual bool isDef();
+    virtual bool isNotDef();
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGClipPath_DEFINED
diff --git a/src/svg/SkSVGDefs.cpp b/src/svg/SkSVGDefs.cpp
new file mode 100644
index 0000000..3b9bc20
--- /dev/null
+++ b/src/svg/SkSVGDefs.cpp
@@ -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.
+ */
+
+
+#include "SkSVGDefs.h"
+
+DEFINE_SVG_NO_INFO(Defs)
+
+bool SkSVGDefs::isDef() {
+    return true;
+}
+
+bool SkSVGDefs::isNotDef() {
+    return false;
+}
+
+void SkSVGDefs::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGDefs.h b/src/svg/SkSVGDefs.h
new file mode 100644
index 0000000..daa6894
--- /dev/null
+++ b/src/svg/SkSVGDefs.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 SkSVGDefs_DEFINED
+#define SkSVGDefs_DEFINED
+
+#include "SkSVGGroup.h"
+
+class SkSVGDefs : public SkSVGGroup {
+    DECLARE_SVG_INFO(Defs);
+    virtual bool isDef();
+    virtual bool isNotDef();
+private:
+    typedef SkSVGGroup INHERITED;
+};
+
+#endif // SkSVGDefs_DEFINED
diff --git a/src/svg/SkSVGElements.cpp b/src/svg/SkSVGElements.cpp
new file mode 100644
index 0000000..66dcd4e
--- /dev/null
+++ b/src/svg/SkSVGElements.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 "SkSVGElements.h"
+#include "SkSVGParser.h"
+
+SkSVGBase::~SkSVGBase() {
+}
+
+void SkSVGBase::addAttribute(SkSVGParser& parser, int attrIndex,
+        const char* attrValue, size_t attrLength) {
+    SkString* first = (SkString*) ((char*) this + sizeof(SkSVGElement));
+    first += attrIndex;
+    first->set(attrValue, attrLength);
+}
+
+
+SkSVGElement::SkSVGElement() : fParent(NULL), fIsDef(false), fIsNotDef(true) {
+}
+
+SkSVGElement::~SkSVGElement() {
+}
+
+SkSVGElement* SkSVGElement::getGradient() {
+    return NULL;
+}
+
+bool SkSVGElement::isGroupParent() {
+    SkSVGElement* parent = fParent;
+    while (parent) {
+        if (parent->getType() != SkSVGType_G)
+            return false;
+        parent = parent->fParent;
+    }
+    return true;
+}
+
+bool SkSVGElement::isDef() {
+    return isGroupParent() == false ? fParent->isDef() : fIsDef;
+}
+
+bool SkSVGElement::isFlushable() {
+    return true;
+}
+
+bool SkSVGElement::isGroup() {
+    return false;
+}
+
+bool SkSVGElement::isNotDef() {
+    return isGroupParent() == false ? fParent->isNotDef() : fIsNotDef;
+}
+
+bool SkSVGElement::onEndElement(SkSVGParser& parser) {
+    if (f_id.size() > 0)
+        parser.getIDs().set(f_id.c_str(), f_id.size(), this);
+    return false;
+}
+
+bool SkSVGElement::onStartElement(SkSVGElement* child) {
+    *fChildren.append() = child;
+    return false;
+}
+
+void SkSVGElement::translate(SkSVGParser& parser, bool) {
+    if (f_id.size() > 0)
+        SVG_ADD_ATTRIBUTE(id);
+}
+
+void SkSVGElement::setIsDef() {
+    fIsDef = isDef();
+}
+
+//void SkSVGElement::setIsNotDef() {
+//  fIsNotDef = isNotDef();
+//}
+
+void SkSVGElement::write(SkSVGParser& , SkString& ) {
+    SkASSERT(0);
+}
+
+
diff --git a/src/svg/SkSVGElements.h b/src/svg/SkSVGElements.h
new file mode 100644
index 0000000..d00e434
--- /dev/null
+++ b/src/svg/SkSVGElements.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 SkSVGElements_DEFINED
+#define SkSVGElements_DEFINED
+
+#include "SkSVGPaintState.h"
+#include "SkSVGTypes.h"
+#include "SkTDArray.h"
+
+class SkSVGParser;
+
+#define DECLARE_SVG_INFO(_type) \
+public: \
+    virtual ~SkSVG##_type(); \
+    static const SkSVGAttribute gAttributes[]; \
+    virtual int getAttributes(const SkSVGAttribute** attrPtr); \
+    virtual SkSVGTypes getType() const; \
+    virtual void translate(SkSVGParser& parser, bool defState); \
+    typedef SkSVG##_type BASE_CLASS
+
+#define DEFINE_SVG_INFO(_type) \
+    SkSVG##_type::~SkSVG##_type() {} \
+    int SkSVG##_type::getAttributes(const SkSVGAttribute** attrPtr) { \
+        *attrPtr = gAttributes; \
+        return SK_ARRAY_COUNT(gAttributes); \
+    } \
+    SkSVGTypes SkSVG##_type::getType() const { return SkSVGType_##_type; }
+
+#define DEFINE_SVG_NO_INFO(_type) \
+    SkSVG##_type::~SkSVG##_type() {} \
+    int SkSVG##_type::getAttributes(const SkSVGAttribute** ) { return 0; } \
+    SkSVGTypes SkSVG##_type::getType() const { return SkSVGType_##_type; }
+
+
+struct SkSVGTypeName {
+    const char* fName;
+    SkSVGTypes fType;
+};
+
+class SkSVGElement : public SkSVGBase {
+public:
+    SkSVGElement();
+    virtual ~SkSVGElement();
+    virtual SkSVGElement* getGradient();
+    virtual SkSVGTypes getType() const  = 0;
+    virtual bool isDef();
+    virtual bool isFlushable();
+    virtual bool isGroup();
+    virtual bool isNotDef();
+    virtual bool onEndElement(SkSVGParser& parser);
+    virtual bool onStartElement(SkSVGElement* child);
+    void setIsDef();
+//  void setIsNotDef();
+    virtual void translate(SkSVGParser& parser, bool defState);
+    virtual void write(SkSVGParser& , SkString& color);
+    SkString f_id;
+    SkSVGPaint fPaintState;
+    SkTDArray<SkSVGElement*> fChildren;
+    SkSVGElement* fParent;
+    bool fIsDef;
+    bool fIsNotDef;
+private:
+    bool isGroupParent();
+};
+
+#endif // SkSVGElements_DEFINED
diff --git a/src/svg/SkSVGEllipse.cpp b/src/svg/SkSVGEllipse.cpp
new file mode 100644
index 0000000..281e4e9
--- /dev/null
+++ b/src/svg/SkSVGEllipse.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.
+ */
+
+
+#include "SkSVGEllipse.h"
+#include "SkSVGParser.h"
+#include "SkParse.h"
+#include <stdio.h>
+
+const SkSVGAttribute SkSVGEllipse::gAttributes[] = {
+    SVG_ATTRIBUTE(cx),
+    SVG_ATTRIBUTE(cy),
+    SVG_ATTRIBUTE(rx),
+    SVG_ATTRIBUTE(ry)
+};
+
+DEFINE_SVG_INFO(Ellipse)
+
+void SkSVGEllipse::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("oval");
+    INHERITED::translate(parser, defState);
+    SkScalar cx, cy, rx, ry;
+    SkParse::FindScalar(f_cx.c_str(), &cx);
+    SkParse::FindScalar(f_cy.c_str(), &cy);
+    SkParse::FindScalar(f_rx.c_str(), &rx);
+    SkParse::FindScalar(f_ry.c_str(), &ry);
+    SkScalar left, top, right, bottom;
+    left = cx - rx;
+    top = cy - ry;
+    right = cx + rx;
+    bottom = cy + ry;
+    char scratch[16];
+    sprintf(scratch, "%g", SkScalarToDouble(left));
+    parser._addAttribute("left", scratch);
+    sprintf(scratch, "%g", SkScalarToDouble(top));
+    parser._addAttribute("top", scratch);
+    sprintf(scratch, "%g", SkScalarToDouble(right));
+    parser._addAttribute("right", scratch);
+    sprintf(scratch, "%g", SkScalarToDouble(bottom));
+    parser._addAttribute("bottom", scratch);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGEllipse.h b/src/svg/SkSVGEllipse.h
new file mode 100644
index 0000000..c039c83
--- /dev/null
+++ b/src/svg/SkSVGEllipse.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 SkSVGEllipse_DEFINED
+#define SkSVGEllipse_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGEllipse : public SkSVGElement {
+    DECLARE_SVG_INFO(Ellipse);
+private:
+    SkString f_cx;
+    SkString f_cy;
+    SkString f_rx;
+    SkString f_ry;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGEllipse_DEFINED
diff --git a/src/svg/SkSVGFeColorMatrix.cpp b/src/svg/SkSVGFeColorMatrix.cpp
new file mode 100644
index 0000000..4e2d32a
--- /dev/null
+++ b/src/svg/SkSVGFeColorMatrix.cpp
@@ -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.
+ */
+
+
+#include "SkSVGFeColorMatrix.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGFeColorMatrix::gAttributes[] = {
+    SVG_LITERAL_ATTRIBUTE(color-interpolation-filters, f_color_interpolation_filters),
+    SVG_ATTRIBUTE(result),
+    SVG_ATTRIBUTE(type),
+    SVG_ATTRIBUTE(values)
+};
+
+DEFINE_SVG_INFO(FeColorMatrix)
+
+void SkSVGFeColorMatrix::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGFeColorMatrix.h b/src/svg/SkSVGFeColorMatrix.h
new file mode 100644
index 0000000..389995a
--- /dev/null
+++ b/src/svg/SkSVGFeColorMatrix.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 SkSVGFeColorMatrix_DEFINED
+#define SkSVGFeColorMatrix_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGFeColorMatrix : public SkSVGElement {
+    DECLARE_SVG_INFO(FeColorMatrix);
+protected:
+    SkString f_color_interpolation_filters;
+    SkString f_result;
+    SkString f_type;
+    SkString f_values;
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGFeColorMatrix_DEFINED
diff --git a/src/svg/SkSVGFilter.cpp b/src/svg/SkSVGFilter.cpp
new file mode 100644
index 0000000..207f176
--- /dev/null
+++ b/src/svg/SkSVGFilter.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 "SkSVGFilter.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGFilter::gAttributes[] = {
+    SVG_ATTRIBUTE(filterUnits),
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(x),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Filter)
+
+void SkSVGFilter::translate(SkSVGParser& parser, bool defState) {
+//  INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGFilter.h b/src/svg/SkSVGFilter.h
new file mode 100644
index 0000000..5891237
--- /dev/null
+++ b/src/svg/SkSVGFilter.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 SkSVGFilter_DEFINED
+#define SkSVGFilter_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGFilter : public SkSVGElement {
+    DECLARE_SVG_INFO(Filter);
+protected:
+    SkString f_filterUnits;
+    SkString f_height;
+    SkString f_width;
+    SkString f_x;
+    SkString f_y;
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGFilter_DEFINEDRITED;
+
diff --git a/src/svg/SkSVGG.cpp b/src/svg/SkSVGG.cpp
new file mode 100644
index 0000000..21a7aa8
--- /dev/null
+++ b/src/svg/SkSVGG.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 "SkSVGG.h"
+
+DEFINE_SVG_NO_INFO(G)
+
+void SkSVGG::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGG.h b/src/svg/SkSVGG.h
new file mode 100644
index 0000000..4fa27d2
--- /dev/null
+++ b/src/svg/SkSVGG.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 SkSVGG_DEFINED
+#define SkSVGG_DEFINED
+
+#include "SkSVGGroup.h"
+
+class SkSVGG : public SkSVGGroup {
+    DECLARE_SVG_INFO(G);
+private:
+    typedef SkSVGGroup INHERITED;
+};
+
+#endif // SkSVGG_DEFINED
diff --git a/src/svg/SkSVGGradient.cpp b/src/svg/SkSVGGradient.cpp
new file mode 100644
index 0000000..d5f7639
--- /dev/null
+++ b/src/svg/SkSVGGradient.cpp
@@ -0,0 +1,115 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkSVGGradient.h"
+#include "SkSVGParser.h"
+#include "SkSVGStop.h"
+
+SkSVGGradient::SkSVGGradient() {
+}
+
+SkSVGElement* SkSVGGradient::getGradient() {
+    return this;
+}
+
+bool SkSVGGradient::isDef() {
+    return true;
+}
+
+bool SkSVGGradient::isNotDef() {
+    return false;
+}
+
+void SkSVGGradient::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+    // !!! no support for 'objectBoundingBox' yet
+    bool first = true;
+    bool addedFirst = false;
+    bool addedLast = false;
+    SkString offsets("[");
+    SkString* lastOffset = NULL;
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkASSERT((*ptr)->getType() == SkSVGType_Stop);
+        SkSVGStop* stop = (SkSVGStop*) *ptr;
+        if (first && stop->f_offset.equals("0") == false) {
+            addedFirst = true;
+            offsets.append("0,");
+        }
+        SkString* thisOffset = &stop->f_offset;
+        if (lastOffset && thisOffset->equals(*lastOffset)) {
+            if (thisOffset->equals("1")) {
+                offsets.remove(offsets.size() - 2, 2);
+                offsets.append(".999,");
+            } else {
+                SkASSERT(0); // !!! need to write this case
+            }
+        }
+        offsets.append(*thisOffset);
+        if (ptr == fChildren.end() - 1) { // last
+            if (stop->f_offset.equals("1") == false) {
+                offsets.append(",1");
+                addedLast = true;
+            }
+        } else
+            offsets.appendUnichar(',');
+        first = false;
+        lastOffset = thisOffset;
+    }
+    offsets.appendUnichar(']');
+    parser._addAttribute("offsets", offsets);
+    if (addedFirst)
+        parser.translate(*fChildren.begin(), defState);
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++)
+        parser.translate(*ptr, defState);
+    if (addedLast)
+        parser.translate(*(fChildren.end() - 1), defState);
+}
+
+void SkSVGGradient::translateGradientUnits(SkString& units) {
+    // !!! no support for 'objectBoundingBox' yet
+    SkASSERT(strcmp(units.c_str(), "userSpaceOnUse") == 0);
+}
+
+void SkSVGGradient::write(SkSVGParser& parser, SkString& baseColor) {
+    if (baseColor.c_str()[0] != '#')
+        return;
+    SkSVGPaint* saveHead = parser.fHead;
+    parser.fHead = &fPaintState;
+    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);
+    SkString originalColors;
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkSVGStop* colorElement = (SkSVGStop*) *ptr;
+        SkString& color = colorElement->fPaintState.f_stopColor;
+        originalColors.append(color);
+        originalColors.appendUnichar(',');
+        SkASSERT(color.c_str()[0] == '#');
+        SkString replacement;
+        replacement.set("0x");
+        replacement.append(color.c_str() + 1, 2); // add stop colors using given color, turning existing stop color into alpha
+        SkASSERT(baseColor.c_str()[0] == '#');
+        SkASSERT(baseColor.size() == 7);
+        replacement.append(baseColor.c_str() + 1);
+        color.set(replacement);
+    }
+    translate(parser, true);
+    const char* originalPtr = originalColors.c_str(); // restore original gradient values
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkSVGStop* color = (SkSVGStop*) *ptr;
+        const char* originalEnd = strchr(originalPtr, ',');
+        color->fPaintState.f_stopColor.set(originalPtr, originalEnd - originalPtr);
+        originalPtr = originalEnd + 1;
+    }
+    f_id.set(originalID);
+    parser.fSuppressPaint = false;
+    parser.fHead = saveHead;
+}
+
diff --git a/src/svg/SkSVGGradient.h b/src/svg/SkSVGGradient.h
new file mode 100644
index 0000000..286ff18
--- /dev/null
+++ b/src/svg/SkSVGGradient.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 SkSVGGradient_DEFINED
+#define SkSVGGradient_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGGradient : public SkSVGElement {
+public:
+    SkSVGGradient();
+    virtual SkSVGElement* getGradient();
+    virtual bool isDef();
+    virtual bool isNotDef();
+    virtual void write(SkSVGParser& , SkString& color);
+protected:
+    void translate(SkSVGParser& , bool defState);
+    void translateGradientUnits(SkString& units);
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGGradient_DEFINED
diff --git a/src/svg/SkSVGGroup.cpp b/src/svg/SkSVGGroup.cpp
new file mode 100644
index 0000000..420179d
--- /dev/null
+++ b/src/svg/SkSVGGroup.cpp
@@ -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.
+ */
+
+
+#include "SkSVGGroup.h"
+#include "SkSVGParser.h"
+
+SkSVGGroup::SkSVGGroup() {
+    fIsNotDef = false;
+}
+
+SkSVGElement* SkSVGGroup::getGradient() {
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkSVGElement* result = (*ptr)->getGradient();
+        if (result != NULL)
+            return result;
+    }
+    return NULL;
+}
+
+bool SkSVGGroup::isDef() {
+    return fParent ? fParent->isDef() : false;
+}
+
+bool SkSVGGroup::isFlushable() {
+    return false;
+}
+
+bool SkSVGGroup::isGroup() {
+    return true;
+}
+
+bool SkSVGGroup::isNotDef() {
+    return fParent ? fParent->isNotDef() : false;
+}
+
+void SkSVGGroup::translate(SkSVGParser& parser, bool defState) {
+    for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++)
+        parser.translate(*ptr, defState);
+}
diff --git a/src/svg/SkSVGGroup.h b/src/svg/SkSVGGroup.h
new file mode 100644
index 0000000..f579a98
--- /dev/null
+++ b/src/svg/SkSVGGroup.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 SkSVGGroup_DEFINED
+#define SkSVGGroup_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGGroup : public SkSVGElement {
+public:
+    SkSVGGroup();
+    virtual SkSVGElement* getGradient();
+    virtual bool isDef();
+    virtual bool isFlushable();
+    virtual bool isGroup();
+    virtual bool isNotDef();
+    void translate(SkSVGParser& , bool defState);
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGGroup_DEFINED
diff --git a/src/svg/SkSVGImage.cpp b/src/svg/SkSVGImage.cpp
new file mode 100644
index 0000000..97b8df8
--- /dev/null
+++ b/src/svg/SkSVGImage.cpp
@@ -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.
+ */
+
+
+#include "SkSVGImage.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGImage::gAttributes[] = {
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(x),
+    SVG_LITERAL_ATTRIBUTE(xlink:href, f_xlink_href),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Image)
+
+void SkSVGImage::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("image");
+    INHERITED::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE(x);
+    SVG_ADD_ATTRIBUTE(y);
+//  SVG_ADD_ATTRIBUTE(width);
+//  SVG_ADD_ATTRIBUTE(height);
+    translateImage(parser);
+    parser._endElement();
+}
+
+void SkSVGImage::translateImage(SkSVGParser& parser) {
+    SkASSERT(f_xlink_href.size() > 0);
+    const char* data = f_xlink_href.c_str();
+    SkASSERT(strncmp(data, "data:image/", 11) == 0);
+    data += 11;
+    SkASSERT(strncmp(data, "png;", 4) == 0 || strncmp(data, "jpeg;", 5) == 0);
+    data = strchr(data, ';');
+    SkASSERT(strncmp(data, ";base64,", 8) == 0);
+    data += 8;
+    parser._addAttribute("base64", data);
+}
diff --git a/src/svg/SkSVGImage.h b/src/svg/SkSVGImage.h
new file mode 100644
index 0000000..b63beef
--- /dev/null
+++ b/src/svg/SkSVGImage.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 SkSVGImage_DEFINED
+#define SkSVGImage_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGImage : public SkSVGElement {
+public:
+    DECLARE_SVG_INFO(Image);
+private:
+    void translateImage(SkSVGParser& parser);
+    SkString f_height;
+    SkString f_width;
+    SkString f_x;
+    SkString f_xlink_href;
+    SkString f_y;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGImage_DEFINED
diff --git a/src/svg/SkSVGLine.cpp b/src/svg/SkSVGLine.cpp
new file mode 100644
index 0000000..9158c99
--- /dev/null
+++ b/src/svg/SkSVGLine.cpp
@@ -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.
+ */
+
+
+#include "SkSVGLine.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGLine::gAttributes[] = {
+    SVG_ATTRIBUTE(x1),
+    SVG_ATTRIBUTE(x2),
+    SVG_ATTRIBUTE(y1),
+    SVG_ATTRIBUTE(y2)
+};
+
+DEFINE_SVG_INFO(Line)
+
+void SkSVGLine::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("line");
+    INHERITED::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE(x1);
+    SVG_ADD_ATTRIBUTE(y1);
+    SVG_ADD_ATTRIBUTE(x2);
+    SVG_ADD_ATTRIBUTE(y2);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGLine.h b/src/svg/SkSVGLine.h
new file mode 100644
index 0000000..3e437e0
--- /dev/null
+++ b/src/svg/SkSVGLine.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 SkSVGLine_DEFINED
+#define SkSVGLine_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGLine : public SkSVGElement {
+    DECLARE_SVG_INFO(Line);
+private:
+    SkString f_x1;
+    SkString f_x2;
+    SkString f_y1;
+    SkString f_y2;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGLine_DEFINED
diff --git a/src/svg/SkSVGLinearGradient.cpp b/src/svg/SkSVGLinearGradient.cpp
new file mode 100644
index 0000000..f89ee53
--- /dev/null
+++ b/src/svg/SkSVGLinearGradient.cpp
@@ -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.
+ */
+
+
+#include "SkSVGLinearGradient.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGLinearGradient::gAttributes[] = {
+    SVG_ATTRIBUTE(gradientTransform),
+    SVG_ATTRIBUTE(gradientUnits),
+    SVG_ATTRIBUTE(x1),
+    SVG_ATTRIBUTE(x2),
+    SVG_ATTRIBUTE(y1),
+    SVG_ATTRIBUTE(y2)
+};
+
+DEFINE_SVG_INFO(LinearGradient)
+
+void SkSVGLinearGradient::translate(SkSVGParser& parser, bool defState) {
+    if (fMatrixID.size() == 0)
+        parser.translateMatrix(f_gradientTransform, &fMatrixID);
+    parser._startElement("linearGradient");
+    if (fMatrixID.size() > 0)
+        parser._addAttribute("matrix", fMatrixID);
+    INHERITED::translateGradientUnits(f_gradientUnits);
+    SkString points;
+    points.appendUnichar('[');
+    points.append(f_x1);
+    points.appendUnichar(',');
+    points.append(f_y1);
+    points.appendUnichar(',');
+    points.append(f_x2);
+    points.appendUnichar(',');
+    points.append(f_y2);
+    points.appendUnichar(']');
+    parser._addAttribute("points", points.c_str());
+    INHERITED::translate(parser, defState);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGLinearGradient.h b/src/svg/SkSVGLinearGradient.h
new file mode 100644
index 0000000..8eda065
--- /dev/null
+++ b/src/svg/SkSVGLinearGradient.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 SkSVGLinearGradient_DEFINED
+#define SkSVGLinearGradient_DEFINED
+
+#include "SkSVGGradient.h"
+
+class SkSVGLinearGradient : public SkSVGGradient {
+    DECLARE_SVG_INFO(LinearGradient);
+private:
+    SkString f_gradientTransform;
+    SkString f_gradientUnits;
+    SkString f_x1;
+    SkString f_x2;
+    SkString f_y1;
+    SkString f_y2;
+    SkString fMatrixID;
+    typedef SkSVGGradient INHERITED;
+};
+
+#endif // SkSVGLinearGradient_DEFINED
diff --git a/src/svg/SkSVGMask.cpp b/src/svg/SkSVGMask.cpp
new file mode 100644
index 0000000..7526d18
--- /dev/null
+++ b/src/svg/SkSVGMask.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 "SkSVGMask.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGMask::gAttributes[] = {
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(maskUnits),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(x),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Mask)
+
+bool SkSVGMask::isDef() {
+    return false;
+}
+
+bool SkSVGMask::isNotDef() {
+    return false;
+}
+
+void SkSVGMask::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGMask.h b/src/svg/SkSVGMask.h
new file mode 100644
index 0000000..2e1fd50
--- /dev/null
+++ b/src/svg/SkSVGMask.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 SkSVGMask_DEFINED
+#define SkSVGMask_DEFINED
+
+#include "SkSVGGroup.h"
+
+class SkSVGMask : public SkSVGGroup {
+    DECLARE_SVG_INFO(Mask);
+    virtual bool isDef();
+    virtual bool isNotDef();
+protected:
+    SkString f_height;
+    SkString f_maskUnits;
+    SkString f_width;
+    SkString f_x;
+    SkString f_y;
+private:
+    typedef SkSVGGroup INHERITED;
+};
+
+#endif // SkSVGMask_DEFINED
diff --git a/src/svg/SkSVGMetadata.cpp b/src/svg/SkSVGMetadata.cpp
new file mode 100644
index 0000000..0f8850e
--- /dev/null
+++ b/src/svg/SkSVGMetadata.cpp
@@ -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.
+ */
+
+
+#include "SkSVGMetadata.h"
+#include "SkSVGParser.h"
+
+DEFINE_SVG_NO_INFO(Metadata)
+
+bool SkSVGMetadata::isDef() {
+    return false;
+}
+
+bool SkSVGMetadata::isNotDef() {
+    return false;
+}
+
+void SkSVGMetadata::translate(SkSVGParser& parser, bool defState) {
+}
diff --git a/src/svg/SkSVGMetadata.h b/src/svg/SkSVGMetadata.h
new file mode 100644
index 0000000..2dcb3a2
--- /dev/null
+++ b/src/svg/SkSVGMetadata.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 SkSVGMetadata_DEFINED
+#define SkSVGMetadata_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGMetadata : public SkSVGElement {
+    DECLARE_SVG_INFO(Metadata);
+    virtual bool isDef();
+    virtual bool isNotDef();
+private:
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGMetadata_DEFINED
diff --git a/src/svg/SkSVGPaintState.cpp b/src/svg/SkSVGPaintState.cpp
new file mode 100644
index 0000000..db30900
--- /dev/null
+++ b/src/svg/SkSVGPaintState.cpp
@@ -0,0 +1,455 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkSVGPaintState.h"
+#include "SkSVGElements.h"
+#include "SkSVGParser.h"
+#include "SkParse.h"
+
+SkSVGAttribute SkSVGPaint::gAttributes[] = {
+    SVG_LITERAL_ATTRIBUTE(clip-path, f_clipPath),
+    SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule),
+    SVG_LITERAL_ATTRIBUTE(enable-background, f_enableBackground),
+    SVG_ATTRIBUTE(fill),
+    SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule),
+    SVG_ATTRIBUTE(filter),
+    SVG_LITERAL_ATTRIBUTE(font-family, f_fontFamily),
+    SVG_LITERAL_ATTRIBUTE(font-size, f_fontSize),
+    SVG_LITERAL_ATTRIBUTE(letter-spacing, f_letterSpacing),
+    SVG_ATTRIBUTE(mask),
+    SVG_ATTRIBUTE(opacity),
+    SVG_LITERAL_ATTRIBUTE(stop-color, f_stopColor),
+    SVG_LITERAL_ATTRIBUTE(stop-opacity, f_stopOpacity),
+    SVG_ATTRIBUTE(stroke),
+    SVG_LITERAL_ATTRIBUTE(stroke-dasharray, f_strokeDasharray),
+    SVG_LITERAL_ATTRIBUTE(stroke-linecap, f_strokeLinecap),
+    SVG_LITERAL_ATTRIBUTE(stroke-linejoin, f_strokeLinejoin),
+    SVG_LITERAL_ATTRIBUTE(stroke-miterlimit, f_strokeMiterlimit),
+    SVG_LITERAL_ATTRIBUTE(stroke-width, f_strokeWidth),
+    SVG_ATTRIBUTE(style),
+    SVG_ATTRIBUTE(transform)
+};
+
+const int SkSVGPaint::kAttributesSize = SK_ARRAY_COUNT(SkSVGPaint::gAttributes);
+
+SkSVGPaint::SkSVGPaint() : fNext(NULL) {
+}
+
+SkString* SkSVGPaint::operator[](int index) {
+    SkASSERT(index >= 0);
+    SkASSERT(index < &fTerminal - &fInitial);
+    SkASSERT(&fTerminal - &fInitial == kTerminal - kInitial);
+    SkString* result = &fInitial + index + 1;
+    return result;
+}
+
+void SkSVGPaint::addAttribute(SkSVGParser& parser, int attrIndex,
+        const char* attrValue, size_t attrLength) {
+    SkString* attr = (*this)[attrIndex];
+    switch(attrIndex) {
+        case kClipPath:
+        case kClipRule:
+        case kEnableBackground:
+        case kFill:
+        case kFillRule:
+        case kFilter:
+        case kFontFamily:
+        case kFontSize:
+        case kLetterSpacing:
+        case kMask:
+        case kOpacity:
+        case kStopColor:
+        case kStopOpacity:
+        case kStroke:
+        case kStroke_Dasharray:
+        case kStroke_Linecap:
+        case kStroke_Linejoin:
+        case kStroke_Miterlimit:
+        case kStroke_Width:
+        case kTransform:
+            attr->set(attrValue, attrLength);
+            return;
+        case kStyle: {
+            // iterate through colon / semi-colon delimited pairs
+            int pairs = SkParse::Count(attrValue, ';');
+            const char* attrEnd = attrValue + attrLength;
+            do {
+                const char* end = strchr(attrValue, ';');
+                if (end == NULL)
+                    end = attrEnd;
+                const char* delimiter = strchr(attrValue, ':');
+                SkASSERT(delimiter != 0 && delimiter < end);
+                int index = parser.findAttribute(this, attrValue, (int) (delimiter - attrValue), true);
+                SkASSERT(index >= 0);
+                delimiter++;
+                addAttribute(parser, index, delimiter, (int) (end - delimiter));
+                attrValue = end + 1;
+            } while (--pairs);
+            return;
+            }
+        default:
+            SkASSERT(0);
+    }
+}
+
+bool SkSVGPaint::flush(SkSVGParser& parser, bool isFlushable, bool isDef) {
+    SkSVGPaint current;
+    SkSVGPaint* walking = parser.fHead;
+    int index;
+    while (walking != NULL) {
+        for (index = kInitial + 1; index < kTerminal; index++) {
+            SkString* lastAttr = (*walking)[index];
+            if (lastAttr->size() == 0)
+                continue;
+            if (current[index]->size() > 0)
+                continue;
+            current[index]->set(*lastAttr);
+        }
+        walking = walking->fNext;
+    }
+    bool paintChanged = false;
+    SkSVGPaint& lastState = parser.fLastFlush;
+    if (isFlushable == false) {
+        if (isDef == true) {
+            if (current.f_mask.size() > 0 && current.f_mask.equals(lastState.f_mask) == false) {
+                SkSVGElement* found;
+                const char* idStart = strchr(current.f_mask.c_str(), '#');
+                SkASSERT(idStart);
+                SkString id(idStart + 1, strlen(idStart) - 2);
+                bool itsFound = parser.fIDs.find(id.c_str(), &found);
+                SkASSERT(itsFound);
+                SkSVGElement* gradient = found->getGradient();
+                if (gradient) {
+                    gradient->write(parser, current.f_fill);
+                    gradient->write(parser, current.f_stroke);
+                }
+            }
+        }
+        goto setLast;
+    }
+    {
+        bool changed[kTerminal];
+        memset(changed, 0, sizeof(changed));
+        for (index = kInitial + 1; index < kTerminal; index++) {
+            if (index == kTransform || index == kClipPath || index == kStopColor || index == kStopOpacity ||
+                    index == kClipRule || index == kFillRule)
+                continue;
+            SkString* lastAttr = lastState[index];
+            SkString* currentAttr = current[index];
+            paintChanged |= changed[index] = lastAttr->equals(*currentAttr) == false;
+        }
+        if (paintChanged) {
+            if (current.f_mask.size() > 0) {
+                if (current.f_fill.equals("none") == false && strncmp(current.f_fill.c_str(), "url(#", 5) != 0) {
+                    SkASSERT(current.f_fill.c_str()[0] == '#');
+                    SkString replacement("url(#mask");
+                    replacement.append(current.f_fill.c_str() + 1);
+                    replacement.appendUnichar(')');
+                    current.f_fill.set(replacement);
+                }
+                if (current.f_stroke.equals("none") == false && strncmp(current.f_stroke.c_str(), "url(#", 5) != 0) {
+                    SkASSERT(current.f_stroke.c_str()[0] == '#');
+                    SkString replacement("url(#mask");
+                    replacement.append(current.f_stroke.c_str() + 1);
+                    replacement.appendUnichar(')');
+                    current.f_stroke.set(replacement);
+                }
+            }
+            if (current.f_fill.equals("none") && current.f_stroke.equals("none"))
+                current.f_opacity.set("0");
+            if (parser.fSuppressPaint == false) {
+                parser._startElement("paint");
+                bool success = writeChangedAttributes(parser, current, changed);
+                if (success == false)
+                    return paintChanged;
+                success = writeChangedElements(parser, current, changed);
+                if (success == false)
+                    return paintChanged;
+                parser._endElement(); // paint
+            }
+        }
+    }
+setLast:
+    for (index = kInitial + 1; index < kTerminal; index++) {
+        SkString* lastAttr = lastState[index];
+        SkString* currentAttr = current[index];
+        lastAttr->set(*currentAttr);
+    }
+    return paintChanged;
+}
+
+int SkSVGPaint::getAttributes(const SkSVGAttribute** attrPtr) {
+    *attrPtr = gAttributes;
+    return kAttributesSize;
+}
+
+void SkSVGPaint::setSave(SkSVGParser& parser) {
+    SkTDArray<SkString*> clips;
+    SkSVGPaint* walking = parser.fHead;
+    int index;
+    SkMatrix sum;
+    sum.reset();
+    while (walking != NULL) {
+        for (index = kInitial + 1; index < kTerminal; index++) {
+            SkString* lastAttr = (*walking)[index];
+            if (lastAttr->size() == 0)
+                continue;
+            if (index == kTransform) {
+                const char* str = lastAttr->c_str();
+                SkASSERT(strncmp(str, "matrix(", 7) == 0);
+                str += 6;
+                const char* strEnd = strrchr(str, ')');
+                SkASSERT(strEnd != NULL);
+                SkString mat(str, strEnd - str);
+                SkSVGParser::ConvertToArray(mat);
+                SkScalar values[6];
+                SkParse::FindScalars(mat.c_str() + 1, values, 6);
+                SkMatrix matrix;
+                matrix.reset();
+                matrix.setScaleX(values[0]);
+                matrix.setSkewY(values[1]);
+                matrix.setSkewX(values[2]);
+                matrix.setScaleY(values[3]);
+                matrix.setTranslateX(values[4]);
+                matrix.setTranslateY(values[5]);
+                sum.setConcat(matrix, sum);
+                continue;
+            }
+            if ( index == kClipPath)
+                *clips.insert(0) = lastAttr;
+        }
+        walking = walking->fNext;
+    }
+    if ((sum == parser.fLastTransform) == false) {
+        SkMatrix inverse;
+        bool success = parser.fLastTransform.invert(&inverse);
+        SkASSERT(success == true);
+        SkMatrix output;
+        output.setConcat(inverse, sum);
+        parser.fLastTransform = sum;
+        SkString outputStr;
+        outputStr.appendUnichar('[');
+        outputStr.appendScalar(output.getScaleX());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getSkewX());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getTranslateX());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getSkewY());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getScaleY());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getTranslateY());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getPerspX());
+        outputStr.appendUnichar(',');
+        outputStr.appendScalar(output.getPerspY());
+        outputStr.append(",1]");
+        parser._startElement("matrix");
+        parser._addAttributeLen("matrix", outputStr.c_str(), outputStr.size());
+        parser._endElement();
+    }
+#if 0   // incomplete
+    if (parser.fTransformClips.size() > 0) {
+        // need to reset the clip when the 'g' scope is ended
+        parser._startElement("add");
+        const char* start = strchr(current->f_clipPath.c_str(), '#') + 1;
+        SkASSERT(start);
+        parser._addAttributeLen("use", start, strlen(start) - 1);
+        parser._endElement();   // clip
+    }
+#endif
+}
+
+bool SkSVGPaint::writeChangedAttributes(SkSVGParser& parser,
+        SkSVGPaint& current, bool* changed) {
+    SkSVGPaint& lastState = parser.fLastFlush;
+    for (int index = kInitial + 1; index < kTerminal; index++) {
+        if (changed[index] == false)
+                continue;
+        SkString* topAttr = current[index];
+        size_t attrLength = topAttr->size();
+        if (attrLength == 0)
+            continue;
+        const char* attrValue = topAttr->c_str();
+        SkString* lastAttr = lastState[index];
+        switch(index) {
+            case kClipPath:
+            case kClipRule:
+            case kEnableBackground:
+                break;
+            case kFill:
+                if (topAttr->equals("none") == false && lastAttr->equals("none") == true)
+                    parser._addAttribute("stroke", "false");
+                goto fillStrokeAttrCommon;
+            case kFillRule:
+            case kFilter:
+            case kFontFamily:
+                break;
+            case kFontSize:
+                parser._addAttributeLen("textSize", attrValue, attrLength);
+                break;
+            case kLetterSpacing:
+                parser._addAttributeLen("textTracking", attrValue, attrLength);
+                break;
+            case kMask:
+                break;
+            case kOpacity:
+                break;
+            case kStopColor:
+                break;
+            case kStopOpacity:
+                break;
+            case kStroke:
+                if (topAttr->equals("none") == false && lastAttr->equals("none") == true)
+                    parser._addAttribute("stroke", "true");
+fillStrokeAttrCommon:
+                if (strncmp(attrValue, "url(", 4) == 0) {
+                    SkASSERT(attrValue[4] == '#');
+                    const char* idStart = attrValue + 5;
+                    const char* idEnd = strrchr(attrValue, ')');
+                    SkASSERT(idStart < idEnd);
+                    SkString id(idStart, idEnd - idStart);
+                    SkSVGElement* found;
+                    if (strncmp(id.c_str(), "mask", 4) != 0) {
+                        bool itsFound = parser.fIDs.find(id.c_str(), &found);
+                        SkASSERT(itsFound);
+                        SkASSERT(found->getType() == SkSVGType_LinearGradient ||
+                            found->getType() == SkSVGType_RadialGradient);
+                    }
+                    parser._addAttribute("shader", id.c_str());
+                }
+                break;
+            case kStroke_Dasharray:
+                break;
+            case kStroke_Linecap:
+                parser._addAttributeLen("strokeCap", attrValue, attrLength);
+                break;
+            case kStroke_Linejoin:
+                parser._addAttributeLen("strokeJoin", attrValue, attrLength);
+                break;
+            case kStroke_Miterlimit:
+                parser._addAttributeLen("strokeMiter", attrValue, attrLength);
+                break;
+            case kStroke_Width:
+                parser._addAttributeLen("strokeWidth", attrValue, attrLength);
+            case kStyle:
+            case kTransform:
+                break;
+        default:
+            SkASSERT(0);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SkSVGPaint::writeChangedElements(SkSVGParser& parser,
+        SkSVGPaint& current, bool* changed) {
+    SkSVGPaint& lastState = parser.fLastFlush;
+    for (int index = kInitial + 1; index < kTerminal; index++) {
+        SkString* topAttr = current[index];
+        size_t attrLength = topAttr->size();
+        if (attrLength == 0)
+            continue;
+        const char* attrValue = topAttr->c_str();
+        SkString* lastAttr = lastState[index];
+        switch(index) {
+            case kClipPath:
+            case kClipRule:
+                // !!! need to add this outside of paint
+                break;
+            case kEnableBackground:
+                // !!! don't know what to do with this
+                break;
+            case kFill:
+                goto addColor;
+            case kFillRule:
+            case kFilter:
+                break;
+            case kFontFamily:
+                parser._startElement("typeface");
+                parser._addAttributeLen("fontName", attrValue, attrLength);
+                parser._endElement();   // typeface
+                break;
+            case kFontSize:
+            case kLetterSpacing:
+                break;
+            case kMask:
+            case kOpacity:
+                if (changed[kStroke] == false && changed[kFill] == false) {
+                    parser._startElement("color");
+                    SkString& opacity = current.f_opacity;
+                    parser._addAttributeLen("color", parser.fLastColor.c_str(), parser.fLastColor.size());
+                    parser._addAttributeLen("alpha", opacity.c_str(), opacity.size());
+                    parser._endElement();   // color
+                }
+                break;
+            case kStopColor:
+                break;
+            case kStopOpacity:
+                break;
+            case kStroke:
+addColor:
+                if (strncmp(lastAttr->c_str(), "url(", 4) == 0 && strncmp(attrValue, "url(", 4) != 0) {
+                    parser._startElement("shader");
+                    parser._endElement();
+                }
+                if (topAttr->equals(*lastAttr))
+                    continue;
+                {
+                    bool urlRef = strncmp(attrValue, "url(", 4) == 0;
+                    bool colorNone = strcmp(attrValue, "none") == 0;
+                    bool lastEqual = parser.fLastColor.equals(attrValue, attrLength);
+                    bool newColor = urlRef == false && colorNone == false && lastEqual == false;
+                    if (newColor || changed[kOpacity]) {
+                        parser._startElement("color");
+                        if (newColor || changed[kOpacity]) {
+                            parser._addAttributeLen("color", attrValue, attrLength);
+                            parser.fLastColor.set(attrValue, attrLength);
+                        }
+                        if (changed[kOpacity]) {
+                            SkString& opacity = current.f_opacity;
+                            parser._addAttributeLen("alpha", opacity.c_str(), opacity.size());
+                        }
+                        parser._endElement();   // color
+                    }
+                }
+                break;
+            case kStroke_Dasharray:
+                parser._startElement("dash");
+                SkSVGParser::ConvertToArray(*topAttr);
+                parser._addAttribute("intervals", topAttr->c_str());
+                parser._endElement();   // dash
+            break;
+            case kStroke_Linecap:
+            case kStroke_Linejoin:
+            case kStroke_Miterlimit:
+            case kStroke_Width:
+            case kStyle:
+            case kTransform:
+                break;
+        default:
+            SkASSERT(0);
+            return false;
+        }
+    }
+    return true;
+}
+
+void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) {
+    newRecord->fNext = *head;
+    *head = newRecord;
+}
+
+void SkSVGPaint::Pop(SkSVGPaint** head) {
+    SkSVGPaint* next = (*head)->fNext;
+    *head = next;
+}
+
diff --git a/src/svg/SkSVGParser.cpp b/src/svg/SkSVGParser.cpp
new file mode 100644
index 0000000..74ea023
--- /dev/null
+++ b/src/svg/SkSVGParser.cpp
@@ -0,0 +1,441 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "SkSVGParser.h"
+#include "SkSVGCircle.h"
+#include "SkSVGClipPath.h"
+#include "SkSVGDefs.h"
+#include "SkSVGEllipse.h"
+#include "SkSVGFeColorMatrix.h"
+#include "SkSVGFilter.h"
+#include "SkSVGG.h"
+#include "SkSVGImage.h"
+#include "SkSVGLine.h"
+#include "SkSVGLinearGradient.h"
+#include "SkSVGMask.h"
+#include "SkSVGMetadata.h"
+#include "SkSVGPath.h"
+#include "SkSVGPolygon.h"
+#include "SkSVGPolyline.h"
+#include "SkSVGRadialGradient.h"
+#include "SkSVGRect.h"
+#include "SkSVGSVG.h"
+#include "SkSVGStop.h"
+#include "SkSVGSymbol.h"
+#include "SkSVGText.h"
+#include "SkSVGUse.h"
+#include "SkTSearch.h"
+#include <stdio.h>
+
+static int gGeneratedMatrixID = 0;
+
+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");
+    fEmptyPaint.f_stroke.set("none");
+    fEmptyPaint.f_strokeMiterlimit.set("4");
+    fEmptyPaint.f_fillRule.set("winding");
+    fEmptyPaint.f_opacity.set("1");
+    fEmptyPaint.fNext = NULL;
+    for (int index = SkSVGPaint::kInitial + 1; index < SkSVGPaint::kTerminal; index++) {
+        SkString* initial = fEmptyPaint[index];
+        if (initial->size() == 0)
+            continue;
+        fLastFlush[index]->set(*initial);
+    }
+}
+
+SkSVGParser::~SkSVGParser() {
+}
+
+void SkSVGParser::Delete(SkTDArray<SkSVGElement*>& fChildren) {
+    SkSVGElement** ptr;
+    for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        Delete((*ptr)->fChildren);
+        delete *ptr;
+    }
+}
+
+int SkSVGParser::findAttribute(SkSVGBase* element, const char* attrValue,
+        size_t len, bool isPaint) {
+    const SkSVGAttribute* attributes;
+    size_t count = element->getAttributes(&attributes);
+    size_t result = 0;
+    while (result < count) {
+        if (strncmp(attributes->fName, attrValue, len) == 0 && strlen(attributes->fName) == len) {
+            SkASSERT(result == (attributes->fOffset -
+                (isPaint ? sizeof(SkString) : sizeof(SkSVGElement))) / sizeof(SkString));
+            return result;
+        }
+        attributes++;
+        result++;
+    }
+    return -1;
+}
+
+#if 0
+const char* SkSVGParser::getFinal() {
+    _startElement("screenplay");
+    // generate defs
+    SkSVGElement** ptr;
+    for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkSVGElement* element = *ptr;
+        translate(element, true);
+    }
+    // generate onLoad
+    _startElement("event");
+    _addAttribute("kind", "onLoad");
+    _startElement("paint");
+    _addAttribute("antiAlias", "true");
+    _endElement();
+    for (ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
+        SkSVGElement* element = *ptr;
+        translate(element, false);
+    }
+    _endElement(); // event
+    _endElement(); // screenplay
+    Delete(fChildren);
+    fStream.write("", 1);
+    return fStream.getStream();
+}
+#endif
+
+SkString& SkSVGParser::getPaintLast(SkSVGPaint::Field field) {
+    SkSVGPaint* state = fHead;
+    do {
+        SkString* attr = (*state)[field];
+        SkASSERT(attr);
+        if (attr->size() > 0)
+            return *attr;
+        state = state->fNext;
+    } while (state);
+    SkASSERT(0);
+    SkASSERT(fEmptyPaint[field]);
+    return *fEmptyPaint[field];
+}
+
+bool SkSVGParser::isStrokeAndFill(  SkSVGPaint** strokeState, SkSVGPaint** fillState) {
+    SkSVGPaint* walking = fHead;
+    bool stroke = false;
+    bool fill = false;
+    bool strokeSet = false;
+    bool fillSet = false;
+    while (walking != NULL) {
+        if (strokeSet == false && walking->f_stroke.size() > 0) {
+            stroke = walking->f_stroke.equals("none") == false;
+            *strokeState = walking;
+            strokeSet = true;
+        }
+        if (fillSet == false && walking->f_fill.size() > 0) {
+            fill = walking->f_fill.equals("none") == false;
+            *fillState = walking;
+            fillSet = true;
+        }
+        walking = walking->fNext;
+    }
+    return stroke && fill;
+}
+
+bool SkSVGParser::onAddAttribute(const char name[], const char value[]) {
+    return onAddAttributeLen(name, value, strlen(value));
+}
+
+bool SkSVGParser::onAddAttributeLen(const char name[], const char value[], size_t len) {
+    if (fCurrElement == NULL)    // this signals we should ignore attributes for this element
+        return true;
+    if (fCurrElement->fIsDef == false && fCurrElement->fIsNotDef == false)
+        return false; // also an ignored element
+    size_t nameLen = strlen(name);
+    int attrIndex = findAttribute(fCurrElement, name, nameLen, false);
+    if (attrIndex == -1) {
+        attrIndex = findAttribute(&fCurrElement->fPaintState, name, nameLen, true);
+        if (attrIndex >= 0) {
+            fCurrElement->fPaintState.addAttribute(*this, attrIndex, value, len);
+            return false;
+        }
+        if (nameLen == 2 && strncmp("id", name, nameLen) == 0) {
+            fCurrElement->f_id.set(value, len);
+            return false;
+        }
+        if (strchr(name, ':') != 0) // part of a different namespace
+            return false;
+    }
+    SkASSERT(attrIndex >= 0);
+    fCurrElement->addAttribute(*this, attrIndex, value, len);
+    return false;
+}
+
+bool SkSVGParser::onEndElement(const char elem[]) {
+    int parentIndex = fParents.count() - 1;
+    if (parentIndex >= 0) {
+        SkSVGElement* element = fParents[parentIndex];
+        element->onEndElement(*this);
+        fParents.remove(parentIndex);
+    }
+    return false;
+}
+
+bool SkSVGParser::onStartElement(const char name[]) {
+    return onStartElementLen(name, strlen(name));
+}
+
+bool SkSVGParser::onStartElementLen(const char name[], size_t len) {
+    if (strncmp(name, "svg", len) == 0) {
+        fInSVG = true;
+    } else if (fInSVG == false)
+        return false;
+    const char* nextColon = strchr(name, ':');
+    if (nextColon && (size_t)(nextColon - name) < len)
+        return false;
+    SkSVGTypes type = GetType(name, len);
+//    SkASSERT(type >= 0);
+    if (type < 0) {
+        type = SkSVGType_G;
+//        return true;
+    }
+    SkSVGElement* parent = fParents.count() > 0 ? fParents.top() : NULL;
+    SkSVGElement* element = CreateElement(type, parent);
+    bool result = false;
+    if (parent) {
+        element->fParent = parent;
+        result = fParents.top()->onStartElement(element);
+    } else
+        *fChildren.append() = element;
+    if (strncmp(name, "svg", len) != 0)
+        *fParents.append() = element;
+    fCurrElement = element;
+    return result;
+}
+
+bool SkSVGParser::onText(const char text[], int len) {
+    if (fInSVG == false)
+        return false;
+    SkSVGTypes type = fCurrElement->getType();
+    if (type != SkSVGType_Text && type != SkSVGType_Tspan)
+        return false;
+    SkSVGText* textElement = (SkSVGText*) fCurrElement;
+    textElement->f_text.set(text, len);
+    return false;
+}
+
+static int32_t strokeFillID = 0;
+
+void SkSVGParser::translate(SkSVGElement* element, bool isDef) {
+    SkSVGPaint::Push(&fHead, &element->fPaintState);
+    bool isFlushable = element->isFlushable();
+    if ((element->fIsDef == false && element->fIsNotDef == false) ||
+        (element->fIsDef && isDef == false && element->fIsNotDef == false) ||
+        (element->fIsDef == false && isDef && element->fIsNotDef)) {
+        isFlushable = false;
+    }
+    SkSVGPaint* strokeState = NULL, * fillState = NULL;
+    if (isFlushable)
+        element->fPaintState.setSave(*this);
+    if (isFlushable && isStrokeAndFill(&strokeState, &fillState)) {
+        SkString& elementID = element->f_id;
+        if (elementID.size() == 0) {
+            elementID.set("sf");
+            elementID.appendS32(++strokeFillID);
+        }
+        SkString saveStroke(strokeState->f_stroke);
+        SkString saveFill(fillState->f_fill);
+        strokeState->f_stroke.set("none");
+        element->fPaintState.flush(*this, isFlushable, isDef);
+        element->translate(*this, isDef);
+        strokeState->f_stroke.set(saveStroke);
+        fillState->f_fill.set("none");
+        if (element->fPaintState.flush(*this, isFlushable, isDef)) {
+            _startElement("add");
+            _addAttributeLen("use", elementID.c_str(), elementID.size());
+            _endElement();  // add
+        }
+        fillState->f_fill.set(saveFill);
+    } else {
+        element->fPaintState.flush(*this, isFlushable, isDef);
+        if (isFlushable || element->isGroup())
+            element->translate(*this, isDef);
+    }
+    SkSVGPaint::Pop(&fHead);
+}
+
+void SkSVGParser::translateMatrix(SkString& string, SkString* stringID) {
+    if (string.size() == 0)
+        return;
+    if (stringID->size() > 0) {
+        _startElement("add");
+        _addAttribute("use", stringID->c_str());
+        _endElement(); // add
+        return;
+    }
+    SkASSERT(strncmp(string.c_str(), "matrix", 6) == 0);
+    ++gGeneratedMatrixID;
+    _startElement("matrix");
+    char idStr[24];
+    strcpy(idStr, "sk_matrix");
+    sprintf(idStr + strlen(idStr), "%d", gGeneratedMatrixID);
+    _addAttribute("id", idStr);
+    stringID->set(idStr);
+    const char* str = string.c_str();
+    SkASSERT(strncmp(str, "matrix(", 7) == 0);
+    str += 6;
+    const char* strEnd = strrchr(str, ')');
+    SkASSERT(strEnd != NULL);
+    SkString mat(str, strEnd - str);
+    ConvertToArray(mat);
+    const char* elems[6];
+    static const int order[] = {0, 3, 1, 4, 2, 5};
+    const int* orderPtr = order;
+    str = mat.c_str();
+    strEnd = str + mat.size();
+    while (str < strEnd) {
+        elems[*orderPtr++] = str;
+        while (str < strEnd && *str != ',' )
+            str++;
+        str++;
+    }
+    string.reset();
+    for (int index = 0; index < 6; index++) {
+        const char* end = strchr(elems[index], ',');
+        if (end == NULL)
+            end= strchr(elems[index], ']');
+        string.append(elems[index], end - elems[index] + 1);
+    }
+    string.remove(string.size() - 1, 1);
+    string.append(",0,0,1]");
+    _addAttribute("matrix", string);
+    _endElement();  // matrix
+}
+
+static bool is_whitespace(char ch) {
+    return ch > 0 && ch <= ' ';
+}
+
+void SkSVGParser::ConvertToArray(SkString& vals) {
+    vals.appendUnichar(']');
+    char* valCh = (char*) vals.c_str();
+    valCh[0] = '[';
+    int index = 1;
+    while (valCh[index] != ']') {
+        while (is_whitespace(valCh[index]))
+            index++;
+        bool foundComma = false;
+        char next;
+        do {
+            next = valCh[index++];
+            if (next == ',') {
+                foundComma = true;
+                continue;
+            }
+            if (next == ']') {
+                index--;
+                goto undoLastComma;
+            }
+            if (next == ' ')
+                break;
+            foundComma = false;
+        } while (is_whitespace(next) == false);
+        if (foundComma == false)
+            valCh[index - 1] = ',';
+    }
+undoLastComma:
+    while (is_whitespace(valCh[--index]))
+        ;
+    if (valCh[index] == ',')
+        valCh[index] = ' ';
+}
+
+#define CASE_NEW(type) case SkSVGType_##type : created = new SkSVG##type(); break
+
+SkSVGElement* SkSVGParser::CreateElement(SkSVGTypes type, SkSVGElement* parent) {
+    SkSVGElement* created = NULL;
+    switch (type) {
+        CASE_NEW(Circle);
+        CASE_NEW(ClipPath);
+        CASE_NEW(Defs);
+        CASE_NEW(Ellipse);
+        CASE_NEW(FeColorMatrix);
+        CASE_NEW(Filter);
+        CASE_NEW(G);
+        CASE_NEW(Image);
+        CASE_NEW(Line);
+        CASE_NEW(LinearGradient);
+        CASE_NEW(Mask);
+        CASE_NEW(Metadata);
+        CASE_NEW(Path);
+        CASE_NEW(Polygon);
+        CASE_NEW(Polyline);
+        CASE_NEW(RadialGradient);
+        CASE_NEW(Rect);
+        CASE_NEW(Stop);
+        CASE_NEW(SVG);
+        CASE_NEW(Symbol);
+        CASE_NEW(Text);
+        CASE_NEW(Tspan);
+        CASE_NEW(Use);
+        default:
+            SkASSERT(0);
+            return NULL;
+    }
+    created->fParent = parent;
+    bool isDef = created->fIsDef = created->isDef();
+    bool isNotDef = created->fIsNotDef = created->isNotDef();
+    if (isDef) {
+        SkSVGElement* up = parent;
+        while (up && up->fIsDef == false) {
+            up->fIsDef = true;
+            up = up->fParent;
+        }
+    }
+    if (isNotDef) {
+        SkSVGElement* up = parent;
+        while (up && up->fIsNotDef == false) {
+            up->fIsNotDef = true;
+            up = up->fParent;
+        }
+    }
+    return created;
+}
+
+const SkSVGTypeName gSVGTypeNames[] = {
+    {"circle", SkSVGType_Circle},
+    {"clipPath", SkSVGType_ClipPath},
+    {"defs", SkSVGType_Defs},
+    {"ellipse", SkSVGType_Ellipse},
+    {"feColorMatrix", SkSVGType_FeColorMatrix},
+    {"filter", SkSVGType_Filter},
+    {"g", SkSVGType_G},
+    {"image", SkSVGType_Image},
+    {"line", SkSVGType_Line},
+    {"linearGradient", SkSVGType_LinearGradient},
+    {"mask", SkSVGType_Mask},
+    {"metadata", SkSVGType_Metadata},
+    {"path", SkSVGType_Path},
+    {"polygon", SkSVGType_Polygon},
+    {"polyline", SkSVGType_Polyline},
+    {"radialGradient", SkSVGType_RadialGradient},
+    {"rect", SkSVGType_Rect},
+    {"stop", SkSVGType_Stop},
+    {"svg", SkSVGType_SVG},
+    {"symbol", SkSVGType_Symbol},
+    {"text", SkSVGType_Text},
+    {"tspan", SkSVGType_Tspan},
+    {"use", SkSVGType_Use}
+};
+
+const int kSVGTypeNamesSize = SK_ARRAY_COUNT(gSVGTypeNames);
+
+SkSVGTypes SkSVGParser::GetType(const char match[], size_t len ) {
+    int index = SkStrSearch(&gSVGTypeNames[0].fName, kSVGTypeNamesSize, match,
+        len, sizeof(gSVGTypeNames[0]));
+    return index >= 0 && index < kSVGTypeNamesSize ? gSVGTypeNames[index].fType :
+        (SkSVGTypes) -1;
+}
diff --git a/src/svg/SkSVGPath.cpp b/src/svg/SkSVGPath.cpp
new file mode 100644
index 0000000..ab18a65
--- /dev/null
+++ b/src/svg/SkSVGPath.cpp
@@ -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.
+ */
+
+
+#include "SkSVGPath.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGPath::gAttributes[] = {
+    SVG_ATTRIBUTE(d)
+};
+
+DEFINE_SVG_INFO(Path)
+
+void SkSVGPath::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("path");
+    INHERITED::translate(parser, defState);
+    bool hasMultiplePaths = false;
+    const char* firstZ = strchr(f_d.c_str(), 'z');
+    if (firstZ != NULL) {
+        firstZ++; // skip over 'z'
+        while (*firstZ == ' ')
+            firstZ++;
+        hasMultiplePaths = *firstZ != '\0';
+    }
+    if (hasMultiplePaths) {
+        SkString& fillRule = parser.getPaintLast(SkSVGPaint::kFillRule);
+        if (fillRule.size() > 0)
+            parser._addAttribute("fillType", fillRule.equals("evenodd") ? "evenOdd" : "winding");
+    }
+    SVG_ADD_ATTRIBUTE(d);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGPath.h b/src/svg/SkSVGPath.h
new file mode 100644
index 0000000..de325a7
--- /dev/null
+++ b/src/svg/SkSVGPath.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 SkSVGPath_DEFINED
+#define SkSVGPath_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGPath : public SkSVGElement {
+    DECLARE_SVG_INFO(Path);
+private:
+    SkString f_d;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGPath_DEFINED
diff --git a/src/svg/SkSVGPolygon.cpp b/src/svg/SkSVGPolygon.cpp
new file mode 100644
index 0000000..4b458db
--- /dev/null
+++ b/src/svg/SkSVGPolygon.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 "SkSVGPolygon.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGPolygon::gAttributes[] = {
+    SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule),
+    SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule),
+    SVG_ATTRIBUTE(points)
+};
+
+DEFINE_SVG_INFO(Polygon)
+
+void SkSVGPolygon::addAttribute(SkSVGParser& parser, int attrIndex,
+        const char* attrValue, size_t attrLength) {
+    INHERITED::addAttribute(parser, attrIndex, attrValue, attrLength);
+}
+
+void SkSVGPolygon::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("polygon");
+    SkSVGElement::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE(points);
+    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
new file mode 100644
index 0000000..b2848d0
--- /dev/null
+++ b/src/svg/SkSVGPolygon.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 SkSVGPolygon_DEFINED
+#define SkSVGPolygon_DEFINED
+
+#include "SkSVGPolyline.h"
+
+class SkSVGPolygon : public SkSVGPolyline {
+    DECLARE_SVG_INFO(Polygon);
+    virtual void addAttribute(SkSVGParser& , int attrIndex,
+        const char* attrValue, size_t attrLength);
+private:
+    typedef SkSVGPolyline INHERITED;
+};
+
+#endif // SkSVGPolygon_DEFINED
diff --git a/src/svg/SkSVGPolyline.cpp b/src/svg/SkSVGPolyline.cpp
new file mode 100644
index 0000000..83dad48
--- /dev/null
+++ b/src/svg/SkSVGPolyline.cpp
@@ -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.
+ */
+
+
+#include "SkSVGPolyline.h"
+#include "SkSVGParser.h"
+
+enum {
+    kCliipRule,
+    kFillRule,
+    kPoints
+};
+
+const SkSVGAttribute SkSVGPolyline::gAttributes[] = {
+    SVG_LITERAL_ATTRIBUTE(clip-rule, f_clipRule),
+    SVG_LITERAL_ATTRIBUTE(fill-rule, f_fillRule),
+    SVG_ATTRIBUTE(points)
+};
+
+DEFINE_SVG_INFO(Polyline)
+
+void SkSVGPolyline::addAttribute(SkSVGParser& , int attrIndex,
+        const char* attrValue, size_t attrLength) {
+    if (attrIndex != kPoints)
+        return;
+    f_points.set("[");
+    f_points.append(attrValue, attrLength);
+    SkSVGParser::ConvertToArray(f_points);
+}
+
+void SkSVGPolyline::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("polyline");
+    INHERITED::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE(points);
+    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
new file mode 100644
index 0000000..b8d5af4
--- /dev/null
+++ b/src/svg/SkSVGPolyline.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 SkSVGPolyline_DEFINED
+#define SkSVGPolyline_DEFINED
+
+#include "SkSVGElements.h"
+#include "SkString.h"
+
+class SkSVGPolyline : public SkSVGElement {
+    DECLARE_SVG_INFO(Polyline);
+    virtual void addAttribute(SkSVGParser& , int attrIndex,
+        const char* attrValue, size_t attrLength);
+protected:
+    SkString f_clipRule;
+    SkString f_fillRule;
+    SkString f_points;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGPolyline_DEFINED
diff --git a/src/svg/SkSVGRadialGradient.cpp b/src/svg/SkSVGRadialGradient.cpp
new file mode 100644
index 0000000..4fdf432
--- /dev/null
+++ b/src/svg/SkSVGRadialGradient.cpp
@@ -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.
+ */
+
+
+#include "SkSVGRadialGradient.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGRadialGradient::gAttributes[] = {
+    SVG_ATTRIBUTE(cx),
+    SVG_ATTRIBUTE(cy),
+    SVG_ATTRIBUTE(fx),
+    SVG_ATTRIBUTE(fy),
+    SVG_ATTRIBUTE(gradientTransform),
+    SVG_ATTRIBUTE(gradientUnits),
+    SVG_ATTRIBUTE(r)
+};
+
+DEFINE_SVG_INFO(RadialGradient)
+
+void SkSVGRadialGradient::translate(SkSVGParser& parser, bool defState) {
+    if (fMatrixID.size() == 0)
+        parser.translateMatrix(f_gradientTransform, &fMatrixID);
+    parser._startElement("radialGradient");
+    if (fMatrixID.size() > 0)
+        parser._addAttribute("matrix", fMatrixID);
+    INHERITED::translateGradientUnits(f_gradientUnits);
+    SkString center;
+    center.appendUnichar('[');
+    center.append(f_cx);
+    center.appendUnichar(',');
+    center.append(f_cy);
+    center.appendUnichar(']');
+    parser._addAttribute("center", center);
+    parser._addAttribute("radius", f_r);
+    INHERITED::translate(parser, defState);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGRadialGradient.h b/src/svg/SkSVGRadialGradient.h
new file mode 100644
index 0000000..8eba3f5
--- /dev/null
+++ b/src/svg/SkSVGRadialGradient.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 SkSVGRadialGradient_DEFINED
+#define SkSVGRadialGradient_DEFINED
+
+#include "SkSVGGradient.h"
+
+class SkSVGRadialGradient : public SkSVGGradient {
+    DECLARE_SVG_INFO(RadialGradient);
+protected:
+    SkString f_cx;
+    SkString f_cy;
+    SkString f_fx;
+    SkString f_fy;
+    SkString f_gradientTransform;
+    SkString f_gradientUnits;
+    SkString f_r;
+    SkString fMatrixID;
+private:
+    typedef SkSVGGradient INHERITED;
+};
+
+#endif // SkSVGRadialGradient_DEFINED
diff --git a/src/svg/SkSVGRect.cpp b/src/svg/SkSVGRect.cpp
new file mode 100644
index 0000000..32a7f99
--- /dev/null
+++ b/src/svg/SkSVGRect.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 "SkSVGRect.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGRect::gAttributes[] = {
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(x),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Rect)
+
+SkSVGRect::SkSVGRect() {
+    f_x.set("0");
+    f_y.set("0");
+}
+
+void SkSVGRect::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("rect");
+    INHERITED::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE_ALIAS(left, x);
+    SVG_ADD_ATTRIBUTE_ALIAS(top, y);
+    SVG_ADD_ATTRIBUTE(width);
+    SVG_ADD_ATTRIBUTE(height);
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGRect.h b/src/svg/SkSVGRect.h
new file mode 100644
index 0000000..4ae820c
--- /dev/null
+++ b/src/svg/SkSVGRect.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 SkSVGRect_DEFINED
+#define SkSVGRect_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGRect : public SkSVGElement {
+    DECLARE_SVG_INFO(Rect);
+    SkSVGRect();
+private:
+    SkString f_height;
+    SkString f_width;
+    SkString f_x;
+    SkString f_y;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGRect_DEFINED
diff --git a/src/svg/SkSVGSVG.cpp b/src/svg/SkSVGSVG.cpp
new file mode 100644
index 0000000..fcce62d
--- /dev/null
+++ b/src/svg/SkSVGSVG.cpp
@@ -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.
+ */
+
+
+#include "SkSVGSVG.h"
+#include "SkParse.h"
+#include "SkRect.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGSVG::gAttributes[] = {
+    SVG_LITERAL_ATTRIBUTE(enable-background, f_enable_background),
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(overflow),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(version),
+    SVG_ATTRIBUTE(viewBox),
+    SVG_ATTRIBUTE(x),
+    SVG_LITERAL_ATTRIBUTE(xml:space, f_xml_space),
+    SVG_ATTRIBUTE(xmlns),
+    SVG_LITERAL_ATTRIBUTE(xmlns:xlink, f_xml_xlink),
+    SVG_ATTRIBUTE(y),
+};
+
+DEFINE_SVG_INFO(SVG)
+
+
+bool SkSVGSVG::isFlushable() {
+    return false;
+}
+
+void SkSVGSVG::translate(SkSVGParser& parser, bool defState) {
+    SkScalar height, width;
+    SkScalar viewBox[4];
+    const char* hSuffix = SkParse::FindScalar(f_height.c_str(), &height);
+    if (strcmp(hSuffix, "pt") == 0)
+        height = SkScalarMulDiv(height, SK_Scalar1 * 72, SK_Scalar1 * 96);
+    const char* wSuffix = SkParse::FindScalar(f_width.c_str(), &width);
+    if (strcmp(wSuffix, "pt") == 0)
+        width = SkScalarMulDiv(width, SK_Scalar1 * 72, SK_Scalar1 * 96);
+    SkParse::FindScalars(f_viewBox.c_str(), viewBox, 4);
+    SkRect box;
+    box.fLeft = SkScalarDiv(viewBox[0], width);
+    box.fTop = SkScalarDiv(viewBox[1], height);
+    box.fRight = SkScalarDiv(viewBox[2], width);
+    box.fBottom = SkScalarDiv(viewBox[3], height);
+    if (box.fLeft == 0 && box.fTop == 0 &&
+        box.fRight == SK_Scalar1 && box.fBottom == SK_Scalar1)
+            return;
+    parser._startElement("matrix");
+    if (box.fLeft != 0) {
+        SkString x;
+        x.appendScalar(box.fLeft);
+        parser._addAttributeLen("translateX", x.c_str(), x.size());
+    }
+    if (box.fTop != 0) {
+        SkString y;
+        y.appendScalar(box.fTop);
+        parser._addAttributeLen("translateY", y.c_str(), y.size());
+    }
+    if (box.fRight != SK_Scalar1) {
+        SkString x;
+        x.appendScalar(box.fRight);
+        parser._addAttributeLen("scaleX", x.c_str(), x.size());
+    }
+    if (box.fBottom != SK_Scalar1) {
+        SkString y;
+        y.appendScalar(box.fBottom);
+        parser._addAttributeLen("scaleY", y.c_str(), y.size());
+    }
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGSVG.h b/src/svg/SkSVGSVG.h
new file mode 100644
index 0000000..155f9a9
--- /dev/null
+++ b/src/svg/SkSVGSVG.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 SkSVGSVG_DEFINED
+#define SkSVGSVG_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGSVG : public SkSVGElement {
+    DECLARE_SVG_INFO(SVG);
+    virtual bool isFlushable();
+private:
+    SkString f_enable_background;
+    SkString f_height;
+    SkString f_overflow;
+    SkString f_width;
+    SkString f_version;
+    SkString f_viewBox;
+    SkString f_x;
+    SkString f_xml_space;
+    SkString f_xmlns;
+    SkString f_xml_xlink;
+    SkString f_y;
+
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGSVG_DEFINED
diff --git a/src/svg/SkSVGStop.cpp b/src/svg/SkSVGStop.cpp
new file mode 100644
index 0000000..0630f61
--- /dev/null
+++ b/src/svg/SkSVGStop.cpp
@@ -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.
+ */
+
+
+#include "SkSVGStop.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGStop::gAttributes[] = {
+    SVG_ATTRIBUTE(offset)
+};
+
+DEFINE_SVG_INFO(Stop)
+
+void SkSVGStop::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("color");
+    INHERITED::translate(parser, defState);
+    parser._addAttribute("color", parser.getPaintLast(SkSVGPaint::kStopColor));
+    parser._endElement();
+}
diff --git a/src/svg/SkSVGStop.h b/src/svg/SkSVGStop.h
new file mode 100644
index 0000000..e55936b
--- /dev/null
+++ b/src/svg/SkSVGStop.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 SkSVGStop_DEFINED
+#define SkSVGStop_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGStop : public SkSVGElement {
+    DECLARE_SVG_INFO(Stop);
+private:
+    SkString f_offset;
+    friend class SkSVGGradient;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGStop_DEFINED
diff --git a/src/svg/SkSVGSymbol.cpp b/src/svg/SkSVGSymbol.cpp
new file mode 100644
index 0000000..ce341e6
--- /dev/null
+++ b/src/svg/SkSVGSymbol.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 "SkSVGSymbol.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGSymbol::gAttributes[] = {
+    SVG_ATTRIBUTE(viewBox)
+};
+
+DEFINE_SVG_INFO(Symbol)
+
+void SkSVGSymbol::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+    // !!! children need to be written into document
+}
diff --git a/src/svg/SkSVGSymbol.h b/src/svg/SkSVGSymbol.h
new file mode 100644
index 0000000..80fd61a
--- /dev/null
+++ b/src/svg/SkSVGSymbol.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 SkSVGSymbol_DEFINED
+#define SkSVGSymbol_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGSymbol : public SkSVGElement {
+    DECLARE_SVG_INFO(Symbol);
+private:
+    SkString f_viewBox;
+    typedef SkSVGElement INHERITED;
+};
+
+#endif // SkSVGSymbol_DEFINED
diff --git a/src/svg/SkSVGText.cpp b/src/svg/SkSVGText.cpp
new file mode 100644
index 0000000..dfaba08
--- /dev/null
+++ b/src/svg/SkSVGText.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 "SkSVGText.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGText::gAttributes[] = {
+    SVG_ATTRIBUTE(x),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Text)
+
+void SkSVGText::translate(SkSVGParser& parser, bool defState) {
+    parser._startElement("text");
+    INHERITED::translate(parser, defState);
+    SVG_ADD_ATTRIBUTE(x);
+    SVG_ADD_ATTRIBUTE(y);
+    SVG_ADD_ATTRIBUTE(text);
+    parser._endElement();
+}
+
+
+const SkSVGAttribute SkSVGTspan::gAttributes[] = {
+    SVG_ATTRIBUTE(x),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Tspan)
+
+void SkSVGTspan::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+}
diff --git a/src/svg/SkSVGText.h b/src/svg/SkSVGText.h
new file mode 100644
index 0000000..5ac2fbd
--- /dev/null
+++ b/src/svg/SkSVGText.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 SkSVGText_DEFINED
+#define SkSVGText_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGText : public SkSVGElement {
+    DECLARE_SVG_INFO(Text);
+protected:
+    SkString f_x;
+    SkString f_y;
+    SkString f_text;    // not an attribute
+private:
+    typedef SkSVGElement INHERITED;
+    friend class SkSVGParser;
+};
+
+class SkSVGTspan : public SkSVGText {
+    DECLARE_SVG_INFO(Tspan);
+private:
+    typedef SkSVGText INHERITED;
+};
+
+#endif // SkSVGText_DEFINED
diff --git a/src/svg/SkSVGUse.cpp b/src/svg/SkSVGUse.cpp
new file mode 100644
index 0000000..0496d98
--- /dev/null
+++ b/src/svg/SkSVGUse.cpp
@@ -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.
+ */
+
+
+#include "SkSVGUse.h"
+#include "SkSVGParser.h"
+
+const SkSVGAttribute SkSVGUse::gAttributes[] = {
+    SVG_ATTRIBUTE(height),
+    SVG_ATTRIBUTE(width),
+    SVG_ATTRIBUTE(x),
+    SVG_LITERAL_ATTRIBUTE(xlink:href, f_xlink_href),
+    SVG_ATTRIBUTE(y)
+};
+
+DEFINE_SVG_INFO(Use)
+
+void SkSVGUse::translate(SkSVGParser& parser, bool defState) {
+    INHERITED::translate(parser, defState);
+    parser._startElement("add");
+    const char* start = strchr(f_xlink_href.c_str(), '#') + 1;
+    SkASSERT(start);
+    parser._addAttributeLen("use", start, strlen(start) - 1);
+    parser._endElement();   // clip
+}
diff --git a/src/svg/SkSVGUse.h b/src/svg/SkSVGUse.h
new file mode 100644
index 0000000..b907189
--- /dev/null
+++ b/src/svg/SkSVGUse.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 SkSVGUse_DEFINED
+#define SkSVGUse_DEFINED
+
+#include "SkSVGElements.h"
+
+class SkSVGUse : public SkSVGElement {
+    DECLARE_SVG_INFO(Use);
+protected:
+    SkString f_height;
+    SkString f_width;
+    SkString f_x;
+    SkString f_xlink_href;
+    SkString f_y;
+private:
+    typedef SkSVGElement INHERITED;
+    friend class SkSVGClipPath;
+};
+
+#endif // SkSVGUse_DEFINED
diff --git a/src/text/SkTextLayout.cpp b/src/text/SkTextLayout.cpp
new file mode 100644
index 0000000..4d87ffe
--- /dev/null
+++ b/src/text/SkTextLayout.cpp
@@ -0,0 +1,81 @@
+
+/*
+ * Copyright 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"
+
+SK_DEFINE_INST_COUNT(SkTextStyle)
+
+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/src/utils/SkBase64.cpp b/src/utils/SkBase64.cpp
new file mode 100644
index 0000000..8e1f223
--- /dev/null
+++ b/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/src/utils/SkBase64.h b/src/utils/SkBase64.h
new file mode 100644
index 0000000..5bf9006
--- /dev/null
+++ b/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 SkImageBaseBitmap;
+};
+
+#endif // SkBase64_DEFINED
diff --git a/src/utils/SkBitSet.cpp b/src/utils/SkBitSet.cpp
new file mode 100755
index 0000000..664c8cd
--- /dev/null
+++ b/src/utils/SkBitSet.cpp
@@ -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.
+ */
+
+
+#include "SkBitSet.h"
+
+SkBitSet::SkBitSet(int numberOfBits)
+    : fBitData(NULL), fDwordCount(0), fBitCount(numberOfBits) {
+    SkASSERT(numberOfBits > 0);
+    // Round up size to 32-bit boundary.
+    fDwordCount = (numberOfBits + 31) / 32;
+    fBitData.set(malloc(fDwordCount * sizeof(uint32_t)));
+    clearAll();
+}
+
+SkBitSet::SkBitSet(const SkBitSet& source)
+    : fBitData(NULL), fDwordCount(0), fBitCount(0) {
+    *this = source;
+}
+
+const SkBitSet& SkBitSet::operator=(const SkBitSet& rhs) {
+    if (this == &rhs) {
+        return *this;
+    }
+    fBitCount = rhs.fBitCount;
+    fBitData.free();
+    fDwordCount = rhs.fDwordCount;
+    fBitData.set(malloc(fDwordCount * sizeof(uint32_t)));
+    memcpy(fBitData.get(), rhs.fBitData.get(), fDwordCount * sizeof(uint32_t));
+    return *this;
+}
+
+bool SkBitSet::operator==(const SkBitSet& rhs) {
+    if (fBitCount == rhs.fBitCount) {
+        if (fBitData.get() != NULL) {
+            return (memcmp(fBitData.get(), rhs.fBitData.get(),
+                           fDwordCount * sizeof(uint32_t)) == 0);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SkBitSet::operator!=(const SkBitSet& rhs) {
+    return !(*this == rhs);
+}
+
+void SkBitSet::clearAll() {
+    if (fBitData.get() != NULL) {
+        sk_bzero(fBitData.get(), fDwordCount * sizeof(uint32_t));
+    }
+}
+
+void SkBitSet::setBit(int index, bool value) {
+    uint32_t mask = 1 << (index % 32);
+    if (value) {
+        *(internalGet(index)) |= mask;
+    } else {
+        *(internalGet(index)) &= ~mask;
+    }
+}
+
+bool SkBitSet::isBitSet(int index) const {
+    uint32_t mask = 1 << (index % 32);
+    return 0 != (*internalGet(index) & mask);
+}
+
+bool SkBitSet::orBits(const SkBitSet& source) {
+    if (fBitCount != source.fBitCount) {
+        return false;
+    }
+    uint32_t* targetBitmap = internalGet(0);
+    uint32_t* sourceBitmap = source.internalGet(0);
+    for (size_t i = 0; i < fDwordCount; ++i) {
+        targetBitmap[i] |= sourceBitmap[i];
+    }
+    return true;
+}
diff --git a/src/utils/SkBitSet.h b/src/utils/SkBitSet.h
new file mode 100755
index 0000000..484fc2a
--- /dev/null
+++ b/src/utils/SkBitSet.h
@@ -0,0 +1,78 @@
+
+/*
+ * 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 SkBitSet_DEFINED
+#define SkBitSet_DEFINED
+
+#include "SkTypes.h"
+#include "SkTDArray.h"
+
+class SkBitSet {
+public:
+    /** NumberOfBits must be greater than zero.
+     */
+    explicit SkBitSet(int numberOfBits);
+    explicit SkBitSet(const SkBitSet& source);
+
+    const SkBitSet& operator=(const SkBitSet& rhs);
+    bool operator==(const SkBitSet& rhs);
+    bool operator!=(const SkBitSet& rhs);
+
+    /** Clear all data.
+     */
+    void clearAll();
+
+    /** Set the value of the index-th bit.
+     */
+    void setBit(int index, bool value);
+
+    /** Test if bit index is set.
+     */
+    bool isBitSet(int index) const;
+
+    /** Or bits from source.  false is returned if this doesn't have the same
+     *  bit count as source.
+     */
+    bool orBits(const SkBitSet& source);
+
+    /** Export indices of set bits to T array.
+     */
+    template<typename T>
+    void exportTo(SkTDArray<T>* array) const {
+        SkASSERT(array);
+        uint32_t* data = reinterpret_cast<uint32_t*>(fBitData.get());
+        for (unsigned int i = 0; i < fDwordCount; ++i) {
+            uint32_t value = data[i];
+            if (value) {  // There are set bits
+                unsigned int index = i * 32;
+                for (unsigned int j = 0; j < 32; ++j) {
+                    if (0x1 & (value >> j)) {
+                        array->push(index + j);
+                    }
+                }
+            }
+        }
+    }
+
+private:
+    SkAutoFree fBitData;
+    // Dword (32-bit) count of the bitset.
+    size_t fDwordCount;
+    size_t fBitCount;
+
+    uint32_t* internalGet(int index) const {
+        SkASSERT((size_t)index < fBitCount);
+        size_t internalIndex = index / 32;
+        SkASSERT(internalIndex < fDwordCount);
+        return reinterpret_cast<uint32_t*>(fBitData.get()) + internalIndex;
+    }
+};
+
+
+#endif
diff --git a/src/utils/SkBoundaryPatch.cpp b/src/utils/SkBoundaryPatch.cpp
new file mode 100644
index 0000000..afc76b5
--- /dev/null
+++ b/src/utils/SkBoundaryPatch.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 "SkBoundaryPatch.h"
+
+SK_DEFINE_INST_COUNT(SkBoundary)
+
+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/src/utils/SkCamera.cpp b/src/utils/SkCamera.cpp
new file mode 100644
index 0000000..11e10f6
--- /dev/null
+++ b/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(*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);
+
+        SkTCast<SkPoint3D*>(&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(*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));
+    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/SkCubicInterval.cpp b/src/utils/SkCubicInterval.cpp
new file mode 100644
index 0000000..904f26b
--- /dev/null
+++ b/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/src/utils/SkCullPoints.cpp b/src/utils/SkCullPoints.cpp
new file mode 100644
index 0000000..e76679d
--- /dev/null
+++ b/src/utils/SkCullPoints.cpp
@@ -0,0 +1,220 @@
+
+/*
+ * Copyright 2006 The Android 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;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#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
new file mode 100644
index 0000000..447418e
--- /dev/null
+++ b/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/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
new file mode 100644
index 0000000..13b8d28
--- /dev/null
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -0,0 +1,957 @@
+
+/*
+ * Copyright 2012 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 "SkChunkAlloc.h"
+#include "SkColorFilter.h"
+#include "SkDevice.h"
+#include "SkDrawFilter.h"
+#include "SkGPipe.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+enum {
+    // Deferred canvas will auto-flush when recording reaches this limit
+    kDefaultMaxRecordingStorageBytes = 64*1024*1024,
+};
+
+enum PlaybackMode {
+    kNormal_PlaybackMode,
+    kSilent_PlaybackMode,
+};
+
+namespace {
+bool shouldDrawImmediately(const SkBitmap* bitmap, const SkPaint* paint) {
+    if (bitmap && bitmap->getTexture() && !bitmap->isImmutable()) {
+        return true;
+    }
+    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;
+            }
+        }
+    }
+    return false;
+}
+}
+
+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)
+    {
+        if (canvas.isDeferredDrawing() && shouldDrawImmediately(bitmap, paint)) {
+            canvas.setDeferredDrawing(false);
+            fCanvas = &canvas;
+        } else {
+            fCanvas = NULL;
+        }
+    }
+
+    SkDeferredCanvas* fCanvas;
+};
+
+namespace {
+
+bool isPaintOpaque(const SkPaint* paint,
+                   const SkBitmap* bmpReplacesShader = NULL) {
+    // TODO: SkXfermode should have a virtual isOpaque method, which would
+    // make it possible to test modes that do not have a Coeff representation.
+
+    if (!paint) {
+        return bmpReplacesShader ? bmpReplacesShader->isOpaque() : true;
+    }
+
+    SkXfermode::Coeff srcCoeff, dstCoeff;
+    if (SkXfermode::AsCoeff(paint->getXfermode(), &srcCoeff, &dstCoeff)){
+        if (SkXfermode::kDA_Coeff == srcCoeff || SkXfermode::kDC_Coeff == srcCoeff ||
+            SkXfermode::kIDA_Coeff == srcCoeff || SkXfermode::kIDC_Coeff == srcCoeff) {
+            return false;
+        }
+        switch (dstCoeff) {
+        case SkXfermode::kZero_Coeff:
+            return true;
+        case SkXfermode::kISA_Coeff:
+            if (paint->getAlpha() != 255) {
+                break;
+            }
+            if (bmpReplacesShader) {
+                if (!bmpReplacesShader->isOpaque()) {
+                    break;
+                }
+            } else if (paint->getShader() && !paint->getShader()->isOpaque()) {
+                break;
+            }
+            if (paint->getColorFilter() &&
+                ((paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        case SkXfermode::kSA_Coeff:
+            if (paint->getAlpha() != 0) {
+                break;
+            }
+            if (paint->getColorFilter() &&
+                ((paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        case SkXfermode::kSC_Coeff:
+            if (paint->getColor() != 0) { // all components must be 0
+                break;
+            }
+            if (bmpReplacesShader || paint->getShader()) {
+                break;
+            }
+            if (paint->getColorFilter() && (
+                (paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        default:
+            break;
+        }
+    }
+    return false;
+}
+
+} // unnamed namespace
+
+//-----------------------------------------------------------------------------
+// DeferredPipeController
+//-----------------------------------------------------------------------------
+
+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);
+    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;
+};
+
+DeferredDevice::DeferredDevice(
+    SkDevice* immediateDevice, SkDeferredCanvas::NotificationClient* notificationClient) :
+    SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
+             immediateDevice->height(), immediateDevice->isOpaque())
+    , fRecordingCanvas(NULL)
+    , fFreshFrame(true)
+    , fPreviousStorageAllocated(0){
+
+    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(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::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)) {
+        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);
+}
+
+
+SkDeferredCanvas::SkDeferredCanvas() {
+    this->init();
+}
+
+SkDeferredCanvas::SkDeferredCanvas(SkDevice* device) {
+    this->init();
+    this->setDevice(device);
+}
+
+void SkDeferredCanvas::init() {
+    fDeferredDrawing = true; // On by default
+}
+
+void SkDeferredCanvas::setMaxRecordingStorage(size_t maxStorage) {
+    this->validate();
+    this->getDeferredDevice()->setMaxRecordingStorage(maxStorage);
+}
+
+size_t SkDeferredCanvas::storageAllocatedForRecording() const {
+    return this->getDeferredDevice()->storageAllocatedForRecording();
+}
+
+size_t SkDeferredCanvas::freeMemoryIfPossible(size_t bytesToFree) {
+    return this->getDeferredDevice()->freeMemoryIfPossible(bytesToFree);
+}
+
+void SkDeferredCanvas::recordedDrawCommand() {
+    if (fDeferredDrawing) {
+        this->getDeferredDevice()->recordedDrawCommand();
+    }
+}
+
+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) {
+    this->validate(); // Must set device before calling this method
+    if (val != fDeferredDrawing) {
+        if (fDeferredDrawing) {
+            // Going live.
+            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) {
+    this->INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (device)))->unref();
+    return device;
+}
+
+SkDeferredCanvas::NotificationClient* SkDeferredCanvas::setNotificationClient(
+    NotificationClient* notificationClient) {
+
+    DeferredDevice* deferredDevice = this->getDeferredDevice();
+    SkASSERT(deferredDevice);
+    if (deferredDevice) {
+        deferredDevice->setNotificationClient(notificationClient);
+    }
+    return notificationClient;
+}
+
+bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
+                                   const SkPaint* paint) const {
+    SkCanvas* canvas = this->drawingCanvas();
+    SkISize canvasSize = this->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) {
+    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) {
+    this->drawingCanvas()->saveLayer(bounds, paint, flags);
+    int count = this->INHERITED::save(flags);
+    this->clipRectBounds(bounds, flags, NULL);
+    this->recordedDrawCommand();
+
+    return count;
+}
+
+void SkDeferredCanvas::restore() {
+    this->drawingCanvas()->restore();
+    this->INHERITED::restore();
+    this->recordedDrawCommand();
+}
+
+bool SkDeferredCanvas::isDrawingToLayer() const {
+    return this->drawingCanvas()->isDrawingToLayer();
+}
+
+bool SkDeferredCanvas::translate(SkScalar dx, SkScalar dy) {
+    this->drawingCanvas()->translate(dx, dy);
+    bool val = this->INHERITED::translate(dx, dy);
+    this->recordedDrawCommand();
+    return val;
+}
+
+bool SkDeferredCanvas::scale(SkScalar sx, SkScalar sy) {
+    this->drawingCanvas()->scale(sx, sy);
+    bool val = this->INHERITED::scale(sx, sy);
+    this->recordedDrawCommand();
+    return val;
+}
+
+bool SkDeferredCanvas::rotate(SkScalar degrees) {
+    this->drawingCanvas()->rotate(degrees);
+    bool val = this->INHERITED::rotate(degrees);
+    this->recordedDrawCommand();
+    return val;
+}
+
+bool SkDeferredCanvas::skew(SkScalar sx, SkScalar sy) {
+    this->drawingCanvas()->skew(sx, sy);
+    bool val = this->INHERITED::skew(sx, sy);
+    this->recordedDrawCommand();
+    return val;
+}
+
+bool SkDeferredCanvas::concat(const SkMatrix& matrix) {
+    this->drawingCanvas()->concat(matrix);
+    bool val = this->INHERITED::concat(matrix);
+    this->recordedDrawCommand();
+    return val;
+}
+
+void SkDeferredCanvas::setMatrix(const SkMatrix& matrix) {
+    this->drawingCanvas()->setMatrix(matrix);
+    this->INHERITED::setMatrix(matrix);
+    this->recordedDrawCommand();
+}
+
+bool SkDeferredCanvas::clipRect(const SkRect& rect,
+                                SkRegion::Op op,
+                                bool doAntiAlias) {
+    this->drawingCanvas()->clipRect(rect, op, doAntiAlias);
+    bool val = this->INHERITED::clipRect(rect, op, doAntiAlias);
+    this->recordedDrawCommand();
+    return val;
+}
+
+bool SkDeferredCanvas::clipPath(const SkPath& path,
+                                SkRegion::Op op,
+                                bool 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) {
+    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) {
+        this->getDeferredDevice()->skipPendingCommands();
+    }
+
+    this->drawingCanvas()->clear(color);
+    this->recordedDrawCommand();
+}
+
+void SkDeferredCanvas::drawPaint(const SkPaint& paint) {
+    if (fDeferredDrawing && this->isFullFrame(NULL, &paint) &&
+        isPaintOpaque(&paint)) {
+        this->getDeferredDevice()->skipPendingCommands();
+    }
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawPaint(paint);
+    this->recordedDrawCommand();
+}
+
+void SkDeferredCanvas::drawPoints(PointMode mode, size_t count,
+                                  const SkPoint pts[], const SkPaint& paint) {
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawPoints(mode, count, pts, paint);
+    this->recordedDrawCommand();
+}
+
+void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
+    if (fDeferredDrawing && this->isFullFrame(&rect, &paint) &&
+        isPaintOpaque(&paint)) {
+        this->getDeferredDevice()->skipPendingCommands();
+    }
+
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawRect(rect, paint);
+    this->recordedDrawCommand();
+}
+
+void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& 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 &&
+        this->isFullFrame(&bitmapRect, paint) &&
+        isPaintOpaque(paint, &bitmap)) {
+        this->getDeferredDevice()->skipPendingCommands();
+    }
+
+    AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
+    this->drawingCanvas()->drawBitmap(bitmap, left, top, paint);
+    this->recordedDrawCommand();
+}
+
+void SkDeferredCanvas::drawBitmapRectToRect(const SkBitmap& bitmap,
+                                            const SkRect* src,
+                                            const SkRect& dst,
+                                            const SkPaint* paint) {
+    if (fDeferredDrawing &&
+        this->isFullFrame(&dst, paint) &&
+        isPaintOpaque(paint, &bitmap)) {
+        this->getDeferredDevice()->skipPendingCommands();
+    }
+
+    AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
+    this->drawingCanvas()->drawBitmapRectToRect(bitmap, src, dst, paint);
+    this->recordedDrawCommand();
+}
+
+
+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
+    AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
+    this->drawingCanvas()->drawBitmapMatrix(bitmap, m, paint);
+    this->recordedDrawCommand();
+}
+
+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
+    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(bitmap.width()),
+        SkIntToScalar(bitmap.height()));
+    if (fDeferredDrawing &&
+        this->isFullFrame(&bitmapRect, paint) &&
+        isPaintOpaque(paint, &bitmap)) {
+        this->getDeferredDevice()->skipPendingCommands();
+    }
+
+    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) {
+    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) {
+    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) {
+    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) {
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawTextOnPath(text, byteLength, path, matrix, paint);
+    this->recordedDrawCommand();
+}
+
+void SkDeferredCanvas::drawPicture(SkPicture& picture) {
+    this->drawingCanvas()->drawPicture(picture);
+    this->recordedDrawCommand();
+}
+
+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) {
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
+                                        indices, indexCount, paint);
+    this->recordedDrawCommand();
+}
+
+SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder) {
+    this->drawingCanvas()->setBounder(bounder);
+    this->INHERITED::setBounder(bounder);
+    this->recordedDrawCommand();
+    return bounder;
+}
+
+SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) {
+    this->drawingCanvas()->setDrawFilter(filter);
+    this->INHERITED::setDrawFilter(filter);
+    this->recordedDrawCommand();
+    return filter;
+}
+
+SkCanvas* SkDeferredCanvas::canvasForDrawIter() {
+    return this->drawingCanvas();
+}
diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp
new file mode 100644
index 0000000..9135849
--- /dev/null
+++ b/src/utils/SkDumpCanvas.cpp
@@ -0,0 +1,482 @@
+
+/*
+ * Copyright 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"
+
+SK_DEFINE_INST_COUNT(SkDumpCanvas::Dumper)
+
+static void toString(const SkRect& r, SkString* str) {
+    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->appendf("[%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, false)) {
+            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->append("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) {
+    str->append("Region:[");
+    toString(rgn.getBounds(), str);
+    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->appendf("bitmap:[%d %d] %s", bm.width(), bm.height(),
+                toString(bm.config()));
+
+    SkPixelRef* pr = bm.pixelRef();
+    if (NULL == pr) {
+        // show null or the explicit pixel address (rare)
+        str->appendf(" pixels:%p", bm.getPixels());
+    } else {
+        const char* uri = pr->getURI();
+        if (uri) {
+            str->appendf(" uri:\"%s\"", uri);
+        } else {
+            str->appendf(" pixelref:%p", pr);
+        }
+    }
+}
+
+static void toString(const void* text, size_t byteLen, SkPaint::TextEncoding enc,
+                     SkString* str) {
+    // FIXME: this code appears to be untested - and probably unused - and probably wrong
+    switch (enc) {
+        case SkPaint::kUTF8_TextEncoding:
+            str->appendf("\"%.*s\"%s", SkMax32(byteLen, 32), (const char*) text,
+                        byteLen > 32 ? "..." : "");
+            break;
+        case SkPaint::kUTF16_TextEncoding:
+            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->append("<glyphs>");
+            break;
+
+        default:
+            SkASSERT(false);
+            break;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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;
+}
+
+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) {
+    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);
+}
+
+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::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* 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 < SkIntToScalar(bitmap.width()) ||
+                src->fBottom < SkIntToScalar(bitmap.height()))) {
+        SkString ss;
+        toString(*src, &ss);
+        rs.prependf("%s ", ss.c_str());
+    }
+
+    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);
+    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) {
+        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++) {
+#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);
+
+    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/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
new file mode 100644
index 0000000..2ef2702
--- /dev/null
+++ b/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/src/utils/SkJSON.cpp b/src/utils/SkJSON.cpp
new file mode 100644
index 0000000..06989f3
--- /dev/null
+++ b/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/src/utils/SkLayer.cpp b/src/utils/SkLayer.cpp
new file mode 100644
index 0000000..54f1840
--- /dev/null
+++ b/src/utils/SkLayer.cpp
@@ -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 "SkLayer.h"
+#include "SkCanvas.h"
+
+//#define DEBUG_DRAW_LAYER_BOUNDS
+//#define DEBUG_TRACK_NEW_DELETE
+
+#ifdef DEBUG_TRACK_NEW_DELETE
+    static int gLayerAllocCount;
+#endif
+
+SK_DEFINE_INST_COUNT(SkLayer)
+
+///////////////////////////////////////////////////////////////////////////////
+
+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) {
+    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/src/utils/SkMatrix44.cpp b/src/utils/SkMatrix44.cpp
new file mode 100644
index 0000000..f00e399
--- /dev/null
+++ b/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/src/utils/SkMeshUtils.cpp b/src/utils/SkMeshUtils.cpp
new file mode 100644
index 0000000..7970ba1
--- /dev/null
+++ b/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/src/utils/SkNWayCanvas.cpp b/src/utils/SkNWayCanvas.cpp
new file mode 100644
index 0000000..3b78163
--- /dev/null
+++ b/src/utils/SkNWayCanvas.cpp
@@ -0,0 +1,293 @@
+
+/*
+ * Copyright 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"
+
+static SkBitmap make_noconfig_bm(int width, int height) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kNo_Config, width, height);
+    return bm;
+}
+
+SkNWayCanvas::SkNWayCanvas(int width, int height)
+        : INHERITED(make_noconfig_bm(width, height)) {}
+
+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::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
+                                  const SkRect& dst, const SkPaint* paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawBitmapRectToRect(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/src/utils/SkNinePatch.cpp b/src/utils/SkNinePatch.cpp
new file mode 100644
index 0000000..9dc8bd2
--- /dev/null
+++ b/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/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
new file mode 100644
index 0000000..1fec35a
--- /dev/null
+++ b/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) || defined(SK_BUILD_FOR_IOS)
+
+#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/src/utils/SkParse.cpp b/src/utils/SkParse.cpp
new file mode 100644
index 0000000..9609ddc
--- /dev/null
+++ b/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/src/utils/SkParseColor.cpp b/src/utils/SkParseColor.cpp
new file mode 100644
index 0000000..db47aae
--- /dev/null
+++ b/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/src/utils/SkParsePath.cpp b/src/utils/SkParsePath.cpp
new file mode 100644
index 0000000..18ea5e0
--- /dev/null
+++ b/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, false)) {
+            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/src/utils/SkProxyCanvas.cpp b/src/utils/SkProxyCanvas.cpp
new file mode 100644
index 0000000..e245c73
--- /dev/null
+++ b/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::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
+                                   const SkRect& dst, const SkPaint* paint) {
+    fProxy->drawBitmapRectToRect(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/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
new file mode 100644
index 0000000..ceff9ca
--- /dev/null
+++ b/src/utils/SkUnitMappers.cpp
@@ -0,0 +1,61 @@
+
+/*
+ * Copyright 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"
+#include "SkFlattenableBuffers.h"
+
+SK_DEFINE_INST_COUNT(SkUnitMapper)
+
+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.readInt();
+    fScale = rb.read32();
+}
+
+void SkDiscreteMapper::flatten(SkFlattenableWriteBuffer& wb) const {
+    this->INHERITED::flatten(wb);
+
+    wb.writeInt(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) {}
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..df20ba9
--- /dev/null
+++ b/src/utils/mac/SkCreateCGImageRef.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 "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 | 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 | 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 | kCGImageAlphaPremultipliedLast;
+#endif
+            break;
+#if 0
+        case SkBitmap::kRGB_565_Config:
+            // doesn't see quite right. Are they thinking 1555?
+            *bitsPerComponent = 5;
+            *info = kCGBitmapByteOrder16Little;
+            break;
+#endif
+        case SkBitmap::kARGB_4444_Config:
+            *bitsPerComponent = 4;
+            *info = kCGBitmapByteOrder16Little | 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..5b3fe6b
--- /dev/null
+++ b/src/utils/mac/SkStream_mac.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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
new file mode 100644
index 0000000..ffc410a
--- /dev/null
+++ b/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/SkEvent.cpp b/src/views/SkEvent.cpp
new file mode 100644
index 0000000..1513170
--- /dev/null
+++ b/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/src/views/SkEventSink.cpp b/src/views/SkEventSink.cpp
new file mode 100644
index 0000000..b6a3a6e
--- /dev/null
+++ b/src/views/SkEventSink.cpp
@@ -0,0 +1,305 @@
+
+/*
+ * Copyright 2006 The Android 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"
+
+SK_DEFINE_INST_COUNT(SkEventSink)
+
+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/SkOSMenu.cpp b/src/views/SkOSMenu.cpp
new file mode 100644
index 0000000..3de0a9e
--- /dev/null
+++ b/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/src/views/SkParsePaint.cpp b/src/views/SkParsePaint.cpp
new file mode 100644
index 0000000..7a305f2
--- /dev/null
+++ b/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/SkProgressView.cpp b/src/views/SkProgressView.cpp
new file mode 100644
index 0000000..edb78bd
--- /dev/null
+++ b/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/SkStackViewLayout.cpp b/src/views/SkStackViewLayout.cpp
new file mode 100644
index 0000000..e4fbb80
--- /dev/null
+++ b/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/SkTagList.cpp b/src/views/SkTagList.cpp
new file mode 100644
index 0000000..e1b1662
--- /dev/null
+++ b/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/src/views/SkTagList.h b/src/views/SkTagList.h
new file mode 100644
index 0000000..47294e3
--- /dev/null
+++ b/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/src/views/SkTextBox.cpp b/src/views/SkTextBox.cpp
new file mode 100644
index 0000000..78828b6
--- /dev/null
+++ b/src/views/SkTextBox.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2006 The Android 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 "SkUtils.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,
+                        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;
+    const char* word_start = text;
+    int prevWS = true;
+    if (trailing) {
+        *trailing = 0;
+    }
+
+    while (text < stop) {
+        const char* prevText = text;
+        SkUnichar uni = SkUTF8_NextUnichar(&text);
+        int currWS = is_ws(uni);
+
+        if (!currWS && prevWS) {
+            word_start = prevText;
+        }
+        prevWS = currWS;
+
+        if (text > start + lengthBreak) {
+            if (currWS) {
+                // eat the rest of the whitespace
+                while (text < stop && is_ws(SkUTF8_ToUnichar(text))) {
+                    text += SkUTF8_CountUTF8Bytes(text);
+                }
+                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;
+}
+
+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 (;;)
+    {
+        size_t trailing;
+        len = linebreak(text, textStop, paint, marginWidth, &trailing);
+        if (y + metrics.fDescent + metrics.fLeading > 0)
+            canvas->drawText(text, len - trailing, 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/src/views/SkTouchGesture.cpp b/src/views/SkTouchGesture.cpp
new file mode 100644
index 0000000..afda8c1
--- /dev/null
+++ b/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 SkScalar MAX_FLING_SPEED = SkIntToScalar(1500);
+
+static SkScalar pin_max_fling(SkScalar 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(SkFloatToScalar(sx), SkFloatToScalar(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(SkFloatToScalar(tx), SkFloatToScalar(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 SkScalar center(float pos0, float pos1) {
+    return SkFloatToScalar((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 = 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) {
+        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/src/views/SkView.cpp b/src/views/SkView.cpp
new file mode 100644
index 0000000..94eb490
--- /dev/null
+++ b/src/views/SkView.cpp
@@ -0,0 +1,845 @@
+
+/*
+ * Copyright 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"
+
+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;
+    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)) {
+                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;
+            if (!child->globalToLocal(x, y, &p)) {
+                continue;
+            }
+
+            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 (NULL == target) {
+        return;
+    }
+
+    click->fIOrig.set(x, y);
+    click->fICurr = click->fIPrev = click->fIOrig;
+
+    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);
+}
+
+void SkView::DoClickMoved(Click* click, int x, int y)
+{
+    SkASSERT(click);
+
+    SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+    if (NULL == target) {
+        return;
+    }
+
+    click->fIPrev = click->fICurr;
+    click->fICurr.set(x, y);
+
+    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);
+}
+
+void SkView::DoClickUp(Click* click, int x, int y)
+{
+    SkASSERT(click);
+
+    SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+    if (NULL == target) {
+        return;
+    }
+
+    click->fIPrev = click->fICurr;
+    click->fICurr.set(x, y);
+
+    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);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+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;
+        }
+    }
+}
+bool SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
+{
+    SkASSERT(this);
+
+    if (NULL != local) {
+        SkMatrix m;
+        this->localToGlobal(&m);
+        if (!m.invert(&m)) {
+            return false;
+        }
+        SkPoint p;
+        m.mapXY(x, y, &p);
+        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.
+*/
+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/src/views/SkViewInflate.cpp b/src/views/SkViewInflate.cpp
new file mode 100644
index 0000000..4d7de04
--- /dev/null
+++ b/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/src/views/SkViewPriv.cpp b/src/views/SkViewPriv.cpp
new file mode 100644
index 0000000..ba40c1e
--- /dev/null
+++ b/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/src/views/SkViewPriv.h b/src/views/SkViewPriv.h
new file mode 100644
index 0000000..8a9c0b7
--- /dev/null
+++ b/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/SkWidgets.cpp b/src/views/SkWidgets.cpp
new file mode 100644
index 0000000..1693a31
--- /dev/null
+++ b/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/src/views/SkWindow.cpp b/src/views/SkWindow.cpp
new file mode 100644
index 0000000..b02631d
--- /dev/null
+++ b/src/views/SkWindow.cpp
@@ -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.
+ */
+#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();
+}
+
+SkCanvas* SkWindow::createCanvas() {
+    return new SkCanvas(this->getBitmap());
+}
+
+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)
+{
+    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
+
+        SkAutoTUnref<SkCanvas> canvas(this->createCanvas());
+
+        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/src/views/animated/SkBorderView.cpp b/src/views/animated/SkBorderView.cpp
new file mode 100644
index 0000000..3eff605
--- /dev/null
+++ b/src/views/animated/SkBorderView.cpp
@@ -0,0 +1,96 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkBorderView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+#include "SkStackViewLayout.h"
+
+SkBorderView::SkBorderView() : fLeft(SkIntToScalar(0)),
+                               fRight(SkIntToScalar(0)),
+                               fTop(SkIntToScalar(0)),
+                               fBottom(SkIntToScalar(0))
+{
+    fAnim.setHostEventSink(this);
+    init_skin_anim(kBorder_SkinEnum, &fAnim);
+}
+
+SkBorderView::~SkBorderView()
+{
+
+}
+
+void SkBorderView::setSkin(const char skin[])
+{
+    init_skin_anim(skin, &fAnim);
+}
+
+/* virtual */ void SkBorderView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+    this->INHERITED::onInflate(dom, node);
+}
+
+/*virtual*/ void SkBorderView::onSizeChange()
+{
+    this->INHERITED::onSizeChange();
+    SkEvent evt("user");
+    evt.setString("id", "setDim");
+    evt.setScalar("dimX", this->width());
+    evt.setScalar("dimY", this->height());
+    fAnim.doUserEvent(evt);
+}
+
+/*virtual*/ void SkBorderView::onDraw(SkCanvas* canvas)
+{
+    SkPaint                        paint;
+    SkAnimator::DifferenceType    diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+    if (diff == SkAnimator::kDifferent)
+        this->inval(NULL);
+    else if (diff == SkAnimator::kPartiallyDifferent)
+    {
+        SkRect    bounds;
+        fAnim.getInvalBounds(&bounds);
+        this->inval(&bounds);
+    }
+}
+
+/*virtual*/ bool SkBorderView::onEvent(const SkEvent& evt)
+{
+    if (evt.isType(SK_EventType_Inval))
+    {
+        this->inval(NULL);
+        return true;
+    }
+    if (evt.isType("recommendDim"))
+    {
+        evt.findScalar("leftMargin", &fLeft);
+        evt.findScalar("rightMargin", &fRight);
+        evt.findScalar("topMargin", &fTop);
+        evt.findScalar("bottomMargin", &fBottom);
+
+        //setup_views.cpp uses SkView::Layout instead of SkStackViewLayout
+        //but that gives me an error
+        SkStackViewLayout* layout;
+        fMargin.set(fLeft, fTop, fRight, fBottom);
+        if (this->getLayout())
+        {
+            layout = (SkStackViewLayout*)this->getLayout();
+            layout->setMargin(fMargin);
+        }
+        else
+        {
+            layout = new SkStackViewLayout;
+            layout->setMargin(fMargin);
+            this->setLayout(layout)->unref();
+        }
+        this->invokeLayout();
+    }
+    return this->INHERITED::onEvent(evt);
+}
diff --git a/src/views/animated/SkImageView.cpp b/src/views/animated/SkImageView.cpp
new file mode 100644
index 0000000..a75aa73
--- /dev/null
+++ b/src/views/animated/SkImageView.cpp
@@ -0,0 +1,303 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkImageView.h"
+#include "SkAnimator.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImageDecoder.h"
+#include "SkMatrix.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+SkImageView::SkImageView()
+{
+    fMatrix        = NULL;
+    fScaleType    = kMatrix_ScaleType;
+
+    fData.fAnim    = NULL;        // handles initializing the other union values
+    fDataIsAnim    = true;
+
+    fUriIsValid    = false;    // an empty string is not valid
+}
+
+SkImageView::~SkImageView()
+{
+    if (fMatrix)
+        sk_free(fMatrix);
+
+    this->freeData();
+}
+
+void SkImageView::getUri(SkString* uri) const
+{
+    if (uri)
+        *uri = fUri;
+}
+
+void SkImageView::setUri(const char uri[])
+{
+    if (!fUri.equals(uri))
+    {
+        fUri.set(uri);
+        this->onUriChange();
+    }
+}
+
+void SkImageView::setUri(const SkString& uri)
+{
+    if (fUri != uri)
+    {
+        fUri = uri;
+        this->onUriChange();
+    }
+}
+
+void SkImageView::setScaleType(ScaleType st)
+{
+    SkASSERT((unsigned)st <= kFitEnd_ScaleType);
+
+    if ((ScaleType)fScaleType != st)
+    {
+        fScaleType = SkToU8(st);
+        if (fUriIsValid)
+            this->inval(NULL);
+    }
+}
+
+bool SkImageView::getImageMatrix(SkMatrix* matrix) const
+{
+    if (fMatrix)
+    {
+        SkASSERT(!fMatrix->isIdentity());
+        if (matrix)
+            *matrix = *fMatrix;
+        return true;
+    }
+    else
+    {
+        if (matrix)
+            matrix->reset();
+        return false;
+    }
+}
+
+void SkImageView::setImageMatrix(const SkMatrix* matrix)
+{
+    bool changed = false;
+
+    if (matrix && !matrix->isIdentity())
+    {
+        if (fMatrix == NULL)
+            fMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix));
+        *fMatrix = *matrix;
+        changed = true;
+    }
+    else    // set us to identity
+    {
+        if (fMatrix)
+        {
+            SkASSERT(!fMatrix->isIdentity());
+            sk_free(fMatrix);
+            fMatrix = NULL;
+            changed = true;
+        }
+    }
+
+    // only redraw if we changed our matrix and we're not in scaleToFit mode
+    if (changed && this->getScaleType() == kMatrix_ScaleType && fUriIsValid)
+        this->inval(NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImageView::onEvent(const SkEvent& evt)
+{
+    if (evt.isType(SK_EventType_Inval))
+    {
+        if (fUriIsValid)
+            this->inval(NULL);
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
+}
+
+static inline SkMatrix::ScaleToFit scaleTypeToScaleToFit(SkImageView::ScaleType st)
+{
+    SkASSERT(st != SkImageView::kMatrix_ScaleType);
+    SkASSERT((unsigned)st <= SkImageView::kFitEnd_ScaleType);
+
+    SkASSERT(SkImageView::kFitXY_ScaleType - 1 == SkMatrix::kFill_ScaleToFit);
+    SkASSERT(SkImageView::kFitStart_ScaleType - 1 == SkMatrix::kStart_ScaleToFit);
+    SkASSERT(SkImageView::kFitCenter_ScaleType - 1 == SkMatrix::kCenter_ScaleToFit);
+    SkASSERT(SkImageView::kFitEnd_ScaleType - 1 == SkMatrix::kEnd_ScaleToFit);
+
+    return (SkMatrix::ScaleToFit)(st - 1);
+}
+
+void SkImageView::onDraw(SkCanvas* canvas)
+{
+    SkRect    src;
+    if (!this->getDataBounds(&src))
+    {
+        SkDEBUGCODE(canvas->drawColor(SK_ColorRED);)
+        return;        // nothing to draw
+    }
+
+    SkAutoCanvasRestore    restore(canvas, true);
+    SkMatrix            matrix;
+
+    if (this->getScaleType() == kMatrix_ScaleType)
+        (void)this->getImageMatrix(&matrix);
+    else
+    {
+        SkRect    dst;
+        dst.set(0, 0, this->width(), this->height());
+        matrix.setRectToRect(src, dst, scaleTypeToScaleToFit(this->getScaleType()));
+    }
+    canvas->concat(matrix);
+
+    SkPaint    paint;
+
+    paint.setAntiAlias(true);
+
+    if (fDataIsAnim)
+    {
+        SkMSec    now = SkTime::GetMSecs();
+
+        SkAnimator::DifferenceType diff = fData.fAnim->draw(canvas, &paint, now);
+
+SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff));
+
+        if (diff == SkAnimator::kDifferent)
+            this->inval(NULL);
+        else if (diff == SkAnimator::kPartiallyDifferent)
+        {
+            SkRect    bounds;
+            fData.fAnim->getInvalBounds(&bounds);
+            matrix.mapRect(&bounds);    // get the bounds into view coordinates
+            this->inval(&bounds);
+        }
+    }
+    else
+        canvas->drawBitmap(*fData.fBitmap, 0, 0, &paint);
+}
+
+void SkImageView::onInflate(const SkDOM& dom, const SkDOMNode* node)
+{
+    this->INHERITED::onInflate(dom, node);
+
+    const char* src = dom.findAttr(node, "src");
+    if (src)
+        this->setUri(src);
+
+    int    index = dom.findList(node, "scaleType", "matrix,fitXY,fitStart,fitCenter,fitEnd");
+    if (index >= 0)
+        this->setScaleType((ScaleType)index);
+
+    // need inflate syntax/reader for matrix
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SkImageView::onUriChange()
+{
+    if (this->freeData())
+        this->inval(NULL);
+    fUriIsValid = true;        // give ensureUriIsLoaded() a shot at the new uri
+}
+
+bool SkImageView::freeData()
+{
+    if (fData.fAnim)    // test is valid for all union values
+    {
+        if (fDataIsAnim)
+            delete fData.fAnim;
+        else
+            delete fData.fBitmap;
+
+        fData.fAnim = NULL;    // valid for all union values
+        return true;
+    }
+    return false;
+}
+
+bool SkImageView::getDataBounds(SkRect* bounds)
+{
+    SkASSERT(bounds);
+
+    if (this->ensureUriIsLoaded())
+    {
+        SkScalar width, height;
+
+        if (fDataIsAnim)
+        {
+            if (SkScalarIsNaN(width = fData.fAnim->getScalar("dimensions", "x")) ||
+                SkScalarIsNaN(height = fData.fAnim->getScalar("dimensions", "y")))
+            {
+                // cons up fake bounds
+                width = this->width();
+                height = this->height();
+            }
+        }
+        else
+        {
+            width = SkIntToScalar(fData.fBitmap->width());
+            height = SkIntToScalar(fData.fBitmap->height());
+        }
+        bounds->set(0, 0, width, height);
+        return true;
+    }
+    return false;
+}
+
+bool SkImageView::ensureUriIsLoaded()
+{
+    if (fData.fAnim)    // test is valid for all union values
+    {
+        SkASSERT(fUriIsValid);
+        return true;
+    }
+    if (!fUriIsValid)
+        return false;
+
+    // try to load the url
+    if (fUri.endsWith(".xml"))    // assume it is screenplay
+    {
+        SkAnimator* anim = new SkAnimator;
+
+        if (!anim->decodeURI(fUri.c_str()))
+        {
+            delete anim;
+            fUriIsValid = false;
+            return false;
+        }
+        anim->setHostEventSink(this);
+
+        fData.fAnim = anim;
+        fDataIsAnim = true;
+    }
+    else    // assume it is an image format
+    {
+    #if 0
+        SkBitmap* bitmap = new SkBitmap;
+
+        if (!SkImageDecoder::DecodeURL(fUri.c_str(), bitmap))
+        {
+            delete bitmap;
+            fUriIsValid = false;
+            return false;
+        }
+        fData.fBitmap = bitmap;
+        fDataIsAnim = false;
+    #else
+        return false;
+    #endif
+    }
+    return true;
+}
+
diff --git a/src/views/animated/SkProgressBarView.cpp b/src/views/animated/SkProgressBarView.cpp
new file mode 100644
index 0000000..e7754eb
--- /dev/null
+++ b/src/views/animated/SkProgressBarView.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 "SkProgressBarView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkTime.h"
+#include "SkSystemEventTypes.h"
+
+SkProgressBarView::SkProgressBarView()
+{
+    init_skin_anim(kProgress_SkinEnum, &fAnim);
+    fAnim.setHostEventSink(this);
+    fProgress = 0;
+    fMax = 100;
+
+}
+
+void SkProgressBarView::changeProgress(int diff)
+{
+    int newProg = fProgress + diff;
+    if (newProg > 0 && newProg < fMax)
+        this->setProgress(newProg);
+    //otherwise i'll just leave it as it is
+    //this implies that if a new max and progress are set, max must be set first
+}
+
+/*virtual*/ void SkProgressBarView::onDraw(SkCanvas* canvas)
+{
+    SkPaint                        paint;
+    SkAnimator::DifferenceType    diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+    if (diff == SkAnimator::kDifferent)
+        this->inval(NULL);
+    else if (diff == SkAnimator::kPartiallyDifferent)
+    {
+        SkRect    bounds;
+        fAnim.getInvalBounds(&bounds);
+        this->inval(&bounds);
+    }
+}
+
+/*virtual*/ bool SkProgressBarView::onEvent(const SkEvent& evt)
+{
+    if (evt.isType(SK_EventType_Inval))
+    {
+        this->inval(NULL);
+        return true;
+    }
+    if (evt.isType("recommendDim"))
+    {
+        SkScalar    height;
+
+        if (evt.findScalar("y", &height))
+            this->setHeight(height);
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
+}
+
+/*virtual*/ void SkProgressBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+    this->INHERITED::onInflate(dom, node);
+    int32_t temp;
+    if (dom.findS32(node, "max", &temp))
+        this->setMax(temp);
+    if (dom.findS32(node, "progress", &temp))
+        this->setProgress(temp);
+}
+
+/*virtual*/ void SkProgressBarView::onSizeChange()
+{
+    this->INHERITED::onSizeChange();
+    SkEvent evt("user");
+    evt.setString("id", "setDim");
+    evt.setScalar("dimX", this->width());
+    evt.setScalar("dimY", this->height());
+    fAnim.doUserEvent(evt);
+}
+
+void SkProgressBarView::reset()
+{
+    fProgress = 0;
+    SkEvent e("user");
+    e.setString("id", "reset");
+    fAnim.doUserEvent(e);
+}
+
+void SkProgressBarView::setMax(int max)
+{
+    fMax = max;
+    SkEvent e("user");
+    e.setString("id", "setMax");
+    e.setS32("newMax", max);
+    fAnim.doUserEvent(e);
+}
+
+void SkProgressBarView::setProgress(int progress)
+{
+    fProgress = progress;
+    SkEvent e("user");
+    e.setString("id", "setProgress");
+    e.setS32("newProgress", progress);
+    fAnim.doUserEvent(e);
+}
diff --git a/src/views/animated/SkScrollBarView.cpp b/src/views/animated/SkScrollBarView.cpp
new file mode 100644
index 0000000..0d93775
--- /dev/null
+++ b/src/views/animated/SkScrollBarView.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 "SkScrollBarView.h"
+#include "SkAnimator.h"
+#include "SkWidgetViews.h"
+#include "SkSystemEventTypes.h"
+#include "SkTime.h"
+
+//see SkProgressBarView.cpp
+//#include "SkWidgetViews.cpp"
+
+SkScrollBarView::SkScrollBarView()
+{
+    fAnim.setHostEventSink(this);
+    init_skin_anim(kScroll_SkinEnum, &fAnim);
+
+    fTotalLength = 0;
+    fStartPoint = 0;
+    fShownLength = 0;
+
+    this->adjust();
+}
+
+void SkScrollBarView::setStart(unsigned start)
+{
+    if ((int)start < 0)
+        start = 0;
+
+    if (fStartPoint != start)
+    {
+        fStartPoint = start;
+        this->adjust();
+    }
+}
+
+void SkScrollBarView::setShown(unsigned shown)
+{
+    if ((int)shown < 0)
+        shown = 0;
+
+    if (fShownLength != shown)
+    {
+        fShownLength = shown;
+        this->adjust();
+    }
+}
+
+void SkScrollBarView::setTotal(unsigned total)
+{
+    if ((int)total < 0)
+        total = 0;
+
+    if (fTotalLength != total)
+    {
+        fTotalLength = total;
+        this->adjust();
+    }
+}
+
+/* virtual */ void SkScrollBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+    this->INHERITED::onInflate(dom, node);
+
+    int32_t value;
+    if (dom.findS32(node, "total", &value))
+        this->setTotal(value);
+    if (dom.findS32(node, "shown", &value))
+        this->setShown(value);
+}
+
+/*virtual*/ void SkScrollBarView::onSizeChange()
+{
+    this->INHERITED::onSizeChange();
+    SkEvent evt("user");
+    evt.setString("id", "setDim");
+    evt.setScalar("dimX", this->width());
+    evt.setScalar("dimY", this->height());
+    fAnim.doUserEvent(evt);
+}
+
+/*virtual*/ void SkScrollBarView::onDraw(SkCanvas* canvas)
+{
+    SkPaint                        paint;
+    SkAnimator::DifferenceType    diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+    if (diff == SkAnimator::kDifferent)
+        this->inval(NULL);
+    else if (diff == SkAnimator::kPartiallyDifferent)
+    {
+        SkRect    bounds;
+        fAnim.getInvalBounds(&bounds);
+        this->inval(&bounds);
+    }
+}
+
+/*virtual*/ bool SkScrollBarView::onEvent(const SkEvent& evt)
+{
+    if (evt.isType(SK_EventType_Inval))
+    {
+        this->inval(NULL);
+        return true;
+    }
+    if (evt.isType("recommendDim"))
+    {
+        SkScalar    width;
+
+        if (evt.findScalar("x", &width))
+            this->setWidth(width);
+        return true;
+    }
+
+    return this->INHERITED::onEvent(evt);
+}
+
+void SkScrollBarView::adjust()
+{
+    int total = fTotalLength;
+    int start = fStartPoint;
+    int shown = fShownLength;
+    int hideBar = 0;
+
+    if (total <= 0 || shown <= 0 || shown >= total)    // no bar to show
+    {
+        total = 1;        // avoid divide-by-zero. should be done by skin/script
+        hideBar = 1;    // signal we don't want a thumb
+    }
+    else
+    {
+        if (start + shown > total)
+            start = total - shown;
+    }
+
+    SkEvent e("user");
+    e.setString("id", "adjustScrollBar");
+    e.setScalar("_totalLength", SkIntToScalar(total));
+    e.setScalar("_startPoint", SkIntToScalar(start));
+    e.setScalar("_shownLength", SkIntToScalar(shown));
+//    e.setS32("hideBar", hideBar);
+    fAnim.doUserEvent(e);
+}
+
diff --git a/src/views/animated/SkStaticTextView.cpp b/src/views/animated/SkStaticTextView.cpp
new file mode 100644
index 0000000..199b2fe
--- /dev/null
+++ b/src/views/animated/SkStaticTextView.cpp
@@ -0,0 +1,192 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkWidgetViews.h"
+#include "SkTextBox.h"
+
+#ifdef SK_DEBUG
+static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+{
+    const char* value = dom.findAttr(node, attr);
+    if (value)
+        SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+}
+#else
+    #define assert_no_attr(dom, node, attr)
+#endif
+
+SkStaticTextView::SkStaticTextView()
+{
+    fMargin.set(0, 0);
+    fMode = kFixedSize_Mode;
+    fSpacingAlign = SkTextBox::kStart_SpacingAlign;
+
+//    init_skin_paint(kStaticText_SkinEnum, &fPaint);
+}
+
+SkStaticTextView::~SkStaticTextView()
+{
+}
+
+void SkStaticTextView::computeSize()
+{
+    if (fMode == kAutoWidth_Mode)
+    {
+        SkScalar width = fPaint.measureText(fText.c_str(), fText.size());
+        this->setWidth(width + fMargin.fX * 2);
+    }
+    else if (fMode == kAutoHeight_Mode)
+    {
+        SkScalar width = this->width() - fMargin.fX * 2;
+        int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
+
+        this->setHeight(lines * fPaint.getFontSpacing() + fMargin.fY * 2);
+    }
+}
+
+void SkStaticTextView::setMode(Mode mode)
+{
+    SkASSERT((unsigned)mode < kModeCount);
+
+    if (fMode != mode)
+    {
+        fMode = SkToU8(mode);
+        this->computeSize();
+    }
+}
+
+void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
+{
+    fSpacingAlign = SkToU8(align);
+    this->inval(NULL);
+}
+
+void SkStaticTextView::getMargin(SkPoint* margin) const
+{
+    if (margin)
+        *margin = fMargin;
+}
+
+void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
+{
+    if (fMargin.fX != dx || fMargin.fY != dy)
+    {
+        fMargin.set(dx, dy);
+        this->computeSize();
+        this->inval(NULL);
+    }
+}
+
+size_t SkStaticTextView::getText(SkString* text) const
+{
+    if (text)
+        *text = fText;
+    return fText.size();
+}
+
+size_t SkStaticTextView::getText(char text[]) const
+{
+    if (text)
+        memcpy(text, fText.c_str(), fText.size());
+    return fText.size();
+}
+
+void SkStaticTextView::setText(const SkString& text)
+{
+    this->setText(text.c_str(), text.size());
+}
+
+void SkStaticTextView::setText(const char text[])
+{
+    if (text == NULL)
+        text = "";
+    this->setText(text, strlen(text));
+}
+
+void SkStaticTextView::setText(const char text[], size_t len)
+{
+    if (!fText.equals(text, len))
+    {
+        fText.set(text, len);
+        this->computeSize();
+        this->inval(NULL);
+    }
+}
+
+void SkStaticTextView::getPaint(SkPaint* paint) const
+{
+    if (paint)
+        *paint = fPaint;
+}
+
+void SkStaticTextView::setPaint(const SkPaint& paint)
+{
+    if (fPaint != paint)
+    {
+        fPaint = paint;
+        this->computeSize();
+        this->inval(NULL);
+    }
+}
+
+void SkStaticTextView::onDraw(SkCanvas* canvas)
+{
+    this->INHERITED::onDraw(canvas);
+
+    if (fText.isEmpty())
+        return;
+
+    SkTextBox    box;
+
+    box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
+    box.setSpacingAlign(this->getSpacingAlign());
+    box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
+    box.draw(canvas, fText.c_str(), fText.size(), fPaint);
+}
+
+void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+if (false) { // avoid bit rot, suppress warning
+    this->INHERITED::onInflate(dom, node);
+
+    int    index;
+    if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0) {
+        this->setMode((Mode)index);
+    } else {
+        assert_no_attr(dom, node, "mode");
+    }
+
+    if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0) {
+        this->setSpacingAlign((SkTextBox::SpacingAlign)index);
+    } else {
+        assert_no_attr(dom, node, "spacing-align");
+    }
+
+    SkScalar s[2];
+    if (dom.findScalars(node, "margin", s, 2)) {
+        this->setMargin(s[0], s[1]);
+    } else {
+        assert_no_attr(dom, node, "margin");
+    }
+
+    const char* text = dom.findAttr(node, "text");
+    if (text) {
+        this->setText(text);
+    }
+
+    if ((node = dom.getFirstChild(node, "paint")) != NULL &&
+        (node = dom.getFirstChild(node, "screenplay")) != NULL)
+    {
+// FIXME: Including inflate_paint causes Windows build to fail -- it complains
+//  that SKListView::SkListView is undefined.
+#if 0
+        inflate_paint(dom, node, &fPaint);
+#endif
+    }
+}
+}
+
diff --git a/src/views/animated/SkWidgetViews.cpp b/src/views/animated/SkWidgetViews.cpp
new file mode 100644
index 0000000..9c1cae7
--- /dev/null
+++ b/src/views/animated/SkWidgetViews.cpp
@@ -0,0 +1,404 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkStream.h"
+#include "SkSystemEventTypes.h"
+
+/*
+I have moved this to SkWidgetViews.h
+enum SkinEnum {
+    kButton_SkinEnum,
+    kProgress_SkinEnum,
+    kScroll_SkinEnum,
+    kStaticText_SkinEnum,
+
+    kSkinEnumCount
+};
+*/
+
+SK_DEFINE_INST_COUNT(SkListSource)
+
+const char* get_skin_enum_path(SkinEnum se)
+{
+    SkASSERT((unsigned)se < kSkinEnumCount);
+
+    static const char* gSkinPaths[] = {
+            "common/default/default/skins/border3.xml",
+            "common/default/default/skins/button.xml",
+            "common/default/default/skins/progressBar.xml",
+            "common/default/default/skins/scrollBar.xml",
+            "common/default/default/skins/statictextpaint.xml"
+    };
+
+    return gSkinPaths[se];
+}
+
+void init_skin_anim(const char path[], SkAnimator* anim)
+{
+    SkASSERT(path && anim);
+
+    SkFILEStream    stream(path);
+
+    if (!stream.isValid())
+    {
+        SkDEBUGF(("init_skin_anim: loading skin failed <%s>\n", path));
+        sk_throw();
+    }
+
+    if (!anim->decodeStream(&stream))
+    {
+        SkDEBUGF(("init_skin_anim: decoding skin failed <%s>\n", path));
+        sk_throw();
+    }
+}
+
+void init_skin_anim(SkinEnum se, SkAnimator* anim)
+{
+    init_skin_anim(get_skin_enum_path(se), anim);
+}
+
+void init_skin_paint(SkinEnum se, SkPaint* paint)
+{
+    SkASSERT(paint);
+
+    SkAnimator    anim;
+    SkCanvas    canvas;
+
+    init_skin_anim(se, &anim);
+    anim.draw(&canvas, paint, 0);
+}
+
+void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint)
+{
+    SkASSERT(paint);
+
+    SkAnimator    anim;
+    SkCanvas    canvas;
+
+    if (!anim.decodeDOM(dom, node))
+    {
+        SkDEBUGF(("inflate_paint: decoding dom failed\n"));
+        SkDEBUGCODE(dom.dump(node);)
+        sk_throw();
+    }
+    anim.draw(&canvas, paint, 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+SkWidgetView::SkWidgetView() : SkView(SkView::kFocusable_Mask | SkView::kEnabled_Mask)
+{
+}
+
+const char* SkWidgetView::getLabel() const
+{
+    return fLabel.c_str();
+}
+
+void SkWidgetView::getLabel(SkString* label) const
+{
+    if (label)
+        *label = fLabel;
+}
+
+void SkWidgetView::setLabel(const char label[])
+{
+    this->setLabel(label, label ? strlen(label) : 0);
+}
+
+void SkWidgetView::setLabel(const char label[], size_t len)
+{
+    if ((label == NULL && fLabel.size() != 0) || !fLabel.equals(label, len))
+    {
+        SkString    tmp(label, len);
+
+        this->onLabelChange(fLabel.c_str(), tmp.c_str());
+        fLabel.swap(tmp);
+    }
+}
+
+void SkWidgetView::setLabel(const SkString& label)
+{
+    if (fLabel != label)
+    {
+        this->onLabelChange(fLabel.c_str(), label.c_str());
+        fLabel = label;
+    }
+}
+
+bool SkWidgetView::postWidgetEvent()
+{
+    if (!fEvent.isType(""))
+    {
+        SkEvent    evt(fEvent);    // make a copy since onPrepareWidgetEvent may edit the event
+
+        if (this->onPrepareWidgetEvent(&evt))
+        {
+            SkDEBUGCODE(evt.dump("SkWidgetView::postWidgetEvent");)
+
+            this->postToListeners(evt);    // wonder if this should return true if there are > 0 listeners...
+            return true;
+        }
+    }
+    return false;
+}
+
+/*virtual*/ void SkWidgetView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+    this->INHERITED::onInflate(dom, node);
+
+    const char* label = dom.findAttr(node, "label");
+    if (label)
+        this->setLabel(label);
+
+    if ((node = dom.getFirstChild(node, "event")) != NULL)
+        fEvent.inflate(dom, node);
+}
+
+/*virtual*/ void SkWidgetView::onLabelChange(const char oldLabel[], const char newLabel[])
+{
+    this->inval(NULL);
+}
+
+static const char gWidgetEventSinkIDSlotName[] = "sk-widget-sinkid-slot";
+
+/*virtual*/ bool SkWidgetView::onPrepareWidgetEvent(SkEvent* evt)
+{
+    evt->setS32(gWidgetEventSinkIDSlotName, this->getSinkID());
+    return true;
+}
+
+SkEventSinkID SkWidgetView::GetWidgetEventSinkID(const SkEvent& evt)
+{
+    int32_t    sinkID;
+
+    return evt.findS32(gWidgetEventSinkIDSlotName, &sinkID) ? (SkEventSinkID)sinkID : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*virtual*/ bool SkButtonView::onEvent(const SkEvent& evt)
+{
+    if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
+    {
+        this->postWidgetEvent();
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkCheckButtonView::SkCheckButtonView() : fCheckState(kOff_CheckState)
+{
+}
+
+void SkCheckButtonView::setCheckState(CheckState state)
+{
+    SkASSERT((unsigned)state <= kUnknown_CheckState);
+
+    if (fCheckState != state)
+    {
+        this->onCheckStateChange(this->getCheckState(), state);
+        fCheckState = SkToU8(state);
+    }
+}
+
+/*virtual*/ void SkCheckButtonView::onCheckStateChange(CheckState oldState, CheckState newState)
+{
+    this->inval(NULL);
+}
+
+/*virtual*/ void SkCheckButtonView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
+{
+    this->INHERITED::onInflate(dom, node);
+
+    int index = dom.findList(node, "check-state", "off,on,unknown");
+    if (index >= 0)
+        this->setCheckState((CheckState)index);
+}
+
+static const char gCheckStateSlotName[] = "sk-checkbutton-check-slot";
+
+/*virtual*/ bool SkCheckButtonView::onPrepareWidgetEvent(SkEvent* evt)
+{
+    // could check if we're "disabled", and return false...
+
+    evt->setS32(gCheckStateSlotName, this->getCheckState());
+    return true;
+}
+
+bool SkCheckButtonView::GetWidgetEventCheckState(const SkEvent& evt, CheckState* state)
+{
+    int32_t    state32;
+
+    if (evt.findS32(gCheckStateSlotName, &state32))
+    {
+        if (state)
+            *state = (CheckState)state32;
+        return true;
+    }
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkTime.h"
+#include <stdio.h>
+
+class SkAnimButtonView : public SkButtonView {
+public:
+    SkAnimButtonView()
+    {
+        fAnim.setHostEventSink(this);
+        init_skin_anim(kButton_SkinEnum, &fAnim);
+    }
+
+protected:
+    virtual void onLabelChange(const char oldLabel[], const char newLabel[])
+    {
+        this->INHERITED::onLabelChange(oldLabel, newLabel);
+
+        SkEvent evt("user");
+        evt.setString("id", "setLabel");
+        evt.setString("LABEL", newLabel);
+        fAnim.doUserEvent(evt);
+    }
+
+    virtual void onFocusChange(bool gainFocus)
+    {
+        this->INHERITED::onFocusChange(gainFocus);
+
+        SkEvent evt("user");
+        evt.setString("id", "setFocus");
+        evt.setS32("FOCUS", gainFocus);
+        fAnim.doUserEvent(evt);
+    }
+
+    virtual void onSizeChange()
+    {
+        this->INHERITED::onSizeChange();
+
+        SkEvent evt("user");
+        evt.setString("id", "setDim");
+        evt.setScalar("dimX", this->width());
+        evt.setScalar("dimY", this->height());
+        fAnim.doUserEvent(evt);
+    }
+
+    virtual void onDraw(SkCanvas* canvas)
+    {
+        SkPaint                        paint;
+        SkAnimator::DifferenceType    diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
+
+        if (diff == SkAnimator::kDifferent)
+            this->inval(NULL);
+        else if (diff == SkAnimator::kPartiallyDifferent)
+        {
+            SkRect    bounds;
+            fAnim.getInvalBounds(&bounds);
+            this->inval(&bounds);
+        }
+    }
+
+    virtual bool onEvent(const SkEvent& evt)
+    {
+        if (evt.isType(SK_EventType_Inval))
+        {
+            this->inval(NULL);
+            return true;
+        }
+        if (evt.isType("recommendDim"))
+        {
+            SkScalar    height;
+
+            if (evt.findScalar("y", &height))
+                this->setHeight(height);
+            return true;
+        }
+        return this->INHERITED::onEvent(evt);
+    }
+
+    virtual bool onPrepareWidgetEvent(SkEvent* evt)
+    {
+        if (this->INHERITED::onPrepareWidgetEvent(evt))
+        {
+            SkEvent    e("user");
+            e.setString("id", "handlePress");
+            (void)fAnim.doUserEvent(e);
+            return true;
+        }
+        return false;
+    }
+
+private:
+    SkAnimator    fAnim;
+
+    typedef SkButtonView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////
+
+SkView* SkWidgetFactory(const char name[])
+{
+    if (name == NULL)
+        return NULL;
+
+    // must be in the same order as the SkSkinWidgetEnum is declared
+    static const char* gNames[] = {
+        "sk-border",
+        "sk-button",
+        "sk-image",
+        "sk-list",
+        "sk-progress",
+        "sk-scroll",
+        "sk-text"
+
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++)
+        if (!strcmp(gNames[i], name))
+            return SkWidgetFactory((SkWidgetEnum)i);
+
+    return NULL;
+}
+
+#include "SkImageView.h"
+#include "SkProgressBarView.h"
+#include "SkScrollBarView.h"
+#include "SkBorderView.h"
+
+SkView* SkWidgetFactory(SkWidgetEnum sw)
+{
+    switch (sw) {
+    case kBorder_WidgetEnum:
+        return new SkBorderView;
+    case kButton_WidgetEnum:
+        return new SkAnimButtonView;
+    case kImage_WidgetEnum:
+        return new SkImageView;
+    case kList_WidgetEnum:
+        return new SkListView;
+    case kProgress_WidgetEnum:
+        return new SkProgressBarView;
+    case kScroll_WidgetEnum:
+        return new SkScrollBarView;
+    case kText_WidgetEnum:
+        return new SkStaticTextView;
+    default:
+        SkDEBUGFAIL("unknown enum passed to SkWidgetFactory");
+        break;
+    }
+    return NULL;
+}
diff --git a/src/views/ios/SkOSWindow_iOS.mm b/src/views/ios/SkOSWindow_iOS.mm
new file mode 100755
index 0000000..6fac765
--- /dev/null
+++ b/src/views/ios/SkOSWindow_iOS.mm
@@ -0,0 +1,66 @@
+#import <UIKit/UIKit.h>
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#import "SkEventNotifier.h"
+#include "SkOSMenu.h"
+#include "SkTime.h"
+#include "SkTypes.h"
+#import "SkUIView.h"
+#include "SkWindow.h"
+
+#define kINVAL_UIVIEW_EventType "inval-uiview"
+
+SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd) {
+    fInvalEventIsPending = false;
+    fNotifier = [[SkEventNotifier alloc] init];
+}
+SkOSWindow::~SkOSWindow() {
+    [(SkEventNotifier*)fNotifier release];
+}
+
+void SkOSWindow::onHandleInval(const SkIRect& r) {
+    if (!fInvalEventIsPending) {
+        fInvalEventIsPending = true;
+        (new SkEvent(kINVAL_UIVIEW_EventType, this->getSinkID()))->post();
+    }
+}
+
+bool SkOSWindow::onEvent(const SkEvent& evt) {
+    if (evt.isType(kINVAL_UIVIEW_EventType)) {
+        fInvalEventIsPending = false;
+        const SkIRect& r = this->getDirtyBounds();
+        [(SkUIView*)fHWND postInvalWithRect:&r];
+        return true;
+    }
+    if ([(SkUIView*)fHWND onHandleEvent:evt]) {
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
+}
+
+bool SkOSWindow::onDispatchClick(int x, int y, Click::State state, void* owner) {
+    return this->INHERITED::onDispatchClick(x, y, state, owner);
+}
+
+void SkOSWindow::onSetTitle(const char title[]) {
+    [(SkUIView*)fHWND setSkTitle:title];
+}
+
+void SkOSWindow::onAddMenu(const SkOSMenu* menu) {
+    [(SkUIView*)fHWND onAddMenu:menu];
+}
+
+void SkOSWindow::onUpdateMenu(SkOSMenu* menu) {
+    [(SkUIView*)fHWND onUpdateMenu:menu];
+}
+
+bool SkOSWindow::attach(SkBackEndTypes /* attachType */,
+                        int /* msaaSampleCount */) {
+    bool success = true;
+    return success;
+}
+
+void SkOSWindow::detach() {}
+
+void SkOSWindow::present() {
+}
diff --git a/src/views/mac/SampleApp-Info.plist b/src/views/mac/SampleApp-Info.plist
new file mode 100644
index 0000000..64c6d48
--- /dev/null
+++ b/src/views/mac/SampleApp-Info.plist
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIconFile</key>
+	<string></string>
+	<key>CFBundleIdentifier</key>
+	<string>com.googlecode.skia.${PRODUCT_NAME:rfc1034identifier}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>${PRODUCT_NAME}</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSMinimumSystemVersion</key>
+	<string>${MACOSX_DEPLOYMENT_TARGET}</string>
+	<key>NSMainNibFile</key>
+	<string>SampleApp</string>
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+</dict>
+</plist>
diff --git a/src/views/mac/SampleApp.xib b/src/views/mac/SampleApp.xib
new file mode 100644
index 0000000..9549e4e
--- /dev/null
+++ b/src/views/mac/SampleApp.xib
@@ -0,0 +1,3962 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
+	<data>
+		<int key="IBDocument.SystemTarget">1060</int>
+		<string key="IBDocument.SystemVersion">10K540</string>
+		<string key="IBDocument.InterfaceBuilderVersion">851</string>
+		<string key="IBDocument.AppKitVersion">1038.36</string>
+		<string key="IBDocument.HIToolboxVersion">461.00</string>
+		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+			<string key="NS.object.0">851</string>
+		</object>
+		<array class="NSMutableArray" key="IBDocument.EditedObjectIDs">
+			<integer value="24"/>
+			<integer value="372"/>
+			<integer value="634"/>
+		</array>
+		<array key="IBDocument.PluginDependencies">
+			<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+		</array>
+		<object class="NSMutableDictionary" key="IBDocument.Metadata">
+			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+			<integer value="1" key="NS.object.0"/>
+		</object>
+		<array class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+			<object class="NSCustomObject" id="1021">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSCustomObject" id="1014">
+				<string key="NSClassName">FirstResponder</string>
+			</object>
+			<object class="NSCustomObject" id="1050">
+				<string key="NSClassName">NSApplication</string>
+			</object>
+			<object class="NSCustomObject" id="976324537">
+				<string key="NSClassName">SampleAppDelegate</string>
+			</object>
+			<object class="NSCustomObject" id="76290771">
+				<string key="NSClassName">NSWindowController</string>
+			</object>
+			<object class="NSWindowTemplate" id="972006081">
+				<int key="NSWindowStyleMask">15</int>
+				<int key="NSWindowBacking">2</int>
+				<string key="NSWindowRect">{{335, 288}, {640, 480}}</string>
+				<int key="NSWTFlags">1417150464</int>
+				<string key="NSWindowTitle">Sample App</string>
+				<string key="NSWindowClass">NSWindow</string>
+				<nil key="NSViewClass"/>
+				<string key="NSWindowContentMaxSize">{3.40282e+38, 3.40282e+38}</string>
+				<object class="NSView" key="NSWindowView" id="439893737">
+					<reference key="NSNextResponder"/>
+					<int key="NSvFlags">256</int>
+					<string key="NSFrameSize">{640, 480}</string>
+					<reference key="NSSuperview"/>
+				</object>
+				<string key="NSScreenRect">{{0, 0}, {1920, 1178}}</string>
+				<string key="NSMaxSize">{3.40282e+38, 3.40282e+38}</string>
+			</object>
+			<object class="NSCustomView" id="758604943">
+				<reference key="NSNextResponder"/>
+				<int key="NSvFlags">4352</int>
+				<array class="NSMutableArray" key="NSSubviews">
+					<object class="NSScrollView" id="1038370525">
+						<reference key="NSNextResponder" ref="758604943"/>
+						<int key="NSvFlags">274</int>
+						<array class="NSMutableArray" key="NSSubviews">
+							<object class="NSClipView" id="250930136">
+								<reference key="NSNextResponder" ref="1038370525"/>
+								<int key="NSvFlags">2304</int>
+								<array class="NSMutableArray" key="NSSubviews">
+									<object class="NSTableView" id="429436769">
+										<reference key="NSNextResponder" ref="250930136"/>
+										<int key="NSvFlags">256</int>
+										<string key="NSFrameSize">{339, 319}</string>
+										<reference key="NSSuperview" ref="250930136"/>
+										<bool key="NSEnabled">YES</bool>
+										<object class="_NSCornerView" key="NSCornerView">
+											<nil key="NSNextResponder"/>
+											<int key="NSvFlags">-2147483392</int>
+											<string key="NSFrame">{{224, 0}, {16, 17}}</string>
+										</object>
+										<array class="NSMutableArray" key="NSTableColumns">
+											<object class="NSTableColumn" id="691918008">
+												<string key="NSIdentifier">Labels</string>
+												<double key="NSWidth">100</double>
+												<double key="NSMinWidth">40</double>
+												<double key="NSMaxWidth">1000</double>
+												<object class="NSTableHeaderCell" key="NSHeaderCell">
+													<int key="NSCellFlags">75628096</int>
+													<int key="NSCellFlags2">2048</int>
+													<string key="NSContents"/>
+													<object class="NSFont" key="NSSupport" id="26">
+														<string key="NSName">LucidaGrande</string>
+														<double key="NSSize">11</double>
+														<int key="NSfFlags">3100</int>
+													</object>
+													<object class="NSColor" key="NSBackgroundColor" id="805714581">
+														<int key="NSColorSpace">3</int>
+														<bytes key="NSWhite">MC4zMzMzMzI5ODU2AA</bytes>
+													</object>
+													<object class="NSColor" key="NSTextColor" id="372600372">
+														<int key="NSColorSpace">6</int>
+														<string key="NSCatalogName">System</string>
+														<string key="NSColorName">headerTextColor</string>
+														<object class="NSColor" key="NSColor" id="1032326875">
+															<int key="NSColorSpace">3</int>
+															<bytes key="NSWhite">MAA</bytes>
+														</object>
+													</object>
+												</object>
+												<object class="NSTextFieldCell" key="NSDataCell" id="241301801">
+													<int key="NSCellFlags">68288064</int>
+													<int key="NSCellFlags2">67241216</int>
+													<string key="NSContents">Text Cell</string>
+													<reference key="NSSupport" ref="26"/>
+													<reference key="NSControlView" ref="429436769"/>
+													<object class="NSColor" key="NSBackgroundColor" id="598476436">
+														<int key="NSColorSpace">6</int>
+														<string key="NSCatalogName">System</string>
+														<string key="NSColorName">controlBackgroundColor</string>
+														<object class="NSColor" key="NSColor" id="319525538">
+															<int key="NSColorSpace">3</int>
+															<bytes key="NSWhite">MC42NjY2NjY2ODY1AA</bytes>
+														</object>
+													</object>
+													<object class="NSColor" key="NSTextColor" id="1055469070">
+														<int key="NSColorSpace">6</int>
+														<string key="NSCatalogName">System</string>
+														<string key="NSColorName">controlTextColor</string>
+														<reference key="NSColor" ref="1032326875"/>
+													</object>
+												</object>
+												<int key="NSResizingMask">3</int>
+												<bool key="NSIsResizeable">YES</bool>
+												<bool key="NSIsEditable">YES</bool>
+												<reference key="NSTableView" ref="429436769"/>
+											</object>
+											<object class="NSTableColumn" id="394988372">
+												<string key="NSIdentifier">Controls</string>
+												<double key="NSWidth">233</double>
+												<double key="NSMinWidth">40</double>
+												<double key="NSMaxWidth">1000</double>
+												<object class="NSTableHeaderCell" key="NSHeaderCell">
+													<int key="NSCellFlags">75628096</int>
+													<int key="NSCellFlags2">2048</int>
+													<string key="NSContents"/>
+													<reference key="NSSupport" ref="26"/>
+													<reference key="NSBackgroundColor" ref="805714581"/>
+													<reference key="NSTextColor" ref="372600372"/>
+												</object>
+												<object class="NSTextFieldCell" key="NSDataCell" id="88358594">
+													<int key="NSCellFlags">67239488</int>
+													<int key="NSCellFlags2">272630784</int>
+													<string key="NSContents">Text</string>
+													<object class="NSFont" key="NSSupport">
+														<string key="NSName">LucidaGrande</string>
+														<double key="NSSize">13</double>
+														<int key="NSfFlags">1044</int>
+													</object>
+													<reference key="NSControlView" ref="429436769"/>
+													<object class="NSColor" key="NSBackgroundColor">
+														<int key="NSColorSpace">6</int>
+														<string key="NSCatalogName">System</string>
+														<string key="NSColorName">controlColor</string>
+														<reference key="NSColor" ref="319525538"/>
+													</object>
+													<reference key="NSTextColor" ref="1055469070"/>
+												</object>
+												<int key="NSResizingMask">3</int>
+												<bool key="NSIsResizeable">YES</bool>
+												<bool key="NSIsEditable">YES</bool>
+												<reference key="NSTableView" ref="429436769"/>
+											</object>
+										</array>
+										<double key="NSIntercellSpacingWidth">3</double>
+										<double key="NSIntercellSpacingHeight">2</double>
+										<object class="NSColor" key="NSBackgroundColor">
+											<int key="NSColorSpace">6</int>
+											<string key="NSCatalogName">System</string>
+											<string key="NSColorName">_sourceListBackgroundColor</string>
+											<object class="NSColor" key="NSColor">
+												<int key="NSColorSpace">1</int>
+												<bytes key="NSRGB">MC44MzkyMTU2OTU5IDAuODY2NjY2Njc0NiAwLjg5ODAzOTIyMTgAA</bytes>
+											</object>
+										</object>
+										<object class="NSColor" key="NSGridColor">
+											<int key="NSColorSpace">6</int>
+											<string key="NSCatalogName">System</string>
+											<string key="NSColorName">gridColor</string>
+											<object class="NSColor" key="NSColor">
+												<int key="NSColorSpace">3</int>
+												<bytes key="NSWhite">MC41AA</bytes>
+											</object>
+										</object>
+										<double key="NSRowHeight">35</double>
+										<int key="NSTvFlags">1128267776</int>
+										<reference key="NSDelegate"/>
+										<reference key="NSDataSource"/>
+										<int key="NSGridStyleMask">2</int>
+										<int key="NSColumnAutoresizingStyle">4</int>
+										<int key="NSDraggingSourceMaskForLocal">15</int>
+										<int key="NSDraggingSourceMaskForNonLocal">0</int>
+										<bool key="NSAllowsTypeSelect">NO</bool>
+										<int key="NSTableViewSelectionHighlightStyle">1</int>
+										<int key="NSTableViewDraggingDestinationStyle">1</int>
+									</object>
+								</array>
+								<string key="NSFrame">{{1, 1}, {339, 319}}</string>
+								<reference key="NSSuperview" ref="1038370525"/>
+								<reference key="NSNextKeyView" ref="429436769"/>
+								<reference key="NSDocView" ref="429436769"/>
+								<reference key="NSBGColor" ref="598476436"/>
+								<int key="NScvFlags">4</int>
+							</object>
+							<object class="NSScroller" id="617550661">
+								<reference key="NSNextResponder" ref="1038370525"/>
+								<int key="NSvFlags">-2147483392</int>
+								<string key="NSFrame">{{317, 1}, {15, 574}}</string>
+								<reference key="NSSuperview" ref="1038370525"/>
+								<reference key="NSTarget" ref="1038370525"/>
+								<string key="NSAction">_doScroller:</string>
+								<double key="NSPercent">0.99687498807907104</double>
+							</object>
+							<object class="NSScroller" id="977018641">
+								<reference key="NSNextResponder" ref="1038370525"/>
+								<int key="NSvFlags">-2147483392</int>
+								<string key="NSFrame">{{1, 263}, {157, 15}}</string>
+								<reference key="NSSuperview" ref="1038370525"/>
+								<int key="NSsFlags">1</int>
+								<reference key="NSTarget" ref="1038370525"/>
+								<string key="NSAction">_doScroller:</string>
+								<double key="NSPercent">0.99705880880355835</double>
+							</object>
+						</array>
+						<string key="NSFrameSize">{341, 321}</string>
+						<reference key="NSSuperview" ref="758604943"/>
+						<reference key="NSNextKeyView" ref="250930136"/>
+						<int key="NSsFlags">562</int>
+						<reference key="NSVScroller" ref="617550661"/>
+						<reference key="NSHScroller" ref="977018641"/>
+						<reference key="NSContentView" ref="250930136"/>
+						<bytes key="NSScrollAmts">QSAAAEEgAABCFAAAQhQAAA</bytes>
+					</object>
+				</array>
+				<string key="NSFrameSize">{341, 321}</string>
+				<reference key="NSSuperview"/>
+				<bool key="NSViewCanDrawConcurrently">YES</bool>
+				<string key="NSClassName">NSView</string>
+			</object>
+			<object class="NSDrawer" id="764451088">
+				<nil key="NSNextResponder"/>
+				<string key="NSContentSize">{300, 100}</string>
+				<string key="NSMinContentSize">{0, 0}</string>
+				<string key="NSMaxContentSize">{10000, 10000}</string>
+				<int key="NSPreferredEdge">2</int>
+				<double key="NSLeadingOffset">0.0</double>
+				<double key="NSTrailingOffset">15</double>
+				<nil key="NSParentWindow"/>
+				<nil key="NSDelegate"/>
+			</object>
+			<object class="NSMenu" id="649796088">
+				<string key="NSTitle">AMainMenu</string>
+				<array class="NSMutableArray" key="NSMenuItems">
+					<object class="NSMenuItem" id="694149608">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">SimpleCocoaApp</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<object class="NSCustomResource" key="NSOnImage" id="35465992">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuCheckmark</string>
+						</object>
+						<object class="NSCustomResource" key="NSMixedImage" id="502551668">
+							<string key="NSClassName">NSImage</string>
+							<string key="NSResourceName">NSMenuMixedState</string>
+						</object>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="110575045">
+							<string key="NSTitle">SimpleCocoaApp</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="238522557">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">About SimpleCocoaApp</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="24092627">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="622903446">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Show Options</string>
+									<string key="NSKeyEquiv"></string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="304266470">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="609285721">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Preferences…</string>
+									<string key="NSKeyEquiv">,</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="481834944">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1046388886">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Services</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="752062318">
+										<string key="NSTitle">Services</string>
+										<array class="NSMutableArray" key="NSMenuItems"/>
+										<string key="NSName">_NSServicesMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="646227648">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="755159360">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Hide SimpleCocoaApp</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="342932134">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Hide Others</string>
+									<string key="NSKeyEquiv">h</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="908899353">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Show All</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1056857174">
+									<reference key="NSMenu" ref="110575045"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="632727374">
+									<reference key="NSMenu" ref="110575045"/>
+									<string key="NSTitle">Quit SimpleCocoaApp</string>
+									<string key="NSKeyEquiv">q</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</array>
+							<string key="NSName">_NSAppleMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="379814623">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">File</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="720053764">
+							<string key="NSTitle">File</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="705341025">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">New</string>
+									<string key="NSKeyEquiv">n</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="722745758">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Open…</string>
+									<string key="NSKeyEquiv">o</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1025936716">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Open Recent</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="1065607017">
+										<string key="NSTitle">Open Recent</string>
+										<array class="NSMutableArray" key="NSMenuItems">
+											<object class="NSMenuItem" id="759406840">
+												<reference key="NSMenu" ref="1065607017"/>
+												<string key="NSTitle">Clear Menu</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</array>
+										<string key="NSName">_NSRecentDocumentsMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="425164168">
+									<reference key="NSMenu" ref="720053764"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="776162233">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Close</string>
+									<string key="NSKeyEquiv">w</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1023925487">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Save</string>
+									<string key="NSKeyEquiv">s</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="117038363">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Save As…</string>
+									<string key="NSKeyEquiv">S</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="579971712">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Revert to Saved</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1010469920">
+									<reference key="NSMenu" ref="720053764"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="294629803">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Page Setup...</string>
+									<string key="NSKeyEquiv">P</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSToolTip"/>
+								</object>
+								<object class="NSMenuItem" id="49223823">
+									<reference key="NSMenu" ref="720053764"/>
+									<string key="NSTitle">Print…</string>
+									<string key="NSKeyEquiv">p</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</array>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="952259628">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Edit</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="789758025">
+							<string key="NSTitle">Edit</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="1058277027">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Undo</string>
+									<string key="NSKeyEquiv">z</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="790794224">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Redo</string>
+									<string key="NSKeyEquiv">Z</string>
+									<int key="NSKeyEquivModMask">1179648</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="1040322652">
+									<reference key="NSMenu" ref="789758025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="296257095">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Cut</string>
+									<string key="NSKeyEquiv">x</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="860595796">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Copy</string>
+									<string key="NSKeyEquiv">c</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="29853731">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Paste</string>
+									<string key="NSKeyEquiv">v</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="82994268">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Paste and Match Style</string>
+									<string key="NSKeyEquiv">V</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="437104165">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Delete</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="583158037">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Select All</string>
+									<string key="NSKeyEquiv">a</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="212016141">
+									<reference key="NSMenu" ref="789758025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="892235320">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Find</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="963351320">
+										<string key="NSTitle">Find</string>
+										<array class="NSMutableArray" key="NSMenuItems">
+											<object class="NSMenuItem" id="447796847">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find…</string>
+												<string key="NSKeyEquiv">f</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="326711663">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find Next</string>
+												<string key="NSKeyEquiv">g</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="270902937">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Find Previous</string>
+												<string key="NSKeyEquiv">G</string>
+												<int key="NSKeyEquivModMask">1179648</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="159080638">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Use Selection for Find</string>
+												<string key="NSKeyEquiv">e</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">7</int>
+											</object>
+											<object class="NSMenuItem" id="88285865">
+												<reference key="NSMenu" ref="963351320"/>
+												<string key="NSTitle">Jump to Selection</string>
+												<string key="NSKeyEquiv">j</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</array>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="972420730">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Spelling and Grammar</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="769623530">
+										<string key="NSTitle">Spelling and Grammar</string>
+										<array class="NSMutableArray" key="NSMenuItems">
+											<object class="NSMenuItem" id="679648819">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Show Spelling and Grammar</string>
+												<string key="NSKeyEquiv">:</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="96193923">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Document Now</string>
+												<string key="NSKeyEquiv">;</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="859480356">
+												<reference key="NSMenu" ref="769623530"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="948374510">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Spelling While Typing</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="967646866">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Check Grammar With Spelling</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="795346622">
+												<reference key="NSMenu" ref="769623530"/>
+												<string key="NSTitle">Correct Spelling Automatically</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</array>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="507821607">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Substitutions</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="698887838">
+										<string key="NSTitle">Substitutions</string>
+										<array class="NSMutableArray" key="NSMenuItems">
+											<object class="NSMenuItem" id="65139061">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Show Substitutions</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="19036812">
+												<reference key="NSMenu" ref="698887838"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="605118523">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Copy/Paste</string>
+												<string key="NSKeyEquiv">f</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="197661976">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Quotes</string>
+												<string key="NSKeyEquiv">g</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="672708820">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Dashes</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="708854459">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Smart Links</string>
+												<string key="NSKeyEquiv">G</string>
+												<int key="NSKeyEquivModMask">1179648</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="537092702">
+												<reference key="NSMenu" ref="698887838"/>
+												<string key="NSTitle">Text Replacement</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</array>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="288088188">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Transformations</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="579392910">
+										<string key="NSTitle">Transformations</string>
+										<array class="NSMutableArray" key="NSMenuItems">
+											<object class="NSMenuItem" id="1060694897">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Make Upper Case</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="879586729">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Make Lower Case</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="56570060">
+												<reference key="NSMenu" ref="579392910"/>
+												<string key="NSTitle">Capitalize</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</array>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="676164635">
+									<reference key="NSMenu" ref="789758025"/>
+									<string key="NSTitle">Speech</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="785027613">
+										<string key="NSTitle">Speech</string>
+										<array class="NSMutableArray" key="NSMenuItems">
+											<object class="NSMenuItem" id="731782645">
+												<reference key="NSMenu" ref="785027613"/>
+												<string key="NSTitle">Start Speaking</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="680220178">
+												<reference key="NSMenu" ref="785027613"/>
+												<string key="NSTitle">Stop Speaking</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</array>
+									</object>
+								</object>
+							</array>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="586577488">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">View</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="466310130">
+							<string key="NSTitle">View</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="87708234">
+									<reference key="NSMenu" ref="466310130"/>
+									<string key="NSTitle">Show Menu Key Equivalents</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<int key="NSState">1</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="102151532">
+									<reference key="NSMenu" ref="466310130"/>
+									<string key="NSTitle">Show Toolbar</string>
+									<string key="NSKeyEquiv">t</string>
+									<int key="NSKeyEquivModMask">1572864</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="237841660">
+									<reference key="NSMenu" ref="466310130"/>
+									<string key="NSTitle">Customize Toolbar…</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</array>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="302598603">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Format</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="941447902">
+							<string key="NSTitle">Format</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="792887677">
+									<reference key="NSMenu" ref="941447902"/>
+									<string key="NSTitle">Font</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="786677654">
+										<string key="NSTitle">Font</string>
+										<array class="NSMutableArray" key="NSMenuItems">
+											<object class="NSMenuItem" id="159677712">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Show Fonts</string>
+												<string key="NSKeyEquiv">t</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="305399458">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Bold</string>
+												<string key="NSKeyEquiv">b</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">2</int>
+											</object>
+											<object class="NSMenuItem" id="814362025">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Italic</string>
+												<string key="NSKeyEquiv">i</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">1</int>
+											</object>
+											<object class="NSMenuItem" id="330926929">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Underline</string>
+												<string key="NSKeyEquiv">u</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="533507878">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="158063935">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Bigger</string>
+												<string key="NSKeyEquiv">+</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">3</int>
+											</object>
+											<object class="NSMenuItem" id="885547335">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Smaller</string>
+												<string key="NSKeyEquiv">-</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<int key="NSTag">4</int>
+											</object>
+											<object class="NSMenuItem" id="901062459">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="767671776">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Kern</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="175441468">
+													<string key="NSTitle">Kern</string>
+													<array class="NSMutableArray" key="NSMenuItems">
+														<object class="NSMenuItem" id="252969304">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="766922938">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Use None</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="677519740">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Tighten</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="238351151">
+															<reference key="NSMenu" ref="175441468"/>
+															<string key="NSTitle">Loosen</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</array>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="691570813">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Ligature</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="1058217995">
+													<string key="NSTitle">Ligature</string>
+													<array class="NSMutableArray" key="NSMenuItems">
+														<object class="NSMenuItem" id="706297211">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="568384683">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use None</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="663508465">
+															<reference key="NSMenu" ref="1058217995"/>
+															<string key="NSTitle">Use All</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</array>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="769124883">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Baseline</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="18263474">
+													<string key="NSTitle">Baseline</string>
+													<array class="NSMutableArray" key="NSMenuItems">
+														<object class="NSMenuItem" id="257962622">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Use Default</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="644725453">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Superscript</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="1037576581">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Subscript</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="941806246">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Raise</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="1045724900">
+															<reference key="NSMenu" ref="18263474"/>
+															<string key="NSTitle">Lower</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</array>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="739652853">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="1012600125">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Show Colors</string>
+												<string key="NSKeyEquiv">C</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="214559597">
+												<reference key="NSMenu" ref="786677654"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="596732606">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Copy Style</string>
+												<string key="NSKeyEquiv">c</string>
+												<int key="NSKeyEquivModMask">1572864</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="393423671">
+												<reference key="NSMenu" ref="786677654"/>
+												<string key="NSTitle">Paste Style</string>
+												<string key="NSKeyEquiv">v</string>
+												<int key="NSKeyEquivModMask">1572864</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</array>
+										<string key="NSName">_NSFontMenu</string>
+									</object>
+								</object>
+								<object class="NSMenuItem" id="215659978">
+									<reference key="NSMenu" ref="941447902"/>
+									<string key="NSTitle">Text</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+									<string key="NSAction">submenuAction:</string>
+									<object class="NSMenu" key="NSSubmenu" id="446991534">
+										<string key="NSTitle">Text</string>
+										<array class="NSMutableArray" key="NSMenuItems">
+											<object class="NSMenuItem" id="875092757">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Align Left</string>
+												<string key="NSKeyEquiv">{</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="630155264">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Center</string>
+												<string key="NSKeyEquiv">|</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="945678886">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Justify</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="512868991">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Align Right</string>
+												<string key="NSKeyEquiv">}</string>
+												<int key="NSKeyEquivModMask">1048576</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="163117631">
+												<reference key="NSMenu" ref="446991534"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="31516759">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Writing Direction</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+												<string key="NSAction">submenuAction:</string>
+												<object class="NSMenu" key="NSSubmenu" id="956096989">
+													<string key="NSTitle">Writing Direction</string>
+													<array class="NSMutableArray" key="NSMenuItems">
+														<object class="NSMenuItem" id="257099033">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<string key="NSTitle">Paragraph</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="551969625">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="249532473">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="607364498">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="508151438">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<bool key="NSIsSeparator">YES</bool>
+															<string key="NSTitle"/>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="981751889">
+															<reference key="NSMenu" ref="956096989"/>
+															<bool key="NSIsDisabled">YES</bool>
+															<string key="NSTitle">Selection</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="380031999">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="825984362">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+														<object class="NSMenuItem" id="560145579">
+															<reference key="NSMenu" ref="956096989"/>
+															<string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+															<string key="NSKeyEquiv"/>
+															<int key="NSMnemonicLoc">2147483647</int>
+															<reference key="NSOnImage" ref="35465992"/>
+															<reference key="NSMixedImage" ref="502551668"/>
+														</object>
+													</array>
+												</object>
+											</object>
+											<object class="NSMenuItem" id="908105787">
+												<reference key="NSMenu" ref="446991534"/>
+												<bool key="NSIsDisabled">YES</bool>
+												<bool key="NSIsSeparator">YES</bool>
+												<string key="NSTitle"/>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="644046920">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Show Ruler</string>
+												<string key="NSKeyEquiv"/>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="231811626">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Copy Ruler</string>
+												<string key="NSKeyEquiv">c</string>
+												<int key="NSKeyEquivModMask">1310720</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+											<object class="NSMenuItem" id="883618387">
+												<reference key="NSMenu" ref="446991534"/>
+												<string key="NSTitle">Paste Ruler</string>
+												<string key="NSKeyEquiv">v</string>
+												<int key="NSKeyEquivModMask">1310720</int>
+												<int key="NSMnemonicLoc">2147483647</int>
+												<reference key="NSOnImage" ref="35465992"/>
+												<reference key="NSMixedImage" ref="502551668"/>
+											</object>
+										</array>
+									</object>
+								</object>
+							</array>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="713487014">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Window</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSKeyEquivModMask">1048576</int>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="835318025">
+							<string key="NSTitle">Window</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="1011231497">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Minimize</string>
+									<string key="NSKeyEquiv">m</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="575023229">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Zoom</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="92029792">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">768 x 1024</string>
+									<string key="NSKeyEquiv">=</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="299356726">
+									<reference key="NSMenu" ref="835318025"/>
+									<bool key="NSIsDisabled">YES</bool>
+									<bool key="NSIsSeparator">YES</bool>
+									<string key="NSTitle"/>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+								<object class="NSMenuItem" id="625202149">
+									<reference key="NSMenu" ref="835318025"/>
+									<string key="NSTitle">Bring All to Front</string>
+									<string key="NSKeyEquiv"/>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</array>
+							<string key="NSName">_NSWindowsMenu</string>
+						</object>
+					</object>
+					<object class="NSMenuItem" id="448692316">
+						<reference key="NSMenu" ref="649796088"/>
+						<string key="NSTitle">Help</string>
+						<string key="NSKeyEquiv"/>
+						<int key="NSMnemonicLoc">2147483647</int>
+						<reference key="NSOnImage" ref="35465992"/>
+						<reference key="NSMixedImage" ref="502551668"/>
+						<string key="NSAction">submenuAction:</string>
+						<object class="NSMenu" key="NSSubmenu" id="992780483">
+							<string key="NSTitle">Help</string>
+							<array class="NSMutableArray" key="NSMenuItems">
+								<object class="NSMenuItem" id="105068016">
+									<reference key="NSMenu" ref="992780483"/>
+									<string key="NSTitle">SimpleCocoaApp Help</string>
+									<string key="NSKeyEquiv">?</string>
+									<int key="NSKeyEquivModMask">1048576</int>
+									<int key="NSMnemonicLoc">2147483647</int>
+									<reference key="NSOnImage" ref="35465992"/>
+									<reference key="NSMixedImage" ref="502551668"/>
+								</object>
+							</array>
+							<string key="NSName">_NSHelpMenu</string>
+						</object>
+					</object>
+				</array>
+				<string key="NSName">_NSMainMenu</string>
+			</object>
+			<object class="NSCustomObject" id="755631768">
+				<string key="NSClassName">NSFontManager</string>
+			</object>
+		</array>
+		<object class="IBObjectContainer" key="IBDocument.Objects">
+			<array class="NSMutableArray" key="connectionRecords">
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performMiniaturize:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1011231497"/>
+					</object>
+					<int key="connectionID">37</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">arrangeInFront:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="625202149"/>
+					</object>
+					<int key="connectionID">39</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">print:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="49223823"/>
+					</object>
+					<int key="connectionID">86</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">runPageLayout:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="294629803"/>
+					</object>
+					<int key="connectionID">87</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">clearRecentDocuments:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="759406840"/>
+					</object>
+					<int key="connectionID">127</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontStandardAboutPanel:</string>
+						<reference key="source" ref="1021"/>
+						<reference key="destination" ref="238522557"/>
+					</object>
+					<int key="connectionID">142</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performClose:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="776162233"/>
+					</object>
+					<int key="connectionID">193</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleContinuousSpellChecking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="948374510"/>
+					</object>
+					<int key="connectionID">222</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">undo:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1058277027"/>
+					</object>
+					<int key="connectionID">223</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copy:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="860595796"/>
+					</object>
+					<int key="connectionID">224</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">checkSpelling:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="96193923"/>
+					</object>
+					<int key="connectionID">225</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">paste:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="29853731"/>
+					</object>
+					<int key="connectionID">226</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">stopSpeaking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="680220178"/>
+					</object>
+					<int key="connectionID">227</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">cut:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="296257095"/>
+					</object>
+					<int key="connectionID">228</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">showGuessPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="679648819"/>
+					</object>
+					<int key="connectionID">230</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">redo:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="790794224"/>
+					</object>
+					<int key="connectionID">231</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">selectAll:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="583158037"/>
+					</object>
+					<int key="connectionID">232</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">startSpeaking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="731782645"/>
+					</object>
+					<int key="connectionID">233</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">delete:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="437104165"/>
+					</object>
+					<int key="connectionID">235</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performZoom:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="575023229"/>
+					</object>
+					<int key="connectionID">240</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="447796847"/>
+					</object>
+					<int key="connectionID">241</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">centerSelectionInVisibleArea:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="88285865"/>
+					</object>
+					<int key="connectionID">245</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleGrammarChecking:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="967646866"/>
+					</object>
+					<int key="connectionID">347</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleSmartInsertDelete:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="605118523"/>
+					</object>
+					<int key="connectionID">355</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticQuoteSubstitution:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="197661976"/>
+					</object>
+					<int key="connectionID">356</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticLinkDetection:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="708854459"/>
+					</object>
+					<int key="connectionID">357</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">saveDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1023925487"/>
+					</object>
+					<int key="connectionID">362</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">saveDocumentAs:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="117038363"/>
+					</object>
+					<int key="connectionID">363</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">revertDocumentToSaved:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="579971712"/>
+					</object>
+					<int key="connectionID">364</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">runToolbarCustomizationPalette:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="237841660"/>
+					</object>
+					<int key="connectionID">365</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleToolbarShown:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="102151532"/>
+					</object>
+					<int key="connectionID">366</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hide:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="755159360"/>
+					</object>
+					<int key="connectionID">367</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">hideOtherApplications:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="342932134"/>
+					</object>
+					<int key="connectionID">368</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">unhideAllApplications:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="908899353"/>
+					</object>
+					<int key="connectionID">370</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">newDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="705341025"/>
+					</object>
+					<int key="connectionID">373</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">openDocument:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="722745758"/>
+					</object>
+					<int key="connectionID">374</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">addFontTrait:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="305399458"/>
+					</object>
+					<int key="connectionID">421</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">addFontTrait:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="814362025"/>
+					</object>
+					<int key="connectionID">422</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">modifyFont:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="885547335"/>
+					</object>
+					<int key="connectionID">423</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontFontPanel:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="159677712"/>
+					</object>
+					<int key="connectionID">424</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">modifyFont:</string>
+						<reference key="source" ref="755631768"/>
+						<reference key="destination" ref="158063935"/>
+					</object>
+					<int key="connectionID">425</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">raiseBaseline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="941806246"/>
+					</object>
+					<int key="connectionID">426</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">lowerBaseline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1045724900"/>
+					</object>
+					<int key="connectionID">427</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copyFont:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="596732606"/>
+					</object>
+					<int key="connectionID">428</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">subscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1037576581"/>
+					</object>
+					<int key="connectionID">429</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">superscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="644725453"/>
+					</object>
+					<int key="connectionID">430</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">tightenKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="677519740"/>
+					</object>
+					<int key="connectionID">431</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">underline:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="330926929"/>
+					</object>
+					<int key="connectionID">432</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontColorPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1012600125"/>
+					</object>
+					<int key="connectionID">433</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useAllLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="663508465"/>
+					</object>
+					<int key="connectionID">434</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">loosenKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="238351151"/>
+					</object>
+					<int key="connectionID">435</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteFont:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="393423671"/>
+					</object>
+					<int key="connectionID">436</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">unscript:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="257962622"/>
+					</object>
+					<int key="connectionID">437</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useStandardKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="252969304"/>
+					</object>
+					<int key="connectionID">438</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">useStandardLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="706297211"/>
+					</object>
+					<int key="connectionID">439</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">turnOffLigatures:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="568384683"/>
+					</object>
+					<int key="connectionID">440</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">turnOffKerning:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="766922938"/>
+					</object>
+					<int key="connectionID">441</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">terminate:</string>
+						<reference key="source" ref="1050"/>
+						<reference key="destination" ref="632727374"/>
+					</object>
+					<int key="connectionID">449</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticSpellingCorrection:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="795346622"/>
+					</object>
+					<int key="connectionID">456</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">orderFrontSubstitutionsPanel:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="65139061"/>
+					</object>
+					<int key="connectionID">458</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticDashSubstitution:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="672708820"/>
+					</object>
+					<int key="connectionID">461</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleAutomaticTextReplacement:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="537092702"/>
+					</object>
+					<int key="connectionID">463</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">uppercaseWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="1060694897"/>
+					</object>
+					<int key="connectionID">464</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">capitalizeWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="56570060"/>
+					</object>
+					<int key="connectionID">467</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">lowercaseWord:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="879586729"/>
+					</object>
+					<int key="connectionID">468</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteAsPlainText:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="82994268"/>
+					</object>
+					<int key="connectionID">486</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="326711663"/>
+					</object>
+					<int key="connectionID">487</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="270902937"/>
+					</object>
+					<int key="connectionID">488</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">performFindPanelAction:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="159080638"/>
+					</object>
+					<int key="connectionID">489</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">showHelp:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="105068016"/>
+					</object>
+					<int key="connectionID">493</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignCenter:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="630155264"/>
+					</object>
+					<int key="connectionID">518</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">pasteRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="883618387"/>
+					</object>
+					<int key="connectionID">519</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="644046920"/>
+					</object>
+					<int key="connectionID">520</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="512868991"/>
+					</object>
+					<int key="connectionID">521</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">copyRuler:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="231811626"/>
+					</object>
+					<int key="connectionID">522</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignJustified:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="945678886"/>
+					</object>
+					<int key="connectionID">523</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">alignLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="875092757"/>
+					</object>
+					<int key="connectionID">524</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionNatural:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="551969625"/>
+					</object>
+					<int key="connectionID">525</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionLeftToRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="249532473"/>
+					</object>
+					<int key="connectionID">526</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeBaseWritingDirectionRightToLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="607364498"/>
+					</object>
+					<int key="connectionID">527</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionNatural:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="380031999"/>
+					</object>
+					<int key="connectionID">528</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionLeftToRight:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="825984362"/>
+					</object>
+					<int key="connectionID">529</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">makeTextWritingDirectionRightToLeft:</string>
+						<reference key="source" ref="1014"/>
+						<reference key="destination" ref="560145579"/>
+					</object>
+					<int key="connectionID">530</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">contentView</string>
+						<reference key="source" ref="764451088"/>
+						<reference key="destination" ref="758604943"/>
+					</object>
+					<int key="connectionID">542</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">parentWindow</string>
+						<reference key="source" ref="764451088"/>
+						<reference key="destination" ref="972006081"/>
+					</object>
+					<int key="connectionID">651</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">delegate</string>
+						<reference key="source" ref="1021"/>
+						<reference key="destination" ref="976324537"/>
+					</object>
+					<int key="connectionID">656</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">fOptionsDelegate</string>
+						<reference key="source" ref="439893737"/>
+						<reference key="destination" ref="429436769"/>
+					</object>
+					<int key="connectionID">667</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">fWindow</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="972006081"/>
+					</object>
+					<int key="connectionID">673</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">fOptions</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="429436769"/>
+					</object>
+					<int key="connectionID">674</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBOutletConnection" key="connection">
+						<string key="label">fView</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="439893737"/>
+					</object>
+					<int key="connectionID">682</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggle:</string>
+						<reference key="source" ref="764451088"/>
+						<reference key="destination" ref="622903446"/>
+					</object>
+					<int key="connectionID">707</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toggleKeyEquivalents:</string>
+						<reference key="source" ref="429436769"/>
+						<reference key="destination" ref="87708234"/>
+					</object>
+					<int key="connectionID">719</int>
+				</object>
+				<object class="IBConnectionRecord">
+					<object class="IBActionConnection" key="connection">
+						<string key="label">toiPadSize:</string>
+						<reference key="source" ref="976324537"/>
+						<reference key="destination" ref="92029792"/>
+					</object>
+					<int key="connectionID">721</int>
+				</object>
+			</array>
+			<object class="IBMutableOrderedSet" key="objectRecords">
+				<array key="orderedObjects">
+					<object class="IBObjectRecord">
+						<int key="objectID">0</int>
+						<array key="object" id="0"/>
+						<reference key="children" ref="1048"/>
+						<nil key="parent"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-2</int>
+						<reference key="object" ref="1021"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">File's Owner</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-1</int>
+						<reference key="object" ref="1014"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">First Responder</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">-3</int>
+						<reference key="object" ref="1050"/>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Application</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">29</int>
+						<reference key="object" ref="649796088"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="713487014"/>
+							<reference ref="694149608"/>
+							<reference ref="952259628"/>
+							<reference ref="379814623"/>
+							<reference ref="586577488"/>
+							<reference ref="448692316"/>
+							<reference ref="302598603"/>
+						</array>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">19</int>
+						<reference key="object" ref="713487014"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="835318025"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">56</int>
+						<reference key="object" ref="694149608"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="110575045"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">217</int>
+						<reference key="object" ref="952259628"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="789758025"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">83</int>
+						<reference key="object" ref="379814623"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="720053764"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">81</int>
+						<reference key="object" ref="720053764"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="1023925487"/>
+							<reference ref="117038363"/>
+							<reference ref="49223823"/>
+							<reference ref="722745758"/>
+							<reference ref="705341025"/>
+							<reference ref="1025936716"/>
+							<reference ref="294629803"/>
+							<reference ref="776162233"/>
+							<reference ref="425164168"/>
+							<reference ref="579971712"/>
+							<reference ref="1010469920"/>
+						</array>
+						<reference key="parent" ref="379814623"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">75</int>
+						<reference key="object" ref="1023925487"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">80</int>
+						<reference key="object" ref="117038363"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">78</int>
+						<reference key="object" ref="49223823"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">72</int>
+						<reference key="object" ref="722745758"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">82</int>
+						<reference key="object" ref="705341025"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">124</int>
+						<reference key="object" ref="1025936716"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="1065607017"/>
+						</array>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">77</int>
+						<reference key="object" ref="294629803"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">73</int>
+						<reference key="object" ref="776162233"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">79</int>
+						<reference key="object" ref="425164168"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">112</int>
+						<reference key="object" ref="579971712"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">74</int>
+						<reference key="object" ref="1010469920"/>
+						<reference key="parent" ref="720053764"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">125</int>
+						<reference key="object" ref="1065607017"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="759406840"/>
+						</array>
+						<reference key="parent" ref="1025936716"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">126</int>
+						<reference key="object" ref="759406840"/>
+						<reference key="parent" ref="1065607017"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">205</int>
+						<reference key="object" ref="789758025"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="437104165"/>
+							<reference ref="583158037"/>
+							<reference ref="1058277027"/>
+							<reference ref="212016141"/>
+							<reference ref="296257095"/>
+							<reference ref="29853731"/>
+							<reference ref="860595796"/>
+							<reference ref="1040322652"/>
+							<reference ref="790794224"/>
+							<reference ref="892235320"/>
+							<reference ref="972420730"/>
+							<reference ref="676164635"/>
+							<reference ref="507821607"/>
+							<reference ref="288088188"/>
+							<reference ref="82994268"/>
+						</array>
+						<reference key="parent" ref="952259628"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">202</int>
+						<reference key="object" ref="437104165"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">198</int>
+						<reference key="object" ref="583158037"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">207</int>
+						<reference key="object" ref="1058277027"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">214</int>
+						<reference key="object" ref="212016141"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">199</int>
+						<reference key="object" ref="296257095"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">203</int>
+						<reference key="object" ref="29853731"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">197</int>
+						<reference key="object" ref="860595796"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">206</int>
+						<reference key="object" ref="1040322652"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">215</int>
+						<reference key="object" ref="790794224"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">218</int>
+						<reference key="object" ref="892235320"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="963351320"/>
+						</array>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">216</int>
+						<reference key="object" ref="972420730"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="769623530"/>
+						</array>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">200</int>
+						<reference key="object" ref="769623530"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="948374510"/>
+							<reference ref="96193923"/>
+							<reference ref="679648819"/>
+							<reference ref="967646866"/>
+							<reference ref="859480356"/>
+							<reference ref="795346622"/>
+						</array>
+						<reference key="parent" ref="972420730"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">219</int>
+						<reference key="object" ref="948374510"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">201</int>
+						<reference key="object" ref="96193923"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">204</int>
+						<reference key="object" ref="679648819"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">220</int>
+						<reference key="object" ref="963351320"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="270902937"/>
+							<reference ref="88285865"/>
+							<reference ref="159080638"/>
+							<reference ref="326711663"/>
+							<reference ref="447796847"/>
+						</array>
+						<reference key="parent" ref="892235320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">213</int>
+						<reference key="object" ref="270902937"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">210</int>
+						<reference key="object" ref="88285865"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">221</int>
+						<reference key="object" ref="159080638"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">208</int>
+						<reference key="object" ref="326711663"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">209</int>
+						<reference key="object" ref="447796847"/>
+						<reference key="parent" ref="963351320"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">57</int>
+						<reference key="object" ref="110575045"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="238522557"/>
+							<reference ref="755159360"/>
+							<reference ref="908899353"/>
+							<reference ref="632727374"/>
+							<reference ref="646227648"/>
+							<reference ref="609285721"/>
+							<reference ref="481834944"/>
+							<reference ref="304266470"/>
+							<reference ref="1046388886"/>
+							<reference ref="1056857174"/>
+							<reference ref="342932134"/>
+							<reference ref="622903446"/>
+							<reference ref="24092627"/>
+						</array>
+						<reference key="parent" ref="694149608"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">58</int>
+						<reference key="object" ref="238522557"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">134</int>
+						<reference key="object" ref="755159360"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">150</int>
+						<reference key="object" ref="908899353"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">136</int>
+						<reference key="object" ref="632727374"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">144</int>
+						<reference key="object" ref="646227648"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">129</int>
+						<reference key="object" ref="609285721"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">143</int>
+						<reference key="object" ref="481834944"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">236</int>
+						<reference key="object" ref="304266470"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">131</int>
+						<reference key="object" ref="1046388886"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="752062318"/>
+						</array>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">149</int>
+						<reference key="object" ref="1056857174"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">145</int>
+						<reference key="object" ref="342932134"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">130</int>
+						<reference key="object" ref="752062318"/>
+						<reference key="parent" ref="1046388886"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">24</int>
+						<reference key="object" ref="835318025"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="299356726"/>
+							<reference ref="625202149"/>
+							<reference ref="575023229"/>
+							<reference ref="1011231497"/>
+							<reference ref="92029792"/>
+						</array>
+						<reference key="parent" ref="713487014"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">92</int>
+						<reference key="object" ref="299356726"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">5</int>
+						<reference key="object" ref="625202149"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">239</int>
+						<reference key="object" ref="575023229"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">23</int>
+						<reference key="object" ref="1011231497"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">295</int>
+						<reference key="object" ref="586577488"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="466310130"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">296</int>
+						<reference key="object" ref="466310130"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="102151532"/>
+							<reference ref="237841660"/>
+							<reference ref="87708234"/>
+						</array>
+						<reference key="parent" ref="586577488"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">297</int>
+						<reference key="object" ref="102151532"/>
+						<reference key="parent" ref="466310130"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">298</int>
+						<reference key="object" ref="237841660"/>
+						<reference key="parent" ref="466310130"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">211</int>
+						<reference key="object" ref="676164635"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="785027613"/>
+						</array>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">212</int>
+						<reference key="object" ref="785027613"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="680220178"/>
+							<reference ref="731782645"/>
+						</array>
+						<reference key="parent" ref="676164635"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">195</int>
+						<reference key="object" ref="680220178"/>
+						<reference key="parent" ref="785027613"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">196</int>
+						<reference key="object" ref="731782645"/>
+						<reference key="parent" ref="785027613"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">346</int>
+						<reference key="object" ref="967646866"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">348</int>
+						<reference key="object" ref="507821607"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="698887838"/>
+						</array>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">349</int>
+						<reference key="object" ref="698887838"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="605118523"/>
+							<reference ref="197661976"/>
+							<reference ref="708854459"/>
+							<reference ref="65139061"/>
+							<reference ref="19036812"/>
+							<reference ref="672708820"/>
+							<reference ref="537092702"/>
+						</array>
+						<reference key="parent" ref="507821607"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">350</int>
+						<reference key="object" ref="605118523"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">351</int>
+						<reference key="object" ref="197661976"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">354</int>
+						<reference key="object" ref="708854459"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">371</int>
+						<reference key="object" ref="972006081"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="439893737"/>
+						</array>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">372</int>
+						<reference key="object" ref="439893737"/>
+						<array class="NSMutableArray" key="children"/>
+						<reference key="parent" ref="972006081"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">375</int>
+						<reference key="object" ref="302598603"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="941447902"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">376</int>
+						<reference key="object" ref="941447902"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="792887677"/>
+							<reference ref="215659978"/>
+						</array>
+						<reference key="parent" ref="302598603"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">377</int>
+						<reference key="object" ref="792887677"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="786677654"/>
+						</array>
+						<reference key="parent" ref="941447902"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">388</int>
+						<reference key="object" ref="786677654"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="159677712"/>
+							<reference ref="305399458"/>
+							<reference ref="814362025"/>
+							<reference ref="330926929"/>
+							<reference ref="533507878"/>
+							<reference ref="158063935"/>
+							<reference ref="885547335"/>
+							<reference ref="901062459"/>
+							<reference ref="767671776"/>
+							<reference ref="691570813"/>
+							<reference ref="769124883"/>
+							<reference ref="739652853"/>
+							<reference ref="1012600125"/>
+							<reference ref="214559597"/>
+							<reference ref="596732606"/>
+							<reference ref="393423671"/>
+						</array>
+						<reference key="parent" ref="792887677"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">389</int>
+						<reference key="object" ref="159677712"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">390</int>
+						<reference key="object" ref="305399458"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">391</int>
+						<reference key="object" ref="814362025"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">392</int>
+						<reference key="object" ref="330926929"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">393</int>
+						<reference key="object" ref="533507878"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">394</int>
+						<reference key="object" ref="158063935"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">395</int>
+						<reference key="object" ref="885547335"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">396</int>
+						<reference key="object" ref="901062459"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">397</int>
+						<reference key="object" ref="767671776"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="175441468"/>
+						</array>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">398</int>
+						<reference key="object" ref="691570813"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="1058217995"/>
+						</array>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">399</int>
+						<reference key="object" ref="769124883"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="18263474"/>
+						</array>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">400</int>
+						<reference key="object" ref="739652853"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">401</int>
+						<reference key="object" ref="1012600125"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">402</int>
+						<reference key="object" ref="214559597"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">403</int>
+						<reference key="object" ref="596732606"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">404</int>
+						<reference key="object" ref="393423671"/>
+						<reference key="parent" ref="786677654"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">405</int>
+						<reference key="object" ref="18263474"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="257962622"/>
+							<reference ref="644725453"/>
+							<reference ref="1037576581"/>
+							<reference ref="941806246"/>
+							<reference ref="1045724900"/>
+						</array>
+						<reference key="parent" ref="769124883"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">406</int>
+						<reference key="object" ref="257962622"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">407</int>
+						<reference key="object" ref="644725453"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">408</int>
+						<reference key="object" ref="1037576581"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">409</int>
+						<reference key="object" ref="941806246"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">410</int>
+						<reference key="object" ref="1045724900"/>
+						<reference key="parent" ref="18263474"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">411</int>
+						<reference key="object" ref="1058217995"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="706297211"/>
+							<reference ref="568384683"/>
+							<reference ref="663508465"/>
+						</array>
+						<reference key="parent" ref="691570813"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">412</int>
+						<reference key="object" ref="706297211"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">413</int>
+						<reference key="object" ref="568384683"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">414</int>
+						<reference key="object" ref="663508465"/>
+						<reference key="parent" ref="1058217995"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">415</int>
+						<reference key="object" ref="175441468"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="252969304"/>
+							<reference ref="766922938"/>
+							<reference ref="677519740"/>
+							<reference ref="238351151"/>
+						</array>
+						<reference key="parent" ref="767671776"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">416</int>
+						<reference key="object" ref="252969304"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">417</int>
+						<reference key="object" ref="766922938"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">418</int>
+						<reference key="object" ref="677519740"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">419</int>
+						<reference key="object" ref="238351151"/>
+						<reference key="parent" ref="175441468"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">420</int>
+						<reference key="object" ref="755631768"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">450</int>
+						<reference key="object" ref="288088188"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="579392910"/>
+						</array>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">451</int>
+						<reference key="object" ref="579392910"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="1060694897"/>
+							<reference ref="879586729"/>
+							<reference ref="56570060"/>
+						</array>
+						<reference key="parent" ref="288088188"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">452</int>
+						<reference key="object" ref="1060694897"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">453</int>
+						<reference key="object" ref="859480356"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">454</int>
+						<reference key="object" ref="795346622"/>
+						<reference key="parent" ref="769623530"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">457</int>
+						<reference key="object" ref="65139061"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">459</int>
+						<reference key="object" ref="19036812"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">460</int>
+						<reference key="object" ref="672708820"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">462</int>
+						<reference key="object" ref="537092702"/>
+						<reference key="parent" ref="698887838"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">465</int>
+						<reference key="object" ref="879586729"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">466</int>
+						<reference key="object" ref="56570060"/>
+						<reference key="parent" ref="579392910"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">485</int>
+						<reference key="object" ref="82994268"/>
+						<reference key="parent" ref="789758025"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">490</int>
+						<reference key="object" ref="448692316"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="992780483"/>
+						</array>
+						<reference key="parent" ref="649796088"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">491</int>
+						<reference key="object" ref="992780483"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="105068016"/>
+						</array>
+						<reference key="parent" ref="448692316"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">492</int>
+						<reference key="object" ref="105068016"/>
+						<reference key="parent" ref="992780483"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">496</int>
+						<reference key="object" ref="215659978"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="446991534"/>
+						</array>
+						<reference key="parent" ref="941447902"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">497</int>
+						<reference key="object" ref="446991534"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="875092757"/>
+							<reference ref="630155264"/>
+							<reference ref="945678886"/>
+							<reference ref="512868991"/>
+							<reference ref="163117631"/>
+							<reference ref="31516759"/>
+							<reference ref="908105787"/>
+							<reference ref="644046920"/>
+							<reference ref="231811626"/>
+							<reference ref="883618387"/>
+						</array>
+						<reference key="parent" ref="215659978"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">498</int>
+						<reference key="object" ref="875092757"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">499</int>
+						<reference key="object" ref="630155264"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">500</int>
+						<reference key="object" ref="945678886"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">501</int>
+						<reference key="object" ref="512868991"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">502</int>
+						<reference key="object" ref="163117631"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">503</int>
+						<reference key="object" ref="31516759"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="956096989"/>
+						</array>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">504</int>
+						<reference key="object" ref="908105787"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">505</int>
+						<reference key="object" ref="644046920"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">506</int>
+						<reference key="object" ref="231811626"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">507</int>
+						<reference key="object" ref="883618387"/>
+						<reference key="parent" ref="446991534"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">508</int>
+						<reference key="object" ref="956096989"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="257099033"/>
+							<reference ref="551969625"/>
+							<reference ref="249532473"/>
+							<reference ref="607364498"/>
+							<reference ref="508151438"/>
+							<reference ref="981751889"/>
+							<reference ref="380031999"/>
+							<reference ref="825984362"/>
+							<reference ref="560145579"/>
+						</array>
+						<reference key="parent" ref="31516759"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">509</int>
+						<reference key="object" ref="257099033"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">510</int>
+						<reference key="object" ref="551969625"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">511</int>
+						<reference key="object" ref="249532473"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">512</int>
+						<reference key="object" ref="607364498"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">513</int>
+						<reference key="object" ref="508151438"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">514</int>
+						<reference key="object" ref="981751889"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">515</int>
+						<reference key="object" ref="380031999"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">516</int>
+						<reference key="object" ref="825984362"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">517</int>
+						<reference key="object" ref="560145579"/>
+						<reference key="parent" ref="956096989"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">538</int>
+						<reference key="object" ref="758604943"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="1038370525"/>
+						</array>
+						<reference key="parent" ref="0"/>
+						<string key="objectName">Drawer Content View</string>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">539</int>
+						<reference key="object" ref="764451088"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">629</int>
+						<reference key="object" ref="1038370525"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="617550661"/>
+							<reference ref="977018641"/>
+							<reference ref="429436769"/>
+						</array>
+						<reference key="parent" ref="758604943"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">630</int>
+						<reference key="object" ref="617550661"/>
+						<reference key="parent" ref="1038370525"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">631</int>
+						<reference key="object" ref="977018641"/>
+						<reference key="parent" ref="1038370525"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">632</int>
+						<reference key="object" ref="429436769"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="691918008"/>
+							<reference ref="394988372"/>
+						</array>
+						<reference key="parent" ref="1038370525"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">634</int>
+						<reference key="object" ref="691918008"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="241301801"/>
+						</array>
+						<reference key="parent" ref="429436769"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">637</int>
+						<reference key="object" ref="241301801"/>
+						<reference key="parent" ref="691918008"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">494</int>
+						<reference key="object" ref="976324537"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">661</int>
+						<reference key="object" ref="76290771"/>
+						<reference key="parent" ref="0"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">635</int>
+						<reference key="object" ref="394988372"/>
+						<array class="NSMutableArray" key="children">
+							<reference ref="88358594"/>
+						</array>
+						<reference key="parent" ref="429436769"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">698</int>
+						<reference key="object" ref="88358594"/>
+						<reference key="parent" ref="394988372"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">705</int>
+						<reference key="object" ref="622903446"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">706</int>
+						<reference key="object" ref="24092627"/>
+						<reference key="parent" ref="110575045"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">718</int>
+						<reference key="object" ref="87708234"/>
+						<reference key="parent" ref="466310130"/>
+					</object>
+					<object class="IBObjectRecord">
+						<int key="objectID">720</int>
+						<reference key="object" ref="92029792"/>
+						<reference key="parent" ref="835318025"/>
+					</object>
+				</array>
+			</object>
+			<dictionary class="NSMutableDictionary" key="flattenedProperties">
+				<string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="112.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="112.ImportedFromIB2"/>
+				<string key="124.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="124.ImportedFromIB2"/>
+				<string key="125.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="125.ImportedFromIB2"/>
+				<string key="125.editorWindowContentRectSynchronizationRect">{{522, 812}, {146, 23}}</string>
+				<string key="126.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="126.ImportedFromIB2"/>
+				<string key="129.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="129.ImportedFromIB2"/>
+				<string key="130.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="130.ImportedFromIB2"/>
+				<string key="130.editorWindowContentRectSynchronizationRect">{{436, 809}, {64, 6}}</string>
+				<string key="131.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="131.ImportedFromIB2"/>
+				<string key="134.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="134.ImportedFromIB2"/>
+				<string key="136.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="136.ImportedFromIB2"/>
+				<string key="143.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="143.ImportedFromIB2"/>
+				<string key="144.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="144.ImportedFromIB2"/>
+				<string key="145.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="145.ImportedFromIB2"/>
+				<string key="149.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="149.ImportedFromIB2"/>
+				<string key="150.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="150.ImportedFromIB2"/>
+				<string key="19.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="19.ImportedFromIB2"/>
+				<string key="195.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="195.ImportedFromIB2"/>
+				<string key="196.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="196.ImportedFromIB2"/>
+				<string key="197.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="197.ImportedFromIB2"/>
+				<string key="198.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="198.ImportedFromIB2"/>
+				<string key="199.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="199.ImportedFromIB2"/>
+				<string key="200.IBEditorWindowLastContentRect">{{753, 187}, {275, 113}}</string>
+				<string key="200.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="200.ImportedFromIB2"/>
+				<string key="200.editorWindowContentRectSynchronizationRect">{{608, 612}, {275, 83}}</string>
+				<string key="201.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="201.ImportedFromIB2"/>
+				<string key="202.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="202.ImportedFromIB2"/>
+				<string key="203.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="203.ImportedFromIB2"/>
+				<string key="204.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="204.ImportedFromIB2"/>
+				<string key="205.IBEditorWindowLastContentRect">{{547, 216}, {254, 283}}</string>
+				<string key="205.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="205.ImportedFromIB2"/>
+				<string key="205.editorWindowContentRectSynchronizationRect">{{187, 434}, {243, 243}}</string>
+				<string key="206.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="206.ImportedFromIB2"/>
+				<string key="207.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="207.ImportedFromIB2"/>
+				<string key="208.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="208.ImportedFromIB2"/>
+				<string key="209.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="209.ImportedFromIB2"/>
+				<string key="210.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="210.ImportedFromIB2"/>
+				<string key="211.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="211.ImportedFromIB2"/>
+				<string key="212.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="212.ImportedFromIB2"/>
+				<string key="212.editorWindowContentRectSynchronizationRect">{{608, 612}, {167, 43}}</string>
+				<string key="213.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="213.ImportedFromIB2"/>
+				<string key="214.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="214.ImportedFromIB2"/>
+				<string key="215.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="215.ImportedFromIB2"/>
+				<string key="216.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="216.ImportedFromIB2"/>
+				<string key="217.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="217.ImportedFromIB2"/>
+				<string key="218.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="218.ImportedFromIB2"/>
+				<string key="219.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="219.ImportedFromIB2"/>
+				<string key="220.IBEditorWindowLastContentRect">{{753, 217}, {238, 103}}</string>
+				<string key="220.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="220.ImportedFromIB2"/>
+				<string key="220.editorWindowContentRectSynchronizationRect">{{608, 612}, {241, 103}}</string>
+				<string key="221.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="221.ImportedFromIB2"/>
+				<string key="23.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="23.ImportedFromIB2"/>
+				<string key="236.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="236.ImportedFromIB2"/>
+				<string key="239.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="239.ImportedFromIB2"/>
+				<string key="24.IBEditorWindowLastContentRect">{{707, 406}, {194, 93}}</string>
+				<string key="24.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="24.ImportedFromIB2"/>
+				<string key="24.editorWindowContentRectSynchronizationRect">{{525, 802}, {197, 73}}</string>
+				<string key="29.IBEditorWindowLastContentRect">{{354, 499}, {485, 20}}</string>
+				<string key="29.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="29.ImportedFromIB2"/>
+				<string key="29.WindowOrigin">{74, 862}</string>
+				<string key="29.editorWindowContentRectSynchronizationRect">{{6, 978}, {478, 20}}</string>
+				<string key="295.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="296.IBEditorWindowLastContentRect">{{591, 436}, {276, 63}}</string>
+				<string key="296.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="296.editorWindowContentRectSynchronizationRect">{{475, 832}, {234, 43}}</string>
+				<string key="297.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="298.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="346.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="346.ImportedFromIB2"/>
+				<string key="348.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="348.ImportedFromIB2"/>
+				<string key="349.IBEditorWindowLastContentRect">{{746, 287}, {220, 133}}</string>
+				<string key="349.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="349.ImportedFromIB2"/>
+				<string key="349.editorWindowContentRectSynchronizationRect">{{608, 612}, {215, 63}}</string>
+				<string key="350.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="350.ImportedFromIB2"/>
+				<string key="351.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="351.ImportedFromIB2"/>
+				<string key="354.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="354.ImportedFromIB2"/>
+				<string key="371.IBEditorWindowLastContentRect">{{254, 23}, {640, 480}}</string>
+				<string key="371.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="371.IBWindowTemplateEditedContentRect">{{254, 23}, {640, 480}}</string>
+				<integer value="1" key="371.NSWindowTemplate.visibleAtLaunch"/>
+				<string key="371.editorWindowContentRectSynchronizationRect">{{33, 99}, {480, 360}}</string>
+				<string key="371.windowTemplate.maxSize">{3.40282e+38, 3.40282e+38}</string>
+				<string key="371.windowTemplate.minSize">{0, 0}</string>
+				<string key="372.CustomClassName">SkSampleNSView</string>
+				<string key="372.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<object class="NSAffineTransform" key="372.IBViewBoundsToFrameTransform"/>
+				<string key="375.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="376.IBEditorWindowLastContentRect">{{591, 456}, {83, 43}}</string>
+				<string key="376.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="377.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="388.IBEditorWindowLastContentRect">{{523, 2}, {178, 283}}</string>
+				<string key="388.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="389.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="390.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="391.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="392.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="393.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="394.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="395.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="396.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="397.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="398.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="399.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="400.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="401.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="402.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="403.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="404.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="405.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="406.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="407.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="408.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="409.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="410.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="411.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="412.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="413.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="414.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="415.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="416.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="417.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="418.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="419.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="450.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="451.IBEditorWindowLastContentRect">{{753, 197}, {170, 63}}</string>
+				<string key="451.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="452.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="453.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="454.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="457.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="459.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="460.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="462.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="465.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="466.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="485.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="490.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="491.IBEditorWindowLastContentRect">{{778, 476}, {221, 23}}</string>
+				<string key="491.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="492.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="496.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="497.IBEditorWindowLastContentRect">{{674, 260}, {204, 183}}</string>
+				<string key="497.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="498.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="499.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="5.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="5.ImportedFromIB2"/>
+				<string key="500.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="501.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="502.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="503.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="504.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="505.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="506.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="507.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="508.IBEditorWindowLastContentRect">{{878, 180}, {164, 173}}</string>
+				<string key="508.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="509.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="510.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="511.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="512.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="513.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="514.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="515.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="516.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="517.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="538.IBEditorWindowLastContentRect">{{136, 685}, {341, 321}}</string>
+				<string key="538.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="539.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="56.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="56.ImportedFromIB2"/>
+				<string key="57.IBEditorWindowLastContentRect">{{366, 286}, {254, 213}}</string>
+				<string key="57.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="57.ImportedFromIB2"/>
+				<string key="57.editorWindowContentRectSynchronizationRect">{{23, 794}, {245, 183}}</string>
+				<string key="58.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="58.ImportedFromIB2"/>
+				<string key="629.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<object class="NSAffineTransform" key="629.IBViewBoundsToFrameTransform">
+					<bytes key="NSTransformStruct">P4AAAL+AAAAAAAAAw5+AAA</bytes>
+				</object>
+				<string key="630.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="631.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="632.CustomClassName">SkOptionsTableView</string>
+				<string key="632.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<object class="NSAffineTransform" key="632.IBViewBoundsToFrameTransform"/>
+				<string key="634.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="635.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="637.CustomClassName">SkTextFieldCell</string>
+				<string key="637.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="661.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="698.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="705.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="706.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="718.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="72.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="72.ImportedFromIB2"/>
+				<string key="720.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<string key="73.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="73.ImportedFromIB2"/>
+				<string key="74.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="74.ImportedFromIB2"/>
+				<string key="75.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="75.ImportedFromIB2"/>
+				<string key="77.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="77.ImportedFromIB2"/>
+				<string key="78.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="78.ImportedFromIB2"/>
+				<string key="79.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="79.ImportedFromIB2"/>
+				<string key="80.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="80.ImportedFromIB2"/>
+				<string key="81.IBEditorWindowLastContentRect">{{505, 296}, {196, 203}}</string>
+				<string key="81.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="81.ImportedFromIB2"/>
+				<string key="81.editorWindowContentRectSynchronizationRect">{{145, 474}, {199, 203}}</string>
+				<string key="82.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="82.ImportedFromIB2"/>
+				<string key="83.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="83.ImportedFromIB2"/>
+				<string key="92.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+				<integer value="1" key="92.ImportedFromIB2"/>
+			</dictionary>
+			<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
+			<nil key="activeLocalization"/>
+			<dictionary class="NSMutableDictionary" key="localizations"/>
+			<nil key="sourceID"/>
+			<int key="maxID">721</int>
+		</object>
+		<object class="IBClassDescriber" key="IBDocument.Classes">
+			<array class="NSMutableArray" key="referencedPartialClassDescriptions">
+				<object class="IBPartialClassDescription">
+					<string key="className">SampleAppDelegate</string>
+					<string key="superclassName">NSObject</string>
+					<object class="NSMutableDictionary" key="actions">
+						<string key="NS.key.0">toiPadSize:</string>
+						<string key="NS.object.0">id</string>
+					</object>
+					<object class="NSMutableDictionary" key="actionInfosByName">
+						<string key="NS.key.0">toiPadSize:</string>
+						<object class="IBActionInfo" key="NS.object.0">
+							<string key="name">toiPadSize:</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</object>
+					<dictionary class="NSMutableDictionary" key="outlets">
+						<string key="fOptions">SkOptionsTableView</string>
+						<string key="fView">SkNSView</string>
+						<string key="fWindow">NSWindow</string>
+					</dictionary>
+					<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<object class="IBToOneOutletInfo" key="fOptions">
+							<string key="name">fOptions</string>
+							<string key="candidateClassName">SkOptionsTableView</string>
+						</object>
+						<object class="IBToOneOutletInfo" key="fView">
+							<string key="name">fView</string>
+							<string key="candidateClassName">SkNSView</string>
+						</object>
+						<object class="IBToOneOutletInfo" key="fWindow">
+							<string key="name">fWindow</string>
+							<string key="candidateClassName">NSWindow</string>
+						</object>
+					</dictionary>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">../../src/utils/mac/SampleAppDelegate.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkNSView</string>
+					<string key="superclassName">NSView</string>
+					<object class="NSMutableDictionary" key="outlets">
+						<string key="NS.key.0">fOptionsDelegate</string>
+						<string key="NS.object.0">id</string>
+					</object>
+					<object class="NSMutableDictionary" key="toOneOutletInfosByName">
+						<string key="NS.key.0">fOptionsDelegate</string>
+						<object class="IBToOneOutletInfo" key="NS.object.0">
+							<string key="name">fOptionsDelegate</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">../../src/utils/mac/SkNSView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkOptionsTableView</string>
+					<string key="superclassName">NSTableView</string>
+					<object class="NSMutableDictionary" key="actions">
+						<string key="NS.key.0">toggleKeyEquivalents:</string>
+						<string key="NS.object.0">id</string>
+					</object>
+					<object class="NSMutableDictionary" key="actionInfosByName">
+						<string key="NS.key.0">toggleKeyEquivalents:</string>
+						<object class="IBActionInfo" key="NS.object.0">
+							<string key="name">toggleKeyEquivalents:</string>
+							<string key="candidateClassName">id</string>
+						</object>
+					</object>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">../../src/utils/mac/SkOptionsTableView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkSampleNSView</string>
+					<string key="superclassName">SkNSView</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">../../src/utils/mac/SkSampleNSView.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">SkTextFieldCell</string>
+					<string key="superclassName">NSTextFieldCell</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBProjectSource</string>
+						<string key="minorKey">../../src/utils/mac/SkTextFieldCell.h</string>
+					</object>
+				</object>
+			</array>
+			<array class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
+				<object class="IBPartialClassDescription">
+					<string key="className">NSFormatter</string>
+					<string key="superclassName">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFormatter.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSClassDescription.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSObjectScripting.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSPortCoder.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptClassDescription.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptKeyValueCoding.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptObjectSpecifiers.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSScriptWhoseTests.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">Foundation.framework/Headers/NSURLDownload.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">PrintCore.framework/Headers/PDEPluginInterface.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">QuartzCore.framework/Headers/CAAnimation.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">QuartzCore.framework/Headers/CALayer.h</string>
+					</object>
+				</object>
+				<object class="IBPartialClassDescription">
+					<string key="className">NSObject</string>
+					<object class="IBClassDescriptionSource" key="sourceIdentifier">
+						<string key="majorKey">IBFrameworkSource</string>
+						<string key="minorKey">QuartzCore.framework/Headers/CIImageProvider.h</string>
+					</object>
+				</object>
+			</array>
+		</object>
+		<int key="IBDocument.localizationMode">0</int>
+		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+		<string key="IBDocument.LastKnownRelativeProjectPath">../../../out/gyp/SampleApp.xcodeproj</string>
+		<int key="IBDocument.defaultPropertyAccessControl">3</int>
+		<dictionary class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+			<string key="NSMenuCheckmark">{9, 8}</string>
+			<string key="NSMenuMixedState">{7, 2}</string>
+		</dictionary>
+	</data>
+</archive>
diff --git a/src/views/mac/SampleAppDelegate.h b/src/views/mac/SampleAppDelegate.h
new file mode 100644
index 0000000..8f2bead
--- /dev/null
+++ b/src/views/mac/SampleAppDelegate.h
@@ -0,0 +1,24 @@
+
+/*
+ * 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 <Cocoa/Cocoa.h>
+#import "SkSampleNSView.h"
+#import "SkOptionsTableView.h"
+@interface SampleAppDelegate : NSObject <NSApplicationDelegate> {
+    NSWindow* fWindow;
+    SkSampleNSView* fView;
+    SkOptionsTableView* fOptions;
+}
+
+@property (assign) IBOutlet NSWindow* fWindow;
+@property (assign) IBOutlet SkSampleNSView* fView;
+@property (assign) IBOutlet SkOptionsTableView* fOptions;
+
+- (IBAction)toiPadSize:(id)sender;
+@end
diff --git a/src/views/mac/SampleAppDelegate.mm b/src/views/mac/SampleAppDelegate.mm
new file mode 100644
index 0000000..91dcada
--- /dev/null
+++ b/src/views/mac/SampleAppDelegate.mm
@@ -0,0 +1,16 @@
+#import "SampleAppDelegate.h"
+@implementation SampleAppDelegate
+@synthesize fWindow, fView, fOptions;
+
+-(void) applicationDidFinishLaunching:(NSNotification *)aNotification {
+    //Load specified skia views after launching
+    fView.fOptionsDelegate = fOptions;
+    [fWindow setAcceptsMouseMovedEvents:YES];
+    [fOptions registerMenus:fView.fWind->getMenus()];
+}
+
+- (IBAction)toiPadSize:(id)sender {
+    NSRect frame = NSMakeRect(fWindow.frame.origin.x, fWindow.frame.origin.y, 768, 1024);
+    [fWindow setFrame:frame display:YES animate:YES];
+}
+@end
diff --git a/src/views/mac/SkEventNotifier.h b/src/views/mac/SkEventNotifier.h
new file mode 100644
index 0000000..ea6bbf1
--- /dev/null
+++ b/src/views/mac/SkEventNotifier.h
@@ -0,0 +1,13 @@
+/*
+ * 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 <Foundation/Foundation.h>
+
+@interface SkEventNotifier : NSObject
+- (void)receiveSkEvent:(NSNotification*)notification;
++ (void)postTimedSkEvent:(NSTimeInterval)ti;
++ (void)timerFireMethod:(NSTimer*)theTimer;
+@end
diff --git a/src/views/mac/SkEventNotifier.mm b/src/views/mac/SkEventNotifier.mm
new file mode 100644
index 0000000..0864380
--- /dev/null
+++ b/src/views/mac/SkEventNotifier.mm
@@ -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.
+ */
+
+#import "SkEventNotifier.h"
+#include "SkEvent.h"
+#define SkEventClass @"SkEvenClass"
+@implementation SkEventNotifier
+- (id)init {
+    self = [super init];
+    if (self) {
+        //Register as an observer for SkEventClass events and call
+        //receiveSkEvent: upon receiving the event
+        [[NSNotificationCenter defaultCenter] addObserver:self
+                                                 selector:@selector(receiveSkEvent:)
+                                                     name:SkEventClass
+                                                   object:nil];
+    }
+    return self;
+}
+
+- (void)dealloc {
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+    [super dealloc];
+}
+
+-(BOOL) acceptsFirstResponder {
+    return YES;
+}
+
+//SkEvent Handers
+- (void)receiveSkEvent:(NSNotification *)notification {
+    if(SkEvent::ProcessEvent())
+        SkEvent::SignalNonEmptyQueue();
+}
+
++ (void)postTimedSkEvent:(NSTimeInterval)timeInterval {
+    [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self
+                                   selector:@selector(timerFireMethod:)
+                                   userInfo:nil repeats:NO];
+}
+
++ (void)timerFireMethod:(NSTimer*)theTimer {
+	SkEvent::ServiceQueueTimer();
+}
+
+@end
+////////////////////////////////////////////////////////////////////////////////
+void SkEvent::SignalNonEmptyQueue() {
+    //post a SkEventClass event to the default notification queue
+    NSNotification* notification = [NSNotification notificationWithName:SkEventClass object:nil];
+    [[NSNotificationQueue defaultQueue] enqueueNotification:notification
+                                               postingStyle:NSPostWhenIdle
+                                               coalesceMask:NSNotificationNoCoalescing
+                                                   forModes:nil];
+}
+
+void SkEvent::SignalQueueTimer(SkMSec delay) {
+	if (delay) {
+        //Convert to seconds
+        NSTimeInterval ti = delay/(float)SK_MSec1;
+        [SkEventNotifier postTimedSkEvent:ti];
+	}
+}
diff --git a/src/views/mac/SkNSView.h b/src/views/mac/SkNSView.h
new file mode 100644
index 0000000..109a998
--- /dev/null
+++ b/src/views/mac/SkNSView.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.
+ */
+
+#import <QuartzCore/QuartzCore.h>
+#import <Cocoa/Cocoa.h>
+#import "SkWindow.h"
+class SkEvent;
+@class SkNSView;
+
+@protocol SkNSViewOptionsDelegate <NSObject>
+@optional
+// Called when the view needs to handle adding an SkOSMenu
+- (void) view:(SkNSView*)view didAddMenu:(const SkOSMenu*)menu;
+- (void) view:(SkNSView*)view didUpdateMenu:(const SkOSMenu*)menu;
+@end
+
+@interface SkNSView : NSView {
+    BOOL fRedrawRequestPending;
+
+    NSString* fTitle;
+    SkOSWindow* fWind;
+#if SK_SUPPORT_GPU
+    NSOpenGLContext* fGLContext;
+#endif
+    id<SkNSViewOptionsDelegate> fOptionsDelegate;
+}
+
+@property (nonatomic, readonly) SkOSWindow *fWind;
+@property (nonatomic, retain) NSString* fTitle;
+#if SK_SUPPORT_GPU
+@property (nonatomic, retain) NSOpenGLContext* fGLContext;
+#endif
+@property (nonatomic, assign) id<SkNSViewOptionsDelegate> fOptionsDelegate;
+
+- (id)initWithDefaults;
+- (void)setUpWindow;
+- (void)resizeSkView:(NSSize)newSize;
+- (void)setSkTitle:(const char*)title;
+- (void)onAddMenu:(const SkOSMenu*)menu;
+- (void)onUpdateMenu:(const SkOSMenu*)menu;
+- (void)postInvalWithRect:(const SkIRect*)rectOrNil;
+- (BOOL)onHandleEvent:(const SkEvent&)event;
+
+- (bool)attach:(SkOSWindow::SkBackEndTypes)attachType withMSAASampleCount:(int) sampleCount;
+- (void)detach;
+- (void)present;
+@end
diff --git a/src/views/mac/SkNSView.mm b/src/views/mac/SkNSView.mm
new file mode 100644
index 0000000..ffa9d7c
--- /dev/null
+++ b/src/views/mac/SkNSView.mm
@@ -0,0 +1,314 @@
+
+/*
+ * 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 "SkNSView.h"
+#include "SkCanvas.h"
+#include "SkCGUtils.h"
+#include "SkEvent.h"
+SK_COMPILE_ASSERT(SK_SUPPORT_GPU, not_implemented_for_non_gpu_build);
+
+//#define FORCE_REDRAW
+@implementation SkNSView
+@synthesize fWind, fTitle, fOptionsDelegate, fGLContext;
+
+- (id)initWithCoder:(NSCoder*)coder {
+    if ((self = [super initWithCoder:coder])) {
+        self = [self initWithDefaults];
+        [self setUpWindow];
+    }
+    return self;
+}
+
+- (id)initWithFrame:(NSRect)frameRect {
+    if ((self = [super initWithFrame:frameRect])) {
+        self = [self initWithDefaults];
+        [self setUpWindow];
+    }
+    return self;
+}
+
+- (id)initWithDefaults {
+    fRedrawRequestPending = false;
+    fWind = NULL;
+    return self;
+}
+
+- (void)setUpWindow {
+    if (NULL != fWind) {
+        fWind->setVisibleP(true);
+        fWind->resize((int) self.frame.size.width, (int) self.frame.size.height, 
+                      SkBitmap::kARGB_8888_Config);
+    }
+}
+
+-(BOOL) isFlipped {
+    return YES;
+}
+
+- (BOOL)acceptsFirstResponder {
+    return YES;
+}
+
+- (void)resizeSkView:(NSSize)newSize {
+    if (NULL != fWind && (fWind->width() != newSize.width || fWind->height() != newSize.height)) {
+        fWind->resize((int) newSize.width, (int) newSize.height);
+        if (NULL != fGLContext) {
+            glClear(GL_STENCIL_BUFFER_BIT);
+        }
+        [fGLContext update];
+    }
+}
+
+- (void) setFrameSize:(NSSize)newSize {
+    [super setFrameSize:newSize];
+    [self resizeSkView:newSize];
+}
+
+- (void)dealloc {
+    delete fWind;
+    self.fGLContext = nil;
+    self.fTitle = nil;
+    [super dealloc];
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+- (void)drawSkia {
+    fRedrawRequestPending = false;
+    if (NULL != fWind) {
+        SkAutoTUnref<SkCanvas> canvas(fWind->createCanvas());
+        fWind->draw(canvas);
+#ifdef FORCE_REDRAW
+        fWind->inval(NULL);
+#endif
+    }
+}
+
+- (void)setSkTitle:(const char *)title {
+    self.fTitle = [NSString stringWithUTF8String:title];
+    [[self window] setTitle:self.fTitle];
+}
+
+- (BOOL)onHandleEvent:(const SkEvent&)evt {
+    return false;
+}
+
+#include "SkOSMenu.h"
+- (void)onAddMenu:(const SkOSMenu*)menu {
+    [self.fOptionsDelegate view:self didAddMenu:menu];
+}
+
+- (void)onUpdateMenu:(const SkOSMenu*)menu {
+    [self.fOptionsDelegate view:self didUpdateMenu:menu];
+}
+
+- (void)postInvalWithRect:(const SkIRect*)r {
+    if (!fRedrawRequestPending) {
+        fRedrawRequestPending = true;
+        [self setNeedsDisplay:YES];
+        [self performSelector:@selector(drawSkia) withObject:nil afterDelay:0];
+    }
+}
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkKey.h"
+enum {
+	SK_MacReturnKey		= 36,
+	SK_MacDeleteKey		= 51,
+	SK_MacEndKey		= 119,
+	SK_MacLeftKey		= 123,
+	SK_MacRightKey		= 124,
+	SK_MacDownKey		= 125,
+	SK_MacUpKey			= 126,
+    SK_Mac0Key          = 0x52,
+    SK_Mac1Key          = 0x53,
+    SK_Mac2Key          = 0x54,
+    SK_Mac3Key          = 0x55,
+    SK_Mac4Key          = 0x56,
+    SK_Mac5Key          = 0x57,
+    SK_Mac6Key          = 0x58,
+    SK_Mac7Key          = 0x59,
+    SK_Mac8Key          = 0x5b,
+    SK_Mac9Key          = 0x5c
+};
+
+static SkKey raw2key(UInt32 raw)
+{
+	static const struct {
+		UInt32  fRaw;
+		SkKey   fKey;
+	} gKeys[] = {
+		{ SK_MacUpKey,		kUp_SkKey		},
+		{ SK_MacDownKey,	kDown_SkKey		},
+		{ SK_MacLeftKey,	kLeft_SkKey		},
+		{ SK_MacRightKey,   kRight_SkKey	},
+		{ SK_MacReturnKey,  kOK_SkKey		},
+		{ SK_MacDeleteKey,  kBack_SkKey		},
+		{ SK_MacEndKey,		kEnd_SkKey		},
+        { SK_Mac0Key,       k0_SkKey        },
+        { SK_Mac1Key,       k1_SkKey        },
+        { SK_Mac2Key,       k2_SkKey        },
+        { SK_Mac3Key,       k3_SkKey        },
+        { SK_Mac4Key,       k4_SkKey        },
+        { SK_Mac5Key,       k5_SkKey        },
+        { SK_Mac6Key,       k6_SkKey        },
+        { SK_Mac7Key,       k7_SkKey        },
+        { SK_Mac8Key,       k8_SkKey        },
+        { SK_Mac9Key,       k9_SkKey        }
+	};
+    
+	for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++)
+		if (gKeys[i].fRaw == raw)
+			return gKeys[i].fKey;
+	return kNONE_SkKey;
+}
+
+- (void)keyDown:(NSEvent *)event {
+    if (NULL == fWind)
+        return;
+    
+    SkKey key = raw2key([event keyCode]);
+    if (kNONE_SkKey != key)
+        fWind->handleKey(key);
+    else{
+        unichar c = [[event characters] characterAtIndex:0];
+        fWind->handleChar((SkUnichar)c);
+    }
+}
+
+- (void)keyUp:(NSEvent *)event {
+    if (NULL == fWind)
+        return;
+    
+    SkKey key = raw2key([event keyCode]);
+    if (kNONE_SkKey != key)
+        fWind->handleKeyUp(key);
+ // else
+ //     unichar c = [[event characters] characterAtIndex:0];
+}
+
+- (void)mouseDown:(NSEvent *)event {
+    NSPoint p = [event locationInWindow];
+    if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
+        NSPoint loc = [self convertPoint:p fromView:nil];
+        fWind->handleClick((int) loc.x, (int) loc.y, SkView::Click::kDown_State, self);
+    }
+}
+
+- (void)mouseDragged:(NSEvent *)event {
+    NSPoint p = [event locationInWindow];
+    if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
+        NSPoint loc = [self convertPoint:p fromView:nil];
+        fWind->handleClick((int) loc.x, (int) loc.y, SkView::Click::kMoved_State, self);
+    }
+}
+
+- (void)mouseMoved:(NSEvent *)event {
+    NSPoint p = [event locationInWindow];
+    if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
+        NSPoint loc = [self convertPoint:p fromView:nil];
+        fWind->handleClick((int) loc.x, (int) loc.y, SkView::Click::kMoved_State, self);
+    }
+}
+
+- (void)mouseUp:(NSEvent *)event {
+    NSPoint p = [event locationInWindow];
+    if ([self mouse:p inRect:[self bounds]] && NULL != fWind) {
+        NSPoint loc = [self convertPoint:p fromView:nil];
+        fWind->handleClick((int) loc.x, (int) loc.y, SkView::Click::kUp_State, self);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#include <OpenGL/OpenGL.h>
+
+namespace { 
+CGLContextObj createGLContext(int msaaSampleCount) {
+    GLint major, minor;
+    CGLGetVersion(&major, &minor);
+    
+    static const CGLPixelFormatAttribute attributes[] = {
+        kCGLPFAStencilSize, (CGLPixelFormatAttribute) 8,
+        kCGLPFAAccelerated,
+        kCGLPFADoubleBuffer,
+        (CGLPixelFormatAttribute)0
+    };
+    
+    CGLPixelFormatObj format;
+    GLint npix = 0;
+    if (msaaSampleCount > 0) {
+        static int kAttributeCount = SK_ARRAY_COUNT(attributes);
+        CGLPixelFormatAttribute msaaAttributes[kAttributeCount + 5];
+        memcpy(msaaAttributes, attributes, sizeof(attributes));
+        SkASSERT(0 == msaaAttributes[kAttributeCount - 1]);
+        msaaAttributes[kAttributeCount - 1] = kCGLPFASampleBuffers;
+        msaaAttributes[kAttributeCount + 0] = (CGLPixelFormatAttribute)1;
+        msaaAttributes[kAttributeCount + 1] = kCGLPFAMultisample;
+        msaaAttributes[kAttributeCount + 2] = kCGLPFASamples;
+        msaaAttributes[kAttributeCount + 3] =
+                                    (CGLPixelFormatAttribute)msaaSampleCount;
+        msaaAttributes[kAttributeCount + 4] = (CGLPixelFormatAttribute)0;
+        CGLChoosePixelFormat(msaaAttributes, &format, &npix);
+    }
+    if (!npix) {
+        CGLChoosePixelFormat(attributes, &format, &npix);
+    }
+    
+    CGLContextObj ctx;
+    CGLCreateContext(format, NULL, &ctx);
+    CGLDestroyPixelFormat(format);
+    
+    static const GLint interval = 1;
+    CGLSetParameter(ctx, kCGLCPSwapInterval, &interval);
+    CGLSetCurrentContext(ctx);
+    return ctx;
+}
+}
+
+- (void)viewDidMoveToWindow {
+    [super viewDidMoveToWindow];
+    
+    //Attaching view to fGLContext requires that the view to be part of a window,
+    //and that the NSWindow instance must have a CoreGraphics counterpart (or 
+    //it must NOT be deferred or should have been on screen at least once)
+    if ([fGLContext view] != self && nil != self.window) {
+        [fGLContext setView:self];
+    }
+}
+- (bool)attach:(SkOSWindow::SkBackEndTypes)attachType
+        withMSAASampleCount:(int) sampleCount {
+    if (nil == fGLContext) {
+        CGLContextObj ctx = createGLContext(sampleCount);
+        fGLContext = [[NSOpenGLContext alloc] initWithCGLContextObj:ctx];
+        CGLReleaseContext(ctx);
+        if (NULL == fGLContext) {
+            return false;
+        }
+        [fGLContext setView:self];
+    }
+    
+    [fGLContext makeCurrentContext];
+    
+    glViewport(0, 0, (int) self.bounds.size.width, (int) self.bounds.size.width);
+    glClearColor(0, 0, 0, 0);
+    glClearStencil(0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+    return true;
+}
+
+- (void)detach {
+    [fGLContext release];
+    fGLContext = nil;
+}
+
+- (void)present {
+    if (nil != fGLContext) {
+        [fGLContext flushBuffer];
+    }
+}
+@end
diff --git a/src/views/mac/SkOSWindow_Mac.cpp b/src/views/mac/SkOSWindow_Mac.cpp
new file mode 100644
index 0000000..e6f3580
--- /dev/null
+++ b/src/views/mac/SkOSWindow_Mac.cpp
@@ -0,0 +1,542 @@
+/*
+ * Copyright 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 defined(SK_BUILD_FOR_MAC)
+
+#include <AGL/agl.h>
+
+#include <Carbon/Carbon.h>
+#include "SkCGUtils.h"
+
+#include "SkWindow.h"
+#include "SkCanvas.h"
+#include "SkOSMenu.h"
+#include "SkTime.h"
+
+#include "SkGraphics.h"
+#include <new.h>
+
+static void (*gPrevNewHandler)();
+
+extern "C" {
+    static void sk_new_handler()
+    {
+        if (SkGraphics::SetFontCacheUsed(0))
+            return;
+        if (gPrevNewHandler)
+            gPrevNewHandler();
+        else
+            sk_throw();
+    }
+}
+
+static SkOSWindow* gCurrOSWin;
+static EventTargetRef gEventTarget;
+static EventQueueRef gCurrEventQ;
+
+static OSStatus MyDrawEventHandler(EventHandlerCallRef myHandler,
+                                   EventRef event, void *userData) {
+    // NOTE: GState is save/restored by the HIView system doing the callback,
+    // so the draw handler doesn't need to do it
+
+    OSStatus status = noErr;
+    CGContextRef context;
+    HIRect        bounds;
+
+    // Get the CGContextRef
+    status = GetEventParameter (event, kEventParamCGContextRef,
+                                typeCGContextRef, NULL,
+                                sizeof (CGContextRef),
+                                NULL,
+                                &context);
+
+    if (status != noErr) {
+        SkDebugf("Got error %d getting the context!\n", status);
+        return status;
+    }
+
+    // Get the bounding rectangle
+    HIViewGetBounds ((HIViewRef) userData, &bounds);
+
+    gCurrOSWin->doPaint(context);
+    return status;
+}
+
+#define SK_MacEventClass            FOUR_CHAR_CODE('SKec')
+#define SK_MacEventKind                FOUR_CHAR_CODE('SKek')
+#define SK_MacEventParamName        FOUR_CHAR_CODE('SKev')
+#define SK_MacEventSinkIDParamName    FOUR_CHAR_CODE('SKes')
+
+static void set_bindingside(HISideBinding* side, HIViewRef parent, HIBindingKind kind) {
+    side->toView = parent;
+    side->kind = kind;
+    side->offset = 0;
+}
+
+static void set_axisscale(HIAxisScale* axis, HIViewRef parent) {
+    axis->toView = parent;
+    axis->kind = kHILayoutScaleAbsolute;
+    axis->ratio = 1;
+}
+
+static void set_axisposition(HIAxisPosition* pos, HIViewRef parent, HIPositionKind kind) {
+    pos->toView = parent;
+    pos->kind = kind;
+    pos->offset = 0;
+}
+
+SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd), fAGLCtx(NULL)
+{
+    OSStatus    result;
+    WindowRef   wr = (WindowRef)hWnd;
+
+    HIViewRef imageView, parent;
+    HIViewRef rootView = HIViewGetRoot(wr);
+    HIViewFindByID(rootView, kHIViewWindowContentID, &parent);
+    result = HIImageViewCreate(NULL, &imageView);
+    SkASSERT(result == noErr);
+
+    result = HIViewAddSubview(parent, imageView);
+    SkASSERT(result == noErr);
+
+    fHVIEW = imageView;
+
+    HIViewSetVisible(imageView, true);
+    HIViewPlaceInSuperviewAt(imageView, 0, 0);
+
+    if (true) {
+        HILayoutInfo layout;
+        layout.version = kHILayoutInfoVersionZero;
+        set_bindingside(&layout.binding.left, parent, kHILayoutBindLeft);
+        set_bindingside(&layout.binding.top, parent, kHILayoutBindTop);
+        set_bindingside(&layout.binding.right, parent, kHILayoutBindRight);
+        set_bindingside(&layout.binding.bottom, parent, kHILayoutBindBottom);
+        set_axisscale(&layout.scale.x, parent);
+        set_axisscale(&layout.scale.y, parent);
+        set_axisposition(&layout.position.x, parent, kHILayoutPositionLeft);
+        set_axisposition(&layout.position.y, rootView, kHILayoutPositionTop);
+        HIViewSetLayoutInfo(imageView, &layout);
+    }
+
+    HIImageViewSetOpaque(imageView, true);
+    HIImageViewSetScaleToFit(imageView, false);
+
+    static const EventTypeSpec  gTypes[] = {
+        { kEventClassKeyboard,  kEventRawKeyDown            },
+        { kEventClassKeyboard,  kEventRawKeyUp              },
+        { kEventClassMouse,        kEventMouseDown                },
+        { kEventClassMouse,        kEventMouseDragged            },
+        { kEventClassMouse,        kEventMouseMoved            },
+        { kEventClassMouse,        kEventMouseUp                },
+        { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent   },
+        { kEventClassWindow,    kEventWindowBoundsChanged    },
+//        { kEventClassWindow,    kEventWindowDrawContent        },
+        { SK_MacEventClass,        SK_MacEventKind                }
+    };
+
+    EventHandlerUPP handlerUPP = NewEventHandlerUPP(SkOSWindow::EventHandler);
+    int                count = SK_ARRAY_COUNT(gTypes);
+
+    result = InstallEventHandler(GetWindowEventTarget(wr), handlerUPP,
+                        count, gTypes, this, nil);
+    SkASSERT(result == noErr);
+
+    gCurrOSWin = this;
+    gCurrEventQ = GetCurrentEventQueue();
+    gEventTarget = GetWindowEventTarget(wr);
+
+    static bool gOnce = true;
+    if (gOnce) {
+        gOnce = false;
+        gPrevNewHandler = set_new_handler(sk_new_handler);
+    }
+}
+
+void SkOSWindow::doPaint(void* ctx)
+{
+#if 0
+    this->update(NULL);
+
+    const SkBitmap& bm = this->getBitmap();
+    CGImageRef img = SkCreateCGImageRef(bm);
+
+    if (img) {
+        CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
+
+        CGContextRef cg = reinterpret_cast<CGContextRef>(ctx);
+
+        CGContextSaveGState(cg);
+        CGContextTranslateCTM(cg, 0, r.size.height);
+        CGContextScaleCTM(cg, 1, -1);
+
+        CGContextDrawImage(cg, r, img);
+
+        CGContextRestoreGState(cg);
+
+        CGImageRelease(img);
+    }
+#endif
+}
+
+void SkOSWindow::updateSize()
+{
+    Rect    r;
+
+    GetWindowBounds((WindowRef)fHWND, kWindowContentRgn, &r);
+    this->resize(r.right - r.left, r.bottom - r.top);
+
+#if 0
+    HIRect    frame;
+    HIViewRef imageView = (HIViewRef)getHVIEW();
+    HIViewRef parent = HIViewGetSuperview(imageView);
+
+    HIViewGetBounds(imageView, &frame);
+    SkDebugf("------ %d bounds %g %g %g %g\n", r.right - r.left,
+             frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
+#endif
+}
+
+void SkOSWindow::onHandleInval(const SkIRect& r)
+{
+    (new SkEvent("inval-imageview", this->getSinkID()))->post();
+}
+
+bool SkOSWindow::onEvent(const SkEvent& evt) {
+    if (evt.isType("inval-imageview")) {
+        this->update(NULL);
+
+        SkEvent query("ignore-window-bitmap");
+        if (!this->doQuery(&query) || !query.getFast32()) {
+            const SkBitmap& bm = this->getBitmap();
+
+            CGImageRef img = SkCreateCGImageRef(bm);
+            HIImageViewSetImage((HIViewRef)getHVIEW(), img);
+            CGImageRelease(img);
+        }
+        return true;
+    }
+    return INHERITED::onEvent(evt);
+}
+
+void SkOSWindow::onSetTitle(const char title[])
+{
+    CFStringRef str = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8);
+    SetWindowTitleWithCFString((WindowRef)fHWND, str);
+    CFRelease(str);
+}
+
+void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
+{
+}
+
+static void getparam(EventRef inEvent, OSType name, OSType type, UInt32 size, void* data)
+{
+    EventParamType  actualType;
+    UInt32            actualSize;
+    OSStatus        status;
+
+    status = GetEventParameter(inEvent, name, type, &actualType, size, &actualSize, data);
+    SkASSERT(status == noErr);
+    SkASSERT(actualType == type);
+    SkASSERT(actualSize == size);
+}
+
+enum {
+    SK_MacReturnKey        = 36,
+    SK_MacDeleteKey        = 51,
+    SK_MacEndKey        = 119,
+    SK_MacLeftKey        = 123,
+    SK_MacRightKey        = 124,
+    SK_MacDownKey        = 125,
+    SK_MacUpKey            = 126,
+
+    SK_Mac0Key          = 0x52,
+    SK_Mac1Key          = 0x53,
+    SK_Mac2Key          = 0x54,
+    SK_Mac3Key          = 0x55,
+    SK_Mac4Key          = 0x56,
+    SK_Mac5Key          = 0x57,
+    SK_Mac6Key          = 0x58,
+    SK_Mac7Key          = 0x59,
+    SK_Mac8Key          = 0x5b,
+    SK_Mac9Key          = 0x5c
+};
+
+static SkKey raw2key(UInt32 raw)
+{
+    static const struct {
+        UInt32  fRaw;
+        SkKey   fKey;
+    } gKeys[] = {
+        { SK_MacUpKey,        kUp_SkKey        },
+        { SK_MacDownKey,    kDown_SkKey        },
+        { SK_MacLeftKey,    kLeft_SkKey        },
+        { SK_MacRightKey,   kRight_SkKey    },
+        { SK_MacReturnKey,  kOK_SkKey        },
+        { SK_MacDeleteKey,  kBack_SkKey        },
+        { SK_MacEndKey,        kEnd_SkKey        },
+        { SK_Mac0Key,       k0_SkKey        },
+        { SK_Mac1Key,       k1_SkKey        },
+        { SK_Mac2Key,       k2_SkKey        },
+        { SK_Mac3Key,       k3_SkKey        },
+        { SK_Mac4Key,       k4_SkKey        },
+        { SK_Mac5Key,       k5_SkKey        },
+        { SK_Mac6Key,       k6_SkKey        },
+        { SK_Mac7Key,       k7_SkKey        },
+        { SK_Mac8Key,       k8_SkKey        },
+        { SK_Mac9Key,       k9_SkKey        }
+    };
+
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++)
+        if (gKeys[i].fRaw == raw)
+            return gKeys[i].fKey;
+    return kNONE_SkKey;
+}
+
+static void post_skmacevent()
+{
+    EventRef    ref;
+    OSStatus    status = CreateEvent(nil, SK_MacEventClass, SK_MacEventKind, 0, 0, &ref);
+    SkASSERT(status == noErr);
+
+#if 0
+    status = SetEventParameter(ref, SK_MacEventParamName, SK_MacEventParamName, sizeof(evt), &evt);
+    SkASSERT(status == noErr);
+    status = SetEventParameter(ref, SK_MacEventSinkIDParamName, SK_MacEventSinkIDParamName, sizeof(sinkID), &sinkID);
+    SkASSERT(status == noErr);
+#endif
+
+    EventTargetRef target = gEventTarget;
+    SetEventParameter(ref, kEventParamPostTarget, typeEventTargetRef, sizeof(target), &target);
+    SkASSERT(status == noErr);
+
+    status = PostEventToQueue(gCurrEventQ, ref, kEventPriorityStandard);
+    SkASSERT(status == noErr);
+
+    ReleaseEvent(ref);
+}
+
+pascal OSStatus SkOSWindow::EventHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData )
+{
+    SkOSWindow* win = (SkOSWindow*)userData;
+    OSStatus    result = eventNotHandledErr;
+    UInt32        wClass = GetEventClass(inEvent);
+    UInt32        wKind = GetEventKind(inEvent);
+
+    gCurrOSWin = win;    // will need to be in TLS. Set this so PostEvent will work
+
+    switch (wClass) {
+        case kEventClassMouse: {
+            Point   pt;
+            getparam(inEvent, kEventParamMouseLocation, typeQDPoint, sizeof(pt), &pt);
+            SetPortWindowPort((WindowRef)win->getHWND());
+            GlobalToLocal(&pt);
+
+            switch (wKind) {
+                case kEventMouseDown:
+                    if (win->handleClick(pt.h, pt.v, Click::kDown_State)) {
+                        result = noErr;
+                    }
+                    break;
+                case kEventMouseMoved:
+                    // fall through
+                case kEventMouseDragged:
+                    (void)win->handleClick(pt.h, pt.v, Click::kMoved_State);
+                  //  result = noErr;
+                    break;
+                case kEventMouseUp:
+                    (void)win->handleClick(pt.h, pt.v, Click::kUp_State);
+                  //  result = noErr;
+                    break;
+                default:
+                    break;
+            }
+            break;
+        }
+        case kEventClassKeyboard:
+            if (wKind == kEventRawKeyDown) {
+                UInt32  raw;
+                getparam(inEvent, kEventParamKeyCode, typeUInt32, sizeof(raw), &raw);
+                SkKey key = raw2key(raw);
+                if (key != kNONE_SkKey)
+                    (void)win->handleKey(key);
+            } else if (wKind == kEventRawKeyUp) {
+                UInt32 raw;
+                getparam(inEvent, kEventParamKeyCode, typeUInt32, sizeof(raw), &raw);
+                SkKey key = raw2key(raw);
+                if (key != kNONE_SkKey)
+                    (void)win->handleKeyUp(key);
+            }
+            break;
+        case kEventClassTextInput:
+            if (wKind == kEventTextInputUnicodeForKeyEvent) {
+                UInt16  uni;
+                getparam(inEvent, kEventParamTextInputSendText, typeUnicodeText, sizeof(uni), &uni);
+                win->handleChar(uni);
+            }
+            break;
+        case kEventClassWindow:
+            switch (wKind) {
+                case kEventWindowBoundsChanged:
+                    win->updateSize();
+                    break;
+                case kEventWindowDrawContent: {
+                    CGContextRef cg;
+                    result = GetEventParameter(inEvent,
+                                               kEventParamCGContextRef,
+                                               typeCGContextRef,
+                                               NULL,
+                                               sizeof (CGContextRef),
+                                               NULL,
+                                               &cg);
+                    if (result != 0) {
+                        cg = NULL;
+                    }
+                    win->doPaint(cg);
+                    break;
+                }
+                default:
+                    break;
+            }
+            break;
+        case SK_MacEventClass: {
+            SkASSERT(wKind == SK_MacEventKind);
+            if (SkEvent::ProcessEvent()) {
+                    post_skmacevent();
+            }
+    #if 0
+            SkEvent*        evt;
+            SkEventSinkID    sinkID;
+            getparam(inEvent, SK_MacEventParamName, SK_MacEventParamName, sizeof(evt), &evt);
+            getparam(inEvent, SK_MacEventSinkIDParamName, SK_MacEventSinkIDParamName, sizeof(sinkID), &sinkID);
+    #endif
+            result = noErr;
+            break;
+        }
+        default:
+            break;
+    }
+    if (result == eventNotHandledErr) {
+        result = CallNextEventHandler(inHandler, inEvent);
+    }
+    return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void SkEvent::SignalNonEmptyQueue()
+{
+    post_skmacevent();
+//    SkDebugf("signal nonempty\n");
+}
+
+static TMTask    gTMTaskRec;
+static TMTask*    gTMTaskPtr;
+
+static void sk_timer_proc(TMTask* rec)
+{
+    SkEvent::ServiceQueueTimer();
+//    SkDebugf("timer task fired\n");
+}
+
+void SkEvent::SignalQueueTimer(SkMSec delay)
+{
+    if (gTMTaskPtr)
+    {
+        RemoveTimeTask((QElem*)gTMTaskPtr);
+        DisposeTimerUPP(gTMTaskPtr->tmAddr);
+        gTMTaskPtr = nil;
+    }
+    if (delay)
+    {
+        gTMTaskPtr = &gTMTaskRec;
+        memset(gTMTaskPtr, 0, sizeof(gTMTaskRec));
+        gTMTaskPtr->tmAddr = NewTimerUPP(sk_timer_proc);
+        OSErr err = InstallTimeTask((QElem*)gTMTaskPtr);
+//        SkDebugf("installtimetask of %d returned %d\n", delay, err);
+        PrimeTimeTask((QElem*)gTMTaskPtr, delay);
+    }
+}
+
+#define USE_MSAA 0
+
+AGLContext create_gl(WindowRef wref)
+{
+    GLint major, minor;
+    AGLContext ctx;
+
+    aglGetVersion(&major, &minor);
+    SkDebugf("---- agl version %d %d\n", major, minor);
+
+    const GLint pixelAttrs[] = {
+        AGL_RGBA,
+        AGL_STENCIL_SIZE, 8,
+#if USE_MSAA
+        AGL_SAMPLE_BUFFERS_ARB, 1,
+        AGL_MULTISAMPLE,
+        AGL_SAMPLES_ARB, 8,
+#endif
+        AGL_ACCELERATED,
+        AGL_DOUBLEBUFFER,
+        AGL_NONE
+    };
+    AGLPixelFormat format = aglChoosePixelFormat(NULL, 0, pixelAttrs);
+    //AGLPixelFormat format = aglCreatePixelFormat(pixelAttrs);
+    SkDebugf("----- agl format %p\n", format);
+    ctx = aglCreateContext(format, NULL);
+    SkDebugf("----- agl context %p\n", ctx);
+    aglDestroyPixelFormat(format);
+
+    static const GLint interval = 1;
+    aglSetInteger(ctx, AGL_SWAP_INTERVAL, &interval);
+    aglSetCurrentContext(ctx);
+    return ctx;
+}
+
+bool SkOSWindow::attach(SkBackEndTypes /* attachType */)
+{
+    if (NULL == fAGLCtx) {
+        fAGLCtx = create_gl((WindowRef)fHWND);
+        if (NULL == fAGLCtx) {
+            return false;
+        }
+    }
+
+    GLboolean success = true;
+
+    int width, height;
+
+    success = aglSetWindowRef((AGLContext)fAGLCtx, (WindowRef)fHWND);
+    width = this->width();
+    height = this->height();
+
+    GLenum err = aglGetError();
+    if (err) {
+        SkDebugf("---- aglSetWindowRef %d %d %s [%d %d]\n", success, err,
+                 aglErrorString(err), width, height);
+    }
+
+    if (success) {
+        glViewport(0, 0, width, height);
+        glClearColor(0, 0, 0, 0);
+        glClearStencil(0);
+        glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+    }
+    return success;
+}
+
+void SkOSWindow::detach() {
+    aglSetWindowRef((AGLContext)fAGLCtx, NULL);
+}
+
+void SkOSWindow::present() {
+    aglSwapBuffers((AGLContext)fAGLCtx);
+}
+
+#endif
diff --git a/src/views/mac/SkOSWindow_Mac.mm b/src/views/mac/SkOSWindow_Mac.mm
new file mode 100644
index 0000000..a288ae3
--- /dev/null
+++ b/src/views/mac/SkOSWindow_Mac.mm
@@ -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.
+ */
+
+#if defined(SK_BUILD_FOR_MAC)
+
+#import  <Cocoa/Cocoa.h>
+#include "SkOSWindow_Mac.h"
+#include "SkOSMenu.h"
+#include "SkTypes.h"
+#include "SkWindow.h"
+#import  "SkNSView.h"
+#import  "SkEventNotifier.h"
+#define  kINVAL_NSVIEW_EventType "inval-nsview"
+
+SK_COMPILE_ASSERT(SK_SUPPORT_GPU, not_implemented_for_non_gpu_build);
+
+SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd) {
+    fInvalEventIsPending = false;
+    fGLContext = NULL;
+    fNotifier = [[SkEventNotifier alloc] init];
+}
+SkOSWindow::~SkOSWindow() {
+    [(SkEventNotifier*)fNotifier release];
+}
+
+void SkOSWindow::onHandleInval(const SkIRect& r) {
+    if (!fInvalEventIsPending) {
+        fInvalEventIsPending = true;
+        (new SkEvent(kINVAL_NSVIEW_EventType, this->getSinkID()))->post();
+    }
+}
+
+bool SkOSWindow::onEvent(const SkEvent& evt) {
+    if (evt.isType(kINVAL_NSVIEW_EventType)) {
+        fInvalEventIsPending = false;
+        const SkIRect& r = this->getDirtyBounds();
+        [(SkNSView*)fHWND postInvalWithRect:&r];
+        [(NSOpenGLContext*)fGLContext update];
+        return true;
+    }
+    if ([(SkNSView*)fHWND onHandleEvent:evt]) {
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
+}
+
+bool SkOSWindow::onDispatchClick(int x, int y, Click::State state, void* owner) {
+    return this->INHERITED::onDispatchClick(x, y, state, owner);
+}
+
+void SkOSWindow::onSetTitle(const char title[]) {
+    [(SkNSView*)fHWND setSkTitle:title];
+}
+
+void SkOSWindow::onAddMenu(const SkOSMenu* menu) {
+    [(SkNSView*)fHWND onAddMenu:menu];
+}
+
+void SkOSWindow::onUpdateMenu(const SkOSMenu* menu) {
+    [(SkNSView*)fHWND onUpdateMenu:menu];
+}
+
+bool SkOSWindow::attach(SkBackEndTypes attachType, int sampleCount) {
+    return [(SkNSView*)fHWND attach:attachType withMSAASampleCount:sampleCount];
+}
+
+void SkOSWindow::detach() {
+    [(SkNSView*)fHWND detach];
+}
+
+void SkOSWindow::present() {
+    [(SkNSView*)fHWND present];
+}
+
+#endif
diff --git a/src/views/mac/SkOptionsTableView.h b/src/views/mac/SkOptionsTableView.h
new file mode 100644
index 0000000..1f9b36a
--- /dev/null
+++ b/src/views/mac/SkOptionsTableView.h
@@ -0,0 +1,40 @@
+
+/*
+ * 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 <Cocoa/Cocoa.h>
+#import "SkNSView.h"
+#import "SkOSMenu.h"
+#import "SkEvent.h"
+@interface SkOptionItem : NSObject {
+    NSCell* fCell;
+    const SkOSMenu::Item* fItem;
+}
+@property (nonatomic, assign) const SkOSMenu::Item* fItem;
+@property (nonatomic, retain) NSCell* fCell;
+@end
+
+@interface SkOptionsTableView : NSTableView <SkNSViewOptionsDelegate, NSTableViewDelegate, NSTableViewDataSource> {
+    NSMutableArray* fItems;
+    const SkTDArray<SkOSMenu*>* fMenus;
+    BOOL fShowKeys;
+}
+@property (nonatomic, retain) NSMutableArray* fItems;
+
+- (void)registerMenus:(const SkTDArray<SkOSMenu*>*)menus;
+- (void)updateMenu:(const SkOSMenu*)menu;
+- (void)loadMenu:(const SkOSMenu*)menu;
+- (IBAction)toggleKeyEquivalents:(id)sender;
+
+- (NSCell*)createAction;
+- (NSCell*)createList:(NSArray*)items current:(int)index;
+- (NSCell*)createSlider:(float)value min:(float)min max:(float)max;
+- (NSCell*)createSwitch:(BOOL)state;
+- (NSCell*)createTextField:(NSString*)placeHolder;
+- (NSCell*)createTriState:(NSCellStateValue)state;
+
+@end
diff --git a/src/views/mac/SkOptionsTableView.mm b/src/views/mac/SkOptionsTableView.mm
new file mode 100644
index 0000000..302e66e
--- /dev/null
+++ b/src/views/mac/SkOptionsTableView.mm
@@ -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.
+ */
+
+#import "SkOptionsTableView.h"
+#import "SkTextFieldCell.h"
+@implementation SkOptionItem
+@synthesize fCell, fItem;
+- (void)dealloc {
+    [fCell release];
+    [super dealloc];
+}
+@end
+
+@implementation SkOptionsTableView
+@synthesize fItems;
+
+- (id)initWithCoder:(NSCoder*)coder {
+    if ((self = [super initWithCoder:coder])) {
+        self.dataSource = self;
+        self.delegate = self;
+        fMenus = NULL;
+        fShowKeys = YES;
+        [self setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone];
+        self.fItems = [NSMutableArray array];
+    }
+    return self;
+}
+
+- (void)dealloc {
+    self.fItems = nil;
+    [super dealloc];
+}
+
+- (void) view:(SkNSView*)view didAddMenu:(const SkOSMenu*)menu {}
+- (void) view:(SkNSView*)view didUpdateMenu:(const SkOSMenu*)menu {
+    [self updateMenu:menu];
+}
+
+- (IBAction)toggleKeyEquivalents:(id)sender {
+    fShowKeys = !fShowKeys;
+    NSMenuItem* item = (NSMenuItem*)sender;
+    [item setState:fShowKeys];
+    [self reloadData];
+}
+
+- (void)registerMenus:(const SkTDArray<SkOSMenu*>*)menus {
+    fMenus = menus;
+    for (int i = 0; i < fMenus->count(); ++i) {
+        [self loadMenu:(*fMenus)[i]];
+    }
+}
+
+- (void)updateMenu:(SkOSMenu*)menu {
+    // the first menu is always assumed to be the static, the second is 
+    // repopulated every time over and over again 
+    int menuIndex = fMenus->find(menu);
+    if (menuIndex >= 0 && menuIndex < fMenus->count()) {
+        NSUInteger first = 0;
+        for (NSInteger i = 0; i < menuIndex; ++i) {
+            first += (*fMenus)[i]->getCount();
+        }
+        [fItems removeObjectsInRange:NSMakeRange(first, [fItems count] - first)];
+        [self loadMenu:menu];
+    }
+    [self reloadData];
+}
+
+- (NSCellStateValue)triStateToNSState:(SkOSMenu::TriState)state {
+    if (SkOSMenu::kOnState == state)
+        return NSOnState;
+    else if (SkOSMenu::kOffState == state)
+        return NSOffState;
+    else
+        return NSMixedState;
+}
+
+- (void)loadMenu:(const SkOSMenu*)menu {
+    const SkOSMenu::Item* menuitems[menu->getCount()];
+    menu->getItems(menuitems);
+    for (int i = 0; i < menu->getCount(); ++i) {
+        const SkOSMenu::Item* item = menuitems[i];
+        SkOptionItem* option = [[SkOptionItem alloc] init];
+        option.fItem = item;
+        
+        if (SkOSMenu::kList_Type == item->getType()) {
+            int index = 0, count = 0;
+            SkOSMenu::FindListItemCount(*item->getEvent(), &count);
+            NSMutableArray* optionstrs = [[NSMutableArray alloc] initWithCapacity:count];
+            SkAutoTDeleteArray<SkString> ada(new SkString[count]);
+            SkString* options = ada.get();
+            SkOSMenu::FindListItems(*item->getEvent(), options);
+            for (int i = 0; i < count; ++i)
+                [optionstrs addObject:[NSString stringWithUTF8String:options[i].c_str()]];
+            SkOSMenu::FindListIndex(*item->getEvent(), item->getSlotName(), &index);
+            option.fCell = [self createList:optionstrs current:index];
+            [optionstrs release];
+        }
+        else {
+            bool state = false;
+            SkString str;
+            SkOSMenu::TriState tristate;
+            switch (item->getType()) {
+                case SkOSMenu::kAction_Type:
+                    option.fCell = [self createAction];
+                    break;
+                case SkOSMenu::kSlider_Type:
+                    SkScalar min, max, value;
+                    SkOSMenu::FindSliderValue(*item->getEvent(), item->getSlotName(), &value);
+                    SkOSMenu::FindSliderMin(*item->getEvent(), &min);
+                    SkOSMenu::FindSliderMax(*item->getEvent(), &max);
+                    option.fCell = [self createSlider:value 
+                                                  min:min 
+                                                  max:max];
+                    break;                    
+                case SkOSMenu::kSwitch_Type:
+                    SkOSMenu::FindSwitchState(*item->getEvent(), item->getSlotName(), &state);
+                    option.fCell = [self createSwitch:(BOOL)state];
+                    break;
+                case SkOSMenu::kTriState_Type:
+                    SkOSMenu::FindTriState(*item->getEvent(), item->getSlotName(), &tristate);
+                    option.fCell = [self createTriState:[self triStateToNSState:tristate]];
+                    break;
+                case SkOSMenu::kTextField_Type:
+                    SkOSMenu::FindText(*item->getEvent(),item->getSlotName(), &str);
+                    option.fCell = [self createTextField:[NSString stringWithUTF8String:str.c_str()]];
+                    break;
+                default:
+                    break;
+            }
+        }
+        [fItems addObject:option];
+        [option release];
+    }
+}
+
+- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
+    return [self.fItems count];
+}
+
+- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
+    int columnIndex = [tableView columnWithIdentifier:[tableColumn identifier]];
+    if (columnIndex == 0) {
+        const SkOSMenu::Item* item = ((SkOptionItem*)[fItems objectAtIndex:row]).fItem;
+        NSString* label = [NSString stringWithUTF8String:item->getLabel()];
+        if (fShowKeys) 
+            return [NSString stringWithFormat:@"%@ (%c)", label, item->getKeyEquivalent()];
+        else 
+            return label;
+    }
+    else
+        return nil;
+}
+
+- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
+    if (tableColumn) {
+        int columnIndex = [tableView columnWithIdentifier:[tableColumn identifier]];
+        if (columnIndex == 1) 
+            return [((SkOptionItem*)[fItems objectAtIndex:row]).fCell copy];
+        else
+            return [[[SkTextFieldCell alloc] init] autorelease];
+    }
+    return nil;
+}
+
+- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
+    int columnIndex = [tableView columnWithIdentifier:[tableColumn identifier]];
+    if (columnIndex == 1) {
+        SkOptionItem* option = (SkOptionItem*)[self.fItems objectAtIndex:row];
+        NSCell* storedCell = option.fCell;
+        const SkOSMenu::Item* item = option.fItem;
+        switch (item->getType()) {
+            case SkOSMenu::kAction_Type:
+                break;                
+            case SkOSMenu::kList_Type:
+                [cell selectItemAtIndex:[(NSPopUpButtonCell*)storedCell indexOfSelectedItem]];
+                break;
+            case SkOSMenu::kSlider_Type:
+                [cell setFloatValue:[storedCell floatValue]];
+                break;
+            case SkOSMenu::kSwitch_Type:
+                [cell setState:[(NSButtonCell*)storedCell state]];
+                break;
+            case SkOSMenu::kTextField_Type:
+                if ([[storedCell stringValue] length] > 0)
+                    [cell setStringValue:[storedCell stringValue]];
+                break;
+            case SkOSMenu::kTriState_Type:
+                [cell setState:[(NSButtonCell*)storedCell state]];
+                break;
+            default:
+                break;
+        }
+    }
+    else {
+        [(SkTextFieldCell*)cell setEditable:NO];
+    }
+}
+
+- (void)tableView:(NSTableView *)tableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
+    int columnIndex = [tableView columnWithIdentifier:[tableColumn identifier]];
+    if (columnIndex == 1) {
+        SkOptionItem* option = (SkOptionItem*)[self.fItems objectAtIndex:row];
+        NSCell* cell = option.fCell;
+        const SkOSMenu::Item* item = option.fItem;
+        switch (item->getType()) {
+            case SkOSMenu::kAction_Type:
+                item->postEvent();
+                break;
+            case SkOSMenu::kList_Type:
+                [(NSPopUpButtonCell*)cell selectItemAtIndex:[anObject intValue]];
+                item->setInt([anObject intValue]);
+                break;
+            case SkOSMenu::kSlider_Type:
+                [cell setFloatValue:[anObject floatValue]];
+                item->setScalar([anObject floatValue]);
+                break;
+            case SkOSMenu::kSwitch_Type:
+                [cell setState:[anObject boolValue]];
+                item->setBool([anObject boolValue]);
+                break;
+            case SkOSMenu::kTextField_Type:
+                if ([anObject length] > 0) {
+                    [cell setStringValue:anObject];
+                    item->setString([anObject UTF8String]);
+                }
+                break;
+            case SkOSMenu::kTriState_Type:
+                [cell setState:[anObject intValue]];
+                item->setTriState((SkOSMenu::TriState)[anObject intValue]);
+                break;
+            default:
+                break;
+        }
+        item->postEvent();
+    }
+}
+
+- (NSCell*)createAction{
+    NSButtonCell* cell = [[[NSButtonCell alloc] init] autorelease];
+    [cell setTitle:@""];
+    [cell setButtonType:NSMomentaryPushInButton];
+    [cell setBezelStyle:NSSmallSquareBezelStyle];
+    return cell;
+}
+
+- (NSCell*)createList:(NSArray*)items current:(int)index {
+    NSPopUpButtonCell* cell = [[[NSPopUpButtonCell alloc] init] autorelease];
+    [cell addItemsWithTitles:items];
+    [cell selectItemAtIndex:index];
+    [cell setArrowPosition:NSPopUpArrowAtBottom];
+    [cell setBezelStyle:NSSmallSquareBezelStyle];
+    return cell; 
+}
+
+- (NSCell*)createSlider:(float)value min:(float)min max:(float)max {
+    NSSliderCell* cell = [[[NSSliderCell alloc] init] autorelease];
+    [cell setFloatValue:value];
+    [cell setMinValue:min];
+    [cell setMaxValue:max];
+    return cell;
+}
+
+- (NSCell*)createSwitch:(BOOL)state {
+    NSButtonCell* cell = [[[NSButtonCell alloc] init] autorelease];
+    [cell setState:state];
+    [cell setTitle:@""];
+    [cell setButtonType:NSSwitchButton];
+    return cell;
+}
+
+- (NSCell*)createTextField:(NSString*)placeHolder; {
+    SkTextFieldCell* cell = [[[SkTextFieldCell alloc] init] autorelease];
+    [cell setEditable:YES];
+    [cell setStringValue:@""];
+    [cell setPlaceholderString:placeHolder];
+    return cell;
+}
+
+- (NSCell*)createTriState:(NSCellStateValue)state {
+    NSButtonCell* cell = [[[NSButtonCell alloc] init] autorelease];
+    [cell setAllowsMixedState:TRUE];
+    [cell setTitle:@""];
+    [cell setState:(NSInteger)state];
+    [cell setButtonType:NSSwitchButton];
+    return cell;
+}
+@end
diff --git a/src/views/mac/SkSampleNSView.h b/src/views/mac/SkSampleNSView.h
new file mode 100644
index 0000000..d3aca9a
--- /dev/null
+++ b/src/views/mac/SkSampleNSView.h
@@ -0,0 +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.
+ */
+
+#import "SkNSView.h"
+@interface SkSampleNSView : SkNSView
+- (id)initWithDefaults;
+@end
diff --git a/src/views/mac/SkSampleNSView.mm b/src/views/mac/SkSampleNSView.mm
new file mode 100644
index 0000000..ce5f8aa
--- /dev/null
+++ b/src/views/mac/SkSampleNSView.mm
@@ -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.
+ */
+
+#import "SkSampleNSView.h"
+#include "SampleApp.h"
+#include <crt_externs.h>
+@implementation SkSampleNSView
+
+- (id)initWithDefaults {
+    if ((self = [super initWithDefaults])) {
+        fWind = new SampleWindow(self, *_NSGetArgc(), *_NSGetArgv(), NULL);
+    }
+    return self;
+}
+
+- (void)dealloc {
+    delete fWind;
+    [super dealloc];
+}
+
+- (void)swipeWithEvent:(NSEvent *)event {
+    CGFloat x = [event deltaX];
+    if (x < 0)
+        ((SampleWindow*)fWind)->previousSample();
+    else if (x > 0)
+        ((SampleWindow*)fWind)->nextSample();
+    else
+        ((SampleWindow*)fWind)->showOverview();
+}
+
+@end
diff --git a/src/views/mac/SkTextFieldCell.h b/src/views/mac/SkTextFieldCell.h
new file mode 100644
index 0000000..93d0e4d
--- /dev/null
+++ b/src/views/mac/SkTextFieldCell.h
@@ -0,0 +1,15 @@
+
+/*
+ * 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 <Cocoa/Cocoa.h>
+//A text field cell that has vertically centered text
+@interface SkTextFieldCell : NSTextFieldCell {
+    BOOL selectingOrEditing;
+}
+@end
diff --git a/src/views/mac/SkTextFieldCell.m b/src/views/mac/SkTextFieldCell.m
new file mode 100644
index 0000000..7aa094d
--- /dev/null
+++ b/src/views/mac/SkTextFieldCell.m
@@ -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.
+ */
+ 
+#import "SkTextFieldCell.h"
+@implementation SkTextFieldCell
+- (NSRect)drawingRectForBounds:(NSRect)theRect {
+    NSRect newRect = [super drawingRectForBounds:theRect];
+    if (selectingOrEditing == NO) {
+        NSSize textSize = [self cellSizeForBounds:theRect];
+        float heightDelta = newRect.size.height - textSize.height;      
+        if (heightDelta > 0) {
+            newRect.size.height -= heightDelta;
+            newRect.origin.y += (heightDelta / 2);
+        }
+    }
+    return newRect;
+}
+
+- (void)selectWithFrame:(NSRect)aRect 
+                 inView:(NSView *)controlView 
+                 editor:(NSText *)textObj 
+               delegate:(id)anObject 
+                  start:(int)selStart 
+                 length:(int)selLength {
+	aRect = [self drawingRectForBounds:aRect];
+	selectingOrEditing = YES;	
+	[super selectWithFrame:aRect 
+                    inView:controlView 
+                    editor:textObj 
+                  delegate:anObject 
+                     start:selStart 
+                    length:selLength];
+	selectingOrEditing = NO;
+}
+
+- (void)editWithFrame:(NSRect)aRect 
+               inView:(NSView *)controlView 
+               editor:(NSText *)textObj 
+             delegate:(id)anObject 
+                event:(NSEvent *)theEvent {	
+	aRect = [self drawingRectForBounds:aRect];
+	selectingOrEditing = YES;
+	[super editWithFrame:aRect 
+                  inView:controlView 
+                  editor:textObj 
+                delegate:anObject 
+                   event:theEvent];
+	selectingOrEditing = NO;
+}
+
+@end
diff --git a/src/views/mac/skia_mac.mm b/src/views/mac/skia_mac.mm
new file mode 100644
index 0000000..e3c31c4
--- /dev/null
+++ b/src/views/mac/skia_mac.mm
@@ -0,0 +1,20 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#import <Cocoa/Cocoa.h>
+#include "SkApplication.h"
+
+int main(int argc, char *argv[]) {
+    signal(SIGPIPE, SIG_IGN);
+    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+    application_init();
+    int retVal =  NSApplicationMain(argc, (const char **)argv);
+    application_term();
+    [pool release];
+    return retVal;
+}
diff --git a/src/views/sdl/SkOSWindow_SDL.cpp b/src/views/sdl/SkOSWindow_SDL.cpp
new file mode 100644
index 0000000..4edc5ac
--- /dev/null
+++ b/src/views/sdl/SkOSWindow_SDL.cpp
@@ -0,0 +1,227 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkOSWindow_SDL.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkGLCanvas.h"
+#include "SkOSMenu.h"
+#include "SkTime.h"
+
+static void post_SkEvent_event() {
+    SDL_Event evt;
+    evt.type = SDL_USEREVENT;
+    evt.user.type = SDL_USEREVENT;
+    evt.user.code = 0;
+    evt.user.data1 = NULL;
+    evt.user.data2 = NULL;
+    SDL_PushEvent(&evt);
+}
+
+static bool skia_setBitmapFromSurface(SkBitmap* dst, SDL_Surface* src) {
+    SkBitmap::Config config;
+
+    switch (src->format->BytesPerPixel) {
+        case 2:
+            config = SkBitmap::kRGB_565_Config;
+            break;
+        case 4:
+            config = SkBitmap::kARGB_8888_Config;
+            break;
+        default:
+            return false;
+    }
+
+    dst->setConfig(config, src->w, src->h, src->pitch);
+    dst->setPixels(src->pixels);
+    return true;
+}
+
+SkOSWindow::SkOSWindow(void* screen) {
+    fScreen = reinterpret_cast<SDL_Surface*>(screen);
+    this->resize(fScreen->w, fScreen->h);
+
+    uint32_t rmask = SK_R32_MASK << SK_R32_SHIFT;
+    uint32_t gmask = SK_G32_MASK << SK_G32_SHIFT;
+    uint32_t bmask = SK_B32_MASK << SK_B32_SHIFT;
+    uint32_t amask = SK_A32_MASK << SK_A32_SHIFT;
+
+    if (fScreen->flags & SDL_OPENGL) {
+        fSurface = NULL;
+        fGLCanvas = new SkGLCanvas;
+        fGLCanvas->setViewport(fScreen->w, fScreen->h);
+    } else {
+        fGLCanvas = NULL;
+        fSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, fScreen->w, fScreen->h,
+                                        32, rmask, gmask, bmask, amask);
+    }
+}
+
+SkOSWindow::~SkOSWindow() {
+    delete fGLCanvas;
+    if (fSurface) {
+        SDL_FreeSurface(fSurface);
+    }
+}
+
+#include <OpenGL/gl.h>
+
+void SkOSWindow::doDraw() {
+    if (fGLCanvas) {
+        glEnable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
+        glEnable(GL_TEXTURE_2D);
+        glClearColor(0, 0, 0, 0);
+        glClear(GL_COLOR_BUFFER_BIT);
+
+        int count = fGLCanvas->save();
+        this->draw(fGLCanvas);
+        fGLCanvas->restoreToCount(count);
+        SDL_GL_SwapBuffers( );
+    } else {
+        if ( SDL_MUSTLOCK(fSurface) ) {
+            if ( SDL_LockSurface(fSurface) < 0 ) {
+                return;
+            }
+        }
+
+        SkBitmap bitmap;
+
+        if (skia_setBitmapFromSurface(&bitmap, fSurface)) {
+            SkCanvas canvas(bitmap);
+            this->draw(&canvas);
+        }
+
+        if ( SDL_MUSTLOCK(fSurface) ) {
+            SDL_UnlockSurface(fSurface);
+        }
+
+        int result = SDL_BlitSurface(fSurface, NULL, fScreen, NULL);
+        if (result) {
+            SkDebugf("------- SDL_BlitSurface returned %d\n", result);
+        }
+        SDL_UpdateRect(fScreen, 0, 0, fScreen->w, fScreen->h);
+    }
+}
+
+static SkKey find_skkey(SDLKey src) {
+    // this array must match the enum order in SkKey.h
+    static const SDLKey gKeys[] = {
+        SDLK_UNKNOWN,
+        SDLK_UNKNOWN,   // left softkey
+        SDLK_UNKNOWN,   // right softkey
+        SDLK_UNKNOWN,   // home
+        SDLK_UNKNOWN,   // back
+        SDLK_UNKNOWN,   // send
+        SDLK_UNKNOWN,   // end
+        SDLK_0,
+        SDLK_1,
+        SDLK_2,
+        SDLK_3,
+        SDLK_4,
+        SDLK_5,
+        SDLK_6,
+        SDLK_7,
+        SDLK_8,
+        SDLK_9,
+        SDLK_ASTERISK,
+        SDLK_HASH,
+        SDLK_UP,
+        SDLK_DOWN,
+        SDLK_LEFT,
+        SDLK_RIGHT,
+        SDLK_RETURN,    // OK
+        SDLK_UNKNOWN,   // volume up
+        SDLK_UNKNOWN,   // volume down
+        SDLK_UNKNOWN,   // power
+        SDLK_UNKNOWN,   // camera
+    };
+
+    const SDLKey* array = gKeys;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gKeys); i++) {
+        if (array[i] == src) {
+            return static_cast<SkKey>(i);
+        }
+    }
+    return kNONE_SkKey;
+}
+
+void SkOSWindow::handleSDLEvent(const SDL_Event& event) {
+    switch (event.type) {
+        case SDL_VIDEORESIZE:
+            this->resize(event.resize.w, event.resize.h);
+            break;
+        case SDL_VIDEOEXPOSE:
+            this->doDraw();
+            break;
+        case SDL_MOUSEMOTION:
+            if (event.motion.state == SDL_PRESSED) {
+                this->handleClick(event.motion.x, event.motion.y,
+                                   SkView::Click::kMoved_State);
+            }
+            break;
+        case SDL_MOUSEBUTTONDOWN:
+        case SDL_MOUSEBUTTONUP:
+            this->handleClick(event.button.x, event.button.y,
+                               event.button.state == SDL_PRESSED ?
+                               SkView::Click::kDown_State :
+                               SkView::Click::kUp_State);
+            break;
+        case SDL_KEYDOWN: {
+            SkKey sk = find_skkey(event.key.keysym.sym);
+            if (kNONE_SkKey != sk) {
+                if (event.key.state == SDL_PRESSED) {
+                    this->handleKey(sk);
+                } else {
+                    this->handleKeyUp(sk);
+                }
+            }
+            break;
+        }
+        case SDL_USEREVENT:
+            if (SkEvent::ProcessEvent()) {
+                post_SkEvent_event();
+            }
+            break;
+    }
+}
+
+void SkOSWindow::onHandleInval(const SkIRect& r) {
+    SDL_Event evt;
+    evt.type = SDL_VIDEOEXPOSE;
+    evt.expose.type = SDL_VIDEOEXPOSE;
+    SDL_PushEvent(&evt);
+}
+
+void SkOSWindow::onSetTitle(const char title[]) {
+    SDL_WM_SetCaption(title, NULL);
+}
+
+void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) {}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void SkEvent::SignalNonEmptyQueue() {
+    SkDebugf("-------- signal nonempty\n");
+    post_SkEvent_event();
+}
+
+static Uint32 timer_callback(Uint32 interval) {
+//    SkDebugf("-------- timercallback %d\n", interval);
+    SkEvent::ServiceQueueTimer();
+    return 0;
+}
+
+void SkEvent::SignalQueueTimer(SkMSec delay)
+{
+    SDL_SetTimer(0, NULL);
+    if (delay) {
+        SDL_SetTimer(delay, timer_callback);
+    }
+}
+
diff --git a/src/views/unix/SkOSWindow_Unix.cpp b/src/views/unix/SkOSWindow_Unix.cpp
new file mode 100644
index 0000000..46a2b3d
--- /dev/null
+++ b/src/views/unix/SkOSWindow_Unix.cpp
@@ -0,0 +1,349 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/XKBlib.h>
+#include <GL/glx.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+#include "SkWindow.h"
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkEvent.h"
+#include "SkKey.h"
+#include "SkWindow.h"
+#include "XkeysToSkKeys.h"
+extern "C" {
+    #include "keysym2ucs.h"
+}
+
+const int WIDTH = 500;
+const int HEIGHT = 500;
+
+// Determine which events to listen for.
+const long EVENT_MASK = StructureNotifyMask|ButtonPressMask|ButtonReleaseMask
+        |ExposureMask|PointerMotionMask|KeyPressMask|KeyReleaseMask;
+
+SkOSWindow::SkOSWindow(void* unused)
+    : fVi(NULL)
+    , fMSAASampleCount(0) {
+    fUnixWindow.fDisplay = NULL;
+    fUnixWindow.fGLContext = NULL;
+    this->initWindow(0);
+    this->resize(WIDTH, HEIGHT);
+}
+
+SkOSWindow::~SkOSWindow() {
+    this->closeWindow();
+}
+
+void SkOSWindow::closeWindow() {
+    if (NULL != fUnixWindow.fDisplay) {
+        this->detach();
+        SkASSERT(NULL != fUnixWindow.fGc);
+        XFreeGC(fUnixWindow.fDisplay, fUnixWindow.fGc);
+        fUnixWindow.fGc = NULL;
+        XDestroyWindow(fUnixWindow.fDisplay, fUnixWindow.fWin);
+        fVi = NULL;
+        XCloseDisplay(fUnixWindow.fDisplay);
+        fUnixWindow.fDisplay = NULL;
+        fMSAASampleCount = 0;
+    }
+}
+
+void SkOSWindow::initWindow(int requestedMSAASampleCount) {
+    if (fMSAASampleCount != requestedMSAASampleCount) {
+        this->closeWindow();
+    }
+    // presence of fDisplay means we already have a window
+    if (NULL != fUnixWindow.fDisplay) {
+        return;
+    }
+    fUnixWindow.fDisplay = XOpenDisplay(NULL);
+    Display* dsp = fUnixWindow.fDisplay;
+    if (NULL == dsp) {
+        SkDebugf("Could not open an X Display");
+        return;
+    }
+    // Attempt to create a window that supports GL
+    GLint att[] = {
+        GLX_RGBA,
+        GLX_DEPTH_SIZE, 24,
+        GLX_DOUBLEBUFFER,
+        GLX_STENCIL_SIZE, 8,
+        None
+    };
+    SkASSERT(NULL == fVi);
+    if (requestedMSAASampleCount > 0) {
+        static const GLint kAttCount = SK_ARRAY_COUNT(att);
+        GLint msaaAtt[kAttCount + 4];
+        memcpy(msaaAtt, att, sizeof(att));
+        SkASSERT(None == msaaAtt[kAttCount - 1]);
+        msaaAtt[kAttCount - 1] = GLX_SAMPLE_BUFFERS_ARB;
+        msaaAtt[kAttCount + 0] = 1;
+        msaaAtt[kAttCount + 1] = GLX_SAMPLES_ARB;
+        msaaAtt[kAttCount + 2] = requestedMSAASampleCount;
+        msaaAtt[kAttCount + 3] = None;
+        fVi = glXChooseVisual(dsp, DefaultScreen(dsp), msaaAtt);
+        fMSAASampleCount = requestedMSAASampleCount;
+    }
+    if (NULL == fVi) {
+        fVi = glXChooseVisual(dsp, DefaultScreen(dsp), att);
+        fMSAASampleCount = 0;
+    }
+
+    if (fVi) {
+        Colormap colorMap = XCreateColormap(dsp,
+                                            RootWindow(dsp, fVi->screen),
+                                            fVi->visual,
+                                             AllocNone);
+        XSetWindowAttributes swa;
+        swa.colormap = colorMap;
+        swa.event_mask = EVENT_MASK;
+        fUnixWindow.fWin = XCreateWindow(dsp,
+                                         RootWindow(dsp, fVi->screen),
+                                         0, 0, // x, y
+                                         WIDTH, HEIGHT,
+                                         0, // border width
+                                         fVi->depth,
+                                         InputOutput,
+                                         fVi->visual,
+                                         CWEventMask | CWColormap,
+                                         &swa);
+    } else {
+        // Create a simple window instead.  We will not be able to show GL
+        fUnixWindow.fWin = XCreateSimpleWindow(dsp,
+                                               DefaultRootWindow(dsp),
+                                               0, 0,  // x, y
+                                               WIDTH, HEIGHT,
+                                               0,     // border width
+                                               0,     // border value
+                                               0);    // background value
+    }
+    this->mapWindowAndWait();
+    fUnixWindow.fGc = XCreateGC(dsp, fUnixWindow.fWin, 0, NULL);
+}
+
+
+void SkOSWindow::post_linuxevent() {
+    // Put an event in the X queue to fire an SkEvent.
+    if (NULL == fUnixWindow.fDisplay) {
+        return;
+    }
+    long event_mask = NoEventMask;
+    XClientMessageEvent event;
+    event.type = ClientMessage;
+    Atom myAtom(0);
+    event.message_type = myAtom;
+    event.format = 32;
+    event.data.l[0] = 0;
+    XSendEvent(fUnixWindow.fDisplay, fUnixWindow.fWin, false, 0,
+               (XEvent*) &event);
+    XFlush(fUnixWindow.fDisplay);
+}
+
+void SkOSWindow::loop() {
+    Display* dsp = fUnixWindow.fDisplay;
+    if (NULL == dsp) {
+        return;
+    }
+    XSelectInput(dsp, fUnixWindow.fWin, EVENT_MASK);
+
+    bool loop = true;
+    XEvent evt;
+    while (loop) {
+        XNextEvent(dsp, &evt);
+        switch (evt.type) {
+            case Expose:
+                if (evt.xexpose.count == 0)
+                    this->inval(NULL);
+                break;
+            case ConfigureNotify:
+                this->resize(evt.xconfigure.width, evt.xconfigure.height);
+                break;
+            case ButtonPress:
+                if (evt.xbutton.button == Button1)
+                    this->handleClick(evt.xbutton.x, evt.xbutton.y, SkView::Click::kDown_State);
+                break;
+            case ButtonRelease:
+                if (evt.xbutton.button == Button1)
+                    this->handleClick(evt.xbutton.x, evt.xbutton.y, SkView::Click::kUp_State);
+                break;
+            case MotionNotify:
+                this->handleClick(evt.xmotion.x, evt.xmotion.y, SkView::Click::kMoved_State);
+                break;
+            case KeyPress: {
+                KeySym keysym = XkbKeycodeToKeysym(dsp, evt.xkey.keycode, 0, 0);
+                //SkDebugf("pressed key %i!\n\tKeySym:%i\n", evt.xkey.keycode, XKeycodeToKeysym(dsp, evt.xkey.keycode, 0));
+                if (keysym == XK_Escape) {
+                    loop = false;
+                    break;
+                }
+                this->handleKey(XKeyToSkKey(keysym));
+                long uni = keysym2ucs(keysym);
+                if (uni != -1) {
+                    this->handleChar((SkUnichar) uni);
+                }
+                break;
+            }
+            case KeyRelease:
+                //SkDebugf("released key %i\n", evt.xkey.keycode);
+                this->handleKeyUp(XKeyToSkKey(XkbKeycodeToKeysym(dsp, evt.xkey.keycode, 0, 0)));
+                break;
+            case ClientMessage:
+                if (SkEvent::ProcessEvent()) {
+                    this->post_linuxevent();
+                }
+                break;
+            default:
+                // Do nothing for other events
+                break;
+        }
+    }
+}
+
+void SkOSWindow::mapWindowAndWait() {
+    SkASSERT(NULL != fUnixWindow.fDisplay);
+    Display* dsp = fUnixWindow.fDisplay;
+    Window win = fUnixWindow.fWin;
+    XMapWindow(dsp, win);
+
+    long eventMask = StructureNotifyMask;
+    XSelectInput(dsp, win, eventMask);
+
+    // Wait until screen is ready.
+    XEvent evt;
+    do {
+        XNextEvent(dsp, &evt);
+    } while(evt.type != MapNotify);
+
+}
+
+bool SkOSWindow::attach(SkBackEndTypes /* attachType */, int msaaSampleCount) {
+    this->initWindow(msaaSampleCount);
+    if (NULL == fUnixWindow.fDisplay) {
+        return false;
+    }
+    if (NULL == fUnixWindow.fGLContext) {
+        SkASSERT(NULL != fVi);
+
+        fUnixWindow.fGLContext = glXCreateContext(fUnixWindow.fDisplay,
+                                                  fVi,
+                                                  NULL,
+                                                  GL_TRUE);
+        if (NULL == fUnixWindow.fGLContext) {
+            return false;
+        }
+    }
+    glXMakeCurrent(fUnixWindow.fDisplay,
+                   fUnixWindow.fWin,
+                   fUnixWindow.fGLContext);
+    glViewport(0, 0,
+               SkScalarRound(this->width()), SkScalarRound(this->height()));
+    glClearColor(0, 0, 0, 0);
+    glClearStencil(0);
+    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+    return true;
+}
+
+void SkOSWindow::detach() {
+    if (NULL == fUnixWindow.fDisplay || NULL == fUnixWindow.fGLContext) {
+        return;
+    }
+    glXMakeCurrent(fUnixWindow.fDisplay, None, NULL);
+    glXDestroyContext(fUnixWindow.fDisplay, fUnixWindow.fGLContext);
+    fUnixWindow.fGLContext = NULL;
+}
+
+void SkOSWindow::present() {
+    if (NULL != fUnixWindow.fDisplay && NULL != fUnixWindow.fGLContext) {
+        glXSwapBuffers(fUnixWindow.fDisplay, fUnixWindow.fWin);
+    }
+}
+
+void SkOSWindow::onSetTitle(const char title[]) {
+    if (NULL == fUnixWindow.fDisplay) {
+        return;
+    }
+    XTextProperty textProp;
+    textProp.value = (unsigned char*)title;
+    textProp.format = 8;
+    textProp.nitems = strlen((char*)textProp.value);
+    textProp.encoding = XA_STRING;
+    XSetWMName(fUnixWindow.fDisplay, fUnixWindow.fWin, &textProp);
+}
+
+void SkOSWindow::onHandleInval(const SkIRect&) {
+    (new SkEvent("inval-imageview", this->getSinkID()))->post();
+}
+
+bool SkOSWindow::onEvent(const SkEvent& evt) {
+    if (evt.isType("inval-imageview")) {
+        update(NULL);
+        if (NULL == fUnixWindow.fGLContext)
+            this->doPaint();
+        return true;
+    }
+    return INHERITED::onEvent(evt);
+}
+
+static bool convertBitmapToXImage(XImage& image, const SkBitmap& bitmap) {
+    sk_bzero(&image, sizeof(image));
+
+    int bitsPerPixel = bitmap.bytesPerPixel() * 8;
+    image.width = bitmap.width();
+    image.height = bitmap.height();
+    image.format = ZPixmap;
+    image.data = (char*) bitmap.getPixels();
+    image.byte_order = LSBFirst;
+    image.bitmap_unit = bitsPerPixel;
+    image.bitmap_bit_order = LSBFirst;
+    image.bitmap_pad = bitsPerPixel;
+    image.depth = 24;
+    image.bytes_per_line = bitmap.rowBytes() - bitmap.width() * bitmap.bytesPerPixel();
+    image.bits_per_pixel = bitsPerPixel;
+    return XInitImage(&image);
+}
+
+void SkOSWindow::doPaint() {
+    if (NULL == fUnixWindow.fDisplay) {
+        return;
+    }
+    // Draw the bitmap to the screen.
+    const SkBitmap& bitmap = getBitmap();
+    int width = bitmap.width();
+    int height = bitmap.height();
+
+    XImage image;
+    if (!convertBitmapToXImage(image, bitmap)) {
+        return;
+    }
+
+    XPutImage(fUnixWindow.fDisplay,
+              fUnixWindow.fWin,
+              fUnixWindow.fGc,
+              &image,
+              0, 0,     // src x,y
+              0, 0,     // dst x,y
+              width, height);
+}
+
+bool SkOSWindow::onHandleChar(SkUnichar) {
+    return false;
+}
+
+bool SkOSWindow::onHandleKey(SkKey key) {
+    return false;
+}
+
+bool SkOSWindow::onHandleKeyUp(SkKey key) {
+    return false;
+}
diff --git a/src/views/unix/keysym2ucs.c b/src/views/unix/keysym2ucs.c
new file mode 100644
index 0000000..a0c4ced
--- /dev/null
+++ b/src/views/unix/keysym2ucs.c
@@ -0,0 +1,848 @@
+/* $XFree86$
+ * This module converts keysym values into the corresponding ISO 10646
+ * (UCS, Unicode) values.
+ *
+ * The array keysymtab[] contains pairs of X11 keysym values for graphical
+ * characters and the corresponding Unicode value. The function
+ * keysym2ucs() maps a keysym onto a Unicode value using a binary search,
+ * therefore keysymtab[] must remain SORTED by keysym value.
+ *
+ * The keysym -> UTF-8 conversion will hopefully one day be provided
+ * by Xlib via XmbLookupString() and should ideally not have to be
+ * done in X applications. But we are not there yet.
+ *
+ * We allow to represent any UCS character in the range U-00000000 to
+ * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff.
+ * This admittedly does not cover the entire 31-bit space of UCS, but
+ * it does cover all of the characters up to U-10FFFF, which can be
+ * represented by UTF-16, and more, and it is very unlikely that higher
+ * UCS codes will ever be assigned by ISO. So to get Unicode character
+ * U+ABCD you can directly use keysym 0x0100abcd.
+ *
+ * NOTE: The comments in the table below contain the actual character
+ * encoded in UTF-8, so for viewing and editing best use an editor in
+ * UTF-8 mode.
+ *
+ * Author: Markus G. Kuhn <http://www.cl.cam.ac.uk/~mgk25/>,
+ *         University of Cambridge, April 2001
+ *
+ * Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing
+ * an initial draft of the mapping table.
+ *
+ * This software is in the public domain. Share and enjoy!
+ *
+ * AUTOMATICALLY GENERATED FILE, DO NOT EDIT !!! (unicode/convmap.pl)
+ */
+
+#include "keysym2ucs.h"
+
+struct codepair {
+  unsigned short keysym;
+  unsigned short ucs;
+} keysymtab[] = {
+  { 0x01a1, 0x0104 }, /*                     Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */
+  { 0x01a2, 0x02d8 }, /*                       breve ˘ BREVE */
+  { 0x01a3, 0x0141 }, /*                     Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */
+  { 0x01a5, 0x013d }, /*                      Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */
+  { 0x01a6, 0x015a }, /*                      Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */
+  { 0x01a9, 0x0160 }, /*                      Scaron Š LATIN CAPITAL LETTER S WITH CARON */
+  { 0x01aa, 0x015e }, /*                    Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */
+  { 0x01ab, 0x0164 }, /*                      Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */
+  { 0x01ac, 0x0179 }, /*                      Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */
+  { 0x01ae, 0x017d }, /*                      Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */
+  { 0x01af, 0x017b }, /*                   Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */
+  { 0x01b1, 0x0105 }, /*                     aogonek ą LATIN SMALL LETTER A WITH OGONEK */
+  { 0x01b2, 0x02db }, /*                      ogonek ˛ OGONEK */
+  { 0x01b3, 0x0142 }, /*                     lstroke ł LATIN SMALL LETTER L WITH STROKE */
+  { 0x01b5, 0x013e }, /*                      lcaron ľ LATIN SMALL LETTER L WITH CARON */
+  { 0x01b6, 0x015b }, /*                      sacute ś LATIN SMALL LETTER S WITH ACUTE */
+  { 0x01b7, 0x02c7 }, /*                       caron ˇ CARON */
+  { 0x01b9, 0x0161 }, /*                      scaron š LATIN SMALL LETTER S WITH CARON */
+  { 0x01ba, 0x015f }, /*                    scedilla ş LATIN SMALL LETTER S WITH CEDILLA */
+  { 0x01bb, 0x0165 }, /*                      tcaron ť LATIN SMALL LETTER T WITH CARON */
+  { 0x01bc, 0x017a }, /*                      zacute ź LATIN SMALL LETTER Z WITH ACUTE */
+  { 0x01bd, 0x02dd }, /*                 doubleacute ˝ DOUBLE ACUTE ACCENT */
+  { 0x01be, 0x017e }, /*                      zcaron ž LATIN SMALL LETTER Z WITH CARON */
+  { 0x01bf, 0x017c }, /*                   zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */
+  { 0x01c0, 0x0154 }, /*                      Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */
+  { 0x01c3, 0x0102 }, /*                      Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */
+  { 0x01c5, 0x0139 }, /*                      Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */
+  { 0x01c6, 0x0106 }, /*                      Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */
+  { 0x01c8, 0x010c }, /*                      Ccaron Č LATIN CAPITAL LETTER C WITH CARON */
+  { 0x01ca, 0x0118 }, /*                     Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */
+  { 0x01cc, 0x011a }, /*                      Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */
+  { 0x01cf, 0x010e }, /*                      Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */
+  { 0x01d0, 0x0110 }, /*                     Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */
+  { 0x01d1, 0x0143 }, /*                      Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */
+  { 0x01d2, 0x0147 }, /*                      Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */
+  { 0x01d5, 0x0150 }, /*                Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
+  { 0x01d8, 0x0158 }, /*                      Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */
+  { 0x01d9, 0x016e }, /*                       Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */
+  { 0x01db, 0x0170 }, /*                Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
+  { 0x01de, 0x0162 }, /*                    Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */
+  { 0x01e0, 0x0155 }, /*                      racute ŕ LATIN SMALL LETTER R WITH ACUTE */
+  { 0x01e3, 0x0103 }, /*                      abreve ă LATIN SMALL LETTER A WITH BREVE */
+  { 0x01e5, 0x013a }, /*                      lacute ĺ LATIN SMALL LETTER L WITH ACUTE */
+  { 0x01e6, 0x0107 }, /*                      cacute ć LATIN SMALL LETTER C WITH ACUTE */
+  { 0x01e8, 0x010d }, /*                      ccaron č LATIN SMALL LETTER C WITH CARON */
+  { 0x01ea, 0x0119 }, /*                     eogonek ę LATIN SMALL LETTER E WITH OGONEK */
+  { 0x01ec, 0x011b }, /*                      ecaron ě LATIN SMALL LETTER E WITH CARON */
+  { 0x01ef, 0x010f }, /*                      dcaron ď LATIN SMALL LETTER D WITH CARON */
+  { 0x01f0, 0x0111 }, /*                     dstroke đ LATIN SMALL LETTER D WITH STROKE */
+  { 0x01f1, 0x0144 }, /*                      nacute ń LATIN SMALL LETTER N WITH ACUTE */
+  { 0x01f2, 0x0148 }, /*                      ncaron ň LATIN SMALL LETTER N WITH CARON */
+  { 0x01f5, 0x0151 }, /*                odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */
+  { 0x01f8, 0x0159 }, /*                      rcaron ř LATIN SMALL LETTER R WITH CARON */
+  { 0x01f9, 0x016f }, /*                       uring ů LATIN SMALL LETTER U WITH RING ABOVE */
+  { 0x01fb, 0x0171 }, /*                udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */
+  { 0x01fe, 0x0163 }, /*                    tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */
+  { 0x01ff, 0x02d9 }, /*                    abovedot ˙ DOT ABOVE */
+  { 0x02a1, 0x0126 }, /*                     Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */
+  { 0x02a6, 0x0124 }, /*                 Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
+  { 0x02a9, 0x0130 }, /*                   Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */
+  { 0x02ab, 0x011e }, /*                      Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */
+  { 0x02ac, 0x0134 }, /*                 Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
+  { 0x02b1, 0x0127 }, /*                     hstroke ħ LATIN SMALL LETTER H WITH STROKE */
+  { 0x02b6, 0x0125 }, /*                 hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */
+  { 0x02b9, 0x0131 }, /*                    idotless ı LATIN SMALL LETTER DOTLESS I */
+  { 0x02bb, 0x011f }, /*                      gbreve ğ LATIN SMALL LETTER G WITH BREVE */
+  { 0x02bc, 0x0135 }, /*                 jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */
+  { 0x02c5, 0x010a }, /*                   Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */
+  { 0x02c6, 0x0108 }, /*                 Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
+  { 0x02d5, 0x0120 }, /*                   Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */
+  { 0x02d8, 0x011c }, /*                 Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
+  { 0x02dd, 0x016c }, /*                      Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */
+  { 0x02de, 0x015c }, /*                 Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
+  { 0x02e5, 0x010b }, /*                   cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */
+  { 0x02e6, 0x0109 }, /*                 ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */
+  { 0x02f5, 0x0121 }, /*                   gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */
+  { 0x02f8, 0x011d }, /*                 gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */
+  { 0x02fd, 0x016d }, /*                      ubreve ŭ LATIN SMALL LETTER U WITH BREVE */
+  { 0x02fe, 0x015d }, /*                 scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */
+  { 0x03a2, 0x0138 }, /*                         kra ĸ LATIN SMALL LETTER KRA */
+  { 0x03a3, 0x0156 }, /*                    Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */
+  { 0x03a5, 0x0128 }, /*                      Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */
+  { 0x03a6, 0x013b }, /*                    Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */
+  { 0x03aa, 0x0112 }, /*                     Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */
+  { 0x03ab, 0x0122 }, /*                    Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */
+  { 0x03ac, 0x0166 }, /*                      Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */
+  { 0x03b3, 0x0157 }, /*                    rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */
+  { 0x03b5, 0x0129 }, /*                      itilde ĩ LATIN SMALL LETTER I WITH TILDE */
+  { 0x03b6, 0x013c }, /*                    lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */
+  { 0x03ba, 0x0113 }, /*                     emacron ē LATIN SMALL LETTER E WITH MACRON */
+  { 0x03bb, 0x0123 }, /*                    gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */
+  { 0x03bc, 0x0167 }, /*                      tslash ŧ LATIN SMALL LETTER T WITH STROKE */
+  { 0x03bd, 0x014a }, /*                         ENG Ŋ LATIN CAPITAL LETTER ENG */
+  { 0x03bf, 0x014b }, /*                         eng ŋ LATIN SMALL LETTER ENG */
+  { 0x03c0, 0x0100 }, /*                     Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */
+  { 0x03c7, 0x012e }, /*                     Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */
+  { 0x03cc, 0x0116 }, /*                   Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */
+  { 0x03cf, 0x012a }, /*                     Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */
+  { 0x03d1, 0x0145 }, /*                    Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */
+  { 0x03d2, 0x014c }, /*                     Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */
+  { 0x03d3, 0x0136 }, /*                    Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */
+  { 0x03d9, 0x0172 }, /*                     Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */
+  { 0x03dd, 0x0168 }, /*                      Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */
+  { 0x03de, 0x016a }, /*                     Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */
+  { 0x03e0, 0x0101 }, /*                     amacron ā LATIN SMALL LETTER A WITH MACRON */
+  { 0x03e7, 0x012f }, /*                     iogonek į LATIN SMALL LETTER I WITH OGONEK */
+  { 0x03ec, 0x0117 }, /*                   eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */
+  { 0x03ef, 0x012b }, /*                     imacron ī LATIN SMALL LETTER I WITH MACRON */
+  { 0x03f1, 0x0146 }, /*                    ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */
+  { 0x03f2, 0x014d }, /*                     omacron ō LATIN SMALL LETTER O WITH MACRON */
+  { 0x03f3, 0x0137 }, /*                    kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */
+  { 0x03f9, 0x0173 }, /*                     uogonek ų LATIN SMALL LETTER U WITH OGONEK */
+  { 0x03fd, 0x0169 }, /*                      utilde ũ LATIN SMALL LETTER U WITH TILDE */
+  { 0x03fe, 0x016b }, /*                     umacron ū LATIN SMALL LETTER U WITH MACRON */
+  { 0x047e, 0x203e }, /*                    overline ‾ OVERLINE */
+  { 0x04a1, 0x3002 }, /*               kana_fullstop 。 IDEOGRAPHIC FULL STOP */
+  { 0x04a2, 0x300c }, /*         kana_openingbracket 「 LEFT CORNER BRACKET */
+  { 0x04a3, 0x300d }, /*         kana_closingbracket 」 RIGHT CORNER BRACKET */
+  { 0x04a4, 0x3001 }, /*                  kana_comma 、 IDEOGRAPHIC COMMA */
+  { 0x04a5, 0x30fb }, /*            kana_conjunctive ・ KATAKANA MIDDLE DOT */
+  { 0x04a6, 0x30f2 }, /*                     kana_WO ヲ KATAKANA LETTER WO */
+  { 0x04a7, 0x30a1 }, /*                      kana_a ァ KATAKANA LETTER SMALL A */
+  { 0x04a8, 0x30a3 }, /*                      kana_i ィ KATAKANA LETTER SMALL I */
+  { 0x04a9, 0x30a5 }, /*                      kana_u ゥ KATAKANA LETTER SMALL U */
+  { 0x04aa, 0x30a7 }, /*                      kana_e ェ KATAKANA LETTER SMALL E */
+  { 0x04ab, 0x30a9 }, /*                      kana_o ォ KATAKANA LETTER SMALL O */
+  { 0x04ac, 0x30e3 }, /*                     kana_ya ャ KATAKANA LETTER SMALL YA */
+  { 0x04ad, 0x30e5 }, /*                     kana_yu ュ KATAKANA LETTER SMALL YU */
+  { 0x04ae, 0x30e7 }, /*                     kana_yo ョ KATAKANA LETTER SMALL YO */
+  { 0x04af, 0x30c3 }, /*                    kana_tsu ッ KATAKANA LETTER SMALL TU */
+  { 0x04b0, 0x30fc }, /*              prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */
+  { 0x04b1, 0x30a2 }, /*                      kana_A ア KATAKANA LETTER A */
+  { 0x04b2, 0x30a4 }, /*                      kana_I イ KATAKANA LETTER I */
+  { 0x04b3, 0x30a6 }, /*                      kana_U ウ KATAKANA LETTER U */
+  { 0x04b4, 0x30a8 }, /*                      kana_E エ KATAKANA LETTER E */
+  { 0x04b5, 0x30aa }, /*                      kana_O オ KATAKANA LETTER O */
+  { 0x04b6, 0x30ab }, /*                     kana_KA カ KATAKANA LETTER KA */
+  { 0x04b7, 0x30ad }, /*                     kana_KI キ KATAKANA LETTER KI */
+  { 0x04b8, 0x30af }, /*                     kana_KU ク KATAKANA LETTER KU */
+  { 0x04b9, 0x30b1 }, /*                     kana_KE ケ KATAKANA LETTER KE */
+  { 0x04ba, 0x30b3 }, /*                     kana_KO コ KATAKANA LETTER KO */
+  { 0x04bb, 0x30b5 }, /*                     kana_SA サ KATAKANA LETTER SA */
+  { 0x04bc, 0x30b7 }, /*                    kana_SHI シ KATAKANA LETTER SI */
+  { 0x04bd, 0x30b9 }, /*                     kana_SU ス KATAKANA LETTER SU */
+  { 0x04be, 0x30bb }, /*                     kana_SE セ KATAKANA LETTER SE */
+  { 0x04bf, 0x30bd }, /*                     kana_SO ソ KATAKANA LETTER SO */
+  { 0x04c0, 0x30bf }, /*                     kana_TA タ KATAKANA LETTER TA */
+  { 0x04c1, 0x30c1 }, /*                    kana_CHI チ KATAKANA LETTER TI */
+  { 0x04c2, 0x30c4 }, /*                    kana_TSU ツ KATAKANA LETTER TU */
+  { 0x04c3, 0x30c6 }, /*                     kana_TE テ KATAKANA LETTER TE */
+  { 0x04c4, 0x30c8 }, /*                     kana_TO ト KATAKANA LETTER TO */
+  { 0x04c5, 0x30ca }, /*                     kana_NA ナ KATAKANA LETTER NA */
+  { 0x04c6, 0x30cb }, /*                     kana_NI ニ KATAKANA LETTER NI */
+  { 0x04c7, 0x30cc }, /*                     kana_NU ヌ KATAKANA LETTER NU */
+  { 0x04c8, 0x30cd }, /*                     kana_NE ネ KATAKANA LETTER NE */
+  { 0x04c9, 0x30ce }, /*                     kana_NO ノ KATAKANA LETTER NO */
+  { 0x04ca, 0x30cf }, /*                     kana_HA ハ KATAKANA LETTER HA */
+  { 0x04cb, 0x30d2 }, /*                     kana_HI ヒ KATAKANA LETTER HI */
+  { 0x04cc, 0x30d5 }, /*                     kana_FU フ KATAKANA LETTER HU */
+  { 0x04cd, 0x30d8 }, /*                     kana_HE ヘ KATAKANA LETTER HE */
+  { 0x04ce, 0x30db }, /*                     kana_HO ホ KATAKANA LETTER HO */
+  { 0x04cf, 0x30de }, /*                     kana_MA マ KATAKANA LETTER MA */
+  { 0x04d0, 0x30df }, /*                     kana_MI ミ KATAKANA LETTER MI */
+  { 0x04d1, 0x30e0 }, /*                     kana_MU ム KATAKANA LETTER MU */
+  { 0x04d2, 0x30e1 }, /*                     kana_ME メ KATAKANA LETTER ME */
+  { 0x04d3, 0x30e2 }, /*                     kana_MO モ KATAKANA LETTER MO */
+  { 0x04d4, 0x30e4 }, /*                     kana_YA ヤ KATAKANA LETTER YA */
+  { 0x04d5, 0x30e6 }, /*                     kana_YU ユ KATAKANA LETTER YU */
+  { 0x04d6, 0x30e8 }, /*                     kana_YO ヨ KATAKANA LETTER YO */
+  { 0x04d7, 0x30e9 }, /*                     kana_RA ラ KATAKANA LETTER RA */
+  { 0x04d8, 0x30ea }, /*                     kana_RI リ KATAKANA LETTER RI */
+  { 0x04d9, 0x30eb }, /*                     kana_RU ル KATAKANA LETTER RU */
+  { 0x04da, 0x30ec }, /*                     kana_RE レ KATAKANA LETTER RE */
+  { 0x04db, 0x30ed }, /*                     kana_RO ロ KATAKANA LETTER RO */
+  { 0x04dc, 0x30ef }, /*                     kana_WA ワ KATAKANA LETTER WA */
+  { 0x04dd, 0x30f3 }, /*                      kana_N ン KATAKANA LETTER N */
+  { 0x04de, 0x309b }, /*                 voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */
+  { 0x04df, 0x309c }, /*             semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
+  { 0x05ac, 0x060c }, /*                Arabic_comma ، ARABIC COMMA */
+  { 0x05bb, 0x061b }, /*            Arabic_semicolon ؛ ARABIC SEMICOLON */
+  { 0x05bf, 0x061f }, /*        Arabic_question_mark ؟ ARABIC QUESTION MARK */
+  { 0x05c1, 0x0621 }, /*                Arabic_hamza ء ARABIC LETTER HAMZA */
+  { 0x05c2, 0x0622 }, /*          Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */
+  { 0x05c3, 0x0623 }, /*          Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */
+  { 0x05c4, 0x0624 }, /*           Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */
+  { 0x05c5, 0x0625 }, /*       Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */
+  { 0x05c6, 0x0626 }, /*           Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */
+  { 0x05c7, 0x0627 }, /*                 Arabic_alef ا ARABIC LETTER ALEF */
+  { 0x05c8, 0x0628 }, /*                  Arabic_beh ب ARABIC LETTER BEH */
+  { 0x05c9, 0x0629 }, /*           Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */
+  { 0x05ca, 0x062a }, /*                  Arabic_teh ت ARABIC LETTER TEH */
+  { 0x05cb, 0x062b }, /*                 Arabic_theh ث ARABIC LETTER THEH */
+  { 0x05cc, 0x062c }, /*                 Arabic_jeem ج ARABIC LETTER JEEM */
+  { 0x05cd, 0x062d }, /*                  Arabic_hah ح ARABIC LETTER HAH */
+  { 0x05ce, 0x062e }, /*                 Arabic_khah خ ARABIC LETTER KHAH */
+  { 0x05cf, 0x062f }, /*                  Arabic_dal د ARABIC LETTER DAL */
+  { 0x05d0, 0x0630 }, /*                 Arabic_thal ذ ARABIC LETTER THAL */
+  { 0x05d1, 0x0631 }, /*                   Arabic_ra ر ARABIC LETTER REH */
+  { 0x05d2, 0x0632 }, /*                 Arabic_zain ز ARABIC LETTER ZAIN */
+  { 0x05d3, 0x0633 }, /*                 Arabic_seen س ARABIC LETTER SEEN */
+  { 0x05d4, 0x0634 }, /*                Arabic_sheen ش ARABIC LETTER SHEEN */
+  { 0x05d5, 0x0635 }, /*                  Arabic_sad ص ARABIC LETTER SAD */
+  { 0x05d6, 0x0636 }, /*                  Arabic_dad ض ARABIC LETTER DAD */
+  { 0x05d7, 0x0637 }, /*                  Arabic_tah ط ARABIC LETTER TAH */
+  { 0x05d8, 0x0638 }, /*                  Arabic_zah ظ ARABIC LETTER ZAH */
+  { 0x05d9, 0x0639 }, /*                  Arabic_ain ع ARABIC LETTER AIN */
+  { 0x05da, 0x063a }, /*                Arabic_ghain غ ARABIC LETTER GHAIN */
+  { 0x05e0, 0x0640 }, /*              Arabic_tatweel ـ ARABIC TATWEEL */
+  { 0x05e1, 0x0641 }, /*                  Arabic_feh ف ARABIC LETTER FEH */
+  { 0x05e2, 0x0642 }, /*                  Arabic_qaf ق ARABIC LETTER QAF */
+  { 0x05e3, 0x0643 }, /*                  Arabic_kaf ك ARABIC LETTER KAF */
+  { 0x05e4, 0x0644 }, /*                  Arabic_lam ل ARABIC LETTER LAM */
+  { 0x05e5, 0x0645 }, /*                 Arabic_meem م ARABIC LETTER MEEM */
+  { 0x05e6, 0x0646 }, /*                 Arabic_noon ن ARABIC LETTER NOON */
+  { 0x05e7, 0x0647 }, /*                   Arabic_ha ه ARABIC LETTER HEH */
+  { 0x05e8, 0x0648 }, /*                  Arabic_waw و ARABIC LETTER WAW */
+  { 0x05e9, 0x0649 }, /*          Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */
+  { 0x05ea, 0x064a }, /*                  Arabic_yeh ي ARABIC LETTER YEH */
+  { 0x05eb, 0x064b }, /*             Arabic_fathatan ً ARABIC FATHATAN */
+  { 0x05ec, 0x064c }, /*             Arabic_dammatan ٌ ARABIC DAMMATAN */
+  { 0x05ed, 0x064d }, /*             Arabic_kasratan ٍ ARABIC KASRATAN */
+  { 0x05ee, 0x064e }, /*                Arabic_fatha َ ARABIC FATHA */
+  { 0x05ef, 0x064f }, /*                Arabic_damma ُ ARABIC DAMMA */
+  { 0x05f0, 0x0650 }, /*                Arabic_kasra ِ ARABIC KASRA */
+  { 0x05f1, 0x0651 }, /*               Arabic_shadda ّ ARABIC SHADDA */
+  { 0x05f2, 0x0652 }, /*                Arabic_sukun ْ ARABIC SUKUN */
+  { 0x06a1, 0x0452 }, /*                 Serbian_dje ђ CYRILLIC SMALL LETTER DJE */
+  { 0x06a2, 0x0453 }, /*               Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */
+  { 0x06a3, 0x0451 }, /*                 Cyrillic_io ё CYRILLIC SMALL LETTER IO */
+  { 0x06a4, 0x0454 }, /*                Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */
+  { 0x06a5, 0x0455 }, /*               Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */
+  { 0x06a6, 0x0456 }, /*                 Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
+  { 0x06a7, 0x0457 }, /*                Ukrainian_yi ї CYRILLIC SMALL LETTER YI */
+  { 0x06a8, 0x0458 }, /*                 Cyrillic_je ј CYRILLIC SMALL LETTER JE */
+  { 0x06a9, 0x0459 }, /*                Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */
+  { 0x06aa, 0x045a }, /*                Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */
+  { 0x06ab, 0x045b }, /*                Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */
+  { 0x06ac, 0x045c }, /*               Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */
+  { 0x06ae, 0x045e }, /*         Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */
+  { 0x06af, 0x045f }, /*               Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */
+  { 0x06b0, 0x2116 }, /*                  numerosign № NUMERO SIGN */
+  { 0x06b1, 0x0402 }, /*                 Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */
+  { 0x06b2, 0x0403 }, /*               Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */
+  { 0x06b3, 0x0401 }, /*                 Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */
+  { 0x06b4, 0x0404 }, /*                Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */
+  { 0x06b5, 0x0405 }, /*               Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */
+  { 0x06b6, 0x0406 }, /*                 Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
+  { 0x06b7, 0x0407 }, /*                Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */
+  { 0x06b8, 0x0408 }, /*                 Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */
+  { 0x06b9, 0x0409 }, /*                Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */
+  { 0x06ba, 0x040a }, /*                Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */
+  { 0x06bb, 0x040b }, /*                Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */
+  { 0x06bc, 0x040c }, /*               Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */
+  { 0x06be, 0x040e }, /*         Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */
+  { 0x06bf, 0x040f }, /*               Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */
+  { 0x06c0, 0x044e }, /*                 Cyrillic_yu ю CYRILLIC SMALL LETTER YU */
+  { 0x06c1, 0x0430 }, /*                  Cyrillic_a а CYRILLIC SMALL LETTER A */
+  { 0x06c2, 0x0431 }, /*                 Cyrillic_be б CYRILLIC SMALL LETTER BE */
+  { 0x06c3, 0x0446 }, /*                Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */
+  { 0x06c4, 0x0434 }, /*                 Cyrillic_de д CYRILLIC SMALL LETTER DE */
+  { 0x06c5, 0x0435 }, /*                 Cyrillic_ie е CYRILLIC SMALL LETTER IE */
+  { 0x06c6, 0x0444 }, /*                 Cyrillic_ef ф CYRILLIC SMALL LETTER EF */
+  { 0x06c7, 0x0433 }, /*                Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */
+  { 0x06c8, 0x0445 }, /*                 Cyrillic_ha х CYRILLIC SMALL LETTER HA */
+  { 0x06c9, 0x0438 }, /*                  Cyrillic_i и CYRILLIC SMALL LETTER I */
+  { 0x06ca, 0x0439 }, /*             Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */
+  { 0x06cb, 0x043a }, /*                 Cyrillic_ka к CYRILLIC SMALL LETTER KA */
+  { 0x06cc, 0x043b }, /*                 Cyrillic_el л CYRILLIC SMALL LETTER EL */
+  { 0x06cd, 0x043c }, /*                 Cyrillic_em м CYRILLIC SMALL LETTER EM */
+  { 0x06ce, 0x043d }, /*                 Cyrillic_en н CYRILLIC SMALL LETTER EN */
+  { 0x06cf, 0x043e }, /*                  Cyrillic_o о CYRILLIC SMALL LETTER O */
+  { 0x06d0, 0x043f }, /*                 Cyrillic_pe п CYRILLIC SMALL LETTER PE */
+  { 0x06d1, 0x044f }, /*                 Cyrillic_ya я CYRILLIC SMALL LETTER YA */
+  { 0x06d2, 0x0440 }, /*                 Cyrillic_er р CYRILLIC SMALL LETTER ER */
+  { 0x06d3, 0x0441 }, /*                 Cyrillic_es с CYRILLIC SMALL LETTER ES */
+  { 0x06d4, 0x0442 }, /*                 Cyrillic_te т CYRILLIC SMALL LETTER TE */
+  { 0x06d5, 0x0443 }, /*                  Cyrillic_u у CYRILLIC SMALL LETTER U */
+  { 0x06d6, 0x0436 }, /*                Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */
+  { 0x06d7, 0x0432 }, /*                 Cyrillic_ve в CYRILLIC SMALL LETTER VE */
+  { 0x06d8, 0x044c }, /*           Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */
+  { 0x06d9, 0x044b }, /*               Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */
+  { 0x06da, 0x0437 }, /*                 Cyrillic_ze з CYRILLIC SMALL LETTER ZE */
+  { 0x06db, 0x0448 }, /*                Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */
+  { 0x06dc, 0x044d }, /*                  Cyrillic_e э CYRILLIC SMALL LETTER E */
+  { 0x06dd, 0x0449 }, /*              Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */
+  { 0x06de, 0x0447 }, /*                Cyrillic_che ч CYRILLIC SMALL LETTER CHE */
+  { 0x06df, 0x044a }, /*           Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */
+  { 0x06e0, 0x042e }, /*                 Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */
+  { 0x06e1, 0x0410 }, /*                  Cyrillic_A А CYRILLIC CAPITAL LETTER A */
+  { 0x06e2, 0x0411 }, /*                 Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */
+  { 0x06e3, 0x0426 }, /*                Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */
+  { 0x06e4, 0x0414 }, /*                 Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */
+  { 0x06e5, 0x0415 }, /*                 Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */
+  { 0x06e6, 0x0424 }, /*                 Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */
+  { 0x06e7, 0x0413 }, /*                Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */
+  { 0x06e8, 0x0425 }, /*                 Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */
+  { 0x06e9, 0x0418 }, /*                  Cyrillic_I И CYRILLIC CAPITAL LETTER I */
+  { 0x06ea, 0x0419 }, /*             Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */
+  { 0x06eb, 0x041a }, /*                 Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */
+  { 0x06ec, 0x041b }, /*                 Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */
+  { 0x06ed, 0x041c }, /*                 Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */
+  { 0x06ee, 0x041d }, /*                 Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */
+  { 0x06ef, 0x041e }, /*                  Cyrillic_O О CYRILLIC CAPITAL LETTER O */
+  { 0x06f0, 0x041f }, /*                 Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */
+  { 0x06f1, 0x042f }, /*                 Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */
+  { 0x06f2, 0x0420 }, /*                 Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */
+  { 0x06f3, 0x0421 }, /*                 Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */
+  { 0x06f4, 0x0422 }, /*                 Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */
+  { 0x06f5, 0x0423 }, /*                  Cyrillic_U У CYRILLIC CAPITAL LETTER U */
+  { 0x06f6, 0x0416 }, /*                Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */
+  { 0x06f7, 0x0412 }, /*                 Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */
+  { 0x06f8, 0x042c }, /*           Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */
+  { 0x06f9, 0x042b }, /*               Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */
+  { 0x06fa, 0x0417 }, /*                 Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */
+  { 0x06fb, 0x0428 }, /*                Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */
+  { 0x06fc, 0x042d }, /*                  Cyrillic_E Э CYRILLIC CAPITAL LETTER E */
+  { 0x06fd, 0x0429 }, /*              Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */
+  { 0x06fe, 0x0427 }, /*                Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */
+  { 0x06ff, 0x042a }, /*           Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */
+  { 0x07a1, 0x0386 }, /*           Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */
+  { 0x07a2, 0x0388 }, /*         Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */
+  { 0x07a3, 0x0389 }, /*             Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */
+  { 0x07a4, 0x038a }, /*            Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */
+  { 0x07a5, 0x03aa }, /*         Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
+  { 0x07a7, 0x038c }, /*         Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */
+  { 0x07a8, 0x038e }, /*         Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */
+  { 0x07a9, 0x03ab }, /*       Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
+  { 0x07ab, 0x038f }, /*           Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */
+  { 0x07ae, 0x0385 }, /*        Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */
+  { 0x07af, 0x2015 }, /*              Greek_horizbar ― HORIZONTAL BAR */
+  { 0x07b1, 0x03ac }, /*           Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */
+  { 0x07b2, 0x03ad }, /*         Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */
+  { 0x07b3, 0x03ae }, /*             Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */
+  { 0x07b4, 0x03af }, /*            Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */
+  { 0x07b5, 0x03ca }, /*          Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */
+  { 0x07b6, 0x0390 }, /*    Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
+  { 0x07b7, 0x03cc }, /*         Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */
+  { 0x07b8, 0x03cd }, /*         Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */
+  { 0x07b9, 0x03cb }, /*       Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */
+  { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
+  { 0x07bb, 0x03ce }, /*           Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */
+  { 0x07c1, 0x0391 }, /*                 Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */
+  { 0x07c2, 0x0392 }, /*                  Greek_BETA Β GREEK CAPITAL LETTER BETA */
+  { 0x07c3, 0x0393 }, /*                 Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */
+  { 0x07c4, 0x0394 }, /*                 Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */
+  { 0x07c5, 0x0395 }, /*               Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */
+  { 0x07c6, 0x0396 }, /*                  Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */
+  { 0x07c7, 0x0397 }, /*                   Greek_ETA Η GREEK CAPITAL LETTER ETA */
+  { 0x07c8, 0x0398 }, /*                 Greek_THETA Θ GREEK CAPITAL LETTER THETA */
+  { 0x07c9, 0x0399 }, /*                  Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */
+  { 0x07ca, 0x039a }, /*                 Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */
+  { 0x07cb, 0x039b }, /*                Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */
+  { 0x07cc, 0x039c }, /*                    Greek_MU Μ GREEK CAPITAL LETTER MU */
+  { 0x07cd, 0x039d }, /*                    Greek_NU Ν GREEK CAPITAL LETTER NU */
+  { 0x07ce, 0x039e }, /*                    Greek_XI Ξ GREEK CAPITAL LETTER XI */
+  { 0x07cf, 0x039f }, /*               Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */
+  { 0x07d0, 0x03a0 }, /*                    Greek_PI Π GREEK CAPITAL LETTER PI */
+  { 0x07d1, 0x03a1 }, /*                   Greek_RHO Ρ GREEK CAPITAL LETTER RHO */
+  { 0x07d2, 0x03a3 }, /*                 Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */
+  { 0x07d4, 0x03a4 }, /*                   Greek_TAU Τ GREEK CAPITAL LETTER TAU */
+  { 0x07d5, 0x03a5 }, /*               Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */
+  { 0x07d6, 0x03a6 }, /*                   Greek_PHI Φ GREEK CAPITAL LETTER PHI */
+  { 0x07d7, 0x03a7 }, /*                   Greek_CHI Χ GREEK CAPITAL LETTER CHI */
+  { 0x07d8, 0x03a8 }, /*                   Greek_PSI Ψ GREEK CAPITAL LETTER PSI */
+  { 0x07d9, 0x03a9 }, /*                 Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */
+  { 0x07e1, 0x03b1 }, /*                 Greek_alpha α GREEK SMALL LETTER ALPHA */
+  { 0x07e2, 0x03b2 }, /*                  Greek_beta β GREEK SMALL LETTER BETA */
+  { 0x07e3, 0x03b3 }, /*                 Greek_gamma γ GREEK SMALL LETTER GAMMA */
+  { 0x07e4, 0x03b4 }, /*                 Greek_delta δ GREEK SMALL LETTER DELTA */
+  { 0x07e5, 0x03b5 }, /*               Greek_epsilon ε GREEK SMALL LETTER EPSILON */
+  { 0x07e6, 0x03b6 }, /*                  Greek_zeta ζ GREEK SMALL LETTER ZETA */
+  { 0x07e7, 0x03b7 }, /*                   Greek_eta η GREEK SMALL LETTER ETA */
+  { 0x07e8, 0x03b8 }, /*                 Greek_theta θ GREEK SMALL LETTER THETA */
+  { 0x07e9, 0x03b9 }, /*                  Greek_iota ι GREEK SMALL LETTER IOTA */
+  { 0x07ea, 0x03ba }, /*                 Greek_kappa κ GREEK SMALL LETTER KAPPA */
+  { 0x07eb, 0x03bb }, /*                Greek_lambda λ GREEK SMALL LETTER LAMDA */
+  { 0x07ec, 0x03bc }, /*                    Greek_mu μ GREEK SMALL LETTER MU */
+  { 0x07ed, 0x03bd }, /*                    Greek_nu ν GREEK SMALL LETTER NU */
+  { 0x07ee, 0x03be }, /*                    Greek_xi ξ GREEK SMALL LETTER XI */
+  { 0x07ef, 0x03bf }, /*               Greek_omicron ο GREEK SMALL LETTER OMICRON */
+  { 0x07f0, 0x03c0 }, /*                    Greek_pi π GREEK SMALL LETTER PI */
+  { 0x07f1, 0x03c1 }, /*                   Greek_rho ρ GREEK SMALL LETTER RHO */
+  { 0x07f2, 0x03c3 }, /*                 Greek_sigma σ GREEK SMALL LETTER SIGMA */
+  { 0x07f3, 0x03c2 }, /*       Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */
+  { 0x07f4, 0x03c4 }, /*                   Greek_tau τ GREEK SMALL LETTER TAU */
+  { 0x07f5, 0x03c5 }, /*               Greek_upsilon υ GREEK SMALL LETTER UPSILON */
+  { 0x07f6, 0x03c6 }, /*                   Greek_phi φ GREEK SMALL LETTER PHI */
+  { 0x07f7, 0x03c7 }, /*                   Greek_chi χ GREEK SMALL LETTER CHI */
+  { 0x07f8, 0x03c8 }, /*                   Greek_psi ψ GREEK SMALL LETTER PSI */
+  { 0x07f9, 0x03c9 }, /*                 Greek_omega ω GREEK SMALL LETTER OMEGA */
+  { 0x08a1, 0x23b7 }, /*                 leftradical ⎷ ??? */
+  { 0x08a2, 0x250c }, /*              topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
+  { 0x08a3, 0x2500 }, /*              horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */
+  { 0x08a4, 0x2320 }, /*                 topintegral ⌠ TOP HALF INTEGRAL */
+  { 0x08a5, 0x2321 }, /*                 botintegral ⌡ BOTTOM HALF INTEGRAL */
+  { 0x08a6, 0x2502 }, /*               vertconnector │ BOX DRAWINGS LIGHT VERTICAL */
+  { 0x08a7, 0x23a1 }, /*            topleftsqbracket ⎡ ??? */
+  { 0x08a8, 0x23a3 }, /*            botleftsqbracket ⎣ ??? */
+  { 0x08a9, 0x23a4 }, /*           toprightsqbracket ⎤ ??? */
+  { 0x08aa, 0x23a6 }, /*           botrightsqbracket ⎦ ??? */
+  { 0x08ab, 0x239b }, /*               topleftparens ⎛ ??? */
+  { 0x08ac, 0x239d }, /*               botleftparens ⎝ ??? */
+  { 0x08ad, 0x239e }, /*              toprightparens ⎞ ??? */
+  { 0x08ae, 0x23a0 }, /*              botrightparens ⎠ ??? */
+  { 0x08af, 0x23a8 }, /*        leftmiddlecurlybrace ⎨ ??? */
+  { 0x08b0, 0x23ac }, /*       rightmiddlecurlybrace ⎬ ??? */
+/*  0x08b1                          topleftsummation ? ??? */
+/*  0x08b2                          botleftsummation ? ??? */
+/*  0x08b3                 topvertsummationconnector ? ??? */
+/*  0x08b4                 botvertsummationconnector ? ??? */
+/*  0x08b5                         toprightsummation ? ??? */
+/*  0x08b6                         botrightsummation ? ??? */
+/*  0x08b7                      rightmiddlesummation ? ??? */
+  { 0x08bc, 0x2264 }, /*               lessthanequal ≤ LESS-THAN OR EQUAL TO */
+  { 0x08bd, 0x2260 }, /*                    notequal ≠ NOT EQUAL TO */
+  { 0x08be, 0x2265 }, /*            greaterthanequal ≥ GREATER-THAN OR EQUAL TO */
+  { 0x08bf, 0x222b }, /*                    integral ∫ INTEGRAL */
+  { 0x08c0, 0x2234 }, /*                   therefore ∴ THEREFORE */
+  { 0x08c1, 0x221d }, /*                   variation ∝ PROPORTIONAL TO */
+  { 0x08c2, 0x221e }, /*                    infinity ∞ INFINITY */
+  { 0x08c5, 0x2207 }, /*                       nabla ∇ NABLA */
+  { 0x08c8, 0x223c }, /*                 approximate ∼ TILDE OPERATOR */
+  { 0x08c9, 0x2243 }, /*                similarequal ≃ ASYMPTOTICALLY EQUAL TO */
+  { 0x08cd, 0x21d4 }, /*                    ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */
+  { 0x08ce, 0x21d2 }, /*                     implies ⇒ RIGHTWARDS DOUBLE ARROW */
+  { 0x08cf, 0x2261 }, /*                   identical ≡ IDENTICAL TO */
+  { 0x08d6, 0x221a }, /*                     radical √ SQUARE ROOT */
+  { 0x08da, 0x2282 }, /*                  includedin ⊂ SUBSET OF */
+  { 0x08db, 0x2283 }, /*                    includes ⊃ SUPERSET OF */
+  { 0x08dc, 0x2229 }, /*                intersection ∩ INTERSECTION */
+  { 0x08dd, 0x222a }, /*                       union ∪ UNION */
+  { 0x08de, 0x2227 }, /*                  logicaland ∧ LOGICAL AND */
+  { 0x08df, 0x2228 }, /*                   logicalor ∨ LOGICAL OR */
+  { 0x08ef, 0x2202 }, /*           partialderivative ∂ PARTIAL DIFFERENTIAL */
+  { 0x08f6, 0x0192 }, /*                    function ƒ LATIN SMALL LETTER F WITH HOOK */
+  { 0x08fb, 0x2190 }, /*                   leftarrow ← LEFTWARDS ARROW */
+  { 0x08fc, 0x2191 }, /*                     uparrow ↑ UPWARDS ARROW */
+  { 0x08fd, 0x2192 }, /*                  rightarrow → RIGHTWARDS ARROW */
+  { 0x08fe, 0x2193 }, /*                   downarrow ↓ DOWNWARDS ARROW */
+/*  0x09df                                     blank ? ??? */
+  { 0x09e0, 0x25c6 }, /*                soliddiamond ◆ BLACK DIAMOND */
+  { 0x09e1, 0x2592 }, /*                checkerboard ▒ MEDIUM SHADE */
+  { 0x09e2, 0x2409 }, /*                          ht ␉ SYMBOL FOR HORIZONTAL TABULATION */
+  { 0x09e3, 0x240c }, /*                          ff ␌ SYMBOL FOR FORM FEED */
+  { 0x09e4, 0x240d }, /*                          cr ␍ SYMBOL FOR CARRIAGE RETURN */
+  { 0x09e5, 0x240a }, /*                          lf ␊ SYMBOL FOR LINE FEED */
+  { 0x09e8, 0x2424 }, /*                          nl ␤ SYMBOL FOR NEWLINE */
+  { 0x09e9, 0x240b }, /*                          vt ␋ SYMBOL FOR VERTICAL TABULATION */
+  { 0x09ea, 0x2518 }, /*              lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */
+  { 0x09eb, 0x2510 }, /*               uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */
+  { 0x09ec, 0x250c }, /*                upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
+  { 0x09ed, 0x2514 }, /*               lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */
+  { 0x09ee, 0x253c }, /*               crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
+  { 0x09ef, 0x23ba }, /*              horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */
+  { 0x09f0, 0x23bb }, /*              horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */
+  { 0x09f1, 0x2500 }, /*              horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */
+  { 0x09f2, 0x23bc }, /*              horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */
+  { 0x09f3, 0x23bd }, /*              horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */
+  { 0x09f4, 0x251c }, /*                       leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
+  { 0x09f5, 0x2524 }, /*                      rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */
+  { 0x09f6, 0x2534 }, /*                        bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */
+  { 0x09f7, 0x252c }, /*                        topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
+  { 0x09f8, 0x2502 }, /*                     vertbar │ BOX DRAWINGS LIGHT VERTICAL */
+  { 0x0aa1, 0x2003 }, /*                     emspace   EM SPACE */
+  { 0x0aa2, 0x2002 }, /*                     enspace   EN SPACE */
+  { 0x0aa3, 0x2004 }, /*                    em3space   THREE-PER-EM SPACE */
+  { 0x0aa4, 0x2005 }, /*                    em4space   FOUR-PER-EM SPACE */
+  { 0x0aa5, 0x2007 }, /*                  digitspace   FIGURE SPACE */
+  { 0x0aa6, 0x2008 }, /*                  punctspace   PUNCTUATION SPACE */
+  { 0x0aa7, 0x2009 }, /*                   thinspace   THIN SPACE */
+  { 0x0aa8, 0x200a }, /*                   hairspace   HAIR SPACE */
+  { 0x0aa9, 0x2014 }, /*                      emdash — EM DASH */
+  { 0x0aaa, 0x2013 }, /*                      endash – EN DASH */
+/*  0x0aac                               signifblank ? ??? */
+  { 0x0aae, 0x2026 }, /*                    ellipsis … HORIZONTAL ELLIPSIS */
+  { 0x0aaf, 0x2025 }, /*             doubbaselinedot ‥ TWO DOT LEADER */
+  { 0x0ab0, 0x2153 }, /*                    onethird ⅓ VULGAR FRACTION ONE THIRD */
+  { 0x0ab1, 0x2154 }, /*                   twothirds ⅔ VULGAR FRACTION TWO THIRDS */
+  { 0x0ab2, 0x2155 }, /*                    onefifth ⅕ VULGAR FRACTION ONE FIFTH */
+  { 0x0ab3, 0x2156 }, /*                   twofifths ⅖ VULGAR FRACTION TWO FIFTHS */
+  { 0x0ab4, 0x2157 }, /*                 threefifths ⅗ VULGAR FRACTION THREE FIFTHS */
+  { 0x0ab5, 0x2158 }, /*                  fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */
+  { 0x0ab6, 0x2159 }, /*                    onesixth ⅙ VULGAR FRACTION ONE SIXTH */
+  { 0x0ab7, 0x215a }, /*                  fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */
+  { 0x0ab8, 0x2105 }, /*                      careof ℅ CARE OF */
+  { 0x0abb, 0x2012 }, /*                     figdash ‒ FIGURE DASH */
+  { 0x0abc, 0x2329 }, /*            leftanglebracket ⟨ LEFT-POINTING ANGLE BRACKET */
+/*  0x0abd                              decimalpoint ? ??? */
+  { 0x0abe, 0x232a }, /*           rightanglebracket ⟩ RIGHT-POINTING ANGLE BRACKET */
+/*  0x0abf                                    marker ? ??? */
+  { 0x0ac3, 0x215b }, /*                   oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */
+  { 0x0ac4, 0x215c }, /*                threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */
+  { 0x0ac5, 0x215d }, /*                 fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */
+  { 0x0ac6, 0x215e }, /*                seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */
+  { 0x0ac9, 0x2122 }, /*                   trademark ™ TRADE MARK SIGN */
+  { 0x0aca, 0x2613 }, /*               signaturemark ☓ SALTIRE */
+/*  0x0acb                         trademarkincircle ? ??? */
+  { 0x0acc, 0x25c1 }, /*            leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */
+  { 0x0acd, 0x25b7 }, /*           rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */
+  { 0x0ace, 0x25cb }, /*                emopencircle ○ WHITE CIRCLE */
+  { 0x0acf, 0x25af }, /*             emopenrectangle ▯ WHITE VERTICAL RECTANGLE */
+  { 0x0ad0, 0x2018 }, /*         leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */
+  { 0x0ad1, 0x2019 }, /*        rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */
+  { 0x0ad2, 0x201c }, /*         leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */
+  { 0x0ad3, 0x201d }, /*        rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */
+  { 0x0ad4, 0x211e }, /*                prescription ℞ PRESCRIPTION TAKE */
+  { 0x0ad6, 0x2032 }, /*                     minutes ′ PRIME */
+  { 0x0ad7, 0x2033 }, /*                     seconds ″ DOUBLE PRIME */
+  { 0x0ad9, 0x271d }, /*                  latincross ✝ LATIN CROSS */
+/*  0x0ada                                  hexagram ? ??? */
+  { 0x0adb, 0x25ac }, /*            filledrectbullet ▬ BLACK RECTANGLE */
+  { 0x0adc, 0x25c0 }, /*         filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */
+  { 0x0add, 0x25b6 }, /*        filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */
+  { 0x0ade, 0x25cf }, /*              emfilledcircle ● BLACK CIRCLE */
+  { 0x0adf, 0x25ae }, /*                emfilledrect ▮ BLACK VERTICAL RECTANGLE */
+  { 0x0ae0, 0x25e6 }, /*            enopencircbullet ◦ WHITE BULLET */
+  { 0x0ae1, 0x25ab }, /*          enopensquarebullet ▫ WHITE SMALL SQUARE */
+  { 0x0ae2, 0x25ad }, /*              openrectbullet ▭ WHITE RECTANGLE */
+  { 0x0ae3, 0x25b3 }, /*             opentribulletup △ WHITE UP-POINTING TRIANGLE */
+  { 0x0ae4, 0x25bd }, /*           opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */
+  { 0x0ae5, 0x2606 }, /*                    openstar ☆ WHITE STAR */
+  { 0x0ae6, 0x2022 }, /*          enfilledcircbullet • BULLET */
+  { 0x0ae7, 0x25aa }, /*            enfilledsqbullet ▪ BLACK SMALL SQUARE */
+  { 0x0ae8, 0x25b2 }, /*           filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */
+  { 0x0ae9, 0x25bc }, /*         filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */
+  { 0x0aea, 0x261c }, /*                 leftpointer ☜ WHITE LEFT POINTING INDEX */
+  { 0x0aeb, 0x261e }, /*                rightpointer ☞ WHITE RIGHT POINTING INDEX */
+  { 0x0aec, 0x2663 }, /*                        club ♣ BLACK CLUB SUIT */
+  { 0x0aed, 0x2666 }, /*                     diamond ♦ BLACK DIAMOND SUIT */
+  { 0x0aee, 0x2665 }, /*                       heart ♥ BLACK HEART SUIT */
+  { 0x0af0, 0x2720 }, /*                maltesecross ✠ MALTESE CROSS */
+  { 0x0af1, 0x2020 }, /*                      dagger † DAGGER */
+  { 0x0af2, 0x2021 }, /*                doubledagger ‡ DOUBLE DAGGER */
+  { 0x0af3, 0x2713 }, /*                   checkmark ✓ CHECK MARK */
+  { 0x0af4, 0x2717 }, /*                 ballotcross ✗ BALLOT X */
+  { 0x0af5, 0x266f }, /*                musicalsharp ♯ MUSIC SHARP SIGN */
+  { 0x0af6, 0x266d }, /*                 musicalflat ♭ MUSIC FLAT SIGN */
+  { 0x0af7, 0x2642 }, /*                  malesymbol ♂ MALE SIGN */
+  { 0x0af8, 0x2640 }, /*                femalesymbol ♀ FEMALE SIGN */
+  { 0x0af9, 0x260e }, /*                   telephone ☎ BLACK TELEPHONE */
+  { 0x0afa, 0x2315 }, /*           telephonerecorder ⌕ TELEPHONE RECORDER */
+  { 0x0afb, 0x2117 }, /*         phonographcopyright ℗ SOUND RECORDING COPYRIGHT */
+  { 0x0afc, 0x2038 }, /*                       caret ‸ CARET */
+  { 0x0afd, 0x201a }, /*          singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */
+  { 0x0afe, 0x201e }, /*          doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */
+/*  0x0aff                                    cursor ? ??? */
+  { 0x0ba3, 0x003c }, /*                   leftcaret < LESS-THAN SIGN */
+  { 0x0ba6, 0x003e }, /*                  rightcaret > GREATER-THAN SIGN */
+  { 0x0ba8, 0x2228 }, /*                   downcaret ∨ LOGICAL OR */
+  { 0x0ba9, 0x2227 }, /*                     upcaret ∧ LOGICAL AND */
+  { 0x0bc0, 0x00af }, /*                     overbar ¯ MACRON */
+  { 0x0bc2, 0x22a5 }, /*                    downtack ⊥ UP TACK */
+  { 0x0bc3, 0x2229 }, /*                      upshoe ∩ INTERSECTION */
+  { 0x0bc4, 0x230a }, /*                   downstile ⌊ LEFT FLOOR */
+  { 0x0bc6, 0x005f }, /*                    underbar _ LOW LINE */
+  { 0x0bca, 0x2218 }, /*                         jot ∘ RING OPERATOR */
+  { 0x0bcc, 0x2395 }, /*                        quad ⎕ APL FUNCTIONAL SYMBOL QUAD */
+  { 0x0bce, 0x22a4 }, /*                      uptack ⊤ DOWN TACK */
+  { 0x0bcf, 0x25cb }, /*                      circle ○ WHITE CIRCLE */
+  { 0x0bd3, 0x2308 }, /*                     upstile ⌈ LEFT CEILING */
+  { 0x0bd6, 0x222a }, /*                    downshoe ∪ UNION */
+  { 0x0bd8, 0x2283 }, /*                   rightshoe ⊃ SUPERSET OF */
+  { 0x0bda, 0x2282 }, /*                    leftshoe ⊂ SUBSET OF */
+  { 0x0bdc, 0x22a2 }, /*                    lefttack ⊢ RIGHT TACK */
+  { 0x0bfc, 0x22a3 }, /*                   righttack ⊣ LEFT TACK */
+  { 0x0cdf, 0x2017 }, /*        hebrew_doublelowline ‗ DOUBLE LOW LINE */
+  { 0x0ce0, 0x05d0 }, /*                hebrew_aleph א HEBREW LETTER ALEF */
+  { 0x0ce1, 0x05d1 }, /*                  hebrew_bet ב HEBREW LETTER BET */
+  { 0x0ce2, 0x05d2 }, /*                hebrew_gimel ג HEBREW LETTER GIMEL */
+  { 0x0ce3, 0x05d3 }, /*                hebrew_dalet ד HEBREW LETTER DALET */
+  { 0x0ce4, 0x05d4 }, /*                   hebrew_he ה HEBREW LETTER HE */
+  { 0x0ce5, 0x05d5 }, /*                  hebrew_waw ו HEBREW LETTER VAV */
+  { 0x0ce6, 0x05d6 }, /*                 hebrew_zain ז HEBREW LETTER ZAYIN */
+  { 0x0ce7, 0x05d7 }, /*                 hebrew_chet ח HEBREW LETTER HET */
+  { 0x0ce8, 0x05d8 }, /*                  hebrew_tet ט HEBREW LETTER TET */
+  { 0x0ce9, 0x05d9 }, /*                  hebrew_yod י HEBREW LETTER YOD */
+  { 0x0cea, 0x05da }, /*            hebrew_finalkaph ך HEBREW LETTER FINAL KAF */
+  { 0x0ceb, 0x05db }, /*                 hebrew_kaph כ HEBREW LETTER KAF */
+  { 0x0cec, 0x05dc }, /*                hebrew_lamed ל HEBREW LETTER LAMED */
+  { 0x0ced, 0x05dd }, /*             hebrew_finalmem ם HEBREW LETTER FINAL MEM */
+  { 0x0cee, 0x05de }, /*                  hebrew_mem מ HEBREW LETTER MEM */
+  { 0x0cef, 0x05df }, /*             hebrew_finalnun ן HEBREW LETTER FINAL NUN */
+  { 0x0cf0, 0x05e0 }, /*                  hebrew_nun נ HEBREW LETTER NUN */
+  { 0x0cf1, 0x05e1 }, /*               hebrew_samech ס HEBREW LETTER SAMEKH */
+  { 0x0cf2, 0x05e2 }, /*                 hebrew_ayin ע HEBREW LETTER AYIN */
+  { 0x0cf3, 0x05e3 }, /*              hebrew_finalpe ף HEBREW LETTER FINAL PE */
+  { 0x0cf4, 0x05e4 }, /*                   hebrew_pe פ HEBREW LETTER PE */
+  { 0x0cf5, 0x05e5 }, /*            hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */
+  { 0x0cf6, 0x05e6 }, /*                 hebrew_zade צ HEBREW LETTER TSADI */
+  { 0x0cf7, 0x05e7 }, /*                 hebrew_qoph ק HEBREW LETTER QOF */
+  { 0x0cf8, 0x05e8 }, /*                 hebrew_resh ר HEBREW LETTER RESH */
+  { 0x0cf9, 0x05e9 }, /*                 hebrew_shin ש HEBREW LETTER SHIN */
+  { 0x0cfa, 0x05ea }, /*                  hebrew_taw ת HEBREW LETTER TAV */
+  { 0x0da1, 0x0e01 }, /*                  Thai_kokai ก THAI CHARACTER KO KAI */
+  { 0x0da2, 0x0e02 }, /*                Thai_khokhai ข THAI CHARACTER KHO KHAI */
+  { 0x0da3, 0x0e03 }, /*               Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */
+  { 0x0da4, 0x0e04 }, /*               Thai_khokhwai ค THAI CHARACTER KHO KHWAI */
+  { 0x0da5, 0x0e05 }, /*                Thai_khokhon ฅ THAI CHARACTER KHO KHON */
+  { 0x0da6, 0x0e06 }, /*             Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */
+  { 0x0da7, 0x0e07 }, /*                 Thai_ngongu ง THAI CHARACTER NGO NGU */
+  { 0x0da8, 0x0e08 }, /*                Thai_chochan จ THAI CHARACTER CHO CHAN */
+  { 0x0da9, 0x0e09 }, /*               Thai_choching ฉ THAI CHARACTER CHO CHING */
+  { 0x0daa, 0x0e0a }, /*               Thai_chochang ช THAI CHARACTER CHO CHANG */
+  { 0x0dab, 0x0e0b }, /*                   Thai_soso ซ THAI CHARACTER SO SO */
+  { 0x0dac, 0x0e0c }, /*                Thai_chochoe ฌ THAI CHARACTER CHO CHOE */
+  { 0x0dad, 0x0e0d }, /*                 Thai_yoying ญ THAI CHARACTER YO YING */
+  { 0x0dae, 0x0e0e }, /*                Thai_dochada ฎ THAI CHARACTER DO CHADA */
+  { 0x0daf, 0x0e0f }, /*                Thai_topatak ฏ THAI CHARACTER TO PATAK */
+  { 0x0db0, 0x0e10 }, /*                Thai_thothan ฐ THAI CHARACTER THO THAN */
+  { 0x0db1, 0x0e11 }, /*          Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */
+  { 0x0db2, 0x0e12 }, /*             Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */
+  { 0x0db3, 0x0e13 }, /*                  Thai_nonen ณ THAI CHARACTER NO NEN */
+  { 0x0db4, 0x0e14 }, /*                  Thai_dodek ด THAI CHARACTER DO DEK */
+  { 0x0db5, 0x0e15 }, /*                  Thai_totao ต THAI CHARACTER TO TAO */
+  { 0x0db6, 0x0e16 }, /*               Thai_thothung ถ THAI CHARACTER THO THUNG */
+  { 0x0db7, 0x0e17 }, /*              Thai_thothahan ท THAI CHARACTER THO THAHAN */
+  { 0x0db8, 0x0e18 }, /*               Thai_thothong ธ THAI CHARACTER THO THONG */
+  { 0x0db9, 0x0e19 }, /*                   Thai_nonu น THAI CHARACTER NO NU */
+  { 0x0dba, 0x0e1a }, /*               Thai_bobaimai บ THAI CHARACTER BO BAIMAI */
+  { 0x0dbb, 0x0e1b }, /*                  Thai_popla ป THAI CHARACTER PO PLA */
+  { 0x0dbc, 0x0e1c }, /*               Thai_phophung ผ THAI CHARACTER PHO PHUNG */
+  { 0x0dbd, 0x0e1d }, /*                   Thai_fofa ฝ THAI CHARACTER FO FA */
+  { 0x0dbe, 0x0e1e }, /*                Thai_phophan พ THAI CHARACTER PHO PHAN */
+  { 0x0dbf, 0x0e1f }, /*                  Thai_fofan ฟ THAI CHARACTER FO FAN */
+  { 0x0dc0, 0x0e20 }, /*             Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */
+  { 0x0dc1, 0x0e21 }, /*                   Thai_moma ม THAI CHARACTER MO MA */
+  { 0x0dc2, 0x0e22 }, /*                  Thai_yoyak ย THAI CHARACTER YO YAK */
+  { 0x0dc3, 0x0e23 }, /*                  Thai_rorua ร THAI CHARACTER RO RUA */
+  { 0x0dc4, 0x0e24 }, /*                     Thai_ru ฤ THAI CHARACTER RU */
+  { 0x0dc5, 0x0e25 }, /*                 Thai_loling ล THAI CHARACTER LO LING */
+  { 0x0dc6, 0x0e26 }, /*                     Thai_lu ฦ THAI CHARACTER LU */
+  { 0x0dc7, 0x0e27 }, /*                 Thai_wowaen ว THAI CHARACTER WO WAEN */
+  { 0x0dc8, 0x0e28 }, /*                 Thai_sosala ศ THAI CHARACTER SO SALA */
+  { 0x0dc9, 0x0e29 }, /*                 Thai_sorusi ษ THAI CHARACTER SO RUSI */
+  { 0x0dca, 0x0e2a }, /*                  Thai_sosua ส THAI CHARACTER SO SUA */
+  { 0x0dcb, 0x0e2b }, /*                  Thai_hohip ห THAI CHARACTER HO HIP */
+  { 0x0dcc, 0x0e2c }, /*                Thai_lochula ฬ THAI CHARACTER LO CHULA */
+  { 0x0dcd, 0x0e2d }, /*                   Thai_oang อ THAI CHARACTER O ANG */
+  { 0x0dce, 0x0e2e }, /*               Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */
+  { 0x0dcf, 0x0e2f }, /*              Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */
+  { 0x0dd0, 0x0e30 }, /*                  Thai_saraa ะ THAI CHARACTER SARA A */
+  { 0x0dd1, 0x0e31 }, /*             Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */
+  { 0x0dd2, 0x0e32 }, /*                 Thai_saraaa า THAI CHARACTER SARA AA */
+  { 0x0dd3, 0x0e33 }, /*                 Thai_saraam ำ THAI CHARACTER SARA AM */
+  { 0x0dd4, 0x0e34 }, /*                  Thai_sarai ิ THAI CHARACTER SARA I */
+  { 0x0dd5, 0x0e35 }, /*                 Thai_saraii ี THAI CHARACTER SARA II */
+  { 0x0dd6, 0x0e36 }, /*                 Thai_saraue ึ THAI CHARACTER SARA UE */
+  { 0x0dd7, 0x0e37 }, /*                Thai_sarauee ื THAI CHARACTER SARA UEE */
+  { 0x0dd8, 0x0e38 }, /*                  Thai_sarau ุ THAI CHARACTER SARA U */
+  { 0x0dd9, 0x0e39 }, /*                 Thai_sarauu ู THAI CHARACTER SARA UU */
+  { 0x0dda, 0x0e3a }, /*                Thai_phinthu ฺ THAI CHARACTER PHINTHU */
+/*  0x0dde                    Thai_maihanakat_maitho ? ??? */
+  { 0x0ddf, 0x0e3f }, /*                   Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */
+  { 0x0de0, 0x0e40 }, /*                  Thai_sarae เ THAI CHARACTER SARA E */
+  { 0x0de1, 0x0e41 }, /*                 Thai_saraae แ THAI CHARACTER SARA AE */
+  { 0x0de2, 0x0e42 }, /*                  Thai_sarao โ THAI CHARACTER SARA O */
+  { 0x0de3, 0x0e43 }, /*          Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */
+  { 0x0de4, 0x0e44 }, /*         Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */
+  { 0x0de5, 0x0e45 }, /*            Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */
+  { 0x0de6, 0x0e46 }, /*               Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */
+  { 0x0de7, 0x0e47 }, /*              Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */
+  { 0x0de8, 0x0e48 }, /*                  Thai_maiek ่ THAI CHARACTER MAI EK */
+  { 0x0de9, 0x0e49 }, /*                 Thai_maitho ้ THAI CHARACTER MAI THO */
+  { 0x0dea, 0x0e4a }, /*                 Thai_maitri ๊ THAI CHARACTER MAI TRI */
+  { 0x0deb, 0x0e4b }, /*            Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */
+  { 0x0dec, 0x0e4c }, /*            Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */
+  { 0x0ded, 0x0e4d }, /*               Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */
+  { 0x0df0, 0x0e50 }, /*                 Thai_leksun ๐ THAI DIGIT ZERO */
+  { 0x0df1, 0x0e51 }, /*                Thai_leknung ๑ THAI DIGIT ONE */
+  { 0x0df2, 0x0e52 }, /*                Thai_leksong ๒ THAI DIGIT TWO */
+  { 0x0df3, 0x0e53 }, /*                 Thai_leksam ๓ THAI DIGIT THREE */
+  { 0x0df4, 0x0e54 }, /*                  Thai_leksi ๔ THAI DIGIT FOUR */
+  { 0x0df5, 0x0e55 }, /*                  Thai_lekha ๕ THAI DIGIT FIVE */
+  { 0x0df6, 0x0e56 }, /*                 Thai_lekhok ๖ THAI DIGIT SIX */
+  { 0x0df7, 0x0e57 }, /*                Thai_lekchet ๗ THAI DIGIT SEVEN */
+  { 0x0df8, 0x0e58 }, /*                Thai_lekpaet ๘ THAI DIGIT EIGHT */
+  { 0x0df9, 0x0e59 }, /*                 Thai_lekkao ๙ THAI DIGIT NINE */
+  { 0x0ea1, 0x3131 }, /*               Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */
+  { 0x0ea2, 0x3132 }, /*          Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */
+  { 0x0ea3, 0x3133 }, /*           Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */
+  { 0x0ea4, 0x3134 }, /*                Hangul_Nieun ㄴ HANGUL LETTER NIEUN */
+  { 0x0ea5, 0x3135 }, /*           Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */
+  { 0x0ea6, 0x3136 }, /*           Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */
+  { 0x0ea7, 0x3137 }, /*               Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */
+  { 0x0ea8, 0x3138 }, /*          Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */
+  { 0x0ea9, 0x3139 }, /*                Hangul_Rieul ㄹ HANGUL LETTER RIEUL */
+  { 0x0eaa, 0x313a }, /*          Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */
+  { 0x0eab, 0x313b }, /*           Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */
+  { 0x0eac, 0x313c }, /*           Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */
+  { 0x0ead, 0x313d }, /*            Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */
+  { 0x0eae, 0x313e }, /*           Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */
+  { 0x0eaf, 0x313f }, /*          Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */
+  { 0x0eb0, 0x3140 }, /*           Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */
+  { 0x0eb1, 0x3141 }, /*                Hangul_Mieum ㅁ HANGUL LETTER MIEUM */
+  { 0x0eb2, 0x3142 }, /*                Hangul_Pieub ㅂ HANGUL LETTER PIEUP */
+  { 0x0eb3, 0x3143 }, /*           Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */
+  { 0x0eb4, 0x3144 }, /*            Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */
+  { 0x0eb5, 0x3145 }, /*                 Hangul_Sios ㅅ HANGUL LETTER SIOS */
+  { 0x0eb6, 0x3146 }, /*            Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */
+  { 0x0eb7, 0x3147 }, /*                Hangul_Ieung ㅇ HANGUL LETTER IEUNG */
+  { 0x0eb8, 0x3148 }, /*                Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */
+  { 0x0eb9, 0x3149 }, /*           Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */
+  { 0x0eba, 0x314a }, /*                Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */
+  { 0x0ebb, 0x314b }, /*               Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */
+  { 0x0ebc, 0x314c }, /*                Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */
+  { 0x0ebd, 0x314d }, /*               Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */
+  { 0x0ebe, 0x314e }, /*                Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */
+  { 0x0ebf, 0x314f }, /*                    Hangul_A ㅏ HANGUL LETTER A */
+  { 0x0ec0, 0x3150 }, /*                   Hangul_AE ㅐ HANGUL LETTER AE */
+  { 0x0ec1, 0x3151 }, /*                   Hangul_YA ㅑ HANGUL LETTER YA */
+  { 0x0ec2, 0x3152 }, /*                  Hangul_YAE ㅒ HANGUL LETTER YAE */
+  { 0x0ec3, 0x3153 }, /*                   Hangul_EO ㅓ HANGUL LETTER EO */
+  { 0x0ec4, 0x3154 }, /*                    Hangul_E ㅔ HANGUL LETTER E */
+  { 0x0ec5, 0x3155 }, /*                  Hangul_YEO ㅕ HANGUL LETTER YEO */
+  { 0x0ec6, 0x3156 }, /*                   Hangul_YE ㅖ HANGUL LETTER YE */
+  { 0x0ec7, 0x3157 }, /*                    Hangul_O ㅗ HANGUL LETTER O */
+  { 0x0ec8, 0x3158 }, /*                   Hangul_WA ㅘ HANGUL LETTER WA */
+  { 0x0ec9, 0x3159 }, /*                  Hangul_WAE ㅙ HANGUL LETTER WAE */
+  { 0x0eca, 0x315a }, /*                   Hangul_OE ㅚ HANGUL LETTER OE */
+  { 0x0ecb, 0x315b }, /*                   Hangul_YO ㅛ HANGUL LETTER YO */
+  { 0x0ecc, 0x315c }, /*                    Hangul_U ㅜ HANGUL LETTER U */
+  { 0x0ecd, 0x315d }, /*                  Hangul_WEO ㅝ HANGUL LETTER WEO */
+  { 0x0ece, 0x315e }, /*                   Hangul_WE ㅞ HANGUL LETTER WE */
+  { 0x0ecf, 0x315f }, /*                   Hangul_WI ㅟ HANGUL LETTER WI */
+  { 0x0ed0, 0x3160 }, /*                   Hangul_YU ㅠ HANGUL LETTER YU */
+  { 0x0ed1, 0x3161 }, /*                   Hangul_EU ㅡ HANGUL LETTER EU */
+  { 0x0ed2, 0x3162 }, /*                   Hangul_YI ㅢ HANGUL LETTER YI */
+  { 0x0ed3, 0x3163 }, /*                    Hangul_I ㅣ HANGUL LETTER I */
+  { 0x0ed4, 0x11a8 }, /*             Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */
+  { 0x0ed5, 0x11a9 }, /*        Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */
+  { 0x0ed6, 0x11aa }, /*         Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */
+  { 0x0ed7, 0x11ab }, /*              Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */
+  { 0x0ed8, 0x11ac }, /*         Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */
+  { 0x0ed9, 0x11ad }, /*         Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */
+  { 0x0eda, 0x11ae }, /*             Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */
+  { 0x0edb, 0x11af }, /*              Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */
+  { 0x0edc, 0x11b0 }, /*        Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */
+  { 0x0edd, 0x11b1 }, /*         Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */
+  { 0x0ede, 0x11b2 }, /*         Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */
+  { 0x0edf, 0x11b3 }, /*          Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */
+  { 0x0ee0, 0x11b4 }, /*         Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */
+  { 0x0ee1, 0x11b5 }, /*        Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */
+  { 0x0ee2, 0x11b6 }, /*         Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */
+  { 0x0ee3, 0x11b7 }, /*              Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */
+  { 0x0ee4, 0x11b8 }, /*              Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */
+  { 0x0ee5, 0x11b9 }, /*          Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */
+  { 0x0ee6, 0x11ba }, /*               Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */
+  { 0x0ee7, 0x11bb }, /*          Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */
+  { 0x0ee8, 0x11bc }, /*              Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */
+  { 0x0ee9, 0x11bd }, /*              Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */
+  { 0x0eea, 0x11be }, /*              Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */
+  { 0x0eeb, 0x11bf }, /*             Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */
+  { 0x0eec, 0x11c0 }, /*              Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */
+  { 0x0eed, 0x11c1 }, /*             Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */
+  { 0x0eee, 0x11c2 }, /*              Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */
+  { 0x0eef, 0x316d }, /*     Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */
+  { 0x0ef0, 0x3171 }, /*    Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */
+  { 0x0ef1, 0x3178 }, /*    Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */
+  { 0x0ef2, 0x317f }, /*              Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */
+  { 0x0ef3, 0x3181 }, /*    Hangul_KkogjiDalrinIeung ㆁ HANGUL LETTER YESIEUNG */
+  { 0x0ef4, 0x3184 }, /*   Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */
+  { 0x0ef5, 0x3186 }, /*          Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */
+  { 0x0ef6, 0x318d }, /*                Hangul_AraeA ㆍ HANGUL LETTER ARAEA */
+  { 0x0ef7, 0x318e }, /*               Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */
+  { 0x0ef8, 0x11eb }, /*            Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */
+  { 0x0ef9, 0x11f0 }, /*  Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */
+  { 0x0efa, 0x11f9 }, /*        Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */
+  { 0x0eff, 0x20a9 }, /*                  Korean_Won ₩ WON SIGN */
+  { 0x13a4, 0x20ac }, /*                        Euro € EURO SIGN */
+  { 0x13bc, 0x0152 }, /*                          OE Œ LATIN CAPITAL LIGATURE OE */
+  { 0x13bd, 0x0153 }, /*                          oe œ LATIN SMALL LIGATURE OE */
+  { 0x13be, 0x0178 }, /*                  Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */
+  { 0x20ac, 0x20ac }, /*                    EuroSign € EURO SIGN */
+};
+
+long keysym2ucs(KeySym keysym)
+{
+    int min = 0;
+    int max = sizeof(keysymtab) / sizeof(struct codepair) - 1;
+    int mid;
+
+    /* first check for Latin-1 characters (1:1 mapping) */
+    if ((keysym >= 0x0020 && keysym <= 0x007e) ||
+        (keysym >= 0x00a0 && keysym <= 0x00ff))
+        return keysym;
+
+    /* also check for directly encoded 24-bit UCS characters */
+    if ((keysym & 0xff000000) == 0x01000000)
+    return keysym & 0x00ffffff;
+
+    /* binary search in table */
+    while (max >= min) {
+    mid = (min + max) / 2;
+    if (keysymtab[mid].keysym < keysym)
+        min = mid + 1;
+    else if (keysymtab[mid].keysym > keysym)
+        max = mid - 1;
+    else {
+        /* found it */
+        return keysymtab[mid].ucs;
+    }
+    }
+
+    /* no matching Unicode value found */
+    return -1;
+}
diff --git a/src/views/unix/skia_unix.cpp b/src/views/unix/skia_unix.cpp
new file mode 100644
index 0000000..108e9ac
--- /dev/null
+++ b/src/views/unix/skia_unix.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 "X11/Xlib.h"
+#include "X11/keysym.h"
+
+#include "SkApplication.h"
+#include "SkEvent.h"
+#include "SkWindow.h"
+#include "SkTypes.h"
+
+#include <signal.h>
+#include <sys/time.h>
+
+SkOSWindow* gWindow;
+
+static void catch_alarm(int sig)
+{
+    SkEvent::ServiceQueueTimer();
+}
+
+int main(int argc, char** argv){
+    signal(SIGALRM, catch_alarm);
+
+    gWindow = create_sk_window(NULL, argc, argv);
+
+    // drain any events that occurred before gWindow was assigned.
+    while (SkEvent::ProcessEvent());
+
+    // Start normal Skia sequence
+    application_init();
+
+    gWindow->loop();
+
+    delete gWindow;
+    application_term();
+    return 0;
+}
+
+// SkEvent handlers
+
+void SkEvent::SignalNonEmptyQueue()
+{
+    if (gWindow) {
+        gWindow->post_linuxevent();
+    }
+}
+
+void SkEvent::SignalQueueTimer(SkMSec delay)
+{
+    itimerval newTimer;
+    newTimer.it_interval.tv_sec = 0;
+    newTimer.it_interval.tv_usec = 0;
+    newTimer.it_value.tv_sec = 0;
+    newTimer.it_value.tv_usec = delay * 1000;
+
+    setitimer(ITIMER_REAL, &newTimer, NULL);
+}
diff --git a/src/views/views_files.mk b/src/views/views_files.mk
new file mode 100644
index 0000000..9c5e9e0
--- /dev/null
+++ b/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/src/views/win/SkOSWindow_win.cpp b/src/views/win/SkOSWindow_win.cpp
new file mode 100644
index 0000000..150be6a
--- /dev/null
+++ b/src/views/win/SkOSWindow_win.cpp
@@ -0,0 +1,659 @@
+
+/*
+ * Copyright 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 defined(SK_BUILD_FOR_WIN)
+
+#include <GL/gl.h>
+#include <WindowsX.h>
+#include "SkWGL.h"
+#include "SkWindow.h"
+#include "SkCanvas.h"
+#include "SkOSMenu.h"
+#include "SkTime.h"
+#include "SkUtils.h"
+
+#include "SkGraphics.h"
+
+#if SK_ANGLE
+#include "gl/GrGLInterface.h"
+
+#include "GLES2/gl2.h"
+
+#define ANGLE_GL_CALL(IFACE, X)                                 \
+    do {                                                        \
+        (IFACE)->f##X;                                          \
+    } while (false)
+
+#endif
+
+#define INVALIDATE_DELAY_MS 200
+
+static SkOSWindow* gCurrOSWin;
+static HWND gEventTarget;
+
+#define WM_EVENT_CALLBACK (WM_USER+0)
+
+void post_skwinevent()
+{
+    PostMessage(gEventTarget, WM_EVENT_CALLBACK, 0, 0);
+}
+
+SkOSWindow::SkOSWindow(void* hWnd) {
+    fHWND = hWnd;
+#if SK_SUPPORT_GPU
+#if SK_ANGLE
+    fDisplay = EGL_NO_DISPLAY;
+    fContext = EGL_NO_CONTEXT;
+    fSurface = EGL_NO_SURFACE;
+#endif
+    fHGLRC = NULL;
+#endif
+    fAttached = kNone_BackEndType;
+    gEventTarget = (HWND)hWnd;
+}
+
+SkOSWindow::~SkOSWindow() {
+#if SK_SUPPORT_GPU
+    if (NULL != fHGLRC) {
+        wglDeleteContext((HGLRC)fHGLRC);
+    }
+#if SK_ANGLE
+    if (EGL_NO_CONTEXT != fContext) {
+        eglDestroyContext(fDisplay, fContext);
+        fContext = EGL_NO_CONTEXT;
+    }
+
+    if (EGL_NO_SURFACE != fSurface) {
+        eglDestroySurface(fDisplay, fSurface);
+        fSurface = EGL_NO_SURFACE;
+    }
+
+    if (EGL_NO_DISPLAY != fDisplay) {
+        eglTerminate(fDisplay);
+        fDisplay = EGL_NO_DISPLAY;
+    }
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
+}
+
+static SkKey winToskKey(WPARAM vk) {
+    static const struct {
+        WPARAM    fVK;
+        SkKey    fKey;
+    } gPair[] = {
+        { VK_BACK,    kBack_SkKey },
+        { VK_CLEAR,    kBack_SkKey },
+        { VK_RETURN, kOK_SkKey },
+        { VK_UP,     kUp_SkKey },
+        { VK_DOWN,     kDown_SkKey },
+        { VK_LEFT,     kLeft_SkKey },
+        { VK_RIGHT,     kRight_SkKey }
+    };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
+        if (gPair[i].fVK == vk) {
+            return gPair[i].fKey;
+        }
+    }
+    return kNONE_SkKey;
+}
+
+bool SkOSWindow::wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+    switch (message) {
+        case WM_KEYDOWN: {
+            SkKey key = winToskKey(wParam);
+            if (kNONE_SkKey != key) {
+                this->handleKey(key);
+                return true;
+            }
+        } break;
+        case WM_KEYUP: {
+            SkKey key = winToskKey(wParam);
+            if (kNONE_SkKey != key) {
+                this->handleKeyUp(key);
+                return true;
+            }
+        } break;
+        case WM_UNICHAR:
+            this->handleChar(wParam);
+            return true;
+        case WM_CHAR: {
+            this->handleChar(SkUTF8_ToUnichar((char*)&wParam));
+            return true;
+        } break;
+        case WM_SIZE:
+            this->resize(lParam & 0xFFFF, lParam >> 16);
+            break;
+        case WM_PAINT: {
+            PAINTSTRUCT ps;
+            HDC hdc = BeginPaint(hWnd, &ps);
+            this->doPaint(hdc);
+            EndPaint(hWnd, &ps);
+            return true;
+            } break;
+
+        case WM_TIMER: {
+            RECT* rect = (RECT*)wParam;
+            InvalidateRect(hWnd, rect, FALSE);
+            KillTimer(hWnd, (UINT_PTR)rect);
+            delete rect;
+            return true;
+        } break;
+
+        case WM_LBUTTONDOWN:
+            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kDown_State);
+            return true;
+
+        case WM_MOUSEMOVE:
+            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kMoved_State);
+            return true;
+
+        case WM_LBUTTONUP:
+            this->handleClick(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), Click::kUp_State);
+            return true;
+
+        case WM_EVENT_CALLBACK:
+            if (SkEvent::ProcessEvent()) {
+                post_skwinevent();
+            }
+            return true;
+    }
+    return false;
+}
+
+void SkOSWindow::doPaint(void* ctx) {
+    this->update(NULL);
+
+    if (kNone_BackEndType == fAttached)
+    {
+        HDC hdc = (HDC)ctx;
+        const SkBitmap& bitmap = this->getBitmap();
+
+        BITMAPINFO bmi;
+        memset(&bmi, 0, sizeof(bmi));
+        bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
+        bmi.bmiHeader.biWidth       = bitmap.width();
+        bmi.bmiHeader.biHeight      = -bitmap.height(); // top-down image
+        bmi.bmiHeader.biPlanes      = 1;
+        bmi.bmiHeader.biBitCount    = 32;
+        bmi.bmiHeader.biCompression = BI_RGB;
+        bmi.bmiHeader.biSizeImage   = 0;
+
+        //
+        // Do the SetDIBitsToDevice.
+        //
+        // TODO(wjmaclean):
+        //       Fix this call to handle SkBitmaps that have rowBytes != width,
+        //       i.e. may have padding at the end of lines. The SkASSERT below
+        //       may be ignored by builds, and the only obviously safe option
+        //       seems to be to copy the bitmap to a temporary (contiguous)
+        //       buffer before passing to SetDIBitsToDevice().
+        SkASSERT(bitmap.width() * bitmap.bytesPerPixel() == bitmap.rowBytes());
+        bitmap.lockPixels();
+        int iRet = SetDIBitsToDevice(hdc,
+            0, 0,
+            bitmap.width(), bitmap.height(),
+            0, 0,
+            0, bitmap.height(),
+            bitmap.getPixels(),
+            &bmi,
+            DIB_RGB_COLORS);
+        bitmap.unlockPixels();
+    }
+}
+
+#if 0
+void SkOSWindow::updateSize()
+{
+    RECT    r;
+    GetWindowRect((HWND)this->getHWND(), &r);
+    this->resize(r.right - r.left, r.bottom - r.top);
+}
+#endif
+
+void SkOSWindow::onHandleInval(const SkIRect& r) {
+    RECT* rect = new RECT;
+    rect->left    = r.fLeft;
+    rect->top     = r.fTop;
+    rect->right   = r.fRight;
+    rect->bottom  = r.fBottom;
+    SetTimer((HWND)fHWND, (UINT_PTR)rect, INVALIDATE_DELAY_MS, NULL);
+}
+
+void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
+{
+}
+
+void SkOSWindow::onSetTitle(const char title[]){
+    SetWindowTextA((HWND)fHWND, title);
+}
+
+enum {
+    SK_MacReturnKey     = 36,
+    SK_MacDeleteKey     = 51,
+    SK_MacEndKey        = 119,
+    SK_MacLeftKey       = 123,
+    SK_MacRightKey      = 124,
+    SK_MacDownKey       = 125,
+    SK_MacUpKey         = 126,
+
+    SK_Mac0Key          = 0x52,
+    SK_Mac1Key          = 0x53,
+    SK_Mac2Key          = 0x54,
+    SK_Mac3Key          = 0x55,
+    SK_Mac4Key          = 0x56,
+    SK_Mac5Key          = 0x57,
+    SK_Mac6Key          = 0x58,
+    SK_Mac7Key          = 0x59,
+    SK_Mac8Key          = 0x5b,
+    SK_Mac9Key          = 0x5c
+};
+
+static SkKey raw2key(uint32_t raw)
+{
+    static const struct {
+        uint32_t  fRaw;
+        SkKey   fKey;
+    } gKeys[] = {
+        { SK_MacUpKey,      kUp_SkKey       },
+        { SK_MacDownKey,    kDown_SkKey     },
+        { SK_MacLeftKey,    kLeft_SkKey     },
+        { SK_MacRightKey,   kRight_SkKey    },
+        { SK_MacReturnKey,  kOK_SkKey       },
+        { SK_MacDeleteKey,  kBack_SkKey     },
+        { SK_MacEndKey,     kEnd_SkKey      },
+        { SK_Mac0Key,       k0_SkKey        },
+        { SK_Mac1Key,       k1_SkKey        },
+        { SK_Mac2Key,       k2_SkKey        },
+        { SK_Mac3Key,       k3_SkKey        },
+        { SK_Mac4Key,       k4_SkKey        },
+        { SK_Mac5Key,       k5_SkKey        },
+        { SK_Mac6Key,       k6_SkKey        },
+        { SK_Mac7Key,       k7_SkKey        },
+        { SK_Mac8Key,       k8_SkKey        },
+        { SK_Mac9Key,       k9_SkKey        }
+    };
+
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++)
+        if (gKeys[i].fRaw == raw)
+            return gKeys[i].fKey;
+    return kNONE_SkKey;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void SkEvent::SignalNonEmptyQueue()
+{
+    post_skwinevent();
+    //SkDebugf("signal nonempty\n");
+}
+
+static UINT_PTR gTimer;
+
+VOID CALLBACK sk_timer_proc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+    SkEvent::ServiceQueueTimer();
+    //SkDebugf("timer task fired\n");
+}
+
+void SkEvent::SignalQueueTimer(SkMSec delay)
+{
+    if (gTimer)
+    {
+        KillTimer(NULL, gTimer);
+        gTimer = NULL;
+    }
+    if (delay)
+    {
+        gTimer = SetTimer(NULL, 0, delay, sk_timer_proc);
+        //SkDebugf("SetTimer of %d returned %d\n", delay, gTimer);
+    }
+}
+
+#if SK_SUPPORT_GPU
+HGLRC create_gl(HWND hwnd, int msaaSampleCount) {
+
+    HDC dc = GetDC(hwnd);
+
+    SkWGLExtensions extensions;
+    if (!extensions.hasExtension(dc, "WGL_ARB_pixel_format")) {
+        return NULL;
+    }
+
+    HDC prevDC = wglGetCurrentDC();
+    HGLRC prevGLRC = wglGetCurrentContext();
+    PIXELFORMATDESCRIPTOR pfd;
+
+    int format = 0;
+
+    static const GLint iAttrs[] = {
+        SK_WGL_DRAW_TO_WINDOW, TRUE,
+        SK_WGL_DOUBLE_BUFFER, TRUE,
+        SK_WGL_ACCELERATION, SK_WGL_FULL_ACCELERATION,
+        SK_WGL_SUPPORT_OPENGL, TRUE,
+        SK_WGL_COLOR_BITS, 24,
+        SK_WGL_ALPHA_BITS, 8,
+        SK_WGL_STENCIL_BITS, 8,
+        0, 0
+    };
+
+    GLfloat fAttrs[] = {0, 0};
+
+    if (msaaSampleCount > 0 &&
+        extensions.hasExtension(dc, "WGL_ARB_multisample")) {
+        static const int kIAttrsCount = SK_ARRAY_COUNT(iAttrs);
+        GLint msaaIAttrs[kIAttrsCount + 6];
+        memcpy(msaaIAttrs, iAttrs, sizeof(GLint) * kIAttrsCount);
+        SkASSERT(0 == msaaIAttrs[kIAttrsCount - 2] &&
+                 0 == msaaIAttrs[kIAttrsCount - 1]);
+        msaaIAttrs[kIAttrsCount - 2] = SK_WGL_SAMPLE_BUFFERS;
+        msaaIAttrs[kIAttrsCount - 1] = TRUE;
+        msaaIAttrs[kIAttrsCount + 0] = SK_WGL_SAMPLES;
+        msaaIAttrs[kIAttrsCount + 1] = msaaSampleCount;
+        if (extensions.hasExtension(dc, "WGL_NV_multisample_coverage")) {
+            msaaIAttrs[kIAttrsCount + 2] = SK_WGL_COLOR_SAMPLES;
+            // We want the fewest number of color samples possible.
+            // Passing 0 gives only the formats where all samples are color
+            // samples.
+            msaaIAttrs[kIAttrsCount + 3] = 1;
+            msaaIAttrs[kIAttrsCount + 4] = 0;
+            msaaIAttrs[kIAttrsCount + 5] = 0;
+        } else {
+            msaaIAttrs[kIAttrsCount + 2] = 0;
+            msaaIAttrs[kIAttrsCount + 3] = 0;
+        }
+        GLuint num;
+        int formats[64];
+        extensions.choosePixelFormat(dc, msaaIAttrs, fAttrs, 64, formats, &num);
+        num = min(num,64);
+        int formatToTry = extensions.selectFormat(formats,
+                                                  num,
+                                                  dc,
+                                                  msaaSampleCount);
+        DescribePixelFormat(dc, formatToTry, sizeof(pfd), &pfd);
+        if (SetPixelFormat(dc, formatToTry, &pfd)) {
+            format = formatToTry;
+        }
+    }
+
+    if (0 == format) {
+        GLuint num;
+        extensions.choosePixelFormat(dc, iAttrs, fAttrs, 1, &format, &num);
+        DescribePixelFormat(dc, format, sizeof(pfd), &pfd);
+        BOOL set = SetPixelFormat(dc, format, &pfd);
+        SkASSERT(TRUE == set);
+    }
+
+    HGLRC glrc = wglCreateContext(dc);
+    SkASSERT(glrc);
+
+    wglMakeCurrent(prevDC, prevGLRC);
+    return glrc;
+}
+
+bool SkOSWindow::attachGL(int msaaSampleCount) {
+    if (NULL == fHGLRC) {
+        fHGLRC = create_gl((HWND)fHWND, msaaSampleCount);
+        if (NULL == fHGLRC) {
+            return false;
+        }
+        glClearStencil(0);
+        glClearColor(0, 0, 0, 0);
+        glStencilMask(0xffffffff);
+        glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+    }
+    if (wglMakeCurrent(GetDC((HWND)fHWND), (HGLRC)fHGLRC)) {
+        glViewport(0, 0, SkScalarRound(this->width()),
+                   SkScalarRound(this->height()));
+        return true;
+    }
+    return false;
+}
+
+void SkOSWindow::detachGL() {
+    wglMakeCurrent(GetDC((HWND)fHWND), 0);
+    wglDeleteContext((HGLRC)fHGLRC);
+    fHGLRC = NULL;
+}
+
+void SkOSWindow::presentGL() {
+    glFlush();
+    SwapBuffers(GetDC((HWND)fHWND));
+}
+
+#if SK_ANGLE
+bool create_ANGLE(EGLNativeWindowType hWnd,
+                  int msaaSampleCount,
+                  EGLDisplay* eglDisplay,
+                  EGLContext* eglContext,
+                  EGLSurface* eglSurface) {
+    static const EGLint contextAttribs[] = {
+        EGL_CONTEXT_CLIENT_VERSION, 2,
+        EGL_NONE, EGL_NONE
+    };
+    static const EGLint configAttribList[] = {
+        EGL_RED_SIZE,       8,
+        EGL_GREEN_SIZE,     8,
+        EGL_BLUE_SIZE,      8,
+        EGL_ALPHA_SIZE,     8,
+        EGL_DEPTH_SIZE,     8,
+        EGL_STENCIL_SIZE,   8,
+        EGL_NONE
+    };
+    static const EGLint surfaceAttribList[] = {
+        EGL_NONE, EGL_NONE
+    };
+
+    EGLDisplay display = eglGetDisplay(GetDC(hWnd));
+    if (display == EGL_NO_DISPLAY ) {
+       return false;
+    }
+
+    // Initialize EGL
+    EGLint majorVersion, minorVersion;
+    if (!eglInitialize(display, &majorVersion, &minorVersion)) {
+       return false;
+    }
+
+    EGLint numConfigs;
+    if (!eglGetConfigs(display, NULL, 0, &numConfigs)) {
+       return false;
+    }
+
+    // Choose config
+    EGLConfig config;
+    bool foundConfig = false;
+    if (msaaSampleCount) {
+        static const int kConfigAttribListCnt =
+                                SK_ARRAY_COUNT(configAttribList);
+        EGLint msaaConfigAttribList[kConfigAttribListCnt + 4];
+        memcpy(msaaConfigAttribList,
+               configAttribList,
+               sizeof(configAttribList));
+        SkASSERT(EGL_NONE == msaaConfigAttribList[kConfigAttribListCnt - 1]);
+        msaaConfigAttribList[kConfigAttribListCnt - 1] = EGL_SAMPLE_BUFFERS;
+        msaaConfigAttribList[kConfigAttribListCnt + 0] = 1;
+        msaaConfigAttribList[kConfigAttribListCnt + 1] = EGL_SAMPLES;
+        msaaConfigAttribList[kConfigAttribListCnt + 2] = msaaSampleCount;
+        msaaConfigAttribList[kConfigAttribListCnt + 3] = EGL_NONE;
+        if (eglChooseConfig(display, configAttribList,
+                                   &config, 1, &numConfigs)) {
+            SkASSERT(numConfigs > 0);
+            foundConfig = true;
+        }
+    }
+    if (!foundConfig) {
+        if (!eglChooseConfig(display, configAttribList,
+                                    &config, 1, &numConfigs)) {
+           return false;
+        }
+    }
+
+    // Create a surface
+    EGLSurface surface = eglCreateWindowSurface(display, config,
+                                                (EGLNativeWindowType)hWnd,
+                                                surfaceAttribList);
+    if (surface == EGL_NO_SURFACE) {
+       return false;
+    }
+
+    // Create a GL context
+    EGLContext context = eglCreateContext(display, config,
+                                          EGL_NO_CONTEXT,
+                                          contextAttribs );
+    if (context == EGL_NO_CONTEXT ) {
+       return false;
+    }
+
+    // Make the context current
+    if (!eglMakeCurrent(display, surface, surface, context)) {
+       return false;
+    }
+
+    *eglDisplay = display;
+    *eglContext = context;
+    *eglSurface = surface;
+    return true;
+}
+
+bool SkOSWindow::attachANGLE(int msaaSampleCount) {
+    if (EGL_NO_DISPLAY == fDisplay) {
+        bool bResult = create_ANGLE((HWND)fHWND,
+                                    msaaSampleCount,
+                                    &fDisplay,
+                                    &fContext,
+                                    &fSurface);
+        if (false == bResult) {
+            return false;
+        }
+        const GrGLInterface* intf = GrGLCreateANGLEInterface();
+
+        if (intf) {
+            ANGLE_GL_CALL(intf, ClearStencil(0));
+            ANGLE_GL_CALL(intf, ClearColor(0, 0, 0, 0));
+            ANGLE_GL_CALL(intf, StencilMask(0xffffffff));
+            ANGLE_GL_CALL(intf, Clear(GL_STENCIL_BUFFER_BIT |GL_COLOR_BUFFER_BIT));
+        }
+    }
+    if (eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
+        const GrGLInterface* intf = GrGLCreateANGLEInterface();
+
+        if (intf ) {
+            ANGLE_GL_CALL(intf, Viewport(0, 0, SkScalarRound(this->width()),
+                                      SkScalarRound(this->height())));
+        }
+        return true;
+    }
+    return false;
+}
+
+void SkOSWindow::detachANGLE() {
+    eglMakeCurrent(fDisplay, EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT);
+
+    eglDestroyContext(fDisplay, fContext);
+    fContext = EGL_NO_CONTEXT;
+
+    eglDestroySurface(fDisplay, fSurface);
+    fSurface = EGL_NO_SURFACE;
+
+    eglTerminate(fDisplay);
+    fDisplay = EGL_NO_DISPLAY;
+}
+
+void SkOSWindow::presentANGLE() {
+    const GrGLInterface* intf = GrGLCreateANGLEInterface();
+
+    if (intf) {
+        ANGLE_GL_CALL(intf, Flush());
+    }
+
+    eglSwapBuffers(fDisplay, fSurface);
+}
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
+
+// return true on success
+bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount) {
+
+    // attach doubles as "windowResize" so we need to allo
+    // already bound states to pass through again
+    // TODO: split out the resize functionality
+//    SkASSERT(kNone_BackEndType == fAttached);
+    bool result = true;
+
+    switch (attachType) {
+    case kNone_BackEndType:
+        // nothing to do
+        break;
+#if SK_SUPPORT_GPU
+    case kNativeGL_BackEndType:
+        result = attachGL(msaaSampleCount);
+        break;
+#if SK_ANGLE
+    case kANGLE_BackEndType:
+        result = attachANGLE(msaaSampleCount);
+        break;
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
+    default:
+        SkASSERT(false);
+        result = false;
+        break;
+    }
+
+    if (result) {
+        fAttached = attachType;
+    }
+
+    return result;
+}
+
+void SkOSWindow::detach() {
+    switch (fAttached) {
+    case kNone_BackEndType:
+        // nothing to do
+        break;
+#if SK_SUPPORT_GPU
+    case kNativeGL_BackEndType:
+        detachGL();
+        break;
+#if SK_ANGLE
+    case kANGLE_BackEndType:
+        detachANGLE();
+        break;
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
+    default:
+        SkASSERT(false);
+        break;
+    }
+    fAttached = kNone_BackEndType;
+}
+
+void SkOSWindow::present() {
+    switch (fAttached) {
+    case kNone_BackEndType:
+        // nothing to do
+        return;
+#if SK_SUPPORT_GPU
+    case kNativeGL_BackEndType:
+        presentGL();
+        break;
+#if SK_ANGLE
+    case kANGLE_BackEndType:
+        presentANGLE();
+        break;
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
+    default:
+        SkASSERT(false);
+        break;
+    }
+}
+
+#endif
diff --git a/src/views/win/skia_win.cpp b/src/views/win/skia_win.cpp
new file mode 100644
index 0000000..2643cc8
--- /dev/null
+++ b/src/views/win/skia_win.cpp
@@ -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.
+ */
+#include <Windows.h>
+#include <tchar.h>
+#define MAX_LOADSTRING 100
+
+// Global Variables:
+HINSTANCE hInst;                            // current instance
+TCHAR szTitle[] = _T("SampleApp");          // The title bar text
+TCHAR szWindowClass[] = _T("SAMPLEAPP");    // the main window class name
+
+// Forward declarations of functions included in this code module:
+ATOM                MyRegisterClass(HINSTANCE hInstance);
+BOOL                InitInstance(HINSTANCE, int, LPTSTR);
+LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
+INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
+
+int APIENTRY _tWinMain(HINSTANCE hInstance,
+                     HINSTANCE hPrevInstance,
+                     LPTSTR    lpCmdLine,
+                     int       nCmdShow)
+{
+    UNREFERENCED_PARAMETER(hPrevInstance);
+
+    MSG msg;
+
+    // Initialize global strings
+    MyRegisterClass(hInstance);
+
+    // Perform application initialization:
+    if (!InitInstance (hInstance, nCmdShow, lpCmdLine))
+    {
+        return FALSE;
+    }
+
+    // Main message loop:
+    while (GetMessage(&msg, NULL, 0, 0))
+    {
+        if (true)
+        {
+            TranslateMessage(&msg);
+            DispatchMessage(&msg);
+        }
+    }
+
+    return (int) msg.wParam;
+}
+
+
+
+//
+//  FUNCTION: MyRegisterClass()
+//
+//  PURPOSE: Registers the window class.
+//
+//  COMMENTS:
+//
+//    This function and its usage are only necessary if you want this code
+//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
+//    function that was added to Windows 95. It is important to call this function
+//    so that the application will get 'well formed' small icons associated
+//    with it.
+//
+ATOM MyRegisterClass(HINSTANCE hInstance)
+{
+    WNDCLASSEX wcex;
+
+    wcex.cbSize = sizeof(WNDCLASSEX);
+
+    wcex.style            = CS_HREDRAW | CS_VREDRAW;
+    wcex.lpfnWndProc    = WndProc;
+    wcex.cbClsExtra        = 0;
+    wcex.cbWndExtra        = 0;
+    wcex.hInstance        = hInstance;
+    wcex.hIcon            = NULL;
+    wcex.hCursor        = NULL;
+    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
+    wcex.lpszMenuName    = NULL;
+    wcex.lpszClassName    = szWindowClass;
+    wcex.hIconSm        = NULL;
+
+    return RegisterClassEx(&wcex);
+}
+
+#include "SkOSWindow_Win.h"
+extern SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv);
+
+static SkOSWindow* gSkWind;
+
+char* tchar_to_utf8(const TCHAR* str) {
+#ifdef _UNICODE
+    int size = WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str), NULL, 0, NULL, NULL);
+    char* str8 = (char*) malloc(size+1);
+    WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str), str8, size, NULL, NULL);
+    str8[size] = '\0';
+    return str8;
+#else
+    return _strdup(str);
+#endif
+}
+
+//
+//   FUNCTION: InitInstance(HINSTANCE, int, LPTSTR)
+//
+//   PURPOSE: Saves instance handle and creates main window
+//
+//   COMMENTS:
+//
+//        In this function, we save the instance handle in a global variable and
+//        create and display the main program window.
+//
+
+
+BOOL InitInstance(HINSTANCE hInstance, int nCmdShow, LPTSTR lpCmdLine)
+{
+   HWND hWnd;
+
+   hInst = hInstance; // Store instance handle in our global variable
+
+   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
+      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
+
+   if (!hWnd)
+   {
+      return FALSE;
+   }
+
+   char* argv[4096];
+   int argc = 0;
+   TCHAR exename[1024], *next;
+   int exenameLen = GetModuleFileName(NULL, exename, 1024);
+   argv[argc++] = tchar_to_utf8(exename);
+   TCHAR* arg = _tcstok_s(lpCmdLine, _T(" "), &next);
+   while (arg != NULL) {
+      argv[argc++] = tchar_to_utf8(arg);
+      arg = _tcstok_s(NULL, _T(" "), &next);
+   }
+
+   gSkWind = create_sk_window(hWnd, argc, argv);
+   for (int i = 0; i < argc; ++i) {
+      free(argv[i]);
+   }
+
+   ShowWindow(hWnd, nCmdShow);
+   UpdateWindow(hWnd);
+
+   return TRUE;
+}
+
+//
+//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
+//
+//  PURPOSE:  Processes messages for the main window.
+//
+//  WM_COMMAND    - process the application menu
+//  WM_PAINT    - Paint the main window
+//  WM_DESTROY    - post a quit message and return
+//
+//
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    switch (message) {
+    case WM_COMMAND:
+        return DefWindowProc(hWnd, message, wParam, lParam);
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        break;
+    default:
+        if (gSkWind->wndProc(hWnd, message, wParam, lParam)) {
+            return 0;
+        } else {
+            return DefWindowProc(hWnd, message, wParam, lParam);
+        }
+    }
+    return 0;
+}
+
+// Message handler for about box.
+INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    UNREFERENCED_PARAMETER(lParam);
+    switch (message)
+    {
+    case WM_INITDIALOG:
+        return (INT_PTR)TRUE;
+
+    case WM_COMMAND:
+        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
+        {
+            EndDialog(hDlg, LOWORD(wParam));
+            return (INT_PTR)TRUE;
+        }
+        break;
+    }
+    return (INT_PTR)FALSE;
+}
diff --git a/src/xml/SkBML_Verbs.h b/src/xml/SkBML_Verbs.h
new file mode 100644
index 0000000..709764d
--- /dev/null
+++ b/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/src/xml/SkBML_XMLParser.cpp b/src/xml/SkBML_XMLParser.cpp
new file mode 100644
index 0000000..dbdfe4b
--- /dev/null
+++ b/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/src/xml/SkDOM.cpp b/src/xml/SkDOM.cpp
new file mode 100644
index 0000000..bdfdd86
--- /dev/null
+++ b/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/src/xml/SkJS.cpp b/src/xml/SkJS.cpp
new file mode 100644
index 0000000..f2e7a83
--- /dev/null
+++ b/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/src/xml/SkJSDisplayable.cpp b/src/xml/SkJSDisplayable.cpp
new file mode 100644
index 0000000..5027797
--- /dev/null
+++ b/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/src/xml/SkXMLParser.cpp b/src/xml/SkXMLParser.cpp
new file mode 100644
index 0000000..63929a9
--- /dev/null
+++ b/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/src/xml/SkXMLPullParser.cpp b/src/xml/SkXMLPullParser.cpp
new file mode 100644
index 0000000..4080aeb
--- /dev/null
+++ b/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/src/xml/SkXMLWriter.cpp b/src/xml/SkXMLWriter.cpp
new file mode 100644
index 0000000..451d1d5
--- /dev/null
+++ b/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/src/xml/xml_files.mk b/src/xml/xml_files.mk
new file mode 100644
index 0000000..d4342dd
--- /dev/null
+++ b/src/xml/xml_files.mk
@@ -0,0 +1,3 @@
+SOURCE := \
+    SkDOM.cpp \
+    SkXMLParser.cpp
diff --git a/tests/AAClipTest.cpp b/tests/AAClipTest.cpp
new file mode 100644
index 0000000..1b060a7
--- /dev/null
+++ b/tests/AAClipTest.cpp
@@ -0,0 +1,408 @@
+/*
+ * Copyright 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 "SkAAClip.h"
+#include "SkCanvas.h"
+#include "SkMask.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+
+static bool operator==(const SkMask& a, const SkMask& b) {
+    if (a.fFormat != b.fFormat || a.fBounds != b.fBounds) {
+        return false;
+    }
+    if (!a.fImage && !b.fImage) {
+        return true;
+    }
+    if (!a.fImage || !b.fImage) {
+        return false;
+    }
+
+    size_t wbytes = a.fBounds.width();
+    switch (a.fFormat) {
+        case SkMask::kBW_Format:
+            wbytes = (wbytes + 7) >> 3;
+            break;
+        case SkMask::kA8_Format:
+        case SkMask::k3D_Format:
+            break;
+        case SkMask::kLCD16_Format:
+            wbytes <<= 1;
+            break;
+        case SkMask::kLCD32_Format:
+        case SkMask::kARGB32_Format:
+            wbytes <<= 2;
+            break;
+        default:
+            SkASSERT(!"unknown mask format");
+            return false;
+    }
+
+    const int h = a.fBounds.height();
+    const char* aptr = (const char*)a.fImage;
+    const char* bptr = (const char*)b.fImage;
+    for (int y = 0; y < h; ++y) {
+        if (memcmp(aptr, bptr, wbytes)) {
+            return false;
+        }
+        aptr += wbytes;
+        bptr += wbytes;
+    }
+    return true;
+}
+
+static void copyToMask(const SkRegion& rgn, SkMask* mask) {
+    mask->fFormat = SkMask::kA8_Format;
+
+    if (rgn.isEmpty()) {
+        mask->fBounds.setEmpty();
+        mask->fRowBytes = 0;
+        mask->fImage = NULL;
+        return;
+    }
+
+    mask->fBounds = rgn.getBounds();
+    mask->fRowBytes = mask->fBounds.width();
+    mask->fImage = SkMask::AllocImage(mask->computeImageSize());
+    sk_bzero(mask->fImage, mask->computeImageSize());
+
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kA8_Config, mask->fBounds.width(),
+                     mask->fBounds.height(), mask->fRowBytes);
+    bitmap.setPixels(mask->fImage);
+
+    // canvas expects its coordinate system to always be 0,0 in the top/left
+    // so we translate the rgn to match that before drawing into the mask.
+    //
+    SkRegion tmpRgn(rgn);
+    tmpRgn.translate(-rgn.getBounds().fLeft, -rgn.getBounds().fTop);
+
+    SkCanvas canvas(bitmap);
+    canvas.clipRegion(tmpRgn);
+    canvas.drawColor(SK_ColorBLACK);
+}
+
+static SkIRect rand_rect(SkRandom& rand, int n) {
+    int x = rand.nextS() % n;
+    int y = rand.nextS() % n;
+    int w = rand.nextU() % n;
+    int h = rand.nextU() % n;
+    return SkIRect::MakeXYWH(x, y, w, h);
+}
+
+static void make_rand_rgn(SkRegion* rgn, SkRandom& rand) {
+    int count = rand.nextU() % 20;
+    for (int i = 0; i < count; ++i) {
+        rgn->op(rand_rect(rand, 100), SkRegion::kXOR_Op);
+    }
+}
+
+static bool operator==(const SkRegion& rgn, const SkAAClip& aaclip) {
+    SkMask mask0, mask1;
+
+    copyToMask(rgn, &mask0);
+    aaclip.copyToMask(&mask1);
+    bool eq = (mask0 == mask1);
+
+    SkMask::FreeImage(mask0.fImage);
+    SkMask::FreeImage(mask1.fImage);
+    return eq;
+}
+
+static bool equalsAAClip(const SkRegion& rgn) {
+    SkAAClip aaclip;
+    aaclip.setRegion(rgn);
+    return rgn == aaclip;
+}
+
+static void setRgnToPath(SkRegion* rgn, const SkPath& path) {
+    SkIRect ir;
+    path.getBounds().round(&ir);
+    rgn->setPath(path, SkRegion(ir));
+}
+
+// aaclip.setRegion should create idential masks to the region
+static void test_rgn(skiatest::Reporter* reporter) {
+    SkRandom rand;
+    for (int i = 0; i < 1000; i++) {
+        SkRegion rgn;
+        make_rand_rgn(&rgn, rand);
+        REPORTER_ASSERT(reporter, equalsAAClip(rgn));
+    }
+
+    {
+        SkRegion rgn;
+        SkPath path;
+        path.addCircle(0, 0, SkIntToScalar(30));
+        setRgnToPath(&rgn, path);
+        REPORTER_ASSERT(reporter, equalsAAClip(rgn));
+
+        path.reset();
+        path.moveTo(0, 0);
+        path.lineTo(SkIntToScalar(100), 0);
+        path.lineTo(SkIntToScalar(100 - 20), SkIntToScalar(20));
+        path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
+        setRgnToPath(&rgn, path);
+        REPORTER_ASSERT(reporter, equalsAAClip(rgn));
+    }
+}
+
+static const SkRegion::Op gRgnOps[] = {
+    SkRegion::kDifference_Op,
+    SkRegion::kIntersect_Op,
+    SkRegion::kUnion_Op,
+    SkRegion::kXOR_Op,
+    SkRegion::kReverseDifference_Op,
+    SkRegion::kReplace_Op
+};
+
+static const char* gRgnOpNames[] = {
+    "DIFF", "INTERSECT", "UNION", "XOR", "REVERSE_DIFF", "REPLACE"
+};
+
+static void imoveTo(SkPath& path, int x, int y) {
+    path.moveTo(SkIntToScalar(x), SkIntToScalar(y));
+}
+
+static void icubicTo(SkPath& path, int x0, int y0, int x1, int y1, int x2, int y2) {
+    path.cubicTo(SkIntToScalar(x0), SkIntToScalar(y0),
+                 SkIntToScalar(x1), SkIntToScalar(y1),
+                 SkIntToScalar(x2), SkIntToScalar(y2));
+}
+
+static void test_path_bounds(skiatest::Reporter* reporter) {
+    SkPath path;
+    SkAAClip clip;
+    const int height = 40;
+    const SkScalar sheight = SkIntToScalar(height);
+
+    path.addOval(SkRect::MakeWH(sheight, sheight));
+    REPORTER_ASSERT(reporter, sheight == path.getBounds().height());
+    clip.setPath(path, NULL, true);
+    REPORTER_ASSERT(reporter, height == clip.getBounds().height());
+
+    // this is the trimmed height of this cubic (with aa). The critical thing
+    // for this test is that it is less than height, which represents just
+    // the bounds of the path's control-points.
+    //
+    // This used to fail until we tracked the MinY in the BuilderBlitter.
+    //
+    const int teardrop_height = 12;
+    path.reset();
+    imoveTo(path, 0, 20);
+    icubicTo(path, 40, 40, 40, 0, 0, 20);
+    REPORTER_ASSERT(reporter, sheight == path.getBounds().height());
+    clip.setPath(path, NULL, true);
+    REPORTER_ASSERT(reporter, teardrop_height == clip.getBounds().height());
+}
+
+static void test_empty(skiatest::Reporter* reporter) {
+    SkAAClip clip0, clip1;
+
+    REPORTER_ASSERT(reporter, clip0.isEmpty());
+    REPORTER_ASSERT(reporter, clip0.getBounds().isEmpty());
+    REPORTER_ASSERT(reporter, clip1 == clip0);
+
+    clip0.translate(10, 10);    // should have no effect on empty
+    REPORTER_ASSERT(reporter, clip0.isEmpty());
+    REPORTER_ASSERT(reporter, clip0.getBounds().isEmpty());
+    REPORTER_ASSERT(reporter, clip1 == clip0);
+
+    SkIRect r = { 10, 10, 40, 50 };
+    clip0.setRect(r);
+    REPORTER_ASSERT(reporter, !clip0.isEmpty());
+    REPORTER_ASSERT(reporter, !clip0.getBounds().isEmpty());
+    REPORTER_ASSERT(reporter, clip0 != clip1);
+    REPORTER_ASSERT(reporter, clip0.getBounds() == r);
+
+    clip0.setEmpty();
+    REPORTER_ASSERT(reporter, clip0.isEmpty());
+    REPORTER_ASSERT(reporter, clip0.getBounds().isEmpty());
+    REPORTER_ASSERT(reporter, clip1 == clip0);
+
+    SkMask mask;
+    mask.fImage = NULL;
+    clip0.copyToMask(&mask);
+    REPORTER_ASSERT(reporter, NULL == mask.fImage);
+    REPORTER_ASSERT(reporter, mask.fBounds.isEmpty());
+}
+
+static void rand_irect(SkIRect* r, int N, SkRandom& rand) {
+    r->setXYWH(0, 0, rand.nextU() % N, rand.nextU() % N);
+    int dx = rand.nextU() % (2*N);
+    int dy = rand.nextU() % (2*N);
+    // use int dx,dy to make the subtract be signed
+    r->offset(N - dx, N - dy);
+}
+
+static void test_irect(skiatest::Reporter* reporter) {
+    SkRandom rand;
+
+    for (int i = 0; i < 10000; i++) {
+        SkAAClip clip0, clip1;
+        SkRegion rgn0, rgn1;
+        SkIRect r0, r1;
+
+        rand_irect(&r0, 10, rand);
+        rand_irect(&r1, 10, rand);
+        clip0.setRect(r0);
+        clip1.setRect(r1);
+        rgn0.setRect(r0);
+        rgn1.setRect(r1);
+        for (size_t j = 0; j < SK_ARRAY_COUNT(gRgnOps); ++j) {
+            SkRegion::Op op = gRgnOps[j];
+            SkAAClip clip2;
+            SkRegion rgn2;
+            bool nonEmptyAA = clip2.op(clip0, clip1, op);
+            bool nonEmptyBW = rgn2.op(rgn0, rgn1, op);
+            if (nonEmptyAA != nonEmptyBW || clip2.getBounds() != rgn2.getBounds()) {
+                SkDebugf("[%d %d %d %d] %s [%d %d %d %d] = BW:[%d %d %d %d] AA:[%d %d %d %d]\n",
+                         r0.fLeft, r0.fTop, r0.right(), r0.bottom(),
+                         gRgnOpNames[j],
+                         r1.fLeft, r1.fTop, r1.right(), r1.bottom(),
+                         rgn2.getBounds().fLeft, rgn2.getBounds().fTop,
+                         rgn2.getBounds().right(), rgn2.getBounds().bottom(),
+                         clip2.getBounds().fLeft, clip2.getBounds().fTop,
+                         clip2.getBounds().right(), clip2.getBounds().bottom());
+            }
+            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);
+        }
+    }
+}
+
+static void test_path_with_hole(skiatest::Reporter* reporter) {
+    static const uint8_t gExpectedImage[] = {
+        0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF,
+    };
+    SkMask expected;
+    expected.fBounds.set(0, 0, 4, 6);
+    expected.fRowBytes = 4;
+    expected.fFormat = SkMask::kA8_Format;
+    expected.fImage = (uint8_t*)gExpectedImage;
+
+    SkPath path;
+    path.addRect(SkRect::MakeXYWH(0, 0,
+                                  SkIntToScalar(4), SkIntToScalar(2)));
+    path.addRect(SkRect::MakeXYWH(0, SkIntToScalar(4),
+                                  SkIntToScalar(4), SkIntToScalar(2)));
+
+    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.892181f);
+        r.fTop = SkFloatToScalar(10.3999996f);
+        r.fRight = SkFloatToScalar(130.892181f);
+        r.fBottom = SkFloatToScalar(20.3999996f);
+        clip.setRect(r, true);
+    }
+}
+
+static void TestAAClip(skiatest::Reporter* reporter) {
+    test_empty(reporter);
+    test_path_bounds(reporter);
+    test_irect(reporter);
+    test_rgn(reporter);
+    test_path_with_hole(reporter);
+    test_regressions(reporter);
+    test_nearly_integral(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("AAClip", AAClipTestClass, TestAAClip)
diff --git a/tests/AnnotationTest.cpp b/tests/AnnotationTest.cpp
new file mode 100644
index 0000000..17da622
--- /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(0);
+
+    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..5275e84
--- /dev/null
+++ b/tests/AtomicTest.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 "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/BitSetTest.cpp b/tests/BitSetTest.cpp
new file mode 100644
index 0000000..7139495
--- /dev/null
+++ b/tests/BitSetTest.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 "Test.h"
+#include "SkBitSet.h"
+
+static void TestBitSet(skiatest::Reporter* reporter) {
+    SkBitSet set0(65536);
+    REPORTER_ASSERT(reporter, set0.isBitSet(0) == false);
+    REPORTER_ASSERT(reporter, set0.isBitSet(32767) == false);
+    REPORTER_ASSERT(reporter, set0.isBitSet(65535) == false);
+
+    SkBitSet set1(65536);
+    REPORTER_ASSERT(reporter, set0 == set1);
+
+    set0.setBit(22, true);
+    REPORTER_ASSERT(reporter, set0.isBitSet(22) == true);
+    set0.setBit(24, true);
+    REPORTER_ASSERT(reporter, set0.isBitSet(24) == true);
+    set0.setBit(35, true);  // on a different DWORD
+    REPORTER_ASSERT(reporter, set0.isBitSet(35) == true);
+    set0.setBit(22, false);
+    REPORTER_ASSERT(reporter, set0.isBitSet(22) == false);
+    REPORTER_ASSERT(reporter, set0.isBitSet(24) == true);
+    REPORTER_ASSERT(reporter, set0.isBitSet(35) == true);
+
+    SkTDArray<unsigned int> data;
+    set0.exportTo(&data);
+    REPORTER_ASSERT(reporter, data.count() == 2);
+    REPORTER_ASSERT(reporter, data[0] == 24);
+    REPORTER_ASSERT(reporter, data[1] == 35);
+
+    set1.setBit(12345, true);
+    set1.orBits(set0);
+    REPORTER_ASSERT(reporter, set0.isBitSet(12345) == false);
+    REPORTER_ASSERT(reporter, set1.isBitSet(12345) == true);
+    REPORTER_ASSERT(reporter, set1.isBitSet(22) == false);
+    REPORTER_ASSERT(reporter, set1.isBitSet(24) == true);
+    REPORTER_ASSERT(reporter, set0.isBitSet(35) == true);
+    REPORTER_ASSERT(reporter, set1 != set0);
+
+    set1.clearAll();
+    REPORTER_ASSERT(reporter, set0.isBitSet(12345) == false);
+    REPORTER_ASSERT(reporter, set1.isBitSet(12345) == false);
+    REPORTER_ASSERT(reporter, set1.isBitSet(22) == false);
+    REPORTER_ASSERT(reporter, set1.isBitSet(24) == false);
+    REPORTER_ASSERT(reporter, set1.isBitSet(35) == false);
+
+    set1.orBits(set0);
+    REPORTER_ASSERT(reporter, set1 == set0);
+
+    SkBitSet set2(1);
+    SkBitSet set3(1);
+    SkBitSet set4(4);
+    SkBitSet set5(33);
+
+    REPORTER_ASSERT(reporter, set2 == set3);
+    REPORTER_ASSERT(reporter, set2 != set4);
+    REPORTER_ASSERT(reporter, set2 != set5);
+
+    set2.setBit(0, true);
+    REPORTER_ASSERT(reporter, set2 != set5);
+    set5.setBit(0, true);
+    REPORTER_ASSERT(reporter, set2 != set5);
+    REPORTER_ASSERT(reporter, set2 != set3);
+    set3.setBit(0, true);
+    REPORTER_ASSERT(reporter, set2 == set3);
+    set3.clearAll();
+    set3 = set2;
+    set2 = set2;
+    REPORTER_ASSERT(reporter, set2 == set3);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("BitSet", BitSetTest, TestBitSet)
diff --git a/tests/BitmapCopyTest.cpp b/tests/BitmapCopyTest.cpp
new file mode 100644
index 0000000..6cfb24b
--- /dev/null
+++ b/tests/BitmapCopyTest.cpp
@@ -0,0 +1,615 @@
+
+/*
+ * Copyright 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 "SkBitmap.h"
+#include "SkRect.h"
+
+static const char* boolStr(bool value) {
+    return value ? "true" : "false";
+}
+
+// these are in the same order as the SkBitmap::Config enum
+static const char* gConfigName[] = {
+    "None", "A1", "A8", "Index8", "565", "4444", "8888", "RLE_Index8"
+};
+
+static void report_opaqueness(skiatest::Reporter* reporter, const SkBitmap& src,
+                              const SkBitmap& dst) {
+    SkString str;
+    str.printf("src %s opaque:%d, dst %s opaque:%d",
+               gConfigName[src.config()], src.isOpaque(),
+               gConfigName[dst.config()], dst.isOpaque());
+    reporter->reportFailed(str);
+}
+
+static bool canHaveAlpha(SkBitmap::Config config) {
+    return config != SkBitmap::kRGB_565_Config;
+}
+
+// copyTo() should preserve isOpaque when it makes sense
+static void test_isOpaque(skiatest::Reporter* reporter, const SkBitmap& src,
+                          SkBitmap::Config dstConfig) {
+    SkBitmap bitmap(src);
+    SkBitmap dst;
+
+    // we need the lock so that we get a valid colorTable (when available)
+    SkAutoLockPixels alp(bitmap);
+    SkColorTable* ctable = bitmap.getColorTable();
+    unsigned ctableFlags = ctable ? ctable->getFlags() : 0;
+
+    if (canHaveAlpha(bitmap.config()) && canHaveAlpha(dstConfig)) {
+        bitmap.setIsOpaque(false);
+        if (ctable) {
+            ctable->setFlags(ctableFlags & ~SkColorTable::kColorsAreOpaque_Flag);
+        }
+        REPORTER_ASSERT(reporter, bitmap.copyTo(&dst, dstConfig));
+        REPORTER_ASSERT(reporter, dst.config() == dstConfig);
+        if (bitmap.isOpaque() != dst.isOpaque()) {
+            report_opaqueness(reporter, bitmap, dst);
+        }
+    }
+
+    bitmap.setIsOpaque(true);
+    if (ctable) {
+        ctable->setFlags(ctableFlags | SkColorTable::kColorsAreOpaque_Flag);
+    }
+    REPORTER_ASSERT(reporter, bitmap.copyTo(&dst, dstConfig));
+    REPORTER_ASSERT(reporter, dst.config() == dstConfig);
+    if (bitmap.isOpaque() != dst.isOpaque()) {
+        report_opaqueness(reporter, bitmap, dst);
+    }
+
+    if (ctable) {
+        ctable->setFlags(ctableFlags);
+    }
+}
+
+static void init_src(const SkBitmap& bitmap, const SkColorTable* ct) {
+    SkAutoLockPixels lock(bitmap);
+    if (bitmap.getPixels()) {
+        if (ct) {
+            sk_bzero(bitmap.getPixels(), bitmap.getSize());
+        } else {
+            bitmap.eraseColor(SK_ColorWHITE);
+        }
+    }
+}
+
+static SkColorTable* init_ctable() {
+    static const SkColor colors[] = {
+        SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE
+    };
+    return new SkColorTable(colors, SK_ARRAY_COUNT(colors));
+}
+
+struct Pair {
+    SkBitmap::Config    fConfig;
+    const char*         fValid;
+};
+
+// Utility functions for copyPixelsTo()/copyPixelsFrom() tests.
+// getPixel()
+// setPixel()
+// getSkConfigName()
+// struct Coordinates
+// reportCopyVerification()
+// writeCoordPixels()
+
+// Utility function to read the value of a given pixel in bm. All
+// values converted to uint32_t for simplification of comparisons.
+static uint32_t getPixel(int x, int y, const SkBitmap& bm) {
+    uint32_t val = 0;
+    uint16_t val16;
+    uint8_t val8, shift;
+    SkAutoLockPixels lock(bm);
+    const void* rawAddr = bm.getAddr(x,y);
+
+    switch (bm.getConfig()) {
+        case SkBitmap::kARGB_8888_Config:
+            memcpy(&val, rawAddr, sizeof(uint32_t));
+            break;
+        case SkBitmap::kARGB_4444_Config:
+        case SkBitmap::kRGB_565_Config:
+            memcpy(&val16, rawAddr, sizeof(uint16_t));
+            val = val16;
+            break;
+        case SkBitmap::kA8_Config:
+        case SkBitmap::kIndex8_Config:
+            memcpy(&val8, rawAddr, sizeof(uint8_t));
+            val = val8;
+            break;
+        case SkBitmap::kA1_Config:
+            memcpy(&val8, rawAddr, sizeof(uint8_t));
+            shift = x % 8;
+            val = (val8 >> shift) & 0x1 ;
+            break;
+        default:
+            break;
+    }
+    return val;
+}
+
+// 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.
+static void setPixel(int x, int y, uint32_t val, SkBitmap& bm) {
+    uint16_t val16;
+    uint8_t val8, shift;
+    SkAutoLockPixels lock(bm);
+    void* rawAddr = bm.getAddr(x,y);
+
+    switch (bm.getConfig()) {
+        case SkBitmap::kARGB_8888_Config:
+            memcpy(rawAddr, &val, sizeof(uint32_t));
+            break;
+        case SkBitmap::kARGB_4444_Config:
+        case SkBitmap::kRGB_565_Config:
+            val16 = val & 0xFFFF;
+            memcpy(rawAddr, &val16, sizeof(uint16_t));
+            break;
+        case SkBitmap::kA8_Config:
+        case SkBitmap::kIndex8_Config:
+            val8 = val & 0xFF;
+            memcpy(rawAddr, &val8, sizeof(uint8_t));
+            break;
+        case SkBitmap::kA1_Config:
+            shift = x % 8; // We assume we're in the right byte.
+            memcpy(&val8, rawAddr, sizeof(uint8_t));
+            if (val & 0x1) // Turn bit on.
+                val8 |= (0x1 << shift);
+            else // Turn bit off.
+                val8 &= ~(0x1 << shift);
+            memcpy(rawAddr, &val8, sizeof(uint8_t));
+            break;
+        default:
+            // Ignore.
+            break;
+    }
+}
+
+// Utility to return string containing name of each format, to
+// simplify diagnostic output.
+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";
+        case SkBitmap::kA8_Config: return "SkBitmap::kA8_Config";
+        case SkBitmap::kIndex8_Config: return "SkBitmap::kIndex8_Config";
+        case SkBitmap::kRGB_565_Config: return "SkBitmap::kRGB_565_Config";
+        case SkBitmap::kARGB_4444_Config: return "SkBitmap::kARGB_4444_Config";
+        case SkBitmap::kARGB_8888_Config: return "SkBitmap::kARGB_8888_Config";
+        case SkBitmap::kRLE_Index8_Config:
+            return "SkBitmap::kRLE_Index8_Config,";
+        default: return "Unknown SkBitmap configuration.";
+    }
+}
+
+// Helper struct to contain pixel locations, while avoiding need for STL.
+struct Coordinates {
+
+    const int length;
+    SkIPoint* const data;
+
+    explicit Coordinates(int _length): length(_length)
+                                     , data(new SkIPoint[length]) { }
+
+    ~Coordinates(){
+        delete [] data;
+    }
+
+    SkIPoint* operator[](int i) const {
+        // Use with care, no bounds checking.
+        return data + i;
+    }
+};
+
+// A function to verify that two bitmaps contain the same pixel values
+// at all coordinates indicated by coords. Simplifies verification of
+// copied bitmaps.
+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) {
+        success = success &&
+                  (getPixel(coords[i]->fX, coords[i]->fY, bm1) ==
+                   getPixel(coords[i]->fX, coords[i]->fY, bm2));
+    }
+
+    if (!success) {
+        SkString str;
+        str.printf("%s [config = %s]",
+                   msg, getSkConfigName(bm1));
+        reporter->reportFailed(str);
+    }
+}
+
+// Writes unique pixel values at locations specified by 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);
+}
+
+static void TestBitmapCopy(skiatest::Reporter* reporter) {
+    static const Pair gPairs[] = {
+        { SkBitmap::kNo_Config,         "00000000"  },
+        { SkBitmap::kA1_Config,         "01000000"  },
+        { SkBitmap::kA8_Config,         "00101110"  },
+        { SkBitmap::kIndex8_Config,     "00111110"  },
+        { SkBitmap::kRGB_565_Config,    "00101110"  },
+        { SkBitmap::kARGB_4444_Config,  "00101110"  },
+        { SkBitmap::kARGB_8888_Config,  "00101110"  },
+// TODO: create valid RLE bitmap to test with
+ //       { SkBitmap::kRLE_Index8_Config, "00101111"  }
+    };
+
+    static const bool isExtracted[] = {
+        false, true
+    };
+
+    const int W = 20;
+    const int H = 33;
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
+        for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
+            SkBitmap src, dst;
+            SkColorTable* ct = NULL;
+
+            src.setConfig(gPairs[i].fConfig, W, H);
+            if (SkBitmap::kIndex8_Config == src.config() ||
+                    SkBitmap::kRLE_Index8_Config == src.config()) {
+                ct = init_ctable();
+            }
+            src.allocPixels(ct);
+            SkSafeUnref(ct);
+
+            init_src(src, ct);
+            bool success = src.copyTo(&dst, gPairs[j].fConfig);
+            bool expected = gPairs[i].fValid[j] != '0';
+            if (success != expected) {
+                SkString str;
+                str.printf("SkBitmap::copyTo from %s to %s. expected %s returned %s",
+                           gConfigName[i], gConfigName[j], boolStr(expected),
+                           boolStr(success));
+                reporter->reportFailed(str);
+            }
+
+            bool canSucceed = src.canCopyTo(gPairs[j].fConfig);
+            if (success != canSucceed) {
+                SkString str;
+                str.printf("SkBitmap::copyTo from %s to %s. returned %s canCopyTo %s",
+                           gConfigName[i], gConfigName[j], boolStr(success),
+                           boolStr(canSucceed));
+                reporter->reportFailed(str);
+            }
+
+            if (success) {
+                REPORTER_ASSERT(reporter, src.width() == dst.width());
+                REPORTER_ASSERT(reporter, src.height() == dst.height());
+                REPORTER_ASSERT(reporter, dst.config() == gPairs[j].fConfig);
+                test_isOpaque(reporter, src, dst.config());
+                if (src.config() == dst.config()) {
+                    SkAutoLockPixels srcLock(src);
+                    SkAutoLockPixels dstLock(dst);
+                    REPORTER_ASSERT(reporter, src.readyToDraw());
+                    REPORTER_ASSERT(reporter, dst.readyToDraw());
+                    const char* srcP = (const char*)src.getAddr(0, 0);
+                    const char* dstP = (const char*)dst.getAddr(0, 0);
+                    REPORTER_ASSERT(reporter, srcP != dstP);
+                    REPORTER_ASSERT(reporter, !memcmp(srcP, dstP,
+                                                      src.getSize()));
+                    REPORTER_ASSERT(reporter, src.getGenerationID() == dst.getGenerationID());
+                } else {
+                    REPORTER_ASSERT(reporter, src.getGenerationID() != dst.getGenerationID());
+                }
+                // test extractSubset
+                {
+                    SkBitmap bitmap(src);
+                    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;
+                        REPORTER_ASSERT(reporter,
+                                        subset.copyTo(&copy, subset.config()));
+                        REPORTER_ASSERT(reporter, copy.width() == 1);
+                        REPORTER_ASSERT(reporter, copy.height() == 1);
+                        REPORTER_ASSERT(reporter, copy.rowBytes() <= 4);
+
+                        SkAutoLockPixels alp0(subset);
+                        SkAutoLockPixels alp1(copy);
+                        // they should both have, or both not-have, a colortable
+                        bool hasCT = subset.getColorTable() != NULL;
+                        REPORTER_ASSERT(reporter,
+                                    (copy.getColorTable() != NULL) == hasCT);
+                    }
+                    bitmap.setIsOpaque(false);
+                    bitmap.setIsVolatile(false);
+                    if (bitmap.extractSubset(&subset, r)) {
+                        REPORTER_ASSERT(reporter,
+                                        subset.isOpaque() == bitmap.isOpaque());
+                        REPORTER_ASSERT(reporter,
+                                        subset.isVolatile() == false);
+                    }
+                }
+            } 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);
+            }
+        } // for (size_t j = ...
+
+        // Tests for getSafeSize(), getSafeSize64(), copyPixelsTo(),
+        // copyPixelsFrom().
+        //
+        for (size_t copyCase = 0; copyCase < SK_ARRAY_COUNT(isExtracted);
+             ++copyCase) {
+            // Test copying to/from external buffer.
+            // Note: the tests below have hard-coded values ---
+            //       Please take care if modifying.
+            if (gPairs[i].fConfig != SkBitmap::kRLE_Index8_Config) {
+
+                // Tests for getSafeSize64().
+                // Test with a very large configuration without pixel buffer
+                // attached.
+                SkBitmap tstSafeSize;
+                tstSafeSize.setConfig(gPairs[i].fConfig, 100000000U,
+                                      100000000U);
+                Sk64 safeSize = tstSafeSize.getSafeSize64();
+                if (safeSize.isNeg()) {
+                    SkString str;
+                    str.printf("getSafeSize64() negative: %s",
+                        getSkConfigName(tstSafeSize));
+                    reporter->reportFailed(str);
+                }
+                bool sizeFail = false;
+                // Compare against hand-computed values.
+                switch (gPairs[i].fConfig) {
+                    case SkBitmap::kNo_Config:
+                        break;
+
+                    case SkBitmap::kA1_Config:
+                        if (safeSize.fHi != 0x470DE ||
+                            safeSize.fLo != 0x4DF82000)
+                            sizeFail = true;
+                        break;
+
+                    case SkBitmap::kA8_Config:
+                    case SkBitmap::kIndex8_Config:
+                        if (safeSize.fHi != 0x2386F2 ||
+                            safeSize.fLo != 0x6FC10000)
+                            sizeFail = true;
+                        break;
+
+                    case SkBitmap::kRGB_565_Config:
+                    case SkBitmap::kARGB_4444_Config:
+                        if (safeSize.fHi != 0x470DE4 ||
+                            safeSize.fLo != 0xDF820000)
+                            sizeFail = true;
+                        break;
+
+                    case SkBitmap::kARGB_8888_Config:
+                        if (safeSize.fHi != 0x8E1BC9 ||
+                            safeSize.fLo != 0xBF040000)
+                            sizeFail = true;
+                        break;
+
+                    case SkBitmap::kRLE_Index8_Config:
+                        break;
+
+                    default:
+                        break;
+                }
+                if (sizeFail) {
+                    SkString str;
+                    str.printf("getSafeSize64() wrong size: %s",
+                        getSkConfigName(tstSafeSize));
+                    reporter->reportFailed(str);
+                }
+
+                size_t subW, subH;
+                // Set sizes to be height = 2 to force the last row of the
+                // source to be used, thus verifying correct operation if
+                // the bitmap is an extracted subset.
+                if (gPairs[i].fConfig == SkBitmap::kA1_Config) {
+                    // If one-bit per pixel, use 9 pixels to force more than
+                    // one byte per row.
+                    subW = 9;
+                    subH = 2;
+                } else {
+                    // All other configurations are at least one byte per pixel,
+                    // and different configs will test copying different numbers
+                    // of bytes.
+                    subW = subH = 2;
+                }
+
+                // Create bitmap to act as source for copies and subsets.
+                SkBitmap src, subset;
+                SkColorTable* ct = NULL;
+                if (isExtracted[copyCase]) { // A larger image to extract from.
+                    src.setConfig(gPairs[i].fConfig, 2 * subW + 1, subH);
+                } else // Tests expect a 2x2 bitmap, so make smaller.
+                    src.setConfig(gPairs[i].fConfig, subW, subH);
+                if (SkBitmap::kIndex8_Config == src.config() ||
+                        SkBitmap::kRLE_Index8_Config == src.config()) {
+                    ct = init_ctable();
+                }
+
+                src.allocPixels(ct);
+                SkSafeUnref(ct);
+
+                // Either copy src or extract into 'subset', which is used
+                // for subsequent calls to copyPixelsTo/From.
+                bool srcReady = false;
+                if (isExtracted[copyCase]) {
+                    // The extractedSubset() test case allows us to test copy-
+                    // ing when src and dst mave possibly different strides.
+                    SkIRect r;
+                    if (gPairs[i].fConfig == SkBitmap::kA1_Config)
+                        // This config seems to need byte-alignment of
+                        // extracted subset bits.
+                        r.set(0, 0, subW, subH);
+                    else
+                        r.set(1, 0, 1 + subW, subH); // 2x2 extracted bitmap
+
+                    srcReady = src.extractSubset(&subset, r);
+                } else {
+                    srcReady = src.copyTo(&subset, src.getConfig());
+                }
+
+                // Not all configurations will generate a valid 'subset'.
+                if (srcReady) {
+
+                    // Allocate our target buffer 'buf' for all copies.
+                    // To simplify verifying correctness of copies attach
+                    // buf to a SkBitmap, but copies are done using the
+                    // raw buffer pointer.
+                    const uint32_t bufSize = subH *
+                        SkBitmap::ComputeRowBytes(src.getConfig(), subW) * 2;
+                    SkAutoMalloc autoBuf (bufSize);
+                    uint8_t* buf = static_cast<uint8_t*>(autoBuf.get());
+
+                    SkBitmap bufBm; // Attach buf to this bitmap.
+                    bool successExpected;
+
+                    // Set up values for each pixel being copied.
+                    Coordinates coords(subW * subH);
+                    for (size_t x = 0; x < subW; ++x)
+                        for (size_t y = 0; y < subH; ++y)
+                        {
+                            int index = y * subW + x;
+                            SkASSERT(index < coords.length);
+                            coords[index]->fX = x;
+                            coords[index]->fY = y;
+                        }
+
+                    writeCoordPixels(subset, coords);
+
+                    // Test #1 ////////////////////////////////////////////
+
+                    // Before/after comparisons easier if we attach buf
+                    // to an appropriately configured SkBitmap.
+                    memset(buf, 0xFF, bufSize);
+                    // Config with stride greater than src but that fits in buf.
+                    bufBm.setConfig(gPairs[i].fConfig, subW, subH,
+                        SkBitmap::ComputeRowBytes(subset.getConfig(), subW)
+                                                  * 2);
+                    bufBm.setPixels(buf);
+                    successExpected = false;
+                    // Then attempt to copy with a stride that is too large
+                    // to fit in the buffer.
+                    REPORTER_ASSERT(reporter,
+                        subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes() * 3)
+                        == successExpected);
+
+                    if (successExpected)
+                        reportCopyVerification(subset, bufBm, coords,
+                            "copyPixelsTo(buf, bufSize, 1.5*maxRowBytes)",
+                            reporter);
+
+                    // Test #2 ////////////////////////////////////////////
+                    // This test should always succeed, but in the case
+                    // of extracted bitmaps only because we handle the
+                    // issue of getSafeSize(). Without getSafeSize()
+                    // buffer overrun/read would occur.
+                    memset(buf, 0xFF, bufSize);
+                    bufBm.setConfig(gPairs[i].fConfig, subW, subH,
+                                    subset.rowBytes());
+                    bufBm.setPixels(buf);
+                    successExpected = subset.getSafeSize() <= bufSize;
+                    REPORTER_ASSERT(reporter,
+                        subset.copyPixelsTo(buf, bufSize) ==
+                            successExpected);
+                    if (successExpected)
+                        reportCopyVerification(subset, bufBm, coords,
+                        "copyPixelsTo(buf, bufSize)", reporter);
+
+                    // Test #3 ////////////////////////////////////////////
+                    // Copy with different stride between src and dst.
+                    memset(buf, 0xFF, bufSize);
+                    bufBm.setConfig(gPairs[i].fConfig, subW, subH,
+                                    subset.rowBytes()+1);
+                    bufBm.setPixels(buf);
+                    successExpected = true; // Should always work.
+                    REPORTER_ASSERT(reporter,
+                            subset.copyPixelsTo(buf, bufSize,
+                                subset.rowBytes()+1) == successExpected);
+                    if (successExpected)
+                        reportCopyVerification(subset, bufBm, coords,
+                        "copyPixelsTo(buf, bufSize, rowBytes+1)", reporter);
+
+                    // Test #4 ////////////////////////////////////////////
+                    // Test copy with stride too small.
+                    memset(buf, 0xFF, bufSize);
+                    bufBm.setConfig(gPairs[i].fConfig, subW, subH);
+                    bufBm.setPixels(buf);
+                    successExpected = false;
+                    // Request copy with stride too small.
+                    REPORTER_ASSERT(reporter,
+                        subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes()-1)
+                            == successExpected);
+                    if (successExpected)
+                        reportCopyVerification(subset, bufBm, coords,
+                        "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter);
+
+#if 0   // copyPixelsFrom is gone
+                    // Test #5 ////////////////////////////////////////////
+                    // Tests the case where the source stride is too small
+                    // for the source configuration.
+                    memset(buf, 0xFF, bufSize);
+                    bufBm.setConfig(gPairs[i].fConfig, subW, subH);
+                    bufBm.setPixels(buf);
+                    writeCoordPixels(bufBm, coords);
+                    REPORTER_ASSERT(reporter,
+                        subset.copyPixelsFrom(buf, bufSize, 1) == false);
+
+                    // Test #6 ///////////////////////////////////////////
+                    // Tests basic copy from an external buffer to the bitmap.
+                    // If the bitmap is "extracted", this also tests the case
+                    // where the source stride is different from the dest.
+                    // stride.
+                    // We've made the buffer large enough to always succeed.
+                    bufBm.setConfig(gPairs[i].fConfig, subW, subH);
+                    bufBm.setPixels(buf);
+                    writeCoordPixels(bufBm, coords);
+                    REPORTER_ASSERT(reporter,
+                        subset.copyPixelsFrom(buf, bufSize, bufBm.rowBytes()) ==
+                            true);
+                    reportCopyVerification(bufBm, subset, coords,
+                        "copyPixelsFrom(buf, bufSize)",
+                        reporter);
+
+                    // Test #7 ////////////////////////////////////////////
+                    // Tests the case where the source buffer is too small
+                    // for the transfer.
+                    REPORTER_ASSERT(reporter,
+                        subset.copyPixelsFrom(buf, 1, subset.rowBytes()) ==
+                            false);
+
+#endif
+                }
+            }
+        } // for (size_t copyCase ...
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("BitmapCopy", TestBitmapCopyClass, TestBitmapCopy)
diff --git a/tests/BitmapGetColorTest.cpp b/tests/BitmapGetColorTest.cpp
new file mode 100644
index 0000000..4204ddf
--- /dev/null
+++ b/tests/BitmapGetColorTest.cpp
@@ -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.
+ */
+#include "Test.h"
+#include "SkBitmap.h"
+
+static void TestGetColor(skiatest::Reporter* reporter) {
+    static const struct Rec {
+        SkBitmap::Config    fConfig;
+        SkColor             fInColor;
+        SkColor             fOutColor;
+    } gRec[] = {
+        // todo: add some tests that involve alpha, so we exercise the
+        // unpremultiply aspect of getColor()
+        {   SkBitmap::kA8_Config,           0xFF000000,     0xFF000000  },
+        {   SkBitmap::kA8_Config,           0,              0           },
+        {   SkBitmap::kARGB_4444_Config,    0xFF224466,     0xFF224466  },
+        {   SkBitmap::kARGB_4444_Config,    0,              0           },
+        {   SkBitmap::kRGB_565_Config,      0xFF00FF00,     0xFF00FF00  },
+        {   SkBitmap::kRGB_565_Config,      0xFFFF00FF,     0xFFFF00FF  },
+        {   SkBitmap::kARGB_8888_Config,    0xFFFFFFFF,     0xFFFFFFFF  },
+        {   SkBitmap::kARGB_8888_Config,    0,              0           },
+        {   SkBitmap::kARGB_8888_Config,    0xFF224466,     0xFF224466  },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+        SkBitmap bm;
+        uint32_t storage[1];
+        bm.setConfig(gRec[i].fConfig, 1, 1);
+        bm.setPixels(storage);
+        bm.eraseColor(gRec[i].fInColor);
+
+        SkColor c = bm.getColor(0, 0);
+        REPORTER_ASSERT(reporter, c == gRec[i].fOutColor);
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("GetColor", TestGetColorClass, TestGetColor)
diff --git a/tests/BlitRowTest.cpp b/tests/BlitRowTest.cpp
new file mode 100644
index 0000000..fca4588
--- /dev/null
+++ b/tests/BlitRowTest.cpp
@@ -0,0 +1,276 @@
+
+/*
+ * Copyright 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 "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkGradientShader.h"
+#include "SkRect.h"
+
+static inline const char* boolStr(bool value) {
+    return value ? "true" : "false";
+}
+
+// these are in the same order as the SkBitmap::Config enum
+static const char* gConfigName[] = {
+    "None", "A1", "A8", "Index8", "565", "4444", "8888", "RLE_Index8"
+};
+
+/** Returns -1 on success, else the x coord of the first bad pixel, return its
+    value in bad
+ */
+typedef int (*Proc)(const void*, int width, uint32_t expected, uint32_t* bad);
+
+static int proc_32(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
+    const SkPMColor* addr = static_cast<const SkPMColor*>(ptr);
+    for (int x = 0; x < w; x++) {
+        if (addr[x] != expected) {
+            *bad = addr[x];
+            return x;
+        }
+    }
+    return -1;
+}
+
+static int proc_16(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
+    const uint16_t* addr = static_cast<const uint16_t*>(ptr);
+    for (int x = 0; x < w; x++) {
+        if (addr[x] != expected) {
+            *bad = addr[x];
+            return x;
+        }
+    }
+    return -1;
+}
+
+static int proc_8(const void* ptr, int w, uint32_t expected, uint32_t* bad) {
+    const SkPMColor* addr = static_cast<const SkPMColor*>(ptr);
+    for (int x = 0; x < w; x++) {
+        if (SkGetPackedA32(addr[x]) != expected) {
+            *bad = SkGetPackedA32(addr[x]);
+            return x;
+        }
+    }
+    return -1;
+}
+
+static int proc_bad(const void* ptr, int, uint32_t, uint32_t* bad) {
+    *bad = 0;
+    return 0;
+}
+
+static Proc find_proc(const SkBitmap& bm, SkPMColor expect32, uint16_t expect16,
+                      uint8_t expect8, uint32_t* expect) {
+    switch (bm.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            *expect = expect32;
+            return proc_32;
+        case SkBitmap::kARGB_4444_Config:
+        case SkBitmap::kRGB_565_Config:
+            *expect = expect16;
+            return proc_16;
+        case SkBitmap::kA8_Config:
+            *expect = expect8;
+            return proc_8;
+        default:
+            *expect = 0;
+            return proc_bad;
+    }
+}
+
+static bool check_color(const SkBitmap& bm, SkPMColor expect32,
+                        uint16_t expect16, uint8_t expect8,
+                        skiatest::Reporter* reporter) {
+    uint32_t expect;
+    Proc proc = find_proc(bm, expect32, expect16, expect8, &expect);
+    for (int y = 0; y < bm.height(); y++) {
+        uint32_t bad;
+        int x = proc(bm.getAddr(0, y), bm.width(), expect, &bad);
+        if (x >= 0) {
+            SkString str;
+            str.printf("BlitRow config=%s [%d %d] expected %x got %x",
+                       gConfigName[bm.config()], x, y, expect, bad);
+            reporter->reportFailed(str);
+            return false;
+        }
+    }
+    return true;
+}
+
+// Make sure our blits always map src==0 to a noop, and src==FF to full opaque
+static void test_00_FF(skiatest::Reporter* reporter) {
+    static const int W = 256;
+
+    static const SkBitmap::Config gDstConfig[] = {
+        SkBitmap::kARGB_8888_Config,
+        SkBitmap::kRGB_565_Config,
+//        SkBitmap::kARGB_4444_Config,
+//        SkBitmap::kA8_Config,
+    };
+
+    static const struct {
+        SkColor     fSrc;
+        SkColor     fDst;
+        SkPMColor   fResult32;
+        uint16_t    fResult16;
+        uint8_t     fResult8;
+    } gSrcRec[] = {
+        { 0,            0,          0,                                    0,      0 },
+        { 0,            0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
+        { 0xFFFFFFFF,   0,          SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
+        { 0xFFFFFFFF,   0xFFFFFFFF, SkPackARGB32(0xFF, 0xFF, 0xFF, 0xFF), 0xFFFF, 0xFF },
+    };
+
+    SkPaint paint;
+
+    SkBitmap srcBM;
+    srcBM.setConfig(SkBitmap::kARGB_8888_Config, W, 1);
+    srcBM.allocPixels();
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gDstConfig); i++) {
+        SkBitmap dstBM;
+        dstBM.setConfig(gDstConfig[i], W, 1);
+        dstBM.allocPixels();
+
+        SkCanvas canvas(dstBM);
+        for (size_t j = 0; j < SK_ARRAY_COUNT(gSrcRec); j++) {
+            srcBM.eraseColor(gSrcRec[j].fSrc);
+            dstBM.eraseColor(gSrcRec[j].fDst);
+
+            for (int k = 0; k < 4; k++) {
+                bool dither = (k & 1) != 0;
+                bool blend = (k & 2) != 0;
+                if (gSrcRec[j].fSrc != 0 && blend) {
+                    // can't make a numerical promise about blending anything
+                    // but 0
+                 //   continue;
+                }
+                paint.setDither(dither);
+                paint.setAlpha(blend ? 0x80 : 0xFF);
+                canvas.drawBitmap(srcBM, 0, 0, &paint);
+                if (!check_color(dstBM, gSrcRec[j].fResult32, gSrcRec[j].fResult16,
+                                 gSrcRec[j].fResult8, reporter)) {
+                    SkDebugf("--- src index %d dither %d blend %d\n", j, dither, blend);
+                }
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct Mesh {
+    SkPoint     fPts[4];
+    uint16_t    fIndices[6];
+
+    Mesh(const SkBitmap& bm, SkPaint* paint) {
+        const SkScalar w = SkIntToScalar(bm.width());
+        const SkScalar h = SkIntToScalar(bm.height());
+        fPts[0].set(0, 0);
+        fPts[1].set(w, 0);
+        fPts[2].set(w, h);
+        fPts[3].set(0, h);
+        SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
+                                                   SkShader::kClamp_TileMode);
+        paint->setShader(s)->unref();
+
+    }
+
+    void draw(SkCanvas* canvas, SkPaint* paint) {
+        canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, fPts, fPts,
+                             NULL, NULL, NULL, 0, *paint);
+    }
+};
+
+#include "SkImageEncoder.h"
+static void save_bm(const SkBitmap& bm, const char name[]) {
+    SkImageEncoder::EncodeFile(name, bm, SkImageEncoder::kPNG_Type, 100);
+}
+
+static bool gOnce;
+
+// Make sure our blits are invariant with the width of the blit (i.e. that
+// special case for 8 at a time have the same results as narrower blits)
+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,
+        //        SkBitmap::kARGB_4444_Config,
+        //        SkBitmap::kA8_Config,
+    };
+
+    static const SkColor gDstBG[] = { 0, 0xFFFFFFFF };
+
+    SkPaint paint;
+
+    SkBitmap srcBM;
+    srcBM.setConfig(SkBitmap::kARGB_8888_Config, W, H);
+    srcBM.allocPixels();
+    SkRect srcR = {
+        0, 0, SkIntToScalar(srcBM.width()), SkIntToScalar(srcBM.height()) };
+
+    // cons up a mesh to draw the bitmap with
+    Mesh mesh(srcBM, &paint);
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gDstConfig); i++) {
+        SkBitmap dstBM0, dstBM1;
+        dstBM0.setConfig(gDstConfig[i], W, H);
+        dstBM1.setConfig(gDstConfig[i], W, H);
+        dstBM0.allocPixels();
+        dstBM1.allocPixels();
+
+        SkCanvas canvas0(dstBM0);
+        SkCanvas canvas1(dstBM1);
+        SkColor bgColor;
+
+        for (size_t j = 0; j < SK_ARRAY_COUNT(gDstBG); j++) {
+            bgColor = gDstBG[j];
+
+            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;
+                    paint.setDither(dither);
+                    paint.setAlpha(alpha);
+
+                    dstBM0.eraseColor(bgColor);
+                    dstBM1.eraseColor(bgColor);
+
+                    canvas0.drawRect(srcR, paint);
+                    mesh.draw(&canvas1, &paint);
+
+                    if (!gOnce && false) {
+                        save_bm(dstBM0, "drawBitmap.png");
+                        save_bm(dstBM1, "drawMesh.png");
+                        gOnce = true;
+                    }
+
+                    if (memcmp(dstBM0.getPixels(), dstBM1.getPixels(), dstBM0.getSize())) {
+                        SkString str;
+                        str.printf("Diagonal config=%s bg=0x%x dither=%d alpha=0x%x src=0x%x",
+                                   gConfigName[gDstConfig[i]], bgColor, dither, alpha, c);
+                        reporter->reportFailed(str);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void TestBlitRow(skiatest::Reporter* reporter) {
+    test_00_FF(reporter);
+    test_diagonal(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("BlitRow", TestBlitRowClass, TestBlitRow)
diff --git a/tests/BlurTest.cpp b/tests/BlurTest.cpp
new file mode 100644
index 0000000..c0d16f8
--- /dev/null
+++ b/tests/BlurTest.cpp
@@ -0,0 +1,162 @@
+
+/*
+ * Copyright 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 "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkMath.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ILLEGAL_MODE    ((SkXfermode::Mode)-1)
+
+static const int outset = 100;
+static const SkColor bgColor = SK_ColorWHITE;
+static const int strokeWidth = 4;
+
+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);
+}
+
+
+struct BlurTest {
+    void (*addPath)(SkPath*);
+    int viewLen;
+    SkIRect views[9];
+};
+
+//Path Draw Procs
+//Beware that paths themselves my draw differently depending on the clip.
+static void draw50x50Rect(SkPath* path) {
+    path->addRect(0, 0, SkIntToScalar(50), SkIntToScalar(50));
+}
+
+//Tests
+static BlurTest tests[] = {
+    { draw50x50Rect, 3, {
+        //inner half of blur
+        { 0, 0, 50, 50 },
+        //blur, but no path.
+        { 50 + strokeWidth/2, 50 + strokeWidth/2, 100, 100 },
+        //just an edge
+        { 40, strokeWidth, 60, 50 - strokeWidth },
+    }},
+};
+
+/** 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_blur(skiatest::Reporter* reporter) {
+
+    SkPaint paint;
+    paint.setColor(SK_ColorGRAY);
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(SkIntToScalar(strokeWidth));
+
+    SkScalar radius = SkIntToScalar(5);
+    for (int style = 0; style < SkBlurMaskFilter::kBlurStyleCount; ++style) {
+        SkBlurMaskFilter::BlurStyle blurStyle =
+            static_cast<SkBlurMaskFilter::BlurStyle>(style);
+
+        const uint32_t flagPermutations = SkBlurMaskFilter::kAll_BlurFlag;
+        for (uint32_t flags = 0; flags < flagPermutations; ++flags) {
+            SkMaskFilter* filter;
+            filter = SkBlurMaskFilter::Create(radius, blurStyle, flags);
+
+            SkMaskFilter::BlurInfo info;
+            sk_bzero(&info, sizeof(info));
+            SkMaskFilter::BlurType type = filter->asABlur(&info);
+
+            REPORTER_ASSERT(reporter, type ==
+                static_cast<SkMaskFilter::BlurType>(style + 1));
+            REPORTER_ASSERT(reporter, info.fRadius == radius);
+            REPORTER_ASSERT(reporter, info.fIgnoreTransform ==
+                SkToBool(flags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag));
+            REPORTER_ASSERT(reporter, info.fHighQuality ==
+                SkToBool(flags & SkBlurMaskFilter::kHighQuality_BlurFlag));
+
+            paint.setMaskFilter(filter);
+            filter->unref();
+
+            for (size_t test = 0; test < SK_ARRAY_COUNT(tests); ++test) {
+                SkPath path;
+                tests[test].addPath(&path);
+                SkPath strokedPath;
+                paint.getFillPath(path, &strokedPath);
+                SkRect refBound = strokedPath.getBounds();
+                SkIRect iref;
+                refBound.roundOut(&iref);
+                iref.inset(-outset, -outset);
+                SkBitmap refBitmap;
+                create(&refBitmap, iref, SkBitmap::kARGB_8888_Config);
+
+                SkCanvas refCanvas(refBitmap);
+                refCanvas.translate(SkIntToScalar(-iref.fLeft),
+                                    SkIntToScalar(-iref.fTop));
+                drawBG(&refCanvas);
+                refCanvas.drawPath(path, paint);
+
+                for (int view = 0; view < tests[test].viewLen; ++view) {
+                    SkIRect itest = tests[test].views[view];
+                    SkBitmap testBitmap;
+                    create(&testBitmap, itest, SkBitmap::kARGB_8888_Config);
+
+                    SkCanvas testCanvas(testBitmap);
+                    testCanvas.translate(SkIntToScalar(-itest.fLeft),
+                                         SkIntToScalar(-itest.fTop));
+                    drawBG(&testCanvas);
+                    testCanvas.drawPath(path, paint);
+
+                    REPORTER_ASSERT(reporter,
+                        compare(refBitmap, iref, testBitmap, itest));
+                }
+            }
+        }
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("BlurMaskFilter", BlurTestClass, test_blur)
diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp
new file mode 100644
index 0000000..6570450
--- /dev/null
+++ b/tests/CanvasTest.cpp
@@ -0,0 +1,896 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*  Description:
+ *      This test defines a series of elementatry test steps that perform
+ *      a single or a small group of canvas API calls. Each test step is
+ *      used in several test cases that verify that different types of SkCanvas
+ *      flavors and derivatives pass it and yield consistent behavior. The
+ *      test cases analyse results that are queryable through the API. They do
+ *      not look at rendering results.
+ *
+ *  Adding test stepss:
+ *      The general pattern for creating a new test step is to write a test
+ *      function of the form:
+ *
+ *          static void MyTestStepFunction(SkCanvas* canvas,
+ *                                         skiatest::Reporter* reporter,
+ *                                         CanvasTestStep* testStep)
+ *          {
+ *              canvas->someCanvasAPImethod();
+ *              (...)
+ *              REPORTER_ASSERT_MESSAGE(reporter, (...), \
+ *                  testStep->assertMessage());
+ *          }
+ *
+ *      The definition of the test step function should be followed by an
+ *      invocation of the TEST_STEP macro, which generates a class and
+ *      instance for the test step:
+ *
+ *          TEST_STEP(MyTestStep, MyTestStepFunction)
+ *
+ *      There are also short hand macros for defining simple test steps
+ *      in a single line of code.  A simple test step is a one that is made
+ *      of a single canvas API call.
+ *
+ *          SIMPLE_TEST_STEP(MytestStep, someCanvasAPIMethod());
+ *
+ *      There is another macro called SIMPLE_TEST_STEP_WITH_ASSERT that
+ *      works the same way as SIMPLE_TEST_STEP, and additionally verifies
+ *      that the invoked method returns a non-zero value.
+ */
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkDeferredCanvas.h"
+#include "SkDevice.h"
+#include "SkMatrix.h"
+#include "SkNWayCanvas.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPicture.h"
+#include "SkPictureRecord.h"
+#include "SkProxyCanvas.h"
+#include "SkRect.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkStream.h"
+#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;
+
+// Format strings that describe the test context.  The %s token is where
+// the name of the test step is inserted.  The context is required for
+// disambiguating the error in the case of failures that are reported in
+// 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 =
+    "Drawing test step %s with SkCanvas";
+static const char* const kPictureDrawAssertMessageFormat =
+    "Drawing test step %s with SkPicture";
+static const char* const kPictureSecondDrawAssertMessageFormat =
+    "Duplicate draw of test step %s with SkPicture";
+static const char* const kPictureReDrawAssertMessageFormat =
+    "Playing back test step %s from an SkPicture to another SkPicture";
+static const char* const kDeferredDrawAssertMessageFormat =
+    "Drawing test step %s with SkDeferredCanvas";
+static const char* const kProxyDrawAssertMessageFormat =
+    "Drawing test step %s with SkProxyCanvas";
+static const char* const kNWayDrawAssertMessageFormat =
+    "Drawing test step %s with SkNWayCanvas";
+static const char* const kRoundTripAssertMessageFormat =
+    "test step %s, SkPicture consistency after round trip";
+static const char* const kPictureRecoringAssertMessageFormat =
+    "test step %s, SkPicture state consistency after recording";
+static const char* const kPicturePlaybackAssertMessageFormat =
+    "test step %s, SkPicture state consistency in playback canvas";
+static const char* const kDeferredPreFlushAssertMessageFormat =
+    "test step %s, SkDeferredCanvas state consistency before flush";
+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";
+static const char* const kProxyStateAssertMessageFormat =
+    "test step %s, SkProxyCanvas state consistency";
+static const char* const kProxyIndirectStateAssertMessageFormat =
+    "test step %s, SkProxyCanvas indirect canvas state consistency";
+static const char* const kNWayStateAssertMessageFormat =
+    "test step %s, SkNWayCanvas state consistency";
+static const char* const kNWayIndirect1StateAssertMessageFormat =
+    "test step %s, SkNWayCanvas indirect canvas 1 state consistency";
+static const char* const kNWayIndirect2StateAssertMessageFormat =
+    "test step %s, SkNWayCanvas indirect canvas 2 state consistency";
+
+static void createBitmap(SkBitmap* bm, SkBitmap::Config config, SkColor color) {
+    bm->setConfig(config, kWidth, kHeight);
+    bm->allocPixels();
+    bm->eraseColor(color);
+}
+
+class CanvasTestStep;
+static SkTDArray<CanvasTestStep*>& testStepArray() {
+    static SkTDArray<CanvasTestStep*> theTests;
+    return theTests;
+}
+
+class CanvasTestStep {
+public:
+    CanvasTestStep() {
+        *testStepArray().append() = this;
+        fAssertMessageFormat = kDefaultAssertMessageFormat;
+    }
+    virtual ~CanvasTestStep() { }
+
+    virtual void draw(SkCanvas*, skiatest::Reporter*) = 0;
+    virtual const char* name() const = 0;
+
+    const char* assertMessage() {
+        fAssertMessage.printf(fAssertMessageFormat, name());
+        return fAssertMessage.c_str();
+    }
+
+    void setAssertMessageFormat(const char* format) {
+        fAssertMessageFormat = format;
+    }
+
+private:
+    SkString fAssertMessage;
+    const char* fAssertMessageFormat;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Constants used by test steps
+
+const SkRect kTestRect =
+    SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
+                     SkIntToScalar(2), SkIntToScalar(1));
+static SkMatrix testMatrix() {
+    SkMatrix matrix;
+    matrix.reset();
+    matrix.setScale(SkIntToScalar(2), SkIntToScalar(3));
+    return matrix;
+}
+const SkMatrix kTestMatrix = testMatrix();
+static SkPath testPath() {
+    SkPath path;
+    path.addRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
+                                  SkIntToScalar(2), SkIntToScalar(1)));
+    return path;
+}
+const SkPath kTestPath = testPath();
+static SkRegion testRegion() {
+    SkRegion region;
+    SkIRect rect = SkIRect::MakeXYWH(0, 0, 2, 1);
+    region.setRect(rect);
+    return region;
+}
+const SkIRect kTestIRect = SkIRect::MakeXYWH(0, 0, 2, 1);
+const SkRegion kTestRegion = testRegion();
+const SkColor kTestColor = 0x01020304;
+const SkPaint kTestPaint;
+const SkPoint kTestPoints[3] = {
+    {SkIntToScalar(0), SkIntToScalar(0)},
+    {SkIntToScalar(2), SkIntToScalar(1)},
+    {SkIntToScalar(0), SkIntToScalar(2)}
+};
+const size_t kTestPointCount = 3;
+static SkBitmap testBitmap() {
+    SkBitmap bitmap;
+    createBitmap(&bitmap, SkBitmap::kARGB_8888_Config, 0x05060708);
+    return bitmap;
+}
+SkBitmap kTestBitmap; // cannot be created during static init
+SkString kTestText("Hello World");
+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
+
+#define TEST_STEP(NAME, FUNCTION)                                       \
+class NAME##_TestStep : public CanvasTestStep{                          \
+public:                                                                 \
+    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*) {                                            \
+    canvas-> CALL ;                                               \
+}                                                                 \
+TEST_STEP(NAME, NAME##TestStep )
+
+#define SIMPLE_TEST_STEP_WITH_ASSERT(NAME, CALL)                           \
+static void NAME##TestStep(SkCanvas* canvas, skiatest::Reporter* reporter, \
+    CanvasTestStep* testStep) {                                            \
+    REPORTER_ASSERT_MESSAGE(reporter, canvas-> CALL ,                      \
+        testStep->assertMessage());                                        \
+}                                                                          \
+TEST_STEP(NAME, NAME##TestStep )
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Basic test steps for most virtual methods in SkCanvas that draw or affect
+// the state of the canvas.
+
+SIMPLE_TEST_STEP_WITH_ASSERT(Translate,
+    translate(SkIntToScalar(1), SkIntToScalar(2)));
+SIMPLE_TEST_STEP_WITH_ASSERT(Scale,
+    scale(SkIntToScalar(1), SkIntToScalar(2)));
+SIMPLE_TEST_STEP_WITH_ASSERT(Rotate, rotate(SkIntToScalar(1)));
+SIMPLE_TEST_STEP_WITH_ASSERT(Skew,
+    skew(SkIntToScalar(1), SkIntToScalar(2)));
+SIMPLE_TEST_STEP_WITH_ASSERT(Concat, concat(kTestMatrix));
+SIMPLE_TEST_STEP(SetMatrix, setMatrix(kTestMatrix));
+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));
+SIMPLE_TEST_STEP(DrawPointsPoints, drawPoints(SkCanvas::kPoints_PointMode,
+    kTestPointCount, kTestPoints, kTestPaint));
+SIMPLE_TEST_STEP(DrawPointsLiness, drawPoints(SkCanvas::kLines_PointMode,
+    kTestPointCount, kTestPoints, kTestPaint));
+SIMPLE_TEST_STEP(DrawPointsPolygon, drawPoints(SkCanvas::kPolygon_PointMode,
+    kTestPointCount, kTestPoints, kTestPaint));
+SIMPLE_TEST_STEP(DrawRect, drawRect(kTestRect, kTestPaint));
+SIMPLE_TEST_STEP(DrawPath, drawPath(kTestPath, kTestPaint));
+SIMPLE_TEST_STEP(DrawBitmap, drawBitmap(kTestBitmap, 0, 0));
+SIMPLE_TEST_STEP(DrawBitmapPaint, drawBitmap(kTestBitmap, 0, 0, &kTestPaint));
+SIMPLE_TEST_STEP(DrawBitmapRect, drawBitmapRect(kTestBitmap, NULL, kTestRect,
+    NULL));
+SIMPLE_TEST_STEP(DrawBitmapRectSrcRect, drawBitmapRect(kTestBitmap,
+    &kTestIRect, kTestRect, NULL));
+SIMPLE_TEST_STEP(DrawBitmapRectPaint, drawBitmapRect(kTestBitmap, NULL,
+    kTestRect, &kTestPaint));
+SIMPLE_TEST_STEP(DrawBitmapMatrix, drawBitmapMatrix(kTestBitmap, kTestMatrix,
+    NULL));
+SIMPLE_TEST_STEP(DrawBitmapMatrixPaint, drawBitmapMatrix(kTestBitmap,
+    kTestMatrix, &kTestPaint));
+SIMPLE_TEST_STEP(DrawBitmapNine, drawBitmapNine(kTestBitmap, kTestIRect,
+    kTestRect, NULL));
+SIMPLE_TEST_STEP(DrawBitmapNinePaint, drawBitmapNine(kTestBitmap, kTestIRect,
+    kTestRect, &kTestPaint));
+SIMPLE_TEST_STEP(DrawSprite, drawSprite(kTestBitmap, 0, 0, NULL));
+SIMPLE_TEST_STEP(DrawSpritePaint, drawSprite(kTestBitmap, 0, 0, &kTestPaint));
+SIMPLE_TEST_STEP(DrawText, drawText(kTestText.c_str(), kTestText.size(),
+    0, 1, kTestPaint));
+SIMPLE_TEST_STEP(DrawPosText, drawPosText(kTestText.c_str(),
+    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(DrawData, drawData(kTestText.c_str(), kTestText.size()));
+
+///////////////////////////////////////////////////////////////////////////////
+// Complex test steps
+
+// 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];
+    pts[0].set(0, 0);
+    pts[1].set(SkIntToScalar(kWidth), 0);
+    pts[2].set(SkIntToScalar(kWidth), SkIntToScalar(kHeight));
+    pts[3].set(0, SkIntToScalar(kHeight));
+    SkPaint paint;
+    SkShader* shader = SkShader::CreateBitmapShader(kTestBitmap,
+        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    paint.setShader(shader)->unref();
+    canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, pts, pts,
+                         NULL, NULL, NULL, 0, paint);
+}
+TEST_STEP(DrawVerticesShader, DrawVerticesShaderTestStep);
+
+static void DrawPictureTestStep(SkCanvas* canvas,
+                                skiatest::Reporter* reporter,
+                                CanvasTestStep* testStep) {
+    SkPicture* testPicture = SkNEW_ARGS(SkPicture, ());
+    SkAutoUnref aup(testPicture);
+    SkCanvas* testCanvas = testPicture->beginRecording(kWidth, kHeight);
+    testCanvas->scale(SkIntToScalar(2), SkIntToScalar(1));
+    testCanvas->clipRect(kTestRect);
+    testCanvas->drawRect(kTestRect, kTestPaint);
+    canvas->drawPicture(*testPicture);
+}
+TEST_STEP(DrawPicture, DrawPictureTestStep);
+
+static void SaveRestoreTestStep(SkCanvas* canvas,
+                                skiatest::Reporter* reporter,
+                                CanvasTestStep* testStep) {
+    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, baseSaveCount + 3 == canvas->getSaveCount(),
+        testStep->assertMessage());
+    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?
+    canvas->restoreToCount(0);
+    REPORTER_ASSERT_MESSAGE(reporter, 1 == canvas->getSaveCount(),
+        testStep->assertMessage());
+}
+TEST_STEP(SaveRestore, SaveRestoreTestStep);
+
+static void DrawLayerTestStep(SkCanvas* canvas,
+                              skiatest::Reporter* reporter,
+                              CanvasTestStep* testStep) {
+    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+    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;
+
+    canvas->saveLayer(bounds, paint);
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+    canvas->restore();
+    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+
+    canvas->saveLayer(bounds, paint);
+    canvas->saveLayer(bounds, paint);
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+    canvas->restore();
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+    canvas->restore();
+    // now layer count should be 0
+    REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
+        testStep->assertMessage());
+}
+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* canvas2,
+                                    CanvasTestStep* testStep) {
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDeviceSize() ==
+        canvas2->getDeviceSize(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getSaveCount() ==
+        canvas2->getSaveCount(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->isDrawingToLayer() ==
+        canvas2->isDrawingToLayer(), testStep->assertMessage());
+
+    SkRect bounds1, bounds2;
+    REPORTER_ASSERT_MESSAGE(reporter,
+        canvas1->getClipBounds(&bounds1) == canvas2->getClipBounds(&bounds2),
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, bounds1 == bounds2,
+                            testStep->assertMessage());
+
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDrawFilter() ==
+        canvas2->getDrawFilter(), testStep->assertMessage());
+    SkIRect deviceBounds1, deviceBounds2;
+    REPORTER_ASSERT_MESSAGE(reporter,
+        canvas1->getClipDeviceBounds(&deviceBounds1) ==
+        canvas2->getClipDeviceBounds(&deviceBounds2),
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, deviceBounds1 == deviceBounds2,
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getBounder() ==
+        canvas2->getBounder(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getTotalMatrix() ==
+        canvas2->getTotalMatrix(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getClipType() ==
+        canvas2->getClipType(), testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getTotalClip() ==
+        canvas2->getTotalClip(), testStep->assertMessage());
+
+    // The following test code is commented out because the test fails when
+    // 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
+    /*
+    SkCanvas::LayerIter layerIter1(const_cast<SkCanvas*>(canvas1), false);
+    SkCanvas::LayerIter layerIter2(const_cast<SkCanvas*>(canvas2), false);
+    while (!layerIter1.done() && !layerIter2.done()) {
+        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.matrix() ==
+            layerIter2.matrix(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.clip() ==
+            layerIter2.clip(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.paint() ==
+            layerIter2.paint(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.x() ==
+            layerIter2.x(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, layerIter1.y() ==
+            layerIter2.y(), testStep->assertMessage());
+        layerIter1.next();
+        layerIter2.next();
+    }
+    REPORTER_ASSERT_MESSAGE(reporter, layerIter1.done(),
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, layerIter2.done(),
+        testStep->assertMessage());
+    */
+}
+
+// The following class groups static functions that need to access
+// 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,
+        skiatest::Reporter* reporter,
+        CanvasTestStep* testStep) {
+
+        REPORTER_ASSERT_MESSAGE(reporter,
+            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,
+                EQ(referenceRecord->fMatrices[i], testRecord->fMatrices[i]),
+                testStep->assertMessage());
+        }
+        REPORTER_ASSERT_MESSAGE(reporter,
+            referenceRecord->fPaints.count() ==
+            testRecord->fPaints.count(), testStep->assertMessage());
+        for (int i = 0; i < referenceRecord->fPaints.count(); ++i) {
+            REPORTER_ASSERT_MESSAGE(reporter,
+                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,
+                EQ(referenceRecord->fRegions[i], testRecord->fRegions[i]),
+                                    testStep->assertMessage());
+        }
+        REPORTER_ASSERT_MESSAGE(reporter,
+            !referenceRecord->fPathHeap ==
+            !testRecord->fPathHeap,
+            testStep->assertMessage());
+        // The following tests are commented out because they currently
+        // fail. Issue: http://code.google.com/p/skia/issues/detail?id=507
+        /*
+        if (referenceRecord->fPathHeap) {
+            REPORTER_ASSERT_MESSAGE(reporter,
+                referenceRecord->fPathHeap->count() ==
+                testRecord->fPathHeap->count(),
+                testStep->assertMessage());
+            for (int i = 0; i < referenceRecord->fPathHeap->count(); ++i) {
+                REPORTER_ASSERT_MESSAGE(reporter,
+                    (*referenceRecord->fPathHeap)[i] ==
+                    (*testRecord->fPathHeap)[i], testStep->assertMessage());
+            }
+        }
+        */
+
+    }
+
+public:
+
+    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, recordFlags);
+        testStep->draw(referenceCanvas, reporter);
+        SkPicture testPicture;
+        SkCanvas* testCanvas = testPicture.beginRecording(kWidth,
+            kHeight, recordFlags);
+        testStep->draw(testCanvas, reporter);
+        testStep->setAssertMessageFormat(kPictureSecondDrawAssertMessageFormat);
+        testStep->draw(testCanvas, reporter);
+
+        SkPictureRecord* referenceRecord = static_cast<SkPictureRecord*>(
+            referenceCanvas);
+        SkPictureRecord* testRecord = static_cast<SkPictureRecord*>(
+            testCanvas);
+        testStep->setAssertMessageFormat(kPictureResourceReuseMessageFormat);
+        AssertFlattenedObjectsEqual(referenceRecord, testRecord,
+            reporter, testStep);
+    }
+};
+
+// 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);
+
+        if (silent) {
+            deferredCanvas.silentFlush();
+        } else {
+            deferredCanvas.flush();
+        }
+
+        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,
+    const SkCanvas& referenceCanvas) {
+
+    SkBitmap indirectStore;
+    createBitmap(&indirectStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice indirectDevice(indirectStore);
+    SkCanvas indirectCanvas(&indirectDevice);
+    SkProxyCanvas proxyCanvas(&indirectCanvas);
+    testStep->setAssertMessageFormat(kProxyDrawAssertMessageFormat);
+    testStep->draw(&proxyCanvas, reporter);
+    // Verify that the SkProxyCanvas reports consitent state
+    testStep->setAssertMessageFormat(kProxyStateAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &proxyCanvas, &referenceCanvas,
+        testStep);
+    // Verify that the indirect canvas reports consitent state
+    testStep->setAssertMessageFormat(kProxyIndirectStateAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &indirectCanvas, &referenceCanvas,
+        testStep);
+}
+
+// unused
+static void TestNWayCanvasStateConsistency(
+    skiatest::Reporter* reporter,
+    CanvasTestStep* testStep,
+    const SkCanvas& referenceCanvas) {
+
+    SkBitmap indirectStore1;
+    createBitmap(&indirectStore1, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice indirectDevice1(indirectStore1);
+    SkCanvas indirectCanvas1(&indirectDevice1);
+
+    SkBitmap indirectStore2;
+    createBitmap(&indirectStore2, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice indirectDevice2(indirectStore2);
+    SkCanvas indirectCanvas2(&indirectDevice2);
+
+    SkISize canvasSize = referenceCanvas.getDeviceSize();
+    SkNWayCanvas nWayCanvas(canvasSize.width(), canvasSize.height());
+    nWayCanvas.addCanvas(&indirectCanvas1);
+    nWayCanvas.addCanvas(&indirectCanvas2);
+
+    testStep->setAssertMessageFormat(kNWayDrawAssertMessageFormat);
+    testStep->draw(&nWayCanvas, reporter);
+    // Verify that the SkProxyCanvas reports consitent state
+    testStep->setAssertMessageFormat(kNWayStateAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &nWayCanvas, &referenceCanvas,
+        testStep);
+    // Verify that the indirect canvases report consitent state
+    testStep->setAssertMessageFormat(kNWayIndirect1StateAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &indirectCanvas1, &referenceCanvas,
+        testStep);
+    testStep->setAssertMessageFormat(kNWayIndirect2StateAssertMessageFormat);
+    AssertCanvasStatesEqual(reporter, &indirectCanvas2, &referenceCanvas,
+        testStep);
+}
+
+/*
+ * This sub-test verifies that the test step passes when executed
+ * with SkCanvas and with classes derrived from SkCanvas. It also verifies
+ * that the all canvas derivatives report the same state as an SkCanvas
+ * after having executed the test step.
+ */
+static void TestOverrideStateConsistency(skiatest::Reporter* reporter,
+                                         CanvasTestStep* testStep) {
+    SkBitmap referenceStore;
+    createBitmap(&referenceStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice referenceDevice(referenceStore);
+    SkCanvas referenceCanvas(&referenceDevice);
+    testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat);
+    testStep->draw(&referenceCanvas, reporter);
+
+    SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas, false);
+
+    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
+
+    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
+
+    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) {
+    // Init global here because bitmap pixels cannot be alocated during
+    // static initialization
+    kTestBitmap = testBitmap();
+
+    for (int testStep = 0; testStep < testStepArray().count(); testStep++) {
+        TestOverrideStateConsistency(reporter, testStepArray()[testStep]);
+        SkPictureTester::TestPictureFlattenedObjectReuse(reporter,
+            testStepArray()[testStep], 0);
+    }
+
+    // Explicitly call reset(), so we don't leak the pixels (since kTestBitmap is a global)
+    kTestBitmap.reset();
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Canvas", TestCanvasClass, TestCanvas)
diff --git a/tests/ClampRangeTest.cpp b/tests/ClampRangeTest.cpp
new file mode 100644
index 0000000..fa3804e
--- /dev/null
+++ b/tests/ClampRangeTest.cpp
@@ -0,0 +1,135 @@
+
+/*
+ * Copyright 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 "gradients/SkClampRange.h"
+#include "SkRandom.h"
+
+static skiatest::Reporter* gReporter;
+
+static void debug_me() {
+    if (NULL == gReporter) {
+        SkDebugf("dsfdssd\n");
+    }
+}
+
+#ifdef USE_REPORTER
+
+#define R_ASSERT(cond)                  \
+    do { if (!(cond)) {                 \
+    debug_me();                         \
+    REPORTER_ASSERT(gReporter, cond);   \
+    }} while (0)
+
+#else
+#define R_ASSERT(cond)                  \
+    do { if (!(cond)) {                 \
+    debug_me();                         \
+    }} while (0)
+#endif
+
+static int classify_value(SkFixed fx, int v0, int v1) {
+    if (fx <= 0) {
+        return v0;
+    }
+    if (fx >= 0xFFFF) {
+        return v1;
+    }
+    R_ASSERT(false);
+    return 0;
+}
+
+#define V0  -42
+#define V1  1024
+
+static void slow_check(const SkClampRange& range,
+                       SkFixed fx, SkFixed dx, int count) {
+    SkASSERT(range.fCount0 + range.fCount1 + range.fCount2 == count);
+
+    int i;
+    if (range.fOverflowed) {
+        fx = range.fFx1;
+        for (i = 0; i < range.fCount1; i++) {
+            R_ASSERT(fx >= 0 && fx <= 0xFFFF);
+            fx += dx;
+        }
+    } else {
+        for (i = 0; i < range.fCount0; i++) {
+            int v = classify_value(fx, V0, V1);
+            R_ASSERT(v == range.fV0);
+            fx += dx;
+        }
+        if (range.fCount1 > 0 && fx != range.fFx1) {
+            SkDebugf("%x %x\n", fx, range.fFx1);
+            R_ASSERT(!"bad fFx1");
+            return;
+        }
+        for (i = 0; i < range.fCount1; i++) {
+            R_ASSERT(fx >= 0 && fx <= 0xFFFF);
+            fx += dx;
+        }
+        for (i = 0; i < range.fCount2; i++) {
+            int v = classify_value(fx, V0, V1);
+            R_ASSERT(v == range.fV1);
+            fx += dx;
+        }
+    }
+}
+
+static void test_range(SkFixed fx, SkFixed dx, int count) {
+    SkClampRange range;
+    range.init(fx, dx, count, V0, V1);
+    slow_check(range, fx, dx, count);
+}
+
+#define ff(x)   SkIntToFixed(x)
+
+void TestClampRange(skiatest::Reporter* reporter);
+void TestClampRange(skiatest::Reporter* reporter) {
+    gReporter = reporter;
+
+    test_range(0, 0, 20);
+    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);
+    test_range(10, -3, 20);
+
+    test_range(ff(1),  ff(16384),  100);
+    test_range(ff(-1), ff(-16384), 100);
+    test_range(ff(1)/2, ff(16384), 100);
+    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;
+        SkFixed sx = rand.nextS() >> 1;
+        int count = rand.nextU() % 1000 + 1;
+        SkFixed dx = (sx - fx) / count;
+        test_range(fx, dx, count);
+    }
+
+    // test overflow cases
+    for (int i = 0; i < 100000; i++) {
+        SkFixed fx = rand.nextS();
+        SkFixed dx = rand.nextS();
+        int count = rand.nextU() % 1000 + 1;
+        test_range(fx, dx, count);
+    }
+}
+
+#ifdef USE_REPORTER
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("ClampRange", ClampRangeClass, TestClampRange)
+
+#endif
diff --git a/tests/ClipCacheTest.cpp b/tests/ClipCacheTest.cpp
new file mode 100644
index 0000000..ec8169e
--- /dev/null
+++ b/tests/ClipCacheTest.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright 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;
+    cache.getLastClip(&cacheClip);
+    REPORTER_ASSERT(reporter, clip == cacheClip);
+
+    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, 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, 1 == texture1->getRefCnt());
+
+    // push the state
+    cache.push();
+
+    // verify that the pushed state is initially empty
+    check_state(reporter, cache, emptyClip, NULL, emptyBound);
+    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
+
+    // modify the new state
+    GrIRect bound2;
+    bound2.set(-10, -10, 10, 10);
+
+    SkClipStack clip2(bound2);
+
+    cache.acquireMask(clip2, 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, 1 == texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());
+
+    // check to make sure canReuse works
+    REPORTER_ASSERT(reporter, cache.canReuse(clip2, bound2));
+    REPORTER_ASSERT(reporter, !cache.canReuse(clip1, bound1));
+
+    // pop the state
+    cache.pop();
+
+    // verify that the old state is restored
+    check_state(reporter, cache, clip1, texture1, bound1);
+    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());
+
+    // manually clear the state
+    cache.reset();
+
+    // verify it is now empty
+    check_state(reporter, cache, emptyClip, NULL, emptyBound);
+    REPORTER_ASSERT(reporter, 1 == texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, 1 == texture2->getRefCnt());
+
+    // 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, 1 == texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, 1 == 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
new file mode 100644
index 0000000..2e913a7
--- /dev/null
+++ b/tests/ClipCubicTest.cpp
@@ -0,0 +1,171 @@
+
+/*
+ * Copyright 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 "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkCubicClipper.h"
+#include "SkGeometry.h"
+
+// Currently the supersampler blitter uses int16_t for its index into an array
+// the width of the clip. Test that we don't crash/assert if we try to draw
+// with a device/clip that is larger.
+static void test_giantClip() {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 64919, 1);
+    bm.allocPixels();
+    SkCanvas canvas(bm);
+    canvas.clear(0);
+
+    SkPath path;
+    path.moveTo(0, 0); path.lineTo(1, 0); path.lineTo(33, 1);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    canvas.drawPath(path, paint);
+}
+
+static void PrintCurve(const char *name, const SkPoint crv[4]) {
+    printf("%s: %.10g, %.10g, %.10g, %.10g, %.10g, %.10g, %.10g, %.10g\n",
+            name,
+            (float)crv[0].fX, (float)crv[0].fY,
+            (float)crv[1].fX, (float)crv[1].fY,
+            (float)crv[2].fX, (float)crv[2].fY,
+            (float)crv[3].fX, (float)crv[3].fY);
+
+}
+
+
+static bool CurvesAreEqual(const SkPoint c0[4],
+                           const SkPoint c1[4],
+                           float tol) {
+    for (int i = 0; i < 4; i++) {
+        if (SkScalarAbs(c0[i].fX - c1[i].fX) > SkFloatToScalar(tol) ||
+            SkScalarAbs(c0[i].fY - c1[i].fY) > SkFloatToScalar(tol)
+        ) {
+            PrintCurve("c0", c0);
+            PrintCurve("c1", c1);
+            return false;
+        }
+    }
+    return true;
+}
+
+
+static SkPoint* SetCurve(float x0, float y0,
+                         float x1, float y1,
+                         float x2, float y2,
+                         float x3, float y3,
+                         SkPoint crv[4]) {
+    crv[0].fX = SkFloatToScalar(x0);   crv[0].fY = SkFloatToScalar(y0);
+    crv[1].fX = SkFloatToScalar(x1);   crv[1].fY = SkFloatToScalar(y1);
+    crv[2].fX = SkFloatToScalar(x2);   crv[2].fY = SkFloatToScalar(y2);
+    crv[3].fX = SkFloatToScalar(x3);   crv[3].fY = SkFloatToScalar(y3);
+    return crv;
+}
+
+
+static void TestCubicClipping(skiatest::Reporter* reporter) {
+    static SkPoint crv[4] = {
+        { SkIntToScalar(0), SkIntToScalar(0)  },
+        { SkIntToScalar(2), SkIntToScalar(3)  },
+        { SkIntToScalar(1), SkIntToScalar(10) },
+        { SkIntToScalar(4), SkIntToScalar(12) }
+    };
+
+    SkCubicClipper clipper;
+    SkPoint clipped[4], shouldbe[4];
+    SkIRect clipRect;
+    bool success;
+    const float tol = SkFloatToScalar(1e-4f);
+
+    // Test no clip, with plenty of room.
+    clipRect.set(-2, -2, 6, 14);
+    clipper.setClip(clipRect);
+    success = clipper.clipCubic(crv, clipped);
+    REPORTER_ASSERT(reporter, success == true);
+    REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+        0, 0, 2, 3, 1, 10, 4, 12, shouldbe), tol));
+
+    // Test no clip, touching first point.
+    clipRect.set(-2, 0, 6, 14);
+    clipper.setClip(clipRect);
+    success = clipper.clipCubic(crv, clipped);
+    REPORTER_ASSERT(reporter, success == true);
+    REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+        0, 0, 2, 3, 1, 10, 4, 12, shouldbe), tol));
+
+    // Test no clip, touching last point.
+    clipRect.set(-2, -2, 6, 12);
+    clipper.setClip(clipRect);
+    success = clipper.clipCubic(crv, clipped);
+    REPORTER_ASSERT(reporter, success == true);
+    REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+        0, 0, 2, 3, 1, 10, 4, 12, shouldbe), tol));
+
+    // Test all clip.
+    clipRect.set(-2, 14, 6, 20);
+    clipper.setClip(clipRect);
+    success = clipper.clipCubic(crv, clipped);
+    REPORTER_ASSERT(reporter, success == false);
+
+    // Test clip at 1.
+    clipRect.set(-2, 1, 6, 14);
+    clipper.setClip(clipRect);
+    success = clipper.clipCubic(crv, clipped);
+    REPORTER_ASSERT(reporter, success == true);
+    REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+        0.5126125216f, 1,
+        1.841195941f,  4.337081432f,
+        1.297019958f,  10.19801331f,
+        4,            12,
+        shouldbe), tol));
+
+    // Test clip at 2.
+    clipRect.set(-2, 2, 6, 14);
+    clipper.setClip(clipRect);
+    success = clipper.clipCubic(crv, clipped);
+    REPORTER_ASSERT(reporter, success == true);
+    REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+        00.8412352204f, 2,
+        1.767683744f,   5.400758266f,
+        1.55052948f,    10.36701965f,
+        4,             12,
+        shouldbe), tol));
+
+    // Test clip at 11.
+    clipRect.set(-2, -2, 6, 11);
+    clipper.setClip(clipRect);
+    success = clipper.clipCubic(crv, clipped);
+    REPORTER_ASSERT(reporter, success == true);
+    REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+        0,           0,
+        1.742904663f, 2.614356995f,
+        1.207521796f, 8.266430855f,
+        3.026495695f, 11,
+        shouldbe), tol));
+
+    // Test clip at 10.
+    clipRect.set(-2, -2, 6, 10);
+    clipper.setClip(clipRect);
+    success = clipper.clipCubic(crv, clipped);
+    REPORTER_ASSERT(reporter, success == true);
+    REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+        0,           0,
+        1.551193237f, 2.326789856f,
+        1.297736168f, 7.059780121f,
+        2.505550385f, 10,
+        shouldbe), tol));
+
+    test_giantClip();
+}
+
+
+
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("CubicClipper", CubicClippingTestClass, TestCubicClipping)
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
new file mode 100644
index 0000000..8a74dc8
--- /dev/null
+++ b/tests/ClipStackTest.cpp
@@ -0,0 +1,519 @@
+
+/*
+ * Copyright 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 "SkClipStack.h"
+#include "SkPath.h"
+#include "SkRect.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);
+    p.lineTo(5, 9);
+    p.close();
+    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);
+
+    // Test that assignment works.
+    SkClipStack copy = s;
+    REPORTER_ASSERT(reporter, s == copy);
+
+    // 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);
+}
+
+static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
+                         int count) {
+    SkClipStack::B2TIter iter(stack);
+    int counter = 0;
+    while (iter.next()) {
+        counter += 1;
+    }
+    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::B2TIter::Clip* clip = NULL;
+
+        SkClipStack::B2TIter iter(stack);
+        int i;
+
+        for (i = 0, clip = iter.next(); clip; ++i, clip = iter.next()) {
+            REPORTER_ASSERT(reporter, *clip->fRect == gRects[i]);
+        }
+
+        SkASSERT(i == 4);
+    }
+
+    // top to bottom iteration
+    {
+        const SkClipStack::Iter::Clip* clip = NULL;
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
+        int i;
+
+        for (i = 3, clip = iter.prev(); clip; --i, clip = iter.prev()) {
+            REPORTER_ASSERT(reporter, *clip->fRect == gRects[i]);
+        }
+
+        SkASSERT(i == -1);
+    }
+
+    // skipToTopmost
+    {
+        const SkClipStack::Iter::Clip*clip = NULL;
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+
+        clip = iter.skipToTopmost(SkRegion::kUnion_Op);
+        REPORTER_ASSERT(reporter, *clip->fRect == 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::Iter::Clip* clip = NULL;
+    int count = 0;
+
+    for (clip = iter.prev(); clip; clip = iter.prev(), ++count) {
+        ;
+    }
+
+    return count;
+}
+
+// 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 TestClipStack(skiatest::Reporter* reporter) {
+    SkClipStack stack;
+
+    REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
+    assert_count(reporter, stack, 0);
+
+    static const SkIRect gRects[] = {
+        { 0, 0, 100, 100 },
+        { 25, 25, 125, 125 },
+        { 0, 0, 1000, 1000 },
+        { 0, 0, 75, 75 }
+    };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
+        stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
+    }
+
+    // all of the above rects should have been intersected, leaving only 1 rect
+    SkClipStack::B2TIter iter(stack);
+    const SkClipStack::B2TIter::Clip* clip = 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);
+    // 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);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("ClipStack", TestClipStackClass, TestClipStack)
diff --git a/tests/ClipperTest.cpp b/tests/ClipperTest.cpp
new file mode 100644
index 0000000..b914374
--- /dev/null
+++ b/tests/ClipperTest.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 "Test.h"
+#include "SkPath.h"
+#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+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) };
+
+    // this should not assert, even though our choppers do a poor numerical
+    // job when computing their t values.
+    // http://code.google.com/p/skia/issues/detail?id=444
+    clipper.clipCubic(pts, clip);
+}
+
+static void test_intersectline(skiatest::Reporter* reporter) {
+    static const SkScalar L = 0;
+    static const SkScalar T = 0;
+    static const SkScalar R = SkIntToScalar(100);
+    static const SkScalar B = SkIntToScalar(100);
+    static const SkScalar CX = SkScalarHalf(L + R);
+    static const SkScalar CY = SkScalarHalf(T + B);
+    static const SkRect gR = { L, T, R, B };
+
+    size_t i;
+    SkPoint dst[2];
+
+    static const SkPoint gEmpty[] = {
+        // sides
+        { L, CY }, { L - 10, CY },
+        { R, CY }, { R + 10, CY },
+        { CX, T }, { CX, T - 10 },
+        { CX, B }, { CX, B + 10 },
+        // corners
+        { L, T }, { L - 10, T - 10 },
+        { L, B }, { L - 10, B + 10 },
+        { R, T }, { R + 10, T - 10 },
+        { R, B }, { R + 10, B + 10 },
+    };
+    for (i = 0; i < SK_ARRAY_COUNT(gEmpty); i += 2) {
+        bool valid = SkLineClipper::IntersectLine(&gEmpty[i], gR, dst);
+        if (valid) {
+            SkDebugf("----- [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
+        }
+        REPORTER_ASSERT(reporter, !valid);
+    }
+
+    static const SkPoint gFull[] = {
+        // diagonals, chords
+        { L, T }, { R, B },
+        { L, B }, { R, T },
+        { CX, T }, { CX, B },
+        { L, CY }, { R, CY },
+        { CX, T }, { R, CY },
+        { CX, T }, { L, CY },
+        { L, CY }, { CX, B },
+        { R, CY }, { CX, B },
+        // edges
+        { L, T }, { L, B },
+        { R, T }, { R, B },
+        { L, T }, { R, T },
+        { L, B }, { R, B },
+    };
+    for (i = 0; i < SK_ARRAY_COUNT(gFull); i += 2) {
+        bool valid = SkLineClipper::IntersectLine(&gFull[i], gR, dst);
+        if (!valid || memcmp(&gFull[i], dst, sizeof(dst))) {
+            SkDebugf("++++ [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
+        }
+        REPORTER_ASSERT(reporter, valid && !memcmp(&gFull[i], dst, sizeof(dst)));
+    }
+
+    static const SkPoint gPartial[] = {
+        { L - 10, CY }, { CX, CY }, { L, CY }, { CX, CY },
+        { CX, T - 10 }, { CX, CY }, { CX, T }, { CX, CY },
+        { R + 10, CY }, { CX, CY }, { R, CY }, { CX, CY },
+        { CX, B + 10 }, { CX, CY }, { CX, B }, { CX, CY },
+        // extended edges
+        { L, T - 10 }, { L, B + 10 }, { L, T }, { L, B },
+        { R, T - 10 }, { R, B + 10 }, { R, T }, { R, B },
+        { L - 10, T }, { R + 10, T }, { L, T }, { R, T },
+        { L - 10, B }, { R + 10, B }, { L, B }, { R, B },
+    };
+    for (i = 0; i < SK_ARRAY_COUNT(gPartial); i += 4) {
+        bool valid = SkLineClipper::IntersectLine(&gPartial[i], gR, dst);
+        if (!valid || memcmp(&gPartial[i+2], dst, sizeof(dst))) {
+            SkDebugf("++++ [%d] %g %g -> %g %g\n", i/2, dst[0].fX, dst[0].fY, dst[1].fX, dst[1].fY);
+        }
+        REPORTER_ASSERT(reporter, valid &&
+                                  !memcmp(&gPartial[i+2], dst, sizeof(dst)));
+    }
+
+}
+
+static void TestClipper(skiatest::Reporter* reporter) {
+    test_intersectline(reporter);
+    test_edgeclipper(reporter);
+    test_hairclipping(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Clipper", TestClipperClass, TestClipper)
diff --git a/tests/ColorFilterTest.cpp b/tests/ColorFilterTest.cpp
new file mode 100644
index 0000000..4016f21
--- /dev/null
+++ b/tests/ColorFilterTest.cpp
@@ -0,0 +1,97 @@
+
+/*
+ * Copyright 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 "SkColor.h"
+#include "SkColorFilter.h"
+#include "SkRandom.h"
+#include "SkXfermode.h"
+#include "SkOrderedReadBuffer.h"
+#include "SkOrderedWriteBuffer.h"
+
+static SkFlattenable* reincarnate_flattenable(SkFlattenable* obj) {
+    SkOrderedWriteBuffer wb(1024);
+    wb.writeFlattenable(obj);
+
+    size_t size = wb.size();
+    SkAutoSMalloc<1024> storage(size);
+    // make a copy into storage
+    wb.writeToMemory(storage.get());
+
+    SkOrderedReadBuffer rb(storage.get(), size);
+    return rb.readFlattenable();
+}
+
+template <typename T> T* reincarnate(T* obj) {
+    return (T*)reincarnate_flattenable(obj);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ILLEGAL_MODE    ((SkXfermode::Mode)-1)
+
+static void test_asColorMode(skiatest::Reporter* reporter) {
+    SkRandom rand;
+
+    for (int mode = 0; mode <= SkXfermode::kLastMode; mode++) {
+        SkColor color = rand.nextU();
+
+        // ensure we always get a filter, by avoiding the possibility of a
+        // special case that would return NULL (if color's alpha is 0 or 0xFF)
+        color = SkColorSetA(color, 0x7F);
+
+        SkColorFilter* cf = SkColorFilter::CreateModeFilter(color,
+                                                        (SkXfermode::Mode)mode);
+
+        // allow for no filter if we're in Dst mode (its a no op)
+        if (SkXfermode::kDst_Mode == mode && NULL == cf) {
+            continue;
+        }
+
+        SkAutoUnref aur(cf);
+        REPORTER_ASSERT(reporter, cf);
+
+        SkColor c = ~color;
+        SkXfermode::Mode m = ILLEGAL_MODE;
+
+        SkColor expectedColor = color;
+        SkXfermode::Mode expectedMode = (SkXfermode::Mode)mode;
+
+//        SkDebugf("--- mc [%d %x] ", mode, color);
+
+        REPORTER_ASSERT(reporter, cf->asColorMode(&c, &m));
+        // handle special-case folding by the factory
+        if (SkXfermode::kClear_Mode == mode) {
+            if (c != expectedColor) {
+                expectedColor = 0;
+            }
+            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);
+            REPORTER_ASSERT(reporter, cf2);
+
+            SkColor c2 = ~color;
+            SkXfermode::Mode m2 = ILLEGAL_MODE;
+            REPORTER_ASSERT(reporter, cf2->asColorMode(&c2, &m2));
+            REPORTER_ASSERT(reporter, c2 == expectedColor);
+            REPORTER_ASSERT(reporter, m2 == expectedMode);
+        }
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("ColorFilter", ColorFilterTestClass, test_asColorMode)
diff --git a/tests/ColorTest.cpp b/tests/ColorTest.cpp
new file mode 100644
index 0000000..a038a06
--- /dev/null
+++ b/tests/ColorTest.cpp
@@ -0,0 +1,189 @@
+
+/*
+ * Copyright 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 "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkMathPriv.h"
+#include "SkRandom.h"
+#include "SkUnPreMultiply.h"
+
+#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 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 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 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 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 int SkDiv65025Round(int x) {
+    return (x + 65025/2) / 65025;
+//    return x / 65025;
+}
+static bool S32A_D565_Blend_2(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+    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 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 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);
+            SkPMColor p0 = SkPreMultiplyColor(c0);
+
+            SkColor c1 = SkUnPreMultiply::PMColorToColor(p0);
+            SkPMColor p1 = SkPreMultiplyColor(c1);
+
+            // we can't promise that c0 == c1, since c0 -> p0 is a many to one
+            // function, however, we can promise that p0 -> c1 -> p1 : p0 == p1
+            REPORTER_ASSERT(reporter, p0 == p1);
+
+            {
+                int ax = SkMulDiv255Ceiling(x, a);
+                REPORTER_ASSERT(reporter, ax <= a);
+            }
+        }
+    }
+}
+
+/**
+  This test fails: SkFourByteInterp does *not* preserve opaque destinations.
+  SkAlpha255To256 implemented as (alpha + 1) is faster than
+  (alpha + (alpha >> 7)), but inaccurate, and Skia intends to phase it out.
+*/
+/*
+static void test_interp(skiatest::Reporter* reporter) {
+    SkRandom r;
+
+    U8CPU a0 = 0;
+    U8CPU a255 = 255;
+    for (int i = 0; i < 200; i++) {
+        SkColor colorSrc = r.nextU();
+        SkColor colorDst = r.nextU();
+        SkPMColor src = SkPreMultiplyColor(colorSrc);
+        SkPMColor dst = SkPreMultiplyColor(colorDst);
+
+        REPORTER_ASSERT(reporter, SkFourByteInterp(src, dst, a0) == dst);
+        REPORTER_ASSERT(reporter, SkFourByteInterp(src, dst, a255) == src);
+    }
+}
+*/
+
+static void test_fast_interp(skiatest::Reporter* reporter) {
+    SkRandom r;
+
+    U8CPU a0 = 0;
+    U8CPU a255 = 255;
+    for (int i = 0; i < 200; i++) {
+        SkColor colorSrc = r.nextU();
+        SkColor colorDst = r.nextU();
+        SkPMColor src = SkPreMultiplyColor(colorSrc);
+        SkPMColor dst = SkPreMultiplyColor(colorDst);
+
+        REPORTER_ASSERT(reporter, SkFastFourByteInterp(src, dst, a0) == dst);
+        REPORTER_ASSERT(reporter, SkFastFourByteInterp(src, dst, a255) == src);
+    }
+}
+
+static void TestColor(skiatest::Reporter* reporter) {
+    test_premul(reporter);
+    //test_interp(reporter);
+    test_fast_interp(reporter);
+//    test_565blend(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Color", ColorTestClass, TestColor)
diff --git a/tests/DataRefTest.cpp b/tests/DataRefTest.cpp
new file mode 100644
index 0000000..d6ba775
--- /dev/null
+++ b/tests/DataRefTest.cpp
@@ -0,0 +1,152 @@
+
+/*
+ * Copyright 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 "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;
+
+static void delete_int_proc(const void* ptr, size_t len, void* context) {
+    int* data = (int*)ptr;
+    SkASSERT(context == gGlobal);
+    delete[] data;
+}
+
+static void assert_len(skiatest::Reporter* reporter, SkData* ref, size_t len) {
+    REPORTER_ASSERT(reporter, ref->size() == len);
+}
+
+static void assert_data(skiatest::Reporter* reporter, SkData* ref,
+                        const void* data, size_t len) {
+    REPORTER_ASSERT(reporter, ref->size() == len);
+    REPORTER_ASSERT(reporter, !memcmp(ref->data(), data, len));
+}
+
+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;
+
+    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));
+
+    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);
+
+    SkData* tmp = SkData::NewSubset(r1, strlen(str), 10);
+    assert_len(reporter, tmp, 0);
+    tmp->unref();
+    tmp = SkData::NewSubset(r1, 0, 0);
+    assert_len(reporter, tmp, 0);
+    tmp->unref();
+
+    test_cstring(reporter);
+    test_dataset(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Data", DataTestClass, TestData)
diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp
new file mode 100644
index 0000000..2c27971
--- /dev/null
+++ b/tests/DeferredCanvasTest.cpp
@@ -0,0 +1,361 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+#include "SkBitmap.h"
+#include "SkDeferredCanvas.h"
+#include "SkDevice.h"
+#include "SkShader.h"
+
+static const int gWidth = 2;
+static const int gHeight = 2;
+
+static void create(SkBitmap* bm, SkBitmap::Config config, SkColor color) {
+    bm->setConfig(config, gWidth, gHeight);
+    bm->allocPixels();
+    bm->eraseColor(color);
+}
+
+static void TestDeferredCanvasBitmapAccess(skiatest::Reporter* reporter) {
+    SkBitmap store;
+
+    create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice device(store);
+    SkDeferredCanvas canvas(&device);
+
+    canvas.clear(0x00000000);
+
+    SkAutoLockPixels alp(store);
+    REPORTER_ASSERT(reporter, store.getColor(0,0) == 0xFFFFFFFF); //verify that clear was deferred
+    SkBitmap accessed = canvas.getDevice()->accessBitmap(false);
+    REPORTER_ASSERT(reporter, store.getColor(0,0) == 0x00000000); //verify that clear was executed
+    REPORTER_ASSERT(reporter, accessed.pixelRef() == store.pixelRef());
+}
+
+static void TestDeferredCanvasFlush(skiatest::Reporter* reporter) {
+    SkBitmap store;
+
+    create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice device(store);
+    SkDeferredCanvas canvas(&device);
+
+    canvas.clear(0x00000000);
+
+    SkAutoLockPixels alp(store);
+    REPORTER_ASSERT(reporter, store.getColor(0,0) == 0xFFFFFFFF); //verify that clear was deferred
+    canvas.flush();
+    REPORTER_ASSERT(reporter, store.getColor(0,0) == 0x00000000); //verify that clear was executed
+}
+
+static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
+    SkBitmap store;
+    SkRect fullRect;
+    fullRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(gWidth),
+        SkIntToScalar(gHeight));
+    SkRect partialRect;
+    partialRect.setXYWH(SkIntToScalar(0), SkIntToScalar(0),
+        SkIntToScalar(1), SkIntToScalar(1));
+    create(&store, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    SkDevice device(store);
+    SkDeferredCanvas canvas(&device);
+
+    // verify that frame is intially fresh
+    REPORTER_ASSERT(reporter, canvas.isFreshFrame());
+    // no clearing op since last call to isFreshFrame -> not fresh
+    REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
+
+    // Verify that clear triggers a fresh frame
+    canvas.clear(0x00000000);
+    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.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.isFreshFrame());
+
+    // Verify that a clear with clipping triggers a fresh frame
+    // (clear is not affected by clipping)
+    canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+    canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false);
+    canvas.clear(0x00000000);
+    canvas.restore();
+    REPORTER_ASSERT(reporter, canvas.isFreshFrame());
+
+    // Verify that full frame rects with different forms of opaque paint
+    // trigger frames to be marked as fresh
+    {
+        SkPaint paint;
+        paint.setStyle( SkPaint::kFill_Style );
+        paint.setAlpha( 255 );
+        canvas.drawRect(fullRect, paint);
+        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;
+        paint.setStyle( SkPaint::kFill_Style );
+        SkBitmap bmp;
+        create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+        bmp.setIsOpaque(true);
+        SkShader* shader = SkShader::CreateBitmapShader(bmp,
+            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+        paint.setShader(shader)->unref();
+        canvas.drawRect(fullRect, paint);
+        REPORTER_ASSERT(reporter, canvas.isFreshFrame());
+    }
+
+    // Verify that full frame rects with different forms of non-opaque paint
+    // do not trigger frames to be marked as fresh
+    {
+        SkPaint paint;
+        paint.setStyle( SkPaint::kFill_Style );
+        paint.setAlpha( 254 );
+        canvas.drawRect(fullRect, paint);
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
+    }
+    {
+        SkPaint paint;
+        paint.setStyle( SkPaint::kFill_Style );
+        SkBitmap bmp;
+        create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+        bmp.setIsOpaque(false);
+        SkShader* shader = SkShader::CreateBitmapShader(bmp,
+            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+        paint.setShader(shader)->unref();
+        canvas.drawRect(fullRect, paint);
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
+    }
+
+    // Verify that incomplete coverage does not trigger a fresh frame
+    {
+        SkPaint paint;
+        paint.setStyle(SkPaint::kFill_Style);
+        paint.setAlpha(255);
+        canvas.drawRect(partialRect, paint);
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
+    }
+
+    // Verify that incomplete coverage due to clipping does not trigger a fresh
+    // frame
+    {
+        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+        canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false);
+        SkPaint paint;
+        paint.setStyle(SkPaint::kFill_Style);
+        paint.setAlpha(255);
+        canvas.drawRect(fullRect, paint);
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
+    }
+
+    // Verify that stroked rect does not trigger a fresh frame
+    {
+        SkPaint paint;
+        paint.setStyle( SkPaint::kStroke_Style );
+        paint.setAlpha( 255 );
+        canvas.drawRect(fullRect, paint);
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
+    }
+
+    // Verify kSrcMode triggers a fresh frame even with transparent color
+    {
+        SkPaint paint;
+        paint.setStyle( SkPaint::kFill_Style );
+        paint.setAlpha( 100 );
+        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+        canvas.drawRect(fullRect, paint);
+        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 TestDeferredCanvas(skiatest::Reporter* reporter) {
+    TestDeferredCanvasBitmapAccess(reporter);
+    TestDeferredCanvasFlush(reporter);
+    TestDeferredCanvasFreshFrame(reporter);
+    TestDeferredCanvasMemoryLimit(reporter);
+    TestDeferredCanvasBitmapCaching(reporter);
+    TestDeferredCanvasSkip(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("DeferredCanvas", TestDeferredCanvasClass, TestDeferredCanvas)
diff --git a/tests/DequeTest.cpp b/tests/DequeTest.cpp
new file mode 100644
index 0000000..62e8e4d
--- /dev/null
+++ b/tests/DequeTest.cpp
@@ -0,0 +1,171 @@
+
+/*
+ * Copyright 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 "SkDeque.h"
+
+static void assert_count(skiatest::Reporter* reporter, const SkDeque& deq, int count) {
+    if (0 == count) {
+        REPORTER_ASSERT(reporter, deq.empty());
+        REPORTER_ASSERT(reporter, 0 == deq.count());
+        REPORTER_ASSERT(reporter, sizeof(int) == deq.elemSize());
+        REPORTER_ASSERT(reporter, NULL == deq.front());
+        REPORTER_ASSERT(reporter, NULL == deq.back());
+    } else {
+        REPORTER_ASSERT(reporter, !deq.empty());
+        REPORTER_ASSERT(reporter, count == deq.count());
+        REPORTER_ASSERT(reporter, sizeof(int) == deq.elemSize());
+        REPORTER_ASSERT(reporter, NULL != deq.front());
+        REPORTER_ASSERT(reporter, NULL != deq.back());
+        if (1 == count) {
+            REPORTER_ASSERT(reporter, deq.back() == deq.front());
+        } else {
+            REPORTER_ASSERT(reporter, deq.back() != deq.front());
+        }
+    }
+}
+
+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 (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);
+}
+
+// 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
+
+    assert_count(reporter, deq, 0);
+    for (i = 1; i <= 10; i++) {
+        *(int*)deq.push_front() = i;
+    }
+    assert_count(reporter, deq, 10);
+    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_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
+
+    for (i = 10; i >= 1; --i) {
+        *(int*)deq.push_back() = i;
+    }
+    assert_count(reporter, deq, 10);
+    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_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 test pushing/popping on both ends
+
+    *(int*)deq.push_front() = 5;
+    *(int*)deq.push_back() = 4;
+    *(int*)deq.push_front() = 6;
+    *(int*)deq.push_back() = 3;
+    *(int*)deq.push_front() = 7;
+    *(int*)deq.push_back() = 2;
+    *(int*)deq.push_front() = 8;
+    *(int*)deq.push_back() = 1;
+    assert_count(reporter, deq, 8);
+    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
new file mode 100644
index 0000000..0147a71
--- /dev/null
+++ b/tests/DrawBitmapRectTest.cpp
@@ -0,0 +1,187 @@
+
+/*
+ * Copyright 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 "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkShader.h"
+
+#ifdef SK_SCALAR_IS_FLOAT
+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(0);  // 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);
+}
+#endif
+
+/*
+ *  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);
+
+    size_t count = bm.width() * bm.bytesPerPixel();
+    for (int y = 0; y < bm.height(); y++) {
+        const uint8_t* ptr = reinterpret_cast<const uint8_t*>(bm.getAddr(0, y));
+        for (size_t i = 0; i < count; i++) {
+            if (ptr[i]) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+static const int gWidth = 256;
+static const int gHeight = 256;
+
+static void create(SkBitmap* bm, SkBitmap::Config config, SkColor color) {
+    bm->setConfig(config, gWidth, gHeight);
+    bm->allocPixels();
+    bm->eraseColor(color);
+}
+
+static void TestDrawBitmapRect(skiatest::Reporter* reporter) {
+    SkBitmap src, dst;
+
+    create(&src, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+    create(&dst, SkBitmap::kARGB_8888_Config, 0);
+
+    SkCanvas canvas(dst);
+
+    SkIRect srcR = { gWidth, 0, gWidth + 16, 16 };
+    SkRect  dstR = { 0, 0, SkIntToScalar(16), SkIntToScalar(16) };
+
+    canvas.drawBitmapRect(src, &srcR, dstR, NULL);
+
+    // 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);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("DrawBitmapRect", TestDrawBitmapRectClass, TestDrawBitmapRect)
diff --git a/tests/DrawPathTest.cpp b/tests/DrawPathTest.cpp
new file mode 100644
index 0000000..55e3164
--- /dev/null
+++ b/tests/DrawPathTest.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 "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) {
+    SkBitmap bm;
+    bm.setConfig(config, w, h, rb);
+    if (addr) {
+        bm.setPixels(addr);
+    } else {
+        bm.allocPixels();
+    }
+    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
+//
+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;
+    SkAutoTUnref<SkCanvas> canvas(new_canvas(33000, 10));
+    canvas.get()->clear(0);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkPath path;
+    path.addOval(SkRect::MakeXYWH(-10, -10, 20 + W, 20 + H));
+    canvas.get()->drawPath(path, paint);
+}
+
+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);
+//    test_crbug131181(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("DrawPath", TestDrawPathClass, TestDrawPath)
diff --git a/tests/DrawTextTest.cpp b/tests/DrawTextTest.cpp
new file mode 100644
index 0000000..4f3eb82
--- /dev/null
+++ b/tests/DrawTextTest.cpp
@@ -0,0 +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)
diff --git a/tests/EmptyPathTest.cpp b/tests/EmptyPathTest.cpp
new file mode 100644
index 0000000..4cff190
--- /dev/null
+++ b/tests/EmptyPathTest.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 "Test.h"
+#include "SkPath.h"
+#include "SkCanvas.h"
+
+static void appendStr(SkString* str, const SkPaint& paint) {
+    str->appendf(" style[%d] cap[%d] join[%d] antialias[%d]",
+                 paint.getStyle(), paint.getStrokeCap(),
+                 paint.getStrokeJoin(), paint.isAntiAlias());
+}
+
+static void appendStr(SkString* str, const SkPath& path) {
+    str->appendf(" filltype[%d] ptcount[%d]",
+                 path.getFillType(), path.countPoints());
+}
+
+#define DIMENSION   32
+
+static void drawAndTest(skiatest::Reporter* reporter, const SkPath& path,
+                        const SkPaint& paint, bool shouldDraw) {
+    SkBitmap bm;
+    // 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);
+
+    SkCanvas canvas(bm);
+    SkPaint p(paint);
+    p.setColor(SK_ColorWHITE);
+
+    canvas.drawPath(path, p);
+
+    size_t count = DIMENSION * DIMENSION;
+    const SkPMColor* ptr = bm.getAddr32(0, 0);
+
+    SkPMColor andValue = ~0U;
+    SkPMColor orValue = 0;
+    for (size_t i = 0; i < count; ++i) {
+        SkPMColor c = ptr[i];
+        andValue &= c;
+        orValue |= c;
+    }
+
+    // success means we drew everywhere or nowhere (depending on shouldDraw)
+    bool success = shouldDraw ? (~0U == andValue) : (0 == orValue);
+
+    if (!success) {
+        SkString str;
+        if (shouldDraw) {
+            str.set("Path expected to draw everywhere, but didn't. ");
+        } else {
+            str.set("Path expected to draw nowhere, but did. ");
+        }
+        appendStr(&str, paint);
+        appendStr(&str, path);
+        reporter->report(str.c_str(), skiatest::Reporter::kFailed);
+
+// uncomment this if you want to step in to see the failure
+//        canvas.drawPath(path, p);
+    }
+}
+
+static void iter_paint(skiatest::Reporter* reporter, const SkPath& path, bool shouldDraw) {
+    static const SkPaint::Cap gCaps[] = {
+        SkPaint::kButt_Cap,
+        SkPaint::kRound_Cap,
+        SkPaint::kSquare_Cap
+    };
+    static const SkPaint::Join gJoins[] = {
+        SkPaint::kMiter_Join,
+        SkPaint::kRound_Join,
+        SkPaint::kBevel_Join
+    };
+    static const SkPaint::Style gStyles[] = {
+        SkPaint::kFill_Style,
+        SkPaint::kStroke_Style,
+        SkPaint::kStrokeAndFill_Style
+    };
+    for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
+        for (size_t join = 0; join < SK_ARRAY_COUNT(gJoins); ++join) {
+            for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
+                SkPaint paint;
+                paint.setStrokeWidth(SkIntToScalar(10));
+
+                paint.setStrokeCap(gCaps[cap]);
+                paint.setStrokeJoin(gJoins[join]);
+                paint.setStyle(gStyles[style]);
+
+                paint.setAntiAlias(false);
+                drawAndTest(reporter, path, paint, shouldDraw);
+                paint.setAntiAlias(true);
+                drawAndTest(reporter, path, paint, shouldDraw);
+            }
+        }
+    }
+}
+
+#define CX  (SkIntToScalar(DIMENSION) / 2)
+#define CY  (SkIntToScalar(DIMENSION) / 2)
+
+static void make_empty(SkPath* path) {}
+static void make_M(SkPath* path) { path->moveTo(CX, CY); }
+static void make_MM(SkPath* path) { path->moveTo(CX, CY); path->moveTo(CX, CY); }
+static void make_MZM(SkPath* path) { path->moveTo(CX, CY); path->close(); path->moveTo(CX, CY); }
+static void make_L(SkPath* path) { path->moveTo(CX, CY); path->lineTo(CX, CY); }
+static void make_Q(SkPath* path) { path->moveTo(CX, CY); path->quadTo(CX, CY, CX, CY); }
+static void make_C(SkPath* path) { path->moveTo(CX, CY); path->cubicTo(CX, CY, CX, CY, CX, CY); }
+
+/*  Two invariants are tested: How does an empty/degenerate path draw?
+ *  - if the path is drawn inverse, it should draw everywhere
+ *  - if the path is drawn non-inverse, it should draw nowhere
+ *
+ *  Things to iterate on:
+ *  - path (empty, degenerate line/quad/cubic w/ and w/o close
+ *  - paint style
+ *  - path filltype
+ *  - path stroke variants (e.g. caps, joins, width)
+ */
+static void test_emptydrawing(skiatest::Reporter* reporter) {
+    static void (*gMakeProc[])(SkPath*) = {
+        make_empty, make_M, make_MM, make_MZM, make_L, make_Q, make_C
+    };
+    static SkPath::FillType gFills[] = {
+        SkPath::kWinding_FillType,
+        SkPath::kEvenOdd_FillType,
+        SkPath::kInverseWinding_FillType,
+        SkPath::kInverseEvenOdd_FillType
+    };
+    for (int doClose = 0; doClose < 2; ++doClose) {
+        for  (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProc); ++i) {
+            SkPath path;
+            gMakeProc[i](&path);
+            if (doClose) {
+                path.close();
+            }
+            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
+                path.setFillType(gFills[fill]);
+                bool shouldDraw = path.isInverseFillType();
+                iter_paint(reporter, path, shouldDraw);
+            }
+        }
+    }
+}
+
+static void TestEmptyPath(skiatest::Reporter* reporter) {
+    test_emptydrawing(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("EmptyPath", TestEmptyPathClass, TestEmptyPath)
diff --git a/tests/FillPathTest.cpp b/tests/FillPathTest.cpp
new file mode 100644
index 0000000..0ddf4e2
--- /dev/null
+++ b/tests/FillPathTest.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 "Test.h"
+#include "SkRegion.h"
+#include "SkPath.h"
+#include "SkScan.h"
+#include "SkBlitter.h"
+
+namespace {
+
+struct FakeBlitter : public SkBlitter {
+  FakeBlitter()
+      : m_blitCount(0)
+  {}
+
+  virtual void blitH(int x, int y, int width) {
+    m_blitCount++;
+  }
+
+  int m_blitCount;
+};
+
+}
+
+// http://code.google.com/p/skia/issues/detail?id=87
+// 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;
+  SkIRect clip;
+  SkPath path;
+  int height = 100;
+  int width  = 200;
+  int expected_lines = 5;
+  clip.set(0, height - expected_lines, width, height);
+  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);
+
+  REPORTER_ASSERT(reporter, blitter.m_blitCount == expected_lines);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("FillPath", FillPathTestClass, TestFillPathInverse)
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
new file mode 100644
index 0000000..879973f
--- /dev/null
+++ b/tests/FlateTest.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 <stdlib.h>
+#include <string.h>
+
+#include "Test.h"
+#include "SkData.h"
+#include "SkFlate.h"
+#include "SkStream.h"
+
+// A memory stream that reports zero size with the standard call, like
+// an unseekable file stream would.
+class SkZeroSizeMemStream : public SkMemoryStream {
+public:
+    virtual size_t read(void* buffer, size_t size) {
+        if (buffer == NULL && size == 0)
+            return 0;
+        if (buffer == NULL && size == kGetSizeKey)
+            size = 0;
+        return SkMemoryStream::read(buffer, size);
+    }
+
+    static const size_t kGetSizeKey = 0xDEADBEEF;
+};
+
+static void TestFlate(skiatest::Reporter* reporter, SkMemoryStream* testStream,
+                      size_t dataSize) {
+    if (testStream == NULL)
+      return;
+
+    SkMemoryStream testData(dataSize);
+    uint8_t* data = (uint8_t*)testData.getMemoryBase();
+    srand(0);  // Make data deterministic.
+    for (size_t i = 0; i < dataSize; i++)
+        data[i] = rand() & 0xFF;
+
+    testStream->setMemory(testData.getMemoryBase(), dataSize, true);
+    SkDynamicMemoryWStream compressed;
+    bool status = SkFlate::Deflate(testStream, &compressed);
+    REPORTER_ASSERT(reporter, status);
+
+    // Check that the input data wasn't changed.
+    size_t inputSize = testStream->getLength();
+    if (inputSize == 0)
+        inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey);
+    REPORTER_ASSERT(reporter, testData.getLength() == inputSize);
+    REPORTER_ASSERT(reporter, memcmp(testData.getMemoryBase(),
+                                     testStream->getMemoryBase(),
+                                     testData.getLength()) == 0);
+
+    // Assume there are two test sizes, big and small.
+    if (dataSize < 1024)
+      REPORTER_ASSERT(reporter, compressed.getOffset() < 1024);
+    else
+      REPORTER_ASSERT(reporter, compressed.getOffset() > 1024);
+
+    SkAutoDataUnref data1(compressed.copyToData());
+
+    testStream->setData(data1.get())->unref();
+    SkDynamicMemoryWStream uncompressed;
+    status = SkFlate::Inflate(testStream, &uncompressed);
+    REPORTER_ASSERT(reporter, status);
+
+    // Check that the input data wasn't changed.
+    inputSize = testStream->getLength();
+    if (inputSize == 0)
+        inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey);
+    REPORTER_ASSERT(reporter, data1->size() == inputSize);
+    REPORTER_ASSERT(reporter, memcmp(testStream->getMemoryBase(),
+                                     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(),
+                                     testData.getLength()) == 0);
+}
+
+static void TestFlateCompression(skiatest::Reporter* reporter) {
+    TestFlate(reporter, NULL, 0);
+#if defined(SK_ZLIB_INCLUDE) && !defined(SK_DEBUG)
+    REPORTER_ASSERT(reporter, SkFlate::HaveFlate());
+
+    SkMemoryStream memStream;
+    TestFlate(reporter, &memStream, 512);
+    TestFlate(reporter, &memStream, 10240);
+
+    SkZeroSizeMemStream fileStream;
+    TestFlate(reporter, &fileStream, 512);
+    TestFlate(reporter, &fileStream, 10240);
+#endif
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Flate", FlateTestClass, TestFlateCompression)
diff --git a/tests/FontHostStreamTest.cpp b/tests/FontHostStreamTest.cpp
new file mode 100644
index 0000000..eb301e3
--- /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);
+        paint.setTypeface(streamTypeface)->unref();
+        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
new file mode 100644
index 0000000..879fdd0
--- /dev/null
+++ b/tests/FontHostTest.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 "Test.h"
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include "SkEndian.h"
+
+//#define DUMP_TABLES
+
+#define kFontTableTag_head          SkSetFourByteTag('h', 'e', 'a', 'd')
+#define kFontTableTag_hhea          SkSetFourByteTag('h', 'h', 'e', 'a')
+#define kFontTableTag_maxp          SkSetFourByteTag('m', 'a', 'x', 'p')
+
+static const struct TagSize {
+    SkFontTableTag  fTag;
+    size_t          fSize;
+} gKnownTableSizes[] = {
+    {   kFontTableTag_head,         54 },
+    {   kFontTableTag_hhea,         36 },
+    {   kFontTableTag_maxp,         32 },
+};
+
+static void test_unitsPerEm(skiatest::Reporter* reporter, SkTypeface* face) {
+    int upem = face->getUnitsPerEm();
+    if (0 == upem) return;
+
+    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) {
+    SkFontID fontID = face->uniqueID();
+    if (false) { // avoid bit rot, suppress warning
+        REPORTER_ASSERT(reporter, fontID);
+    }
+
+    int count = face->countTables();
+
+    SkAutoTMalloc<SkFontTableTag> storage(count);
+    SkFontTableTag* tags = storage.get();
+
+    int count2 = face->getTableTags(tags);
+    REPORTER_ASSERT(reporter, count2 == count);
+
+    for (int i = 0; i < count; ++i) {
+        size_t size = face->getTableSize(tags[i]);
+        REPORTER_ASSERT(reporter, size > 0);
+
+#ifdef DUMP_TABLES
+        char name[5];
+        name[0] = (tags[i] >> 24) & 0xFF;
+        name[1] = (tags[i] >> 16) & 0xFF;
+        name[2] = (tags[i] >>  8) & 0xFF;
+        name[3] = (tags[i] >>  0) & 0xFF;
+        name[4] = 0;
+        SkDebugf("%s %d\n", name, size);
+#endif
+
+        for (size_t j = 0; j < SK_ARRAY_COUNT(gKnownTableSizes); ++j) {
+            if (gKnownTableSizes[j].fTag == tags[i]) {
+                REPORTER_ASSERT(reporter, gKnownTableSizes[j].fSize == size);
+            }
+        }
+
+        // do we get the same size from GetTableData and GetTableSize
+        {
+            SkAutoMalloc data(size);
+            size_t size2 = face->getTableData(tags[i], 0, size, data.get());
+            REPORTER_ASSERT(reporter, size2 == size);
+        }
+    }
+}
+
+static void test_tables(skiatest::Reporter* reporter) {
+    static const char* const gNames[] = {
+        NULL,   // default font
+        "Arial", "Times", "Times New Roman", "Helvetica", "Courier",
+        "Courier New", "Terminal", "MS Sans Serif",
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) {
+        SkTypeface* face = SkTypeface::CreateFromName(gNames[i],
+                                                      SkTypeface::kNormal);
+        if (face) {
+#ifdef DUMP_TABLES
+            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
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("FontHost", FontHostTestClass, TestFontHost)
diff --git a/tests/GLInterfaceValidation.cpp b/tests/GLInterfaceValidation.cpp
new file mode 100755
index 0000000..c49de3e
--- /dev/null
+++ b/tests/GLInterfaceValidation.cpp
@@ -0,0 +1,85 @@
+
+/*
+ * Copyright 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
+
+#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)();
+    struct {
+       interfaceFactory fFactory;
+       const char* fName;
+    } interfaceFactories[] = {
+#if SK_ANGLE
+        {GrGLCreateANGLEInterface, "ANGLE"},
+#endif
+        {GrGLCreateNativeInterface, "Native"},
+#if SK_MESA
+        {GrGLCreateMesaInterface, "Mesa"},
+#endif
+        {GrGLCreateDebugInterface, "Debug"},
+        {GrGLCreateNullInterface, "Null"},
+    };
+
+    // On some platforms GrGLCreateNativeInterface will fail unless an OpenGL
+    // context has been created. Also, preserve the current context that may
+    // be in use by outer test harness.
+    SkNativeGLContext::AutoContextRestore nglacr;
+    SkNativeGLContext nglctx;
+    static const int gBOGUS_SIZE = 16;
+    bool nativeContextInit = nglctx.init(gBOGUS_SIZE, gBOGUS_SIZE);
+    REPORTER_ASSERT(reporter, nativeContextInit);
+    if (!nativeContextInit) {
+        return;
+    }
+#if SK_MESA
+    // We must have a current OSMesa context to initialize an OSMesa
+    // GrGLInterface
+    SkMesaGLContext::AutoContextRestore mglacr;
+    SkMesaGLContext mglctx;
+    bool mesaContextInit = mglctx.init(gBOGUS_SIZE, gBOGUS_SIZE);
+    REPORTER_ASSERT(reporter, mesaContextInit);
+    if(!mesaContextInit) {
+        return;
+    }
+#endif
+
+    SkAutoTUnref<const GrGLInterface> iface;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(interfaceFactories); ++i) {
+        iface.reset(interfaceFactories[i].fFactory());
+        REPORTER_ASSERT(reporter, NULL != iface.get());
+        if (iface.get()) {
+            for (GrGLBinding binding = kFirstGrGLBinding;
+                 binding <= kLastGrGLBinding;
+                 binding = static_cast<GrGLBinding>(binding << 1)) {
+                if (iface.get()->fBindingsExported & binding) {
+                    REPORTER_ASSERT(reporter, iface.get()->validate(binding));
+                }
+            }
+        }
+    }
+}
+
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("GLInterfaceValidation",
+                 GLInterfaceValidationTestClass,
+                 GLInterfaceValidationTest)
+
+#endif
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
new file mode 100644
index 0000000..deaf034
--- /dev/null
+++ b/tests/GLProgramsTest.cpp
@@ -0,0 +1,197 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This is a GPU-backend specific test. It relies on static intializers to work
+
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU && SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+
+#include "gl/GrGpuGL.h"
+#include "GrProgramStageFactory.h"
+#include "effects/GrConfigConversionEffect.h"
+
+#include "GrRandom.h"
+#include "Test.h"
+
+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);
+}
+
+bool random_bool(GrRandom* r) {
+    return r->nextF() > .5f;
+}
+
+typedef GrGLProgram::StageDesc StageDesc;
+// TODO: Effects should be able to register themselves for inclusion in the
+// randomly generated shaders. They should be able to configure themselves
+// randomly.
+const GrCustomStage* create_random_effect(StageDesc* stageDesc,
+                                          GrRandom* random,
+                                          GrContext* context,
+                                          GrTexture* dummyTextures[]) {
+
+    // The new code uses SkRandom not GrRandom.
+    // TODO: Remove GrRandom.
+    SkRandom sk_random;
+    sk_random.setSeed(random->nextU());
+    GrCustomStage* stage = GrCustomStageTestFactory::CreateStage(&sk_random,
+                                                                    context,
+                                                                    dummyTextures);
+    GrAssert(stage);
+    return stage;
+}
+}
+
+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));
+
+    // GrGLSLGeneration glslGeneration =
+            GrGetGLSLGeneration(this->glBinding(), this->glInterface());
+    static const int STAGE_OPTS[] = {
+        0,
+        StageDesc::kNoPerspective_OptFlagBit,
+    };
+
+    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
+
+        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) ?
+                                    GrDrawTarget::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 |= GrDrawTarget::kEdge_VertexLayoutBit;
+            if (this->getCaps().shaderDerivativeSupport()) {
+                pdesc.fVertexEdgeType = (GrDrawState::VertexEdgeType) random_int(&random, GrDrawState::kVertexEdgeTypeCnt);
+            } else {
+                pdesc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
+            }
+        } else {
+        }
+
+        pdesc.fColorMatrixEnabled = random_bool(&random);
+
+        if (this->getCaps().dualSourceBlendingSupport()) {
+            pdesc.fDualSrcOutput = random_int(&random, ProgramDesc::kDualSrcOutputCnt);
+        } else {
+            pdesc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
+        }
+
+        SkAutoTUnref<const GrCustomStage> customStages[GrDrawState::kNumStages];
+
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            StageDesc& stage = pdesc.fStages[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);
+                }
+                stage.setEnabled(true);
+            }
+            // use text-formatted verts?
+            if (random_bool(&random)) {
+                pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
+            }
+
+            stage.fCustomStageKey = 0;
+
+            stage.fOptFlags |= STAGE_OPTS[random_int(&random, GR_ARRAY_COUNT(STAGE_OPTS))];
+
+            if (stage.isEnabled()) {
+                GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()};
+                customStages[s].reset(create_random_effect(&stage,
+                                                           &random,
+                                                           getContext(),
+                                                           dummyTextures));
+                if (NULL != customStages[s]) {
+                    stage.fCustomStageKey =
+                        customStages[s]->getFactory().glStageKey(*customStages[s], this->glCaps());
+                }
+            }
+        }
+        GR_STATIC_ASSERT(sizeof(customStages) ==
+                         GrDrawState::kNumStages * sizeof(GrCustomStage*));
+        const GrCustomStage** stages = reinterpret_cast<const GrCustomStage**>(&customStages);
+        SkAutoTUnref<GrGLProgram> program(GrGLProgram::Create(this->glContextInfo(),
+                                                              pdesc,
+                                                              stages));
+        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 initilializers
+// 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"
+
+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);
+}
+
+#endif
diff --git a/tests/GeometryTest.cpp b/tests/GeometryTest.cpp
new file mode 100644
index 0000000..69d980a
--- /dev/null
+++ b/tests/GeometryTest.cpp
@@ -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.
+ */
+#include "Test.h"
+#include "SkGeometry.h"
+
+static bool nearly_equal(const SkPoint& a, const SkPoint& b) {
+    return SkScalarNearlyEqual(a.fX, b.fX) && SkScalarNearlyEqual(a.fY, b.fY);
+}
+
+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[] = {
+        { SkIntToScalar(2190), SkIntToScalar(5130) },
+        { SkIntToScalar(2190), SkIntToScalar(5070) },
+        { SkIntToScalar(2220), SkIntToScalar(5010) },
+        { SkIntToScalar(2205), SkIntToScalar(4980) },
+    };
+    SkPoint dst[13];
+    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);
+    }
+}
+
+
+static void TestGeometry(skiatest::Reporter* reporter) {
+    SkPoint pts[3], dst[5];
+
+    pts[0].set(0, 0);
+    pts[1].set(100, 50);
+    pts[2].set(0, 100);
+
+    int count = SkChopQuadAtMaxCurvature(pts, dst);
+    REPORTER_ASSERT(reporter, count == 1 || count == 2);
+
+    pts[0].set(0, 0);
+    pts[1].set(SkIntToScalar(3), 0);
+    pts[2].set(SkIntToScalar(3), SkIntToScalar(3));
+    SkConvertQuadToCubic(pts, dst);
+    const SkPoint cubic[] = {
+        { 0, 0, },
+        { SkIntToScalar(2), 0, },
+        { SkIntToScalar(3), SkIntToScalar(1), },
+        { SkIntToScalar(3), SkIntToScalar(3) },
+    };
+    for (int i = 0; i < 4; ++i) {
+        REPORTER_ASSERT(reporter, nearly_equal(cubic[i], dst[i]));
+    }
+
+    testChopCubic(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Geometry", GeometryTestClass, TestGeometry)
diff --git a/tests/GpuBitmapCopyTest.cpp b/tests/GpuBitmapCopyTest.cpp
new file mode 100644
index 0000000..0389048
--- /dev/null
+++ b/tests/GpuBitmapCopyTest.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.
+ */
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "SkBitmap.h"
+#include "SkGpuDevice.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;
+};
+
+// 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) {
+    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++) {
+        for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
+            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);
+
+            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);
+            }
+
+            if (success) {
+                REPORTER_ASSERT(reporter, src.width() == dst.width());
+                REPORTER_ASSERT(reporter, src.height() == dst.height());
+                REPORTER_ASSERT(reporter, dst.config() == gPairs[j].fConfig);
+                if (src.config() == dst.config()) {
+                    REPORTER_ASSERT(reporter, src.getGenerationID() == dst.getGenerationID());
+                    // Do read backs and make sure that the two are the same.
+                    SkBitmap srcReadBack, dstReadBack;
+                    REPORTER_ASSERT(reporter, src.pixelRef() != NULL
+                                    && dst.pixelRef() != NULL);
+                    src.pixelRef()->readPixels(&srcReadBack);
+                    dst.pixelRef()->readPixels(&dstReadBack);
+                    SkAutoLockPixels srcLock(srcReadBack);
+                    SkAutoLockPixels dstLock(dstReadBack);
+                    REPORTER_ASSERT(reporter, srcReadBack.readyToDraw()
+                                    && dstReadBack.readyToDraw());
+                    const char* srcP = (const char*)srcReadBack.getAddr(0, 0);
+                    const char* dstP = (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());
+                }
+            } 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);
+            }
+        } // for (size_t j = ...
+    }
+}
+
+#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..3f00f67
--- /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) {
+#ifdef SK_ENABLE_INST_COUNT
+        SkASSERT(0 == GetInstanceCount());
+#endif
+        GrMemoryPool* pool = new GrMemoryPool(preallocSize, minAllocSize);
+        gPool.reset(pool);
+    }
+
+    static void ResetAllocator() {
+#ifdef 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;
+            }
+#ifdef 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..007caa3
--- /dev/null
+++ b/tests/GradientTest.cpp
@@ -0,0 +1,164 @@
+
+/*
+ * Copyright 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 "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)));
+}
+
+typedef void (*GradProc)(skiatest::Reporter* reporter, const GradRec&);
+
+static void TestGradients(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);
+    }
+}
+
+#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
new file mode 100644
index 0000000..1dc6ca7
--- /dev/null
+++ b/tests/InfRectTest.cpp
@@ -0,0 +1,55 @@
+
+/*
+ * Copyright 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 "SkRect.h"
+
+#ifdef SK_SCALAR_IS_FLOAT
+static float make_zero() {
+    return sk_float_sin(0);
+}
+#endif
+
+static void check_invalid(skiatest::Reporter* reporter,
+                          SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
+    SkRect rect;
+    rect.set(l, t, r, b);
+    REPORTER_ASSERT(reporter, !rect.isFinite());
+}
+
+// Tests that isFinite() will reject any rect with +/-inf values
+// as one of its coordinates.
+static void TestInfRect(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    float inf = 1 / make_zero();    // infinity
+    float nan = inf * 0;
+    SkASSERT(!(nan == nan));
+#else
+    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());
+
+    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);
+    }
+}
+
+// need tests for SkStrSearch
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("InfRect", InfRectTestClass, TestInfRect)
diff --git a/tests/MathTest.cpp b/tests/MathTest.cpp
new file mode 100644
index 0000000..c0babee
--- /dev/null
+++ b/tests/MathTest.cpp
@@ -0,0 +1,601 @@
+
+/*
+ * Copyright 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 "SkFloatBits.h"
+#include "SkFloatingPoint.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;
+}
+
+static int blend31(int src, int dst, int a31) {
+    return dst + ((src - dst) * a31 * 2114 >> 16);
+    //    return dst + ((src - dst) * a31 * 33 >> 10);
+}
+
+static int blend31_slow(int src, int dst, int a31) {
+    int prod = src * a31 + (31 - a31) * dst + 16;
+    prod = (prod + (prod >> 5)) >> 5;
+    return prod;
+}
+
+static int blend31_round(int src, int dst, int a31) {
+    int prod = (src - dst) * a31 + 16;
+    prod = (prod + (prod >> 5)) >> 5;
+    return dst + prod;
+}
+
+static int blend31_old(int src, int dst, int a31) {
+    a31 += a31 >> 4;
+    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++) {
+//                int r0 = blend31(src, dst, a);
+//                int r0 = blend31_round(src, dst, a);
+//                int r0 = blend31_old(src, dst, a);
+                int r0 = blend31_slow(src, dst, a);
+
+                float f = float_blend(src, dst, a / 31.f);
+                int r1 = (int)f;
+                int r2 = SkScalarRoundToInt(SkFloatToScalar(f));
+
+                if (r0 != r1 && r0 != r2) {
+                    printf("src:%d dst:%d a:%d result:%d float:%g\n",
+                                 src, dst, a, r0, f);
+                    failed += 1;
+                }
+                if (r0 > 255) {
+                    death += 1;
+                    printf("death src:%d dst:%d a:%d result:%d float:%g\n",
+                           src, dst, a, r0, f);
+                }
+            }
+        }
+    }
+    SkDebugf("---- failed %d death %d\n", failed, death);
+}
+
+static void test_blend(skiatest::Reporter* reporter) {
+    for (int src = 0; src <= 255; src++) {
+        for (int dst = 0; dst <= 255; dst++) {
+            for (int a = 0; a <= 255; a++) {
+                int r0 = SkAlphaBlend255(src, dst, a);
+                float f1 = float_blend(src, dst, a / 255.f);
+                int r1 = SkScalarRoundToInt(SkFloatToScalar(f1));
+
+                if (r0 != r1) {
+                    float diff = sk_float_abs(f1 - r1);
+                    diff = sk_float_abs(diff - 0.5f);
+                    if (diff > (1 / 255.f)) {
+#ifdef SK_DEBUG
+                        SkDebugf("src:%d dst:%d a:%d result:%d float:%g\n",
+                                 src, dst, a, r0, f1);
+#endif
+                        REPORTER_ASSERT(reporter, false);
+                    }
+                }
+            }
+        }
+    }
+}
+
+#if defined(SkLONGLONG)
+static int symmetric_fixmul(int a, int b) {
+    int sa = SkExtractSign(a);
+    int sb = SkExtractSign(b);
+
+    a = SkApplySign(a, sa);
+    b = SkApplySign(b, sb);
+
+#if 1
+    int c = (int)(((SkLONGLONG)a * b) >> 16);
+
+    return SkApplySign(c, sa ^ sb);
+#else
+    SkLONGLONG ab = (SkLONGLONG)a * b;
+    if (sa ^ sb) {
+        ab = -ab;
+    }
+    return ab >> 16;
+#endif
+}
+#endif
+
+static void check_length(skiatest::Reporter* reporter,
+                         const SkPoint& p, SkScalar targetLen) {
+    float x = SkScalarToFloat(p.fX);
+    float y = SkScalarToFloat(p.fY);
+    float len = sk_float_sqrt(x*x + y*y);
+
+    len /= SkScalarToFloat(targetLen);
+
+    REPORTER_ASSERT(reporter, len > 0.999f && len < 1.001f);
+}
+
+static float nextFloat(SkRandom& rand) {
+    SkFloatIntUnion data;
+    data.fSignBitInt = rand.nextU();
+    return data.fFloat;
+}
+
+/*  returns true if a == b as resulting from (int)x. Since it is undefined
+ what to do if the float exceeds 2^32-1, we check for that explicitly.
+ */
+static bool equal_float_native_skia(float x, uint32_t ni, uint32_t si) {
+    if (!(x == x)) {    // NAN
+        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 ((int32_t)si) == SK_MaxS32;
+    }
+    if (x < -SK_MaxS32) {
+        return ((int32_t)si) == SK_MinS32;
+    }
+    return si == ni;
+}
+
+static void assert_float_equal(skiatest::Reporter* reporter, const char op[],
+                               float x, uint32_t ni, uint32_t si) {
+    if (!equal_float_native_skia(x, ni, si)) {
+        SkString desc;
+        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);
+    }
+}
+
+static void test_float_cast(skiatest::Reporter* reporter, float x) {
+    int ix = (int)x;
+    int iix = SkFloatToIntCast(x);
+    assert_float_equal(reporter, "cast", x, ix, iix);
+}
+
+static void test_float_floor(skiatest::Reporter* reporter, float x) {
+    int ix = (int)floor(x);
+    int iix = SkFloatToIntFloor(x);
+    assert_float_equal(reporter, "floor", x, ix, iix);
+}
+
+static void test_float_round(skiatest::Reporter* reporter, float x) {
+    double xx = x + 0.5;    // need intermediate double to avoid temp loss
+    int ix = (int)floor(xx);
+    int iix = SkFloatToIntRound(x);
+    assert_float_equal(reporter, "round", x, ix, iix);
+}
+
+static void test_float_ceil(skiatest::Reporter* reporter, float x) {
+    int ix = (int)ceil(x);
+    int iix = SkFloatToIntCeil(x);
+    assert_float_equal(reporter, "ceil", x, ix, iix);
+}
+
+static void test_float_conversions(skiatest::Reporter* reporter, float x) {
+    test_float_cast(reporter, x);
+    test_float_floor(reporter, x);
+    test_float_round(reporter, x);
+    test_float_ceil(reporter, x);
+}
+
+static void test_int2float(skiatest::Reporter* reporter, int ival) {
+    float x0 = (float)ival;
+    float x1 = SkIntToFloatCast(ival);
+    float x2 = SkIntToFloatCast_NoOverflowCheck(ival);
+    REPORTER_ASSERT(reporter, x0 == x1);
+    REPORTER_ASSERT(reporter, x0 == x2);
+}
+
+static void unittest_fastfloat(skiatest::Reporter* reporter) {
+    SkRandom rand;
+    size_t i;
+
+    static const float gFloats[] = {
+        0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3,
+        0.000000001f, 1000000000.f,     // doesn't overflow
+        0.0000000001f, 10000000000.f    // does overflow
+    };
+    for (i = 0; i < SK_ARRAY_COUNT(gFloats); i++) {
+        test_float_conversions(reporter, gFloats[i]);
+        test_float_conversions(reporter, -gFloats[i]);
+    }
+
+    for (int outer = 0; outer < 100; outer++) {
+        rand.setSeed(outer);
+        for (i = 0; i < 100000; i++) {
+            float x = nextFloat(rand);
+            test_float_conversions(reporter, x);
+        }
+
+        test_int2float(reporter, 0);
+        test_int2float(reporter, 1);
+        test_int2float(reporter, -1);
+        for (i = 0; i < 100000; i++) {
+            // for now only test ints that are 24bits or less, since we don't
+            // round (down) large ints the same as IEEE...
+            int ival = rand.nextU() & 0xFFFFFF;
+            test_int2float(reporter, ival);
+            test_int2float(reporter, -ival);
+        }
+    }
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+static float make_zero() {
+    return sk_float_sin(0);
+}
+#endif
+
+static void unittest_isfinite(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    float nan = sk_float_asin(2);
+    float inf = 1.0f / make_zero();
+    float big = 3.40282e+038f;
+
+    REPORTER_ASSERT(reporter, !SkScalarIsNaN(inf));
+    REPORTER_ASSERT(reporter, !SkScalarIsNaN(-inf));
+    REPORTER_ASSERT(reporter, !SkScalarIsFinite(inf));
+    REPORTER_ASSERT(reporter, !SkScalarIsFinite(-inf));
+#else
+    SkFixed nan = SK_FixedNaN;
+    SkFixed big = SK_FixedMax;
+#endif
+
+    REPORTER_ASSERT(reporter,  SkScalarIsNaN(nan));
+    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));
+}
+
+static void test_muldiv255(skiatest::Reporter* reporter) {
+    for (int a = 0; a <= 255; a++) {
+        for (int b = 0; b <= 255; b++) {
+            int ab = a * b;
+            float s = ab / 255.0f;
+            int round = (int)floorf(s + 0.5f);
+            int trunc = (int)floorf(s);
+
+            int iround = SkMulDiv255Round(a, b);
+            int itrunc = SkMulDiv255Trunc(a, b);
+
+            REPORTER_ASSERT(reporter, iround == round);
+            REPORTER_ASSERT(reporter, itrunc == trunc);
+
+            REPORTER_ASSERT(reporter, itrunc <= iround);
+            REPORTER_ASSERT(reporter, iround <= a);
+            REPORTER_ASSERT(reporter, iround <= b);
+        }
+    }
+}
+
+static void test_muldiv255ceiling(skiatest::Reporter* reporter) {
+    for (int c = 0; c <= 255; c++) {
+        for (int a = 0; a <= 255; a++) {
+            int product = (c * a + 255);
+            int expected_ceiling = (product + (product >> 8)) >> 8;
+            int webkit_ceiling = (c * a + 254) / 255;
+            REPORTER_ASSERT(reporter, expected_ceiling == webkit_ceiling);
+            int skia_ceiling = SkMulDiv255Ceiling(c, a);
+            REPORTER_ASSERT(reporter, skia_ceiling == webkit_ceiling);
+        }
+    }
+}
+
+static void test_copysign(skiatest::Reporter* reporter) {
+    static const int32_t gTriples[] = {
+        // x, y, expected result
+        0, 0, 0,
+        0, 1, 0,
+        0, -1, 0,
+        1, 0, 1,
+        1, 1, 1,
+        1, -1, -1,
+        -1, 0, 1,
+        -1, 1, 1,
+        -1, -1, -1,
+    };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTriples); i += 3) {
+        REPORTER_ASSERT(reporter,
+                        SkCopySign32(gTriples[i], gTriples[i+1]) == gTriples[i+2]);
+        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);
+    }
+
+    SkRandom rand;
+    for (int j = 0; j < 1000; j++) {
+        int ix = rand.nextS();
+        REPORTER_ASSERT(reporter, SkCopySign32(ix, ix) == ix);
+        REPORTER_ASSERT(reporter, SkCopySign32(ix, -ix) == -ix);
+        REPORTER_ASSERT(reporter, SkCopySign32(-ix, ix) == ix);
+        REPORTER_ASSERT(reporter, SkCopySign32(-ix, -ix) == -ix);
+
+        SkScalar sx = rand.nextSScalar1();
+        REPORTER_ASSERT(reporter, SkScalarCopySign(sx, sx) == sx);
+        REPORTER_ASSERT(reporter, SkScalarCopySign(sx, -sx) == -sx);
+        REPORTER_ASSERT(reporter, SkScalarCopySign(-sx, sx) == sx);
+        REPORTER_ASSERT(reporter, SkScalarCopySign(-sx, -sx) == -sx);
+    }
+}
+
+static void TestMath(skiatest::Reporter* reporter) {
+    int         i;
+    int32_t     x;
+    SkRandom    rand;
+
+    // these should assert
+#if 0
+    SkToS8(128);
+    SkToS8(-129);
+    SkToU8(256);
+    SkToU8(-5);
+
+    SkToS16(32768);
+    SkToS16(-32769);
+    SkToU16(65536);
+    SkToU16(-5);
+
+    if (sizeof(size_t) > 4) {
+        SkToS32(4*1024*1024);
+        SkToS32(-4*1024*1024);
+        SkToU32(5*1024*1024);
+        SkToU32(-5);
+    }
+#endif
+
+    test_muldiv255(reporter);
+    test_muldiv255ceiling(reporter);
+    test_copysign(reporter);
+
+    {
+        SkScalar x = SK_ScalarNaN;
+        REPORTER_ASSERT(reporter, SkScalarIsNaN(x));
+    }
+
+    for (i = 1; i <= 10; i++) {
+        x = SkCubeRootBits(i*i*i, 11);
+        REPORTER_ASSERT(reporter, x == i);
+    }
+
+    x = SkFixedSqrt(SK_Fixed1);
+    REPORTER_ASSERT(reporter, x == SK_Fixed1);
+    x = SkFixedSqrt(SK_Fixed1/4);
+    REPORTER_ASSERT(reporter, x == SK_Fixed1/2);
+    x = SkFixedSqrt(SK_Fixed1*4);
+    REPORTER_ASSERT(reporter, x == SK_Fixed1*2);
+
+    x = SkFractSqrt(SK_Fract1);
+    REPORTER_ASSERT(reporter, x == SK_Fract1);
+    x = SkFractSqrt(SK_Fract1/4);
+    REPORTER_ASSERT(reporter, x == SK_Fract1/2);
+    x = SkFractSqrt(SK_Fract1/16);
+    REPORTER_ASSERT(reporter, x == SK_Fract1/4);
+
+    for (i = 1; i < 100; i++) {
+        x = SkFixedSqrt(SK_Fixed1 * i * i);
+        REPORTER_ASSERT(reporter, x == SK_Fixed1 * i);
+    }
+
+    for (i = 0; i < 1000; i++) {
+        int value = rand.nextS16();
+        int max = rand.nextU16();
+
+        int clamp = SkClampMax(value, max);
+        int clamp2 = value < 0 ? 0 : (value > max ? max : value);
+        REPORTER_ASSERT(reporter, clamp == clamp2);
+    }
+
+    for (i = 0; i < 10000; i++) {
+        SkPoint p;
+
+        // 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((SkScalar) (rand.nextS() >> 13),
+                    (SkScalar) (rand.nextS() >> 13),
+                    SK_Scalar1);
+        check_length(reporter, p, SK_Scalar1);
+    }
+
+    {
+        SkFixed result = SkFixedDiv(100, 100);
+        REPORTER_ASSERT(reporter, result == SK_Fixed1);
+        result = SkFixedDiv(1, SK_Fixed1);
+        REPORTER_ASSERT(reporter, result == 1);
+    }
+
+    unittest_fastfloat(reporter);
+    unittest_isfinite(reporter);
+
+#ifdef SkLONGLONG
+    for (i = 0; i < 10000; i++) {
+        SkFixed numer = rand.nextS();
+        SkFixed denom = rand.nextS();
+        SkFixed result = SkFixedDiv(numer, denom);
+        SkLONGLONG check = ((SkLONGLONG)numer << 16) / denom;
+
+        (void)SkCLZ(numer);
+        (void)SkCLZ(denom);
+
+        REPORTER_ASSERT(reporter, result != (SkFixed)SK_NaN32);
+        if (check > SK_MaxS32) {
+            check = SK_MaxS32;
+        } else if (check < -SK_MaxS32) {
+            check = SK_MinS32;
+        }
+        REPORTER_ASSERT(reporter, result == (int32_t)check);
+
+        result = SkFractDiv(numer, denom);
+        check = ((SkLONGLONG)numer << 30) / denom;
+
+        REPORTER_ASSERT(reporter, result != (SkFixed)SK_NaN32);
+        if (check > SK_MaxS32) {
+            check = SK_MaxS32;
+        } else if (check < -SK_MaxS32) {
+            check = SK_MinS32;
+        }
+        REPORTER_ASSERT(reporter, result == (int32_t)check);
+
+        // make them <= 2^24, so we don't overflow in fixmul
+        numer = numer << 8 >> 8;
+        denom = denom << 8 >> 8;
+
+        result = SkFixedMul(numer, denom);
+        SkFixed r2 = symmetric_fixmul(numer, denom);
+        //        SkASSERT(result == r2);
+
+        result = SkFixedMul(numer, numer);
+        r2 = SkFixedSquare(numer);
+        REPORTER_ASSERT(reporter, result == r2);
+
+        if (numer >= 0 && denom >= 0) {
+            SkFixed mean = SkFixedMean(numer, denom);
+            float prod = SkFixedToFloat(numer) * SkFixedToFloat(denom);
+            float fm = sk_float_sqrt(sk_float_abs(prod));
+            SkFixed mean2 = SkFloatToFixed(fm);
+            int diff = SkAbs32(mean - mean2);
+            REPORTER_ASSERT(reporter, diff <= 1);
+        }
+
+        {
+            SkFixed mod = SkFixedMod(numer, denom);
+            float n = SkFixedToFloat(numer);
+            float d = SkFixedToFloat(denom);
+            float m = sk_float_mod(n, d);
+            // ensure the same sign
+            REPORTER_ASSERT(reporter, mod == 0 || (mod < 0) == (m < 0));
+            int diff = SkAbs32(mod - SkFloatToFixed(m));
+            REPORTER_ASSERT(reporter, (diff >> 7) == 0);
+        }
+    }
+#endif
+
+    for (i = 0; i < 10000; i++) {
+        SkFract x = rand.nextU() >> 1;
+        double xx = (double)x / SK_Fract1;
+        SkFract xr = SkFractSqrt(x);
+        SkFract check = SkFloatToFract(sqrt(xx));
+        REPORTER_ASSERT(reporter, xr == check ||
+                                  xr == check-1 ||
+                                  xr == check+1);
+
+        xr = SkFixedSqrt(x);
+        xx = (double)x / SK_Fixed1;
+        check = SkFloatToFixed(sqrt(xx));
+        REPORTER_ASSERT(reporter, xr == check || xr == check-1);
+
+        xr = SkSqrt32(x);
+        xx = (double)x;
+        check = (int32_t)sqrt(xx);
+        REPORTER_ASSERT(reporter, xr == check || xr == check-1);
+    }
+
+#if !defined(SK_SCALAR_IS_FLOAT)
+    {
+        SkFixed s, c;
+        s = SkFixedSinCos(0, &c);
+        REPORTER_ASSERT(reporter, s == 0);
+        REPORTER_ASSERT(reporter, c == SK_Fixed1);
+    }
+
+    int maxDiff = 0;
+    for (i = 0; i < 1000; i++) {
+        SkFixed rads = rand.nextS() >> 10;
+        double frads = SkFixedToFloat(rads);
+
+        SkFixed s, c;
+        s = SkScalarSinCos(rads, &c);
+
+        double fs = sin(frads);
+        double fc = cos(frads);
+
+        SkFixed is = SkFloatToFixed(fs);
+        SkFixed ic = SkFloatToFixed(fc);
+
+        maxDiff = SkMax32(maxDiff, SkAbs32(is - s));
+        maxDiff = SkMax32(maxDiff, SkAbs32(ic - c));
+    }
+    SkDebugf("SinCos: maximum error = %d\n", maxDiff);
+#endif
+
+#ifdef SK_SCALAR_IS_FLOAT
+    test_blend(reporter);
+#endif
+
+//    test_floor(reporter);
+
+    // disable for now
+    if (false) test_blend31();  // avoid bit rot, suppress warning
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Math", MathTestClass, TestMath)
diff --git a/tests/Matrix44Test.cpp b/tests/Matrix44Test.cpp
new file mode 100644
index 0000000..7037877
--- /dev/null
+++ b/tests/Matrix44Test.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 "Test.h"
+#include "SkMatrix44.h"
+
+static bool nearly_equal_scalar(SkMScalar a, SkMScalar b) {
+    // Note that we get more compounded error for multiple operations when
+    // SK_SCALAR_IS_FIXED.
+#ifdef SK_SCALAR_IS_FLOAT
+    const SkScalar tolerance = SK_Scalar1 / 200000;
+#else
+    const SkScalar tolerance = SK_Scalar1 / 1024;
+#endif
+
+    return SkScalarAbs(a - b) <= tolerance;
+}
+
+template <typename T> void assert16(skiatest::Reporter* reporter, const T data[],
+                                    T m0,  T m1,  T m2,  T m3,
+                                    T m4,  T m5,  T m6,  T m7,
+                                    T m8,  T m9,  T m10, T m11,
+                                    T m12, T m13, T m14, T m15) {
+    REPORTER_ASSERT(reporter, data[0] == m0);
+    REPORTER_ASSERT(reporter, data[1] == m1);
+    REPORTER_ASSERT(reporter, data[2] == m2);
+    REPORTER_ASSERT(reporter, data[3] == m3);
+
+    REPORTER_ASSERT(reporter, data[4] == m4);
+    REPORTER_ASSERT(reporter, data[5] == m5);
+    REPORTER_ASSERT(reporter, data[6] == m6);
+    REPORTER_ASSERT(reporter, data[7] == m7);
+
+    REPORTER_ASSERT(reporter, data[8] == m8);
+    REPORTER_ASSERT(reporter, data[9] == m9);
+    REPORTER_ASSERT(reporter, data[10] == m10);
+    REPORTER_ASSERT(reporter, data[11] == m11);
+
+    REPORTER_ASSERT(reporter, data[12] == m12);
+    REPORTER_ASSERT(reporter, data[13] == m13);
+    REPORTER_ASSERT(reporter, data[14] == m14);
+    REPORTER_ASSERT(reporter, data[15] == m15);
+}
+
+static bool nearly_equal(const SkMatrix44& a, const SkMatrix44& b) {
+    for (int i = 0; i < 4; ++i) {
+        for (int j = 0; j < 4; ++j) {
+            if (!nearly_equal_scalar(a.get(i, j), b.get(i, j))) {
+                printf("not equal %g %g\n", a.get(i, j), b.get(i, j));
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+static bool is_identity(const SkMatrix44& m) {
+    SkMatrix44 identity;
+    identity.reset();
+    return nearly_equal(m, identity);
+}
+
+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, SkIntToScalar(common_angles[i]));
+
+        SkMatrix rot3x3 = rot;
+        REPORTER_ASSERT(reporter, rot3x3.rectStaysRect());
+    }
+}
+
+static void TestMatrix44(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    SkMatrix44 mat, inverse, iden1, iden2, rot;
+
+    mat.reset();
+    mat.setTranslate(SK_Scalar1, SK_Scalar1, SK_Scalar1);
+    mat.invert(&inverse);
+    iden1.setConcat(mat, inverse);
+    REPORTER_ASSERT(reporter, is_identity(iden1));
+
+    mat.setScale(SkIntToScalar(2), SkIntToScalar(2), SkIntToScalar(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.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.postConcat(rot);
+    REPORTER_ASSERT(reporter, mat.invert(NULL));
+    mat.invert(&inverse);
+    iden1.setConcat(mat, inverse);
+    REPORTER_ASSERT(reporter, is_identity(iden1));
+    iden2.setConcat(inverse, mat);
+    REPORTER_ASSERT(reporter, is_identity(iden2));
+
+    // test rol/col Major getters
+    {
+        mat.setTranslate(2, 3, 4);
+        float dataf[16];
+        double datad[16];
+
+        mat.asColMajorf(dataf);
+        assert16<float>(reporter, dataf,
+                 1, 0, 0, 0,
+                 0, 1, 0, 0,
+                 0, 0, 1, 0,
+                 2, 3, 4, 1);
+        mat.asColMajord(datad);
+        assert16<double>(reporter, datad, 1, 0, 0, 0,
+                        0, 1, 0, 0,
+                        0, 0, 1, 0,
+                        2, 3, 4, 1);
+        mat.asRowMajorf(dataf);
+        assert16<float>(reporter, dataf, 1, 0, 0, 2,
+                        0, 1, 0, 3,
+                        0, 0, 1, 4,
+                        0, 0, 0, 1);
+        mat.asRowMajord(datad);
+        assert16<double>(reporter, datad, 1, 0, 0, 2,
+                        0, 1, 0, 3,
+                        0, 0, 1, 4,
+                        0, 0, 0, 1);
+    }
+
+    if (false) { // avoid bit rot, suppress warning (working on making this pass)
+        test_common_angles(reporter);
+    }
+#endif
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Matrix44", Matrix44TestClass, TestMatrix44)
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
new file mode 100644
index 0000000..efbd02d
--- /dev/null
+++ b/tests/MatrixTest.cpp
@@ -0,0 +1,493 @@
+
+/*
+ * Copyright 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 "SkMath.h"
+#include "SkMatrix.h"
+#include "SkRandom.h"
+
+static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
+    // Note that we get more compounded error for multiple operations when
+    // SK_SCALAR_IS_FIXED.
+#ifdef SK_SCALAR_IS_FLOAT
+    const SkScalar tolerance = SK_Scalar1 / 200000;
+#else
+    const SkScalar tolerance = SK_Scalar1 / 1024;
+#endif
+
+    return SkScalarAbs(a - b) <= tolerance;
+}
+
+static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
+    for (int i = 0; i < 9; i++) {
+        if (!nearly_equal_scalar(a[i], b[i])) {
+            printf("not equal %g %g\n", (float)a[i], (float)b[i]);
+            return false;
+        }
+    }
+    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) {
+#if 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.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.readFromMemory(buffer);
+    REPORTER_ASSERT(reporter, size1 == size3);
+    REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
+
+    char buffer2[SkMatrix::kMaxFlattenSize + 100];
+    size3 = m2.writeToMemory(buffer2);
+    REPORTER_ASSERT(reporter, size1 == size3);
+    REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
+}
+
+static void test_matrix_max_stretch(skiatest::Reporter* reporter) {
+    SkMatrix identity;
+    identity.reset();
+    REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());
+
+    SkMatrix scale;
+    scale.setScale(SK_Scalar1 * 2, SK_Scalar1 * 4);
+    REPORTER_ASSERT(reporter, SK_Scalar1 * 4 == scale.getMaxStretch());
+
+    SkMatrix rot90Scale;
+    rot90Scale.setRotate(90 * SK_Scalar1);
+    rot90Scale.postScale(SK_Scalar1 / 4, SK_Scalar1 / 2);
+    REPORTER_ASSERT(reporter, SK_Scalar1 / 2 == rot90Scale.getMaxStretch());
+
+    SkMatrix rotate;
+    rotate.setRotate(128 * SK_Scalar1);
+    REPORTER_ASSERT(reporter, SkScalarAbs(SK_Scalar1 - rotate.getMaxStretch()) <= SK_ScalarNearlyZero);
+
+    SkMatrix translate;
+    translate.setTranslate(10 * SK_Scalar1, -5 * SK_Scalar1);
+    REPORTER_ASSERT(reporter, SK_Scalar1 == translate.getMaxStretch());
+
+    SkMatrix perspX;
+    perspX.reset();
+    perspX.setPerspX(SkScalarToPersp(SK_Scalar1 / 1000));
+    REPORTER_ASSERT(reporter, -SK_Scalar1 == perspX.getMaxStretch());
+
+    SkMatrix perspY;
+    perspY.reset();
+    perspY.setPerspX(SkScalarToPersp(-SK_Scalar1 / 500));
+    REPORTER_ASSERT(reporter, -SK_Scalar1 == perspY.getMaxStretch());
+
+    SkMatrix baseMats[] = {scale, rot90Scale, rotate,
+                           translate, perspX, perspY};
+    SkMatrix mats[2*SK_ARRAY_COUNT(baseMats)];
+    for (size_t i = 0; i < SK_ARRAY_COUNT(baseMats); ++i) {
+        mats[i] = baseMats[i];
+        bool invertable = mats[i].invert(&mats[i + SK_ARRAY_COUNT(baseMats)]);
+        REPORTER_ASSERT(reporter, invertable);
+    }
+    SkRandom rand;
+    for (int m = 0; m < 1000; ++m) {
+        SkMatrix mat;
+        mat.reset();
+        for (int i = 0; i < 4; ++i) {
+            int x = rand.nextU() % SK_ARRAY_COUNT(mats);
+            mat.postConcat(mats[x]);
+        }
+        SkScalar stretch = mat.getMaxStretch();
+
+        if ((stretch < 0) != mat.hasPerspective()) {
+            stretch = mat.getMaxStretch();
+        }
+
+        REPORTER_ASSERT(reporter, (stretch < 0) == mat.hasPerspective());
+
+        if (mat.hasPerspective()) {
+            m -= 1; // try another non-persp matrix
+            continue;
+        }
+
+        // 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.
+        static const SkScalar gStretchTol = (105 * SK_Scalar1) / 100;
+        static const SkScalar gMaxStretchTol = (97 * SK_Scalar1) / 100;
+        SkScalar max = 0;
+        SkVector vectors[1000];
+        for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
+            vectors[i].fX = rand.nextSScalar1();
+            vectors[i].fY = rand.nextSScalar1();
+            if (!vectors[i].normalize()) {
+                i -= 1;
+                continue;
+            }
+        }
+        mat.mapVectors(vectors, SK_ARRAY_COUNT(vectors));
+        for (size_t i = 0; i < SK_ARRAY_COUNT(vectors); ++i) {
+            SkScalar d = vectors[i].length();
+            REPORTER_ASSERT(reporter, SkScalarDiv(d, stretch) < gStretchTol);
+            if (max < d) {
+                max = d;
+            }
+        }
+        REPORTER_ASSERT(reporter, SkScalarDiv(max, stretch) >= gMaxStretchTol);
+    }
+}
+
+// This function is extracted from src/gpu/SkGpuDevice.cpp,
+// in order to make sure this function works correctly.
+static bool isSimilarityTransformation(const SkMatrix& matrix,
+                                       SkScalar tol = SK_ScalarNearlyZero) {
+    if (matrix.isIdentity() || matrix.getType() == SkMatrix::kTranslate_Mask) {
+        return true;
+    }
+    if (matrix.hasPerspective()) {
+        return false;
+    }
+
+    SkScalar mx = matrix.get(SkMatrix::kMScaleX);
+    SkScalar sx = matrix.get(SkMatrix::kMSkewX);
+    SkScalar my = matrix.get(SkMatrix::kMScaleY);
+    SkScalar sy = matrix.get(SkMatrix::kMSkewY);
+
+    if (mx == 0 && sx == 0 && my == 0 && sy == 0) {
+        return false;
+    }
+
+    // it has scales or skews, but it could also be rotation, check it out.
+    SkVector vec[2];
+    vec[0].set(mx, sx);
+    vec[1].set(sy, my);
+
+    return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
+           SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
+                SkScalarSquare(tol));
+}
+
+static void test_matrix_is_similarity_transform(skiatest::Reporter* reporter) {
+    SkMatrix mat;
+
+    // identity
+    mat.setIdentity();
+    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+
+    // translation only
+    mat.reset();
+    mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
+    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+
+    // scale with same size
+    mat.reset();
+    mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
+    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+
+    // scale with one negative
+    mat.reset();
+    mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
+    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+
+    // scale with different size
+    mat.reset();
+    mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+
+    // scale with same size at a pivot point
+    mat.reset();
+    mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
+                 SkIntToScalar(2), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+
+    // scale with different size at a pivot point
+    mat.reset();
+    mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
+                 SkIntToScalar(2), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+
+    // skew with same size
+    mat.reset();
+    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+
+    // skew with different size
+    mat.reset();
+    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+
+    // skew with same size at a pivot point
+    mat.reset();
+    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
+                SkIntToScalar(2), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+
+    // skew with different size at a pivot point
+    mat.reset();
+    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
+                SkIntToScalar(2), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+
+    // perspective x
+    mat.reset();
+    mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+
+    // perspective y
+    mat.reset();
+    mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+
+#if 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, isSimilarityTransformation(mat));
+    }
+
+    // see if there are any accumulated precision issues
+    mat.reset();
+    for (int i = 1; i < 360; i++) {
+        mat.postRotate(SkIntToScalar(1));
+    }
+    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+
+    // rotate + translate
+    mat.reset();
+    mat.setRotate(SkIntToScalar(30));
+    mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
+    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+
+    // rotate + uniform scale
+    mat.reset();
+    mat.setRotate(SkIntToScalar(30));
+    mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+
+    // rotate + non-uniform scale
+    mat.reset();
+    mat.setRotate(SkIntToScalar(30));
+    mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+#endif
+
+    // all zero
+    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+
+    // all zero except perspective
+    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
+    REPORTER_ASSERT(reporter, !isSimilarityTransformation(mat));
+
+    // scales zero, only skews
+    mat.setAll(0, SK_Scalar1, 0,
+               SK_Scalar1, 0, 0,
+               0, 0, SkMatrix::I()[8]);
+    REPORTER_ASSERT(reporter, isSimilarityTransformation(mat));
+}
+
+static void TestMatrix(skiatest::Reporter* reporter) {
+    SkMatrix    mat, inverse, iden1, iden2;
+
+    mat.reset();
+    mat.setTranslate(SK_Scalar1, SK_Scalar1);
+    REPORTER_ASSERT(reporter, mat.invert(&inverse));
+    iden1.setConcat(mat, inverse);
+    REPORTER_ASSERT(reporter, is_identity(iden1));
+
+    mat.setScale(SkIntToScalar(2), SkIntToScalar(2));
+    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);
+    REPORTER_ASSERT(reporter, mat.invert(&inverse));
+    iden1.setConcat(mat, inverse);
+    REPORTER_ASSERT(reporter, is_identity(iden1));
+    test_flatten(reporter, mat);
+
+    mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
+    mat.postRotate(SkIntToScalar(25));
+    REPORTER_ASSERT(reporter, mat.invert(NULL));
+    REPORTER_ASSERT(reporter, mat.invert(&inverse));
+    iden1.setConcat(mat, inverse);
+    REPORTER_ASSERT(reporter, is_identity(iden1));
+    iden2.setConcat(inverse, mat);
+    REPORTER_ASSERT(reporter, is_identity(iden2));
+    test_flatten(reporter, mat);
+    test_flatten(reporter, iden2);
+
+    // rectStaysRect test
+    {
+        static const struct {
+            SkScalar    m00, m01, m10, m11;
+            bool        mStaysRect;
+        }
+        gRectStaysRectSamples[] = {
+            {          0,          0,          0,           0, false },
+            {          0,          0,          0,  SK_Scalar1, false },
+            {          0,          0, SK_Scalar1,           0, false },
+            {          0,          0, SK_Scalar1,  SK_Scalar1, false },
+            {          0, SK_Scalar1,          0,           0, false },
+            {          0, SK_Scalar1,          0,  SK_Scalar1, false },
+            {          0, SK_Scalar1, SK_Scalar1,           0, true },
+            {          0, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false },
+            { SK_Scalar1,          0,          0,           0, false },
+            { SK_Scalar1,          0,          0,  SK_Scalar1, true },
+            { SK_Scalar1,          0, SK_Scalar1,           0, false },
+            { SK_Scalar1,          0, SK_Scalar1,  SK_Scalar1, false },
+            { SK_Scalar1, SK_Scalar1,          0,           0, false },
+            { SK_Scalar1, SK_Scalar1,          0,  SK_Scalar1, false },
+            { SK_Scalar1, SK_Scalar1, SK_Scalar1,           0, false },
+            { SK_Scalar1, SK_Scalar1, SK_Scalar1,  SK_Scalar1, false }
+        };
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
+            SkMatrix    m;
+
+            m.reset();
+            m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
+            m.set(SkMatrix::kMSkewX,  gRectStaysRectSamples[i].m01);
+            m.set(SkMatrix::kMSkewY,  gRectStaysRectSamples[i].m10);
+            m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
+            REPORTER_ASSERT(reporter,
+                    m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
+        }
+    }
+
+    mat.reset();
+    mat.set(SkMatrix::kMScaleX, SkIntToScalar(1));
+    mat.set(SkMatrix::kMSkewX,  SkIntToScalar(2));
+    mat.set(SkMatrix::kMTransX, SkIntToScalar(3));
+    mat.set(SkMatrix::kMSkewY,  SkIntToScalar(4));
+    mat.set(SkMatrix::kMScaleY, SkIntToScalar(5));
+    mat.set(SkMatrix::kMTransY, SkIntToScalar(6));
+    SkScalar affine[6];
+    REPORTER_ASSERT(reporter, mat.asAffine(affine));
+
+    #define affineEqual(e) affine[SkMatrix::kA##e] == mat.get(SkMatrix::kM##e)
+    REPORTER_ASSERT(reporter, affineEqual(ScaleX));
+    REPORTER_ASSERT(reporter, affineEqual(SkewY));
+    REPORTER_ASSERT(reporter, affineEqual(SkewX));
+    REPORTER_ASSERT(reporter, affineEqual(ScaleY));
+    REPORTER_ASSERT(reporter, affineEqual(TransX));
+    REPORTER_ASSERT(reporter, affineEqual(TransY));
+    #undef affineEqual
+
+    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_transform(reporter);
+    test_matrix_recttorect(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix)
diff --git a/tests/MemsetTest.cpp b/tests/MemsetTest.cpp
new file mode 100644
index 0000000..ad1a21c
--- /dev/null
+++ b/tests/MemsetTest.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright 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) {
+        ptr[i] = 0;
+    }
+}
+
+#define MAX_ALIGNMENT   64
+#define MAX_COUNT       ((MAX_ALIGNMENT) * 32)
+#define PAD             32
+#define TOTAL           (PAD + MAX_ALIGNMENT + MAX_COUNT + PAD)
+
+#define VALUE16         0x1234
+#define VALUE32         0x12345678
+
+static bool compare16(const uint16_t base[], uint16_t value, int count) {
+    for (int i = 0; i < count; ++i) {
+        if (base[i] != value) {
+            SkDebugf("[%d] expected %x found %x\n", i, value, base[i]);
+            return false;
+        }
+    }
+    return true;
+}
+
+static bool compare32(const uint32_t base[], uint32_t value, int count) {
+    for (int i = 0; i < count; ++i) {
+        if (base[i] != value) {
+            SkDebugf("[%d] expected %x found %x\n", i, value, base[i]);
+            return false;
+        }
+    }
+    return true;
+}
+
+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);
+        }
+    }
+}
+
+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);
+        }
+    }
+}
+
+/**
+ *  Test sk_memset16 and sk_memset32.
+ *  For performance considerations, implementations may take different paths
+ *  depending on the alignment of the dst, and/or the size of the count.
+ */
+static void TestMemset(skiatest::Reporter* reporter) {
+    test_16(reporter);
+    test_32(reporter);
+
+    test_chunkalloc(reporter);
+};
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Memset", TestMemsetClass, TestMemset)
diff --git a/tests/MetaDataTest.cpp b/tests/MetaDataTest.cpp
new file mode 100644
index 0000000..4390652
--- /dev/null
+++ b/tests/MetaDataTest.cpp
@@ -0,0 +1,119 @@
+
+/*
+ * Copyright 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 "SkMetaData.h"
+
+static void test_ptrs(skiatest::Reporter* reporter) {
+    SkRefCnt ref;
+    REPORTER_ASSERT(reporter, 1 == ref.getRefCnt());
+
+    {
+        SkMetaData md0, md1;
+        const char name[] = "refcnt";
+
+        md0.setRefCnt(name, &ref);
+        REPORTER_ASSERT(reporter, md0.findRefCnt(name));
+        REPORTER_ASSERT(reporter, md0.hasRefCnt(name, &ref));
+        REPORTER_ASSERT(reporter, 2 == ref.getRefCnt());
+
+        md1 = md0;
+        REPORTER_ASSERT(reporter, md1.findRefCnt(name));
+        REPORTER_ASSERT(reporter, md1.hasRefCnt(name, &ref));
+        REPORTER_ASSERT(reporter, 3 == ref.getRefCnt());
+
+        REPORTER_ASSERT(reporter, md0.removeRefCnt(name));
+        REPORTER_ASSERT(reporter, !md0.findRefCnt(name));
+        REPORTER_ASSERT(reporter, !md0.hasRefCnt(name, &ref));
+        REPORTER_ASSERT(reporter, 2 == ref.getRefCnt());
+    }
+    REPORTER_ASSERT(reporter, 1 == ref.getRefCnt());
+}
+
+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"));
+    REPORTER_ASSERT(reporter, !m1.removeS32("int"));
+    REPORTER_ASSERT(reporter, !m1.removeScalar("scalar"));
+    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;
+        int                 fCount;
+    } gElems[] = {
+        { "int",    SkMetaData::kS32_Type,      1 },
+        { "scalar", SkMetaData::kScalar_Type,   1 },
+        { "ptr",    SkMetaData::kPtr_Type,      1 },
+        { "hello",  SkMetaData::kString_Type,   sizeof("world") },
+        { "true",   SkMetaData::kBool_Type,     1 },
+        { "false",  SkMetaData::kBool_Type,     1 }
+    };
+
+    int                 loop = 0;
+    int count;
+    SkMetaData::Type    t;
+    while ((name = iter.next(&t, &count)) != NULL)
+    {
+        int match = 0;
+        for (unsigned i = 0; i < SK_ARRAY_COUNT(gElems); i++)
+        {
+            if (!strcmp(name, gElems[i].fName))
+            {
+                match += 1;
+                REPORTER_ASSERT(reporter, gElems[i].fType == t);
+                REPORTER_ASSERT(reporter, gElems[i].fCount == count);
+            }
+        }
+        REPORTER_ASSERT(reporter, match == 1);
+        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"));
+    REPORTER_ASSERT(reporter, !m1.findBool("true"));
+    REPORTER_ASSERT(reporter, !m1.findBool("false"));
+
+    test_ptrs(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("MetaData", TestMetaDataClass, TestMetaData)
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
new file mode 100644
index 0000000..5cbb905
--- /dev/null
+++ b/tests/PDFPrimitivesTest.cpp
@@ -0,0 +1,332 @@
+
+/*
+ * 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 "Test.h"
+#include "SkData.h"
+#include "SkFlate.h"
+#include "SkPDFCatalog.h"
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkScalar.h"
+#include "SkStream.h"
+#include "SkTypes.h"
+
+class SkPDFTestDict : public SkPDFDict {
+public:
+    void getResources(SkTDArray<SkPDFObject*>* resourceList) {
+        resourceList->setReserve(resourceList->count() + fResources.count());
+        for (int i = 0; i < fResources.count(); i++) {
+            resourceList->push(fResources[i]);
+            fResources[i]->ref();
+        }
+    }
+
+    void addResource(SkPDFObject* object) {
+        fResources.append(1, &object);
+    }
+
+private:
+    SkTDArray<SkPDFObject*> fResources;
+};
+
+static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
+                          const void* buffer, size_t len) {
+    SkAutoDataUnref data(stream.copyToData());
+    if (offset + len > data->size()) {
+        return false;
+    }
+    return memcmp(data->bytes() + offset, buffer, len) == 0;
+}
+
+static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
+                              const char* expectedData, size_t expectedSize,
+                              bool indirect, bool compression) {
+    SkPDFDocument::Flags docFlags = (SkPDFDocument::Flags) 0;
+    if (!compression) {
+        docFlags = SkTBitOr(docFlags, SkPDFDocument::kNoCompression_Flags);
+    }
+    SkPDFCatalog catalog(docFlags);
+    size_t directSize = obj->getOutputSize(&catalog, false);
+    REPORTER_ASSERT(reporter, directSize == expectedSize);
+
+    SkDynamicMemoryWStream buffer;
+    obj->emit(&buffer, &catalog, false);
+    REPORTER_ASSERT(reporter, directSize == buffer.getOffset());
+    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedData,
+                                            directSize));
+
+    if (indirect) {
+        // Indirect output.
+        static char header[] = "1 0 obj\n";
+        static size_t headerLen = strlen(header);
+        static char footer[] = "\nendobj\n";
+        static size_t footerLen = strlen(footer);
+
+        catalog.addObject(obj, false);
+
+        size_t indirectSize = obj->getOutputSize(&catalog, true);
+        REPORTER_ASSERT(reporter,
+                        indirectSize == directSize + headerLen + footerLen);
+
+        buffer.reset();
+        obj->emit(&buffer, &catalog, true);
+        REPORTER_ASSERT(reporter, indirectSize == buffer.getOffset());
+        REPORTER_ASSERT(reporter, stream_equals(buffer, 0, header, headerLen));
+        REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen, expectedData,
+                                                directSize));
+        REPORTER_ASSERT(reporter, stream_equals(buffer, headerLen + directSize,
+                                                footer, footerLen));
+    }
+}
+
+static void SimpleCheckObjectOutput(skiatest::Reporter* reporter,
+                                    SkPDFObject* obj,
+                                    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.
+    SimpleCheckObjectOutput(
+        reporter, stream.get(),
+        "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
+    stream->insert("Attribute", new SkPDFInt(42))->unref();
+    SimpleCheckObjectOutput(reporter, stream.get(),
+                            "<</Length 12\n/Attribute 42\n>> stream\n"
+                                "Test\nFoo\tBar\nendstream");
+
+    if (SkFlate::HaveFlate()) {
+        char streamBytes2[] = "This is a longer string, so that compression "
+                              "can do something with it. With shorter strings, "
+                              "the short circuit logic cuts in and we end up "
+                              "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.
+
+        SkDynamicMemoryWStream compressedByteStream;
+        SkFlate::Deflate(streamData2.get(), &compressedByteStream);
+        SkAutoDataUnref compressedData(compressedByteStream.copyToData());
+
+        // Check first without compression.
+        SkDynamicMemoryWStream expectedResult1;
+        expectedResult1.writeText("<</Length 167\n>> stream\n");
+        expectedResult1.writeText(streamBytes2);
+        expectedResult1.writeText("\nendstream");
+        SkAutoDataUnref expectedResultData1(expectedResult1.copyToData());
+        CheckObjectOutput(reporter, stream.get(),
+                          (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.writeText("\nendstream");
+        SkAutoDataUnref expectedResultData2(expectedResult2.copyToData());
+        CheckObjectOutput(reporter, stream.get(),
+                          (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());
+
+    catalog.addObject(int1.get(), false);
+    catalog.addObject(int2.get(), false);
+    catalog.addObject(int3.get(), false);
+
+    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
+    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
+    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int3.get()) == 3);
+
+    SkDynamicMemoryWStream buffer;
+    catalog.emitObjectNumber(&buffer, int1.get());
+    catalog.emitObjectNumber(&buffer, int2.get());
+    catalog.emitObjectNumber(&buffer, int3.get());
+    catalog.emitObjectNumber(&buffer, int1Again.get());
+    char expectedResult[] = "1 02 03 01 0";
+    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
+                                            strlen(expectedResult)));
+}
+
+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.
+
+    SkPDFCatalog catalog((SkPDFDocument::Flags)0);
+    catalog.addObject(int1.get(), false);
+    catalog.addObject(int2.get(), false);
+    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int1.get()) == 3);
+    REPORTER_ASSERT(reporter, catalog.getObjectNumberSize(int2.get()) == 3);
+
+    char expectedResult[] = "2 0 R";
+    SkDynamicMemoryWStream buffer;
+    int2ref->emitObject(&buffer, &catalog, false);
+    REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
+    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
+                                            buffer.getOffset()));
+}
+
+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.
+
+    stub->insert("Value", int33.get());
+    stubResource->insert("InnerValue", int44.get());
+    stub->addResource(stubResource.get());
+
+    SkPDFCatalog catalog((SkPDFDocument::Flags)0);
+    catalog.addObject(proxy.get(), false);
+    catalog.setSubstitute(proxy.get(), stub.get());
+
+    SkDynamicMemoryWStream buffer;
+    proxy->emit(&buffer, &catalog, false);
+    catalog.emitSubstituteResources(&buffer, false);
+
+    char objectResult[] = "2 0 obj\n<</Value 33\n>>\nendobj\n";
+    REPORTER_ASSERT(
+        reporter,
+        catalog.setFileOffset(proxy.get(), 0) == strlen(objectResult));
+
+    char expectedResult[] =
+        "<</Value 33\n>>1 0 obj\n<</InnerValue 44\n>>\nendobj\n";
+    REPORTER_ASSERT(reporter, buffer.getOffset() == strlen(expectedResult));
+    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
+                                            buffer.getOffset()));
+}
+
+static void TestPDFPrimitives(skiatest::Reporter* reporter) {
+    SkRefPtr<SkPDFInt> int42 = new SkPDFInt(42);
+    int42->unref();  // SkRefPtr and new both took a reference.
+    SimpleCheckObjectOutput(reporter, int42.get(), "42");
+
+    SkRefPtr<SkPDFScalar> realHalf = new SkPDFScalar(SK_ScalarHalf);
+    realHalf->unref();  // SkRefPtr and new both took a reference.
+    SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
+
+#if defined(SK_SCALAR_IS_FLOAT)
+    SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75f);
+    bigScalar->unref();  // SkRefPtr and new both took a reference.
+#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.
+    SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
+
+    SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536);
+    smallestScalar->unref();  // SkRefPtr and new both took a reference.
+    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.
+    SimpleCheckObjectOutput(reporter, stringSimple.get(),
+                            "(test \\) string \\( foo)");
+    SkRefPtr<SkPDFString> stringComplex =
+        new SkPDFString("\ttest ) string ( foo");
+    stringComplex->unref();  // SkRefPtr and new both took a reference.
+    SimpleCheckObjectOutput(reporter, stringComplex.get(),
+                            "<0974657374202920737472696E67202820666F6F>");
+
+    SkRefPtr<SkPDFName> name = new SkPDFName("Test name\twith#tab");
+    name->unref();  // SkRefPtr and new both took a reference.
+    const char expectedResult[] = "/Test#20name#09with#23tab";
+    CheckObjectOutput(reporter, name.get(), expectedResult,
+                      strlen(expectedResult), false, false);
+
+    SkRefPtr<SkPDFName> escapedName = new SkPDFName("A#/%()<>[]{}B");
+    escapedName->unref();  // SkRefPtr and new both took a reference.
+    const char escapedNameExpected[] = "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB";
+    CheckObjectOutput(reporter, escapedName.get(), escapedNameExpected,
+                      strlen(escapedNameExpected), false, false);
+
+    // Test that we correctly handle characters with the high-bit set.
+    const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0};
+    SkRefPtr<SkPDFName> highBitName = new SkPDFName((const char*)highBitCString);
+    highBitName->unref();  // SkRefPtr and new both took a reference.
+    const char highBitExpectedResult[] = "/#DE#ADbe#EF";
+    CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult,
+                      strlen(highBitExpectedResult), false, false);
+
+    SkRefPtr<SkPDFArray> array = new SkPDFArray;
+    array->unref();  // SkRefPtr and new both took a reference.
+    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.
+    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.
+    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.
+    SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
+    SkRefPtr<SkPDFName> n1 = new SkPDFName("n1");
+    n1->unref();  // SkRefPtr and new both took a reference.
+    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.
+    dict->insert(n2.get(), realHalf.get());
+    dict->insert(n3.get(), array.get());
+    SimpleCheckObjectOutput(reporter, dict.get(),
+                            "<</n1 42\n/n2 0.5\n/n3 [1 0.5 0]\n>>");
+
+    TestPDFStream(reporter);
+
+    TestCatalog(reporter);
+
+    TestObjectRef(reporter);
+
+    TestSubstitute(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("PDFPrimitives", PDFPrimitivesTestClass, TestPDFPrimitives)
diff --git a/tests/PackBitsTest.cpp b/tests/PackBitsTest.cpp
new file mode 100644
index 0000000..f7d4b8e
--- /dev/null
+++ b/tests/PackBitsTest.cpp
@@ -0,0 +1,134 @@
+
+/*
+ * Copyright 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 "SkPackBits.h"
+
+static const uint16_t gTest0[] = { 0, 0, 1, 1 };
+static const uint16_t gTest1[] = { 1, 2, 3, 4, 5, 6 };
+static const uint16_t gTest2[] = { 0, 0, 0, 1, 2, 3, 3, 3 };
+static const uint16_t gTest3[] = { 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 0, 0, 1 };
+
+#include "SkRandom.h"
+static SkRandom gRand;
+static void rand_fill(uint16_t buffer[], int count) {
+    for (int i = 0; i < count; i++)
+        buffer[i] = (uint16_t)gRand.nextU();
+}
+
+static void test_pack16(skiatest::Reporter* reporter) {
+    static const struct {
+        const uint16_t* fSrc;
+        int             fCount;
+    } gTests[] = {
+        { gTest0, SK_ARRAY_COUNT(gTest0) },
+        { gTest1, SK_ARRAY_COUNT(gTest1) },
+        { gTest2, SK_ARRAY_COUNT(gTest2) },
+        { gTest3, SK_ARRAY_COUNT(gTest3) }
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); i++) {
+        uint8_t dst[100];
+        size_t dstSize = SkPackBits::Pack16(gTests[i].fSrc,
+                                            gTests[i].fCount, dst);
+        uint16_t src[100];
+        int srcCount = SkPackBits::Unpack16(dst, dstSize, src);
+        bool match = gTests[i].fCount == srcCount && memcmp(gTests[i].fSrc, src,
+                                    gTests[i].fCount * sizeof(uint16_t)) == 0;
+        REPORTER_ASSERT(reporter, match);
+    }
+
+    for (int n = 1000; n; n--) {
+        size_t size = 50;
+        uint16_t src[100], src2[100];
+        uint8_t dst[200];
+        rand_fill(src, size);
+
+        size_t dstSize = SkPackBits::Pack16(src, size, dst);
+        size_t maxSize = SkPackBits::ComputeMaxSize16(size);
+        REPORTER_ASSERT(reporter, maxSize >= dstSize);
+
+        size_t srcCount = SkPackBits::Unpack16(dst, dstSize, src2);
+        REPORTER_ASSERT(reporter, size == srcCount);
+        bool match = memcmp(src, src2, size * sizeof(uint16_t)) == 0;
+        REPORTER_ASSERT(reporter, match);
+    }
+}
+
+static const uint8_t gTest80[] = { 0, 0, 1, 1 };
+static const uint8_t gTest81[] = { 1, 2, 3, 4, 5, 6 };
+static const uint8_t gTest82[] = { 0, 0, 0, 1, 2, 3, 3, 3 };
+static const uint8_t gTest83[] = { 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 0, 0, 1 };
+static const uint8_t gTest84[] = { 1, 0, 3, 0, 0, 0, 2, 1, 1, 2 };
+
+static void rand_fill(uint8_t buffer[], int count) {
+    for (int i = 0; i < count; i++)
+        buffer[i] = (uint8_t)((gRand.nextU() >> 8) & 0x3);
+}
+
+static void test_pack8(skiatest::Reporter* reporter) {
+    static const struct {
+        const uint8_t* fSrc;
+        int             fCount;
+    } gTests[] = {
+        { gTest80, SK_ARRAY_COUNT(gTest80) },
+        { gTest81, SK_ARRAY_COUNT(gTest81) },
+        { gTest82, SK_ARRAY_COUNT(gTest82) },
+        { gTest83, SK_ARRAY_COUNT(gTest83) },
+        { gTest84, SK_ARRAY_COUNT(gTest84) }
+    };
+
+    for (size_t i = 4; i < SK_ARRAY_COUNT(gTests); i++) {
+        uint8_t dst[100];
+        size_t maxSize = SkPackBits::ComputeMaxSize8(gTests[i].fCount);
+        size_t dstSize = SkPackBits::Pack8(gTests[i].fSrc,
+                                           gTests[i].fCount, dst);
+        REPORTER_ASSERT(reporter, dstSize <= maxSize);
+        uint8_t src[100];
+        int srcCount = SkPackBits::Unpack8(dst, dstSize, src);
+        bool match = gTests[i].fCount == srcCount &&
+                    memcmp(gTests[i].fSrc, src,
+                           gTests[i].fCount * sizeof(uint8_t)) == 0;
+        REPORTER_ASSERT(reporter, match);
+    }
+
+    for (size_t size = 1; size <= 512; size += 1) {
+        for (int n = 100; n; n--) {
+            uint8_t src[600], src2[600];
+            uint8_t dst[600];
+            rand_fill(src, size);
+
+            size_t dstSize = SkPackBits::Pack8(src, size, dst);
+            size_t maxSize = SkPackBits::ComputeMaxSize8(size);
+            REPORTER_ASSERT(reporter, maxSize >= dstSize);
+
+            size_t srcCount = SkPackBits::Unpack8(dst, dstSize, src2);
+            REPORTER_ASSERT(reporter, size == srcCount);
+            bool match = memcmp(src, src2, size * sizeof(uint8_t)) == 0;
+            REPORTER_ASSERT(reporter, match);
+
+            for (int j = 0; j < 100; j++) {
+                size_t skip = gRand.nextU() % size;
+                size_t write = gRand.nextU() % size;
+                if (skip + write > size) {
+                    write = size - skip;
+                }
+                SkPackBits::Unpack8(src, skip, write, dst);
+                bool match = memcmp(src, src2 + skip, write) == 0;
+                REPORTER_ASSERT(reporter, match);
+            }
+        }
+    }
+}
+
+static void TestPackBits(skiatest::Reporter* reporter) {
+    test_pack8(reporter);
+    test_pack16(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("PackBits", PackBitsTestClass, TestPackBits)
diff --git a/tests/PaintTest.cpp b/tests/PaintTest.cpp
new file mode 100644
index 0000000..2a84e4c
--- /dev/null
+++ b/tests/PaintTest.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 "Test.h"
+#include "SkPath.h"
+#include "SkPaint.h"
+#include "SkLayerDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+
+static void test_copy(skiatest::Reporter* reporter) {
+    SkPaint paint;
+    // set a few member variables
+    paint.setStyle(SkPaint::kStrokeAndFill_Style);
+    paint.setTextAlign(SkPaint::kLeft_Align);
+    paint.setStrokeWidth(SkIntToScalar(2));
+    // set a few pointers
+    SkLayerDrawLooper* looper = new SkLayerDrawLooper();
+    paint.setLooper(looper)->unref();
+    SkMaskFilter* mask = SkBlurMaskFilter::Create(1, SkBlurMaskFilter::kNormal_BlurStyle);
+    paint.setMaskFilter(mask)->unref();
+
+    // copy the paint using the copy constructor and check they are the same
+    SkPaint copiedPaint = paint;
+    REPORTER_ASSERT(reporter, paint == copiedPaint);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    // the copy constructor should preserve the Generation ID
+    int32_t paintGenID = paint.getGenerationID();
+    int32_t copiedPaintGenID = copiedPaint.getGenerationID();
+    REPORTER_ASSERT(reporter, paintGenID == copiedPaintGenID);
+    REPORTER_ASSERT(reporter, !memcmp(&paint, &copiedPaint, sizeof(paint)));
+#endif
+
+    // copy the paint using the equal operator and check they are the same
+    copiedPaint = paint;
+    REPORTER_ASSERT(reporter, paint == copiedPaint);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    // the equals operator should increment the Generation ID
+    REPORTER_ASSERT(reporter, paint.getGenerationID() == paintGenID);
+    REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID);
+    copiedPaintGenID = copiedPaint.getGenerationID(); // reset to the new value
+    REPORTER_ASSERT(reporter, memcmp(&paint, &copiedPaint, sizeof(paint)));
+#endif
+
+    // clean the paint and check they are back to their initial states
+    SkPaint cleanPaint;
+    paint.reset();
+    copiedPaint.reset();
+    REPORTER_ASSERT(reporter, cleanPaint == paint);
+    REPORTER_ASSERT(reporter, cleanPaint == copiedPaint);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    // the reset function should increment the Generation ID
+    REPORTER_ASSERT(reporter, paint.getGenerationID() != paintGenID);
+    REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID);
+    REPORTER_ASSERT(reporter, memcmp(&cleanPaint, &paint, sizeof(cleanPaint)));
+    REPORTER_ASSERT(reporter, memcmp(&cleanPaint, &copiedPaint, sizeof(cleanPaint)));
+#endif
+}
+
+// found and fixed for webkit: mishandling when we hit recursion limit on
+// mostly degenerate cubic flatness test
+static void regression_cubic(skiatest::Reporter* reporter) {
+    SkPath path, stroke;
+    SkPaint paint;
+
+    path.moveTo(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();
+
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(SkIntToScalar(2));
+    paint.getFillPath(path, &stroke);
+    strokeR = stroke.getBounds();
+
+    SkRect maxR = fillR;
+    SkScalar miter = SkMaxScalar(SK_Scalar1, paint.getStrokeMiter());
+    SkScalar inset = paint.getStrokeJoin() == SkPaint::kMiter_Join ?
+                            SkScalarMul(paint.getStrokeWidth(), miter) :
+                            paint.getStrokeWidth();
+    maxR.inset(-inset, -inset);
+
+    // test that our stroke didn't explode
+    REPORTER_ASSERT(reporter, maxR.contains(strokeR));
+}
+
+static void TestPaint(skiatest::Reporter* reporter) {
+    // TODO add general paint tests
+    test_copy(reporter);
+
+    // regression tests
+    regression_cubic(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Paint", TestPaintClass, TestPaint)
diff --git a/tests/ParsePathTest.cpp b/tests/ParsePathTest.cpp
new file mode 100644
index 0000000..c911860
--- /dev/null
+++ b/tests/ParsePathTest.cpp
@@ -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.
+ */
+#include "Test.h"
+#include "SkParsePath.h"
+
+static void test_to_from(skiatest::Reporter* reporter, const SkPath& path) {
+    SkString str, str2;
+    SkParsePath::ToSVGString(path, &str);
+
+    SkPath path2;
+    bool success = SkParsePath::FromSVGString(str.c_str(), &path2);
+    REPORTER_ASSERT(reporter, success);
+
+    SkParsePath::ToSVGString(path2, &str2);
+    REPORTER_ASSERT(reporter, str == str2);
+#if 0 // closed paths are not equal, the iter explicitly gives the closing
+      // edge, even if it is not in the path.
+    REPORTER_ASSERT(reporter, path == path2);
+    if (path != path2) {
+        SkDebugf("str1=%s\nstr2=%s\n", str.c_str(), str2.c_str());
+    }
+#endif
+}
+
+static 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) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+        SkPath  path;
+        bool success = SkParsePath::FromSVGString(gRec[i].fStr, &path);
+        REPORTER_ASSERT(reporter, success);
+        const SkRect& expectedBounds = gRec[i].fBounds;
+        const SkRect& pathBounds = path.getBounds();
+        REPORTER_ASSERT(reporter, expectedBounds == pathBounds);
+
+        test_to_from(reporter, path);
+    }
+
+    SkRect r;
+    r.set(0, 0, SkFloatToScalar(10), SkFloatToScalar(10.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.5f));
+    test_to_from(reporter, p);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("ParsePath", ParsePathClass, TestParsePath)
diff --git a/tests/PathCoverageTest.cpp b/tests/PathCoverageTest.cpp
new file mode 100644
index 0000000..87ec908
--- /dev/null
+++ b/tests/PathCoverageTest.cpp
@@ -0,0 +1,169 @@
+
+/*
+ * Copyright 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"
+#include "SkPoint.h"
+#include "SkScalar.h"
+#include "Test.h"
+
+/*
+   Duplicates lots of code from gpu/src/GrPathUtils.cpp
+   It'd be nice not to do so, but that code's set up currently to only have
+   a single implementation.
+*/
+
+// Sk uses 6, Gr (implicitly) used 10, both apparently arbitrarily.
+#define MAX_COEFF_SHIFT     6
+static const uint32_t MAX_POINTS_PER_CURVE = 1 << MAX_COEFF_SHIFT;
+
+// max + 0.5 min has error [0.0, 0.12]
+// max + 0.375 min has error [-.03, 0.07]
+// 0.96043387 max + 0.397824735 min has error [-.06, +.05]
+// For determining the maximum possible number of points to use in
+// drawing a quadratic, we want to err on the high side.
+static inline int cheap_distance(SkScalar dx, SkScalar dy) {
+    int idx = SkAbs32(SkScalarRound(dx));
+    int idy = SkAbs32(SkScalarRound(dy));
+    if (idx > idy) {
+        idx += idy >> 1;
+    } else {
+        idx = idy + (idx >> 1);
+    }
+    return idx;
+}
+
+static inline int estimate_distance(const SkPoint points[]) {
+    return cheap_distance(points[1].fX * 2 - points[2].fX - points[0].fX,
+                          points[1].fY * 2 - points[2].fY - points[0].fY);
+}
+
+static inline SkScalar compute_distance(const SkPoint points[]) {
+    return points[1].distanceToLineSegmentBetween(points[0], points[2]);
+}
+
+static inline uint32_t estimate_pointCount(int distance) {
+    // Includes -2 bias because this estimator runs 4x high?
+    int shift = 30 - SkCLZ(distance);
+    // Clamp to zero if above subtraction went negative.
+    shift &= ~(shift>>31);
+    if (shift > MAX_COEFF_SHIFT) {
+        shift = MAX_COEFF_SHIFT;
+    }
+    return 1 << shift;
+}
+
+static inline uint32_t compute_pointCount(SkScalar d, SkScalar tol) {
+    if (d < tol) {
+       return 1;
+    } else {
+       int temp = SkScalarCeilToInt(SkScalarSqrt(SkScalarDiv(d, tol)));
+       uint32_t count = SkMin32(SkNextPow2(temp), MAX_POINTS_PER_CURVE);
+       return count;
+    }
+}
+
+static uint32_t quadraticPointCount_EE(const SkPoint points[], SkScalar tol) {
+    int distance = estimate_distance(points);
+    return estimate_pointCount(distance);
+}
+
+static uint32_t quadraticPointCount_EC(const SkPoint points[], SkScalar tol) {
+    int distance = estimate_distance(points);
+    return compute_pointCount(SkIntToScalar(distance), tol);
+}
+
+static uint32_t quadraticPointCount_CE(const SkPoint points[], SkScalar tol) {
+    SkScalar distance = compute_distance(points);
+    return estimate_pointCount(SkScalarRound(distance));
+}
+
+static uint32_t quadraticPointCount_CC(const SkPoint points[], SkScalar tol) {
+    SkScalar distance = compute_distance(points);
+    return compute_pointCount(distance, tol);
+}
+
+// Curve from samplecode/SampleSlides.cpp
+static const int gXY[] = {
+    4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
+};
+
+static const int gSawtooth[] = {
+    0, 0, 10, 10, 20, 20, 30, 10, 40, 0, 50, -10, 60, -20, 70, -10, 80, 0
+};
+
+static const int gOvalish[] = {
+    0, 0, 5, 15, 20, 20, 35, 15, 40, 0
+};
+
+static const int gSharpSawtooth[] = {
+    0, 0, 1, 10, 2, 0, 3, -10, 4, 0
+};
+
+// Curve crosses back over itself around 0,10
+static const int gRibbon[] = {
+   -4, 0, 4, 20, 0, 25, -4, 20, 4, 0
+};
+
+static bool one_d_pe(const int* array, const unsigned int count,
+                     skiatest::Reporter* reporter) {
+    SkPoint path [3];
+    path[1] = SkPoint::Make(SkIntToScalar(array[0]), SkIntToScalar(array[1]));
+    path[2] = SkPoint::Make(SkIntToScalar(array[2]), SkIntToScalar(array[3]));
+    int numErrors = 0;
+    for (unsigned i = 4; i < count; i += 2) {
+        path[0] = path[1];
+        path[1] = path[2];
+        path[2] = SkPoint::Make(SkIntToScalar(array[i]),
+                                SkIntToScalar(array[i+1]));
+        uint32_t computedCount =
+            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) &&
+            (estimatedCount <= 2 * computedCount);
+
+        if (!isAccurate) {
+            SkString errorDescription;
+            errorDescription.printf(
+                "Curve from %.2f %.2f through %.2f %.2f to %.2f %.2f "
+                "computes %d, estimates %d\n",
+                path[0].fX, path[0].fY, path[1].fX, path[1].fY,
+                path[2].fX, path[2].fY, computedCount, estimatedCount);
+            numErrors++;
+            reporter->reportFailed(errorDescription);
+        }
+    }
+
+    return (numErrors == 0);
+}
+
+
+
+static void TestQuadPointCount(skiatest::Reporter* reporter) {
+    one_d_pe(gXY, SK_ARRAY_COUNT(gXY), reporter);
+    one_d_pe(gSawtooth, SK_ARRAY_COUNT(gSawtooth), reporter);
+    one_d_pe(gOvalish, SK_ARRAY_COUNT(gOvalish), reporter);
+    one_d_pe(gSharpSawtooth, SK_ARRAY_COUNT(gSharpSawtooth), reporter);
+    one_d_pe(gRibbon, SK_ARRAY_COUNT(gRibbon), reporter);
+}
+
+static void TestPathCoverage(skiatest::Reporter* reporter) {
+    TestQuadPointCount(reporter);
+
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("PathCoverage", PathCoverageTestClass, TestPathCoverage)
diff --git a/tests/PathMeasureTest.cpp b/tests/PathMeasureTest.cpp
new file mode 100644
index 0000000..e8477c6
--- /dev/null
+++ b/tests/PathMeasureTest.cpp
@@ -0,0 +1,212 @@
+
+/*
+ * Copyright 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 "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;
+
+    path.moveTo(0, 0);
+    path.lineTo(SK_Scalar1, 0);
+    path.lineTo(SK_Scalar1, SK_Scalar1);
+    path.lineTo(0, SK_Scalar1);
+
+    SkPathMeasure   meas(path, true);
+    SkScalar        length = meas.getLength();
+    SkASSERT(length == SK_Scalar1*4);
+
+    path.reset();
+    path.moveTo(0, 0);
+    path.lineTo(SK_Scalar1*3, SK_Scalar1*4);
+    meas.setPath(&path, false);
+    length = meas.getLength();
+    REPORTER_ASSERT(reporter, length == SK_Scalar1*5);
+
+    path.reset();
+    path.addCircle(0, 0, SK_Scalar1);
+    meas.setPath(&path, true);
+    length = meas.getLength();
+//    SkDebugf("circle arc-length = %g\n", length);
+
+    // Test the behavior following a close not followed by a move.
+    path.reset();
+    path.lineTo(SK_Scalar1, 0);
+    path.lineTo(SK_Scalar1, SK_Scalar1);
+    path.lineTo(0, SK_Scalar1);
+    path.close();
+    path.lineTo(-SK_Scalar1, 0);
+    meas.setPath(&path, false);
+    length = meas.getLength();
+    REPORTER_ASSERT(reporter, length == SK_Scalar1 * 4);
+    meas.nextContour();
+    length = meas.getLength();
+    REPORTER_ASSERT(reporter, length == SK_Scalar1);
+    SkPoint position;
+    SkVector tangent;
+    REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
+    REPORTER_ASSERT(reporter,
+        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);
+
+    // Test degenerate paths
+    path.reset();
+    path.moveTo(0, 0);
+    path.lineTo(0, 0);
+    path.lineTo(SK_Scalar1, 0);
+    path.quadTo(SK_Scalar1, 0, SK_Scalar1, 0);
+    path.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1 * 2);
+    path.cubicTo(SK_Scalar1, SK_Scalar1 * 2,
+                 SK_Scalar1, SK_Scalar1 * 2,
+                 SK_Scalar1, SK_Scalar1 * 2);
+    path.cubicTo(SK_Scalar1*2, SK_Scalar1 * 2,
+                 SK_Scalar1*3, SK_Scalar1 * 2,
+                 SK_Scalar1*4, SK_Scalar1 * 2);
+    meas.setPath(&path, false);
+    length = meas.getLength();
+    REPORTER_ASSERT(reporter, length == SK_Scalar1 * 6);
+    REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
+    REPORTER_ASSERT(reporter,
+        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(SkFloatToScalar(2.5f), &position, &tangent));
+    REPORTER_ASSERT(reporter,
+        SkScalarNearlyEqual(position.fX, SK_Scalar1, SkFloatToScalar(0.0001f)));
+    REPORTER_ASSERT(reporter,
+        SkScalarNearlyEqual(position.fY, SkFloatToScalar(1.5f)));
+    REPORTER_ASSERT(reporter, tangent.fX == 0);
+    REPORTER_ASSERT(reporter, tangent.fY == SK_Scalar1);
+    REPORTER_ASSERT(reporter, meas.getPosTan(SkFloatToScalar(4.5f), &position, &tangent));
+    REPORTER_ASSERT(reporter,
+        SkScalarNearlyEqual(position.fX,
+                            SkFloatToScalar(2.5f),
+                            SkFloatToScalar(0.0001f)));
+    REPORTER_ASSERT(reporter,
+        SkScalarNearlyEqual(position.fY,
+                            SkFloatToScalar(2.0f),
+                            SkFloatToScalar(0.0001f)));
+    REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
+    REPORTER_ASSERT(reporter, tangent.fY == 0);
+
+    path.reset();
+    path.moveTo(0, 0);
+    path.lineTo(SK_Scalar1, 0);
+    path.moveTo(SK_Scalar1, SK_Scalar1);
+    path.moveTo(SK_Scalar1 * 2, SK_Scalar1 * 2);
+    path.lineTo(SK_Scalar1, SK_Scalar1 * 2);
+    meas.setPath(&path, false);
+    length = meas.getLength();
+    REPORTER_ASSERT(reporter, length == SK_Scalar1);
+    REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
+    REPORTER_ASSERT(reporter,
+        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);
+    meas.nextContour();
+    length = meas.getLength();
+    REPORTER_ASSERT(reporter, length == SK_Scalar1);
+    REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
+    REPORTER_ASSERT(reporter,
+        SkScalarNearlyEqual(position.fX,
+                            SkFloatToScalar(1.5f),
+                            SkFloatToScalar(0.0001f)));
+    REPORTER_ASSERT(reporter,
+        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"
+DEFINE_TESTCLASS("PathMeasure", PathMeasureTestClass, TestPathMeasure)
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
new file mode 100644
index 0000000..8197e68
--- /dev/null
+++ b/tests/PathTest.cpp
@@ -0,0 +1,1622 @@
+
+/*
+ * Copyright 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 "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"
+
+static SkSurface* new_surface(int w, int h) {
+    SkImage::Info info = {
+        w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
+    };
+    return SkSurface::NewRaster(info, NULL);
+}
+
+// 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 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 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, -inf);
+    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());
+}
+
+/**
+ * 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);
+    }
+    REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
+}
+
+static void test_direction(skiatest::Reporter* reporter) {
+    size_t i;
+    SkPath path;
+    REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
+    REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
+    REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
+
+    static const char* gDegen[] = {
+        "M 10 10",
+        "M 10 10 M 20 20",
+        "M 10 10 L 20 20",
+        "M 10 10 L 10 10 L 10 10",
+        "M 10 10 Q 10 10 10 10",
+        "M 10 10 C 10 10 10 10 10 10",
+    };
+    for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
+        path.reset();
+        bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
+        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"
+    };
+    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);
+    }
+
+    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"
+    };
+    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);
+    }
+
+    // Test two donuts, each wound a different direction. Only the outer contour
+    // determines the cheap direction
+    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);
+
+    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);
+
+#ifdef SK_SCALAR_IS_FLOAT
+    // triangle with one point really far from the origin.
+    path.reset();
+    // the first point is roughly 1.05e10, 1.05e10
+    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);
+#endif
+}
+
+static void add_rect(SkPath* path, const SkRect& r) {
+    path->moveTo(r.fLeft, r.fTop);
+    path->lineTo(r.fRight, r.fTop);
+    path->lineTo(r.fRight, r.fBottom);
+    path->lineTo(r.fLeft, r.fBottom);
+    path->close();
+}
+
+static void test_bounds(skiatest::Reporter* reporter) {
+    static const SkRect rects[] = {
+        { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
+        { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
+        { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
+        { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
+    };
+
+    SkPath path0, path1;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
+        path0.addRect(rects[i]);
+        add_rect(&path1, rects[i]);
+    }
+
+    REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
+}
+
+static void stroke_cubic(const SkPoint pts[4]) {
+    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);
+}
+
+// just ensure this can run w/o any SkASSERTS firing in the debug build
+// we used to assert due to differences in how we determine a degenerate vector
+// but that was fixed with the introduction of SkPoint::CanNormalize
+static void stroke_tiny_cubic() {
+    SkPoint p0[] = {
+        { 372.0f,   92.0f },
+        { 372.0f,   92.0f },
+        { 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, SkToBool(i));
+        SkPoint mv;
+        SkPoint pts[4];
+        SkPath::Verb v;
+        int nMT = 0;
+        int nCL = 0;
+        mv.set(0, 0);
+        while (SkPath::kDone_Verb != (v = iter.next(pts))) {
+            switch (v) {
+                case SkPath::kMove_Verb:
+                    mv = pts[0];
+                    ++nMT;
+                    break;
+                case SkPath::kClose_Verb:
+                    REPORTER_ASSERT(reporter, mv == pts[0]);
+                    ++nCL;
+                    break;
+                default:
+                    break;
+            }
+        }
+        // if we force a close on the interator we should have a close
+        // for every moveTo
+        REPORTER_ASSERT(reporter, !i || nMT == nCL);
+    }
+}
+
+static void test_close(skiatest::Reporter* reporter) {
+    SkPath closePt;
+    closePt.moveTo(0, 0);
+    closePt.close();
+    check_close(reporter, closePt);
+
+    SkPath openPt;
+    openPt.moveTo(0, 0);
+    check_close(reporter, openPt);
+
+    SkPath empty;
+    check_close(reporter, empty);
+    empty.close();
+    check_close(reporter, empty);
+
+    SkPath rect;
+    rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
+    check_close(reporter, rect);
+    rect.close();
+    check_close(reporter, rect);
+
+    SkPath quad;
+    quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
+    check_close(reporter, quad);
+    quad.close();
+    check_close(reporter, quad);
+
+    SkPath cubic;
+    quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
+                 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
+    check_close(reporter, cubic);
+    cubic.close();
+    check_close(reporter, cubic);
+
+    SkPath line;
+    line.moveTo(SK_Scalar1, SK_Scalar1);
+    line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
+    check_close(reporter, line);
+    line.close();
+    check_close(reporter, line);
+
+    SkPath rect2;
+    rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
+    rect2.close();
+    rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
+    check_close(reporter, rect2);
+    rect2.close();
+    check_close(reporter, rect2);
+
+    SkPath oval3;
+    oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
+    oval3.close();
+    oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
+    check_close(reporter, oval3);
+    oval3.close();
+    check_close(reporter, oval3);
+
+    SkPath moves;
+    moves.moveTo(SK_Scalar1, SK_Scalar1);
+    moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
+    moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
+    moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
+    check_close(reporter, moves);
+
+    stroke_tiny_cubic();
+}
+
+static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
+                            SkPath::Convexity expected) {
+    SkPath::Convexity c = SkPath::ComputeConvexity(path);
+    REPORTER_ASSERT(reporter, c == expected);
+}
+
+static void test_convexity2(skiatest::Reporter* reporter) {
+    SkPath pt;
+    pt.moveTo(0, 0);
+    pt.close();
+    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
+
+    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);
+
+    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);
+
+    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);
+
+    SkPath square;
+    square.moveTo(0, 0);
+    square.lineTo(SK_Scalar1, 0);
+    square.lineTo(SK_Scalar1, SK_Scalar1);
+    square.lineTo(0, SK_Scalar1);
+    square.close();
+    check_convexity(reporter, square, SkPath::kConvex_Convexity);
+
+    SkPath redundantSquare;
+    redundantSquare.moveTo(0, 0);
+    redundantSquare.lineTo(0, 0);
+    redundantSquare.lineTo(0, 0);
+    redundantSquare.lineTo(SK_Scalar1, 0);
+    redundantSquare.lineTo(SK_Scalar1, 0);
+    redundantSquare.lineTo(SK_Scalar1, 0);
+    redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
+    redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
+    redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
+    redundantSquare.lineTo(0, SK_Scalar1);
+    redundantSquare.lineTo(0, SK_Scalar1);
+    redundantSquare.lineTo(0, SK_Scalar1);
+    redundantSquare.close();
+    check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
+
+    SkPath bowTie;
+    bowTie.moveTo(0, 0);
+    bowTie.lineTo(0, 0);
+    bowTie.lineTo(0, 0);
+    bowTie.lineTo(SK_Scalar1, SK_Scalar1);
+    bowTie.lineTo(SK_Scalar1, SK_Scalar1);
+    bowTie.lineTo(SK_Scalar1, SK_Scalar1);
+    bowTie.lineTo(SK_Scalar1, 0);
+    bowTie.lineTo(SK_Scalar1, 0);
+    bowTie.lineTo(SK_Scalar1, 0);
+    bowTie.lineTo(0, SK_Scalar1);
+    bowTie.lineTo(0, SK_Scalar1);
+    bowTie.lineTo(0, SK_Scalar1);
+    bowTie.close();
+    check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
+
+    SkPath spiral;
+    spiral.moveTo(0, 0);
+    spiral.lineTo(100*SK_Scalar1, 0);
+    spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
+    spiral.lineTo(0, 100*SK_Scalar1);
+    spiral.lineTo(0, 50*SK_Scalar1);
+    spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
+    spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
+    spiral.close();
+    check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
+
+    SkPath dent;
+    dent.moveTo(0, 0);
+    dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
+    dent.lineTo(0, 100*SK_Scalar1);
+    dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
+    dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
+    dent.close();
+    check_convexity(reporter, dent, SkPath::kConcave_Convexity);
+}
+
+static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
+                                const SkRect& bounds) {
+    REPORTER_ASSERT(reporter, p.isConvex());
+    REPORTER_ASSERT(reporter, p.getBounds() == bounds);
+
+    SkPath p2(p);
+    REPORTER_ASSERT(reporter, p2.isConvex());
+    REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
+
+    SkPath other;
+    other.swap(p2);
+    REPORTER_ASSERT(reporter, other.isConvex());
+    REPORTER_ASSERT(reporter, other.getBounds() == bounds);
+}
+
+static void setFromString(SkPath* path, const char str[]) {
+    bool first = true;
+    while (str) {
+        SkScalar x, y;
+        str = SkParse::FindScalar(str, &x);
+        if (NULL == str) {
+            break;
+        }
+        str = SkParse::FindScalar(str, &y);
+        SkASSERT(str);
+        if (first) {
+            path->moveTo(x, y);
+            first = false;
+        } else {
+            path->lineTo(x, y);
+        }
+    }
+}
+
+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));
+    path.addCircle(0, 0, SkIntToScalar(10));
+    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
+    path.addCircle(0, 0, SkIntToScalar(10));   // 2nd circle
+    REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
+    path.reset();
+    path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
+    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
+    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));
+    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
+
+    static const struct {
+        const char*         fPathStr;
+        SkPath::Convexity   fExpectedConvexity;
+    } 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 },
+    };
+
+    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);
+    }
+}
+
+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));
+}
+
+// Simple isRect test is inline TestPath, below.
+// test_isRect provides more extensive testing.
+static void test_isRect(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
+    };
+    SkPoint* lastPass = re;
+    SkPoint* lastClose = f8;
+    bool fail = false;
+    bool close = true;
+    const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+    size_t index;
+    for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
+        SkPath path;
+        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();
+        }
+        REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
+        if (tests[testIndex] == lastPass) {
+            fail = true;
+        }
+        if (tests[testIndex] == lastClose) {
+            close = false;
+        }
+    }
+
+    // fail, close then line
+    SkPath path1;
+    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);
+    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
+
+    // fail, move in the middle
+    path1.reset();
+    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();
+    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
+
+    // fail, move on the edge
+    path1.reset();
+    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();
+    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
+
+    // fail, quad
+    path1.reset();
+    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();
+    REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
+
+    // fail, cubic
+    path1.reset();
+    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();
+    REPORTER_ASSERT(reporter, fail ^ path1.isRect(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;
+
+    static const SkPoint pts[] = {
+        { 0, 0 },
+        { SkIntToScalar(10), SkIntToScalar(10) },
+        { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
+        { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
+    };
+    p.moveTo(pts[0]);
+    p.lineTo(pts[1]);
+    p.quadTo(pts[2], pts[3]);
+    p.cubicTo(pts[4], pts[5], pts[6]);
+
+    write_and_read_back(reporter, 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) },
+        { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
+        { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
+    };
+    p.moveTo(pts[0]);
+    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];
+    int count = p1.getPoints(pts1, 7);
+    REPORTER_ASSERT(reporter, 7 == count);
+    for (int i = 0; i < count; ++i) {
+        SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
+        REPORTER_ASSERT(reporter, newPt == pts1[i]);
+    }
+}
+
+static void test_zero_length_paths(skiatest::Reporter* reporter) {
+    SkPath  p;
+    uint8_t verbs[32];
+
+    struct zeroPathTestData {
+        const char* testPath;
+        const size_t numResultPts;
+        const SkRect resultBound;
+        const SkPath::Verb* resultVerbs;
+        const size_t numResultVerbs;
+    };
+
+    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)
+        }
+    };
+
+    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 {
+    SkPath fPath;
+    int    fPointCount;
+};
+
+#define kCurveSegmentMask   (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
+
+static void test_segment_masks(skiatest::Reporter* reporter) {
+    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;
+    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
+    iter.setPath(p, true);
+    REPORTER_ASSERT(reporter, iter.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;
+    };
+
+    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) }
+    };
+
+    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
+}
+
+static void test_raw_iter(skiatest::Reporter* reporter) {
+    SkPath p;
+    SkPoint pts[4];
+
+    // Test an iterator with no path
+    SkPath::RawIter noPathIter;
+    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+    // 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);
+
+    // Test that a move-only path returns the move.
+    p.moveTo(SK_Scalar1, 0);
+    iter.setPath(p);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+    REPORTER_ASSERT(reporter, pts[0].fY == 0);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // No matter how many moves we add, we should get them all back
+    p.moveTo(SK_Scalar1*2, SK_Scalar1);
+    p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
+    iter.setPath(p);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+    REPORTER_ASSERT(reporter, pts[0].fY == 0);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // Initial close is never ever stored
+    p.reset();
+    p.close();
+    iter.setPath(p);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // Move/close sequences
+    p.reset();
+    p.close(); // Not stored, no purpose
+    p.moveTo(SK_Scalar1, 0);
+    p.close();
+    p.close(); // Not stored, no purpose
+    p.moveTo(SK_Scalar1*2, SK_Scalar1);
+    p.close();
+    p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
+    p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
+    p.close();
+    iter.setPath(p);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+    REPORTER_ASSERT(reporter, pts[0].fY == 0);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+    REPORTER_ASSERT(reporter, pts[0].fY == 0);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // Generate random paths and verify
+    SkPoint randomPts[25];
+    for (int i = 0; i < 5; ++i) {
+        for (int j = 0; j < 5; ++j) {
+            randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
+        }
+    }
+
+    // Max of 10 segments, max 3 points per segment
+    SkRandom rand(9876543);
+    SkPoint          expectedPts[31]; // May have leading moveTo
+    SkPath::Verb     expectedVerbs[22]; // May have leading moveTo
+    SkPath::Verb     nextVerb;
+
+    for (int i = 0; i < 500; ++i) {
+        p.reset();
+        bool lastWasClose = true;
+        bool haveMoveTo = false;
+        SkPoint lastMoveToPt = { 0, 0 };
+        int numPoints = 0;
+        int numVerbs = (rand.nextU() >> 16) % 10;
+        int numIterVerbs = 0;
+        for (int j = 0; j < numVerbs; ++j) {
+            do {
+                nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
+            } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
+            switch (nextVerb) {
+                case SkPath::kMove_Verb:
+                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+                    p.moveTo(expectedPts[numPoints]);
+                    lastMoveToPt = expectedPts[numPoints];
+                    numPoints += 1;
+                    lastWasClose = false;
+                    haveMoveTo = true;
+                    break;
+                case SkPath::kLine_Verb:
+                    if (!haveMoveTo) {
+                        expectedPts[numPoints++] = lastMoveToPt;
+                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
+                        haveMoveTo = true;
+                    }
+                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+                    p.lineTo(expectedPts[numPoints]);
+                    numPoints += 1;
+                    lastWasClose = false;
+                    break;
+                case SkPath::kQuad_Verb:
+                    if (!haveMoveTo) {
+                        expectedPts[numPoints++] = lastMoveToPt;
+                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
+                        haveMoveTo = true;
+                    }
+                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+                    expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
+                    p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
+                    numPoints += 2;
+                    lastWasClose = false;
+                    break;
+                case SkPath::kCubic_Verb:
+                    if (!haveMoveTo) {
+                        expectedPts[numPoints++] = lastMoveToPt;
+                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
+                        haveMoveTo = true;
+                    }
+                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+                    expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
+                    expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
+                    p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
+                              expectedPts[numPoints + 2]);
+                    numPoints += 3;
+                    lastWasClose = false;
+                    break;
+                case SkPath::kClose_Verb:
+                    p.close();
+                    haveMoveTo = false;
+                    lastWasClose = true;
+                    break;
+                default:;
+            }
+            expectedVerbs[numIterVerbs++] = nextVerb;
+        }
+
+        iter.setPath(p);
+        numVerbs = numIterVerbs;
+        numIterVerbs = 0;
+        int numIterPts = 0;
+        SkPoint lastMoveTo;
+        SkPoint lastPt;
+        lastMoveTo.set(0, 0);
+        lastPt.set(0, 0);
+        while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
+            REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
+            numIterVerbs++;
+            switch (nextVerb) {
+                case SkPath::kMove_Verb:
+                    REPORTER_ASSERT(reporter, numIterPts < numPoints);
+                    REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
+                    lastPt = lastMoveTo = pts[0];
+                    numIterPts += 1;
+                    break;
+                case SkPath::kLine_Verb:
+                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
+                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
+                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
+                    lastPt = pts[1];
+                    numIterPts += 1;
+                    break;
+                case SkPath::kQuad_Verb:
+                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
+                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
+                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
+                    REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
+                    lastPt = pts[2];
+                    numIterPts += 2;
+                    break;
+                case SkPath::kCubic_Verb:
+                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
+                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
+                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
+                    REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
+                    REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
+                    lastPt = pts[3];
+                    numIterPts += 3;
+                    break;
+                case SkPath::kClose_Verb:
+                    REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
+                    lastPt = lastMoveTo;
+                    break;
+                default:;
+            }
+        }
+        REPORTER_ASSERT(reporter, numIterPts == numPoints);
+        REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
+    }
+}
+
+static void check_for_circle(skiatest::Reporter* reporter,
+                             const SkPath& path, bool expected) {
+    SkRect rect;
+    REPORTER_ASSERT(reporter, path.isOval(&rect) == expected);
+    if (expected) {
+        REPORTER_ASSERT(reporter, rect.height() == rect.width());
+    }
+}
+
+static void test_circle_skew(skiatest::Reporter* reporter,
+                             const SkPath& path) {
+    SkPath tmp;
+
+    SkMatrix m;
+    m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
+    path.transform(m, &tmp);
+    check_for_circle(reporter, tmp, false);
+}
+
+static void test_circle_translate(skiatest::Reporter* reporter,
+                                  const SkPath& path) {
+    SkPath tmp;
+
+    // translate at small offset
+    SkMatrix m;
+    m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
+    path.transform(m, &tmp);
+    check_for_circle(reporter, tmp, true);
+
+    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);
+}
+
+static void test_circle_rotate(skiatest::Reporter* reporter,
+                               const SkPath& path) {
+    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 mutiple 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);
+        } else {
+            check_for_circle(reporter, tmp, false);
+        }
+    }
+}
+
+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);
+    test_circle_rotate(reporter, path);
+    test_circle_translate(reporter, path);
+    test_circle_skew(reporter, path);
+
+    // circle at an offset at (10, 10)
+    path.reset();
+    path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
+                   SkIntToScalar(20), dir);
+    check_for_circle(reporter, path, true);
+    test_circle_rotate(reporter, path);
+    test_circle_translate(reporter, path);
+    test_circle_skew(reporter, path);
+}
+
+static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
+    SkPath path;
+    SkPath circle;
+    SkPath rect;
+    SkPath empty;
+
+    circle.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
+    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);
+
+    // circle + empty (translate)
+    path = circle;
+    path.addPath(empty, translate);
+    check_for_circle(reporter, path, false);
+
+    // test reverseAddPath
+    path = circle;
+    path.reverseAddPath(rect);
+    check_for_circle(reporter, path, false);
+}
+
+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);
+
+    // 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);
+
+    // 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);
+
+    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;
+    SkRect  bounds, bounds2;
+
+    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);
+    REPORTER_ASSERT(reporter, !p.isInverseFillType());
+    REPORTER_ASSERT(reporter, p == p2);
+    REPORTER_ASSERT(reporter, !(p != p2));
+
+    REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
+
+    bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
+
+    p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
+    check_convex_bounds(reporter, p, bounds);
+    // we have quads or cubics
+    REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
+    REPORTER_ASSERT(reporter, !p.isEmpty());
+
+    p.reset();
+    REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
+    REPORTER_ASSERT(reporter, p.isEmpty());
+
+    p.addOval(bounds);
+    check_convex_bounds(reporter, p, bounds);
+    REPORTER_ASSERT(reporter, !p.isEmpty());
+
+    p.reset();
+    p.addRect(bounds);
+    check_convex_bounds(reporter, p, bounds);
+    // we have only lines
+    REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
+    REPORTER_ASSERT(reporter, !p.isEmpty());
+
+    REPORTER_ASSERT(reporter, p != p2);
+    REPORTER_ASSERT(reporter, !(p == p2));
+
+    // 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);
+
+    bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
+    p.offset(SK_Scalar1*3, SK_Scalar1*4);
+    REPORTER_ASSERT(reporter, bounds == p.getBounds());
+
+    REPORTER_ASSERT(reporter, p.isRect(NULL));
+    bounds2.setEmpty();
+    REPORTER_ASSERT(reporter, p.isRect(&bounds2));
+    REPORTER_ASSERT(reporter, bounds == bounds2);
+
+    // now force p to not be a rect
+    bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
+    p.addRect(bounds);
+    REPORTER_ASSERT(reporter, !p.isRect(NULL));
+
+    test_isLine(reporter);
+    test_isRect(reporter);
+    test_zero_length_paths(reporter);
+    test_direction(reporter);
+    test_convexity(reporter);
+    test_convexity2(reporter);
+    test_close(reporter);
+    test_segment_masks(reporter);
+    test_flattening(reporter);
+    test_transform(reporter);
+    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);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Path", PathTestClass, TestPath)
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
new file mode 100644
index 0000000..44cf59a
--- /dev/null
+++ b/tests/PictureTest.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.
+ */
+#include "Test.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkRandom.h"
+#include "SkStream.h"
+
+#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();
+    }
+}
+
+static void TestPicture(skiatest::Reporter* reporter) {
+#ifdef SK_DEBUG
+    test_deleting_empty_playback();
+    test_serializing_empty_picture();
+#endif
+    test_peephole(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..7790c16
--- /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(0);
+
+    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
new file mode 100644
index 0000000..de0ae84
--- /dev/null
+++ b/tests/PointTest.cpp
@@ -0,0 +1,48 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+// Unit tests for src/core/SkPoint.cpp and its header
+
+#include "SkPoint.h"
+#include "Test.h"
+
+// Tests that SkPoint::length() and SkPoint::Length() both return
+// approximately expectedLength for this (x,y).
+static void test_length(skiatest::Reporter* reporter, SkScalar x, SkScalar y,
+                        SkScalar expectedLength) {
+    SkPoint point;
+    point.set(x, y);
+    SkScalar s1 = point.length();
+    SkScalar s2 = SkPoint::Length(x, y);
+    //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));
+}
+
+// Tests SkPoint::Normalize() for this (x,y)
+static void test_Normalize(skiatest::Reporter* reporter,
+                           SkScalar x, SkScalar y) {
+    SkPoint point;
+    point.set(x, y);
+    SkScalar oldLength = point.length();
+    SkScalar returned = SkPoint::Normalize(&point);
+    SkScalar newLength = point.length();
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(returned, oldLength));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, SK_Scalar1));
+}
+
+static void PointTest(skiatest::Reporter* reporter) {
+    test_length(reporter, SkIntToScalar(3), SkIntToScalar(4), SkIntToScalar(5));
+    test_length(reporter, SkFloatToScalar(0.6f), SkFloatToScalar(0.8f),
+                SK_Scalar1);
+    test_Normalize(reporter, SkIntToScalar(3), SkIntToScalar(4));
+    test_Normalize(reporter, SkFloatToScalar(0.6f), SkFloatToScalar(0.8f));
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Point", PointTestClass, PointTest)
diff --git a/tests/PremulAlphaRoundTripTest.cpp b/tests/PremulAlphaRoundTripTest.cpp
new file mode 100644
index 0000000..17fb42a
--- /dev/null
+++ b/tests/PremulAlphaRoundTripTest.cpp
@@ -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.
+ */
+
+#include "Test.h"
+#include "SkCanvas.h"
+#include "SkConfig8888.h"
+#include "SkDevice.h"
+
+#if SK_SUPPORT_GPU
+#include "SkGpuDevice.h"
+#endif
+
+
+namespace {
+
+void fillCanvas(SkCanvas* canvas, SkCanvas::Config8888 unpremulConfig) {
+    SkBitmap bmp;
+    bmp.setConfig(SkBitmap::kARGB_8888_Config, 256, 256);
+    bmp.allocPixels();
+    SkAutoLockPixels alp(bmp);
+    uint32_t* pixels = reinterpret_cast<uint32_t*>(bmp.getPixels());
+
+    for (int a = 0; a < 256; ++a) {
+        for (int r = 0; r < 256; ++r) {
+            pixels[a * 256 + r] = SkPackConfig8888(unpremulConfig, a, r, 0, 0);
+        }
+    }
+    canvas->writePixels(bmp, 0, 0, unpremulConfig);
+}
+
+static const SkCanvas::Config8888 gUnpremulConfigs[] = {
+    SkCanvas::kNative_Unpremul_Config8888,
+/**
+ * There is a bug in Ganesh (http://code.google.com/p/skia/issues/detail?id=438)
+ * that causes the readback of pixels from BGRA canvas to an RGBA bitmap to
+ * fail. This should be removed as soon as the issue above is resolved.
+ */
+#if !defined(SK_BUILD_FOR_ANDROID)
+    SkCanvas::kBGRA_Unpremul_Config8888,
+#endif
+    SkCanvas::kRGBA_Unpremul_Config8888,
+};
+
+void PremulAlphaRoundTripTest(skiatest::Reporter* reporter,
+                              GrContext* context) {
+    SkAutoTUnref<SkDevice> device;
+    for (int dtype = 0; dtype < 2; ++dtype) {
+        if (0 == dtype) {
+            device.reset(new SkDevice(SkBitmap::kARGB_8888_Config,
+                                          256,
+                                          256,
+                                          false));
+        } else {
+#if !SK_SUPPORT_GPU || defined(SK_SCALAR_IS_FIXED)
+            // GPU device known not to work in the fixed pt build.
+            continue;
+#else
+            device.reset(new SkGpuDevice(context,
+                                             SkBitmap::kARGB_8888_Config,
+                                             256,
+                                             256));
+#endif
+        }
+        SkCanvas canvas(device);
+
+        SkBitmap readBmp1;
+        readBmp1.setConfig(SkBitmap::kARGB_8888_Config, 256, 256);
+        readBmp1.allocPixels();
+        SkBitmap readBmp2;
+        readBmp2.setConfig(SkBitmap::kARGB_8888_Config, 256, 256);
+        readBmp2.allocPixels();
+
+        for (size_t upmaIdx = 0;
+             upmaIdx < SK_ARRAY_COUNT(gUnpremulConfigs);
+             ++upmaIdx) {
+            fillCanvas(&canvas, gUnpremulConfigs[upmaIdx]);
+            {
+                SkAutoLockPixels alp1(readBmp1);
+                SkAutoLockPixels alp2(readBmp2);
+                sk_bzero(readBmp1.getPixels(), readBmp1.getSafeSize());
+                sk_bzero(readBmp2.getPixels(), readBmp2.getSafeSize());
+            }
+
+            canvas.readPixels(&readBmp1, 0, 0, gUnpremulConfigs[upmaIdx]);
+            canvas.writePixels(readBmp1, 0, 0, gUnpremulConfigs[upmaIdx]);
+            canvas.readPixels(&readBmp2, 0, 0, gUnpremulConfigs[upmaIdx]);
+
+            SkAutoLockPixels alp1(readBmp1);
+            SkAutoLockPixels alp2(readBmp2);
+            uint32_t* pixels1 =
+                reinterpret_cast<uint32_t*>(readBmp1.getPixels());
+            uint32_t* pixels2 =
+                reinterpret_cast<uint32_t*>(readBmp2.getPixels());
+            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, success = pixels1[i] == pixels2[i]);
+                }
+            }
+        }
+    }
+}
+}
+
+#include "TestClassDef.h"
+DEFINE_GPUTESTCLASS("PremulAlphaRoundTripTest", PremulAlphaRoundTripTestClass, PremulAlphaRoundTripTest)
+
diff --git a/tests/QuickRejectTest.cpp b/tests/QuickRejectTest.cpp
new file mode 100644
index 0000000..b8fb989
--- /dev/null
+++ b/tests/QuickRejectTest.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 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 "SkDrawLooper.h"
+#include "Test.h"
+
+/*
+ *  Subclass of looper that just draws once, with an offset in X.
+ */
+class TestLooper : public SkDrawLooper {
+public:
+    bool fOnce;
+
+    virtual void init(SkCanvas*) SK_OVERRIDE {
+        fOnce = true;
+    }
+
+    virtual bool next(SkCanvas* canvas, SkPaint*) SK_OVERRIDE {
+        if (fOnce) {
+            fOnce = false;
+            canvas->translate(SkIntToScalar(10), 0);
+            return true;
+        }
+        return false;
+    }
+
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
+};
+
+static void test_drawBitmap(skiatest::Reporter* reporter) {
+    SkBitmap src;
+    src.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
+    src.allocPixels();
+    src.eraseColor(SK_ColorWHITE);
+
+    SkBitmap dst;
+    dst.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
+    dst.allocPixels();
+    dst.eraseColor(0);
+
+    SkCanvas canvas(dst);
+    SkPaint  paint;
+
+    // we are initially transparent
+    REPORTER_ASSERT(reporter, 0 == *dst.getAddr32(5, 5));
+
+    // we see the bitmap drawn
+    canvas.drawBitmap(src, 0, 0, &paint);
+    REPORTER_ASSERT(reporter, 0xFFFFFFFF == *dst.getAddr32(5, 5));
+
+    // reverify we are clear again
+    dst.eraseColor(0);
+    REPORTER_ASSERT(reporter, 0 == *dst.getAddr32(5, 5));
+
+    // if the bitmap is clipped out, we don't draw it
+    canvas.drawBitmap(src, SkIntToScalar(-10), 0, &paint);
+    REPORTER_ASSERT(reporter, 0 == *dst.getAddr32(5, 5));
+
+    // now install our looper, which will draw, since it internally translates
+    // to the left. The test is to ensure that canvas' quickReject machinary
+    // allows us through, even though sans-looper we would look like we should
+    // be clipped out.
+    paint.setLooper(new TestLooper)->unref();
+    canvas.drawBitmap(src, SkIntToScalar(-10), 0, &paint);
+    REPORTER_ASSERT(reporter, 0xFFFFFFFF == *dst.getAddr32(5, 5));
+}
+
+static void test(skiatest::Reporter* reporter) {
+    test_drawBitmap(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("QuickReject", QuickRejectClass, test)
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
new file mode 100644
index 0000000..8b47906
--- /dev/null
+++ b/tests/ReadPixelsTest.cpp
@@ -0,0 +1,401 @@
+
+/*
+ * Copyright 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 "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,
+                                                DEV_H * SK_Scalar1);
+
+namespace {
+SkPMColor getCanvasColor(int x, int y) {
+    SkASSERT(x >= 0 && x < DEV_W);
+    SkASSERT(y >= 0 && y < DEV_H);
+
+    U8CPU r = x;
+    U8CPU g = y;
+    U8CPU b = 0xc;
+
+    U8CPU a = 0xff;
+    switch ((x+y) % 5) {
+        case 0:
+            a = 0xff;
+            break;
+        case 1:
+            a = 0x80;
+            break;
+        case 2:
+            a = 0xCC;
+            break;
+        case 4:
+            a = 0x01;
+            break;
+        case 3:
+            a = 0x00;
+            break;
+    }
+    return SkPremultiplyARGBInline(a, r, g, b);
+}
+
+SkPMColor getBitmapColor(int x, int y, int w, int h) {
+    int n = y * w + x;
+
+    U8CPU b = n & 0xff;
+    U8CPU g = (n >> 8) & 0xff;
+    U8CPU r = (n >> 16) & 0xff;
+    return SkPackARGB32(0xff, r, g , b);
+}
+
+SkPMColor convertConfig8888ToPMColor(SkCanvas::Config8888 config8888,
+                                     uint32_t color,
+                                     bool* premul) {
+    const uint8_t* c = reinterpret_cast<uint8_t*>(&color);
+    U8CPU a,r,g,b;
+    *premul = false;
+    switch (config8888) {
+        case SkCanvas::kNative_Premul_Config8888:
+            return color;
+        case SkCanvas::kNative_Unpremul_Config8888:
+            *premul = true;
+            a = SkGetPackedA32(color);
+            r = SkGetPackedR32(color);
+            g = SkGetPackedG32(color);
+            b = SkGetPackedB32(color);
+            break;
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            *premul = true; // fallthru
+        case SkCanvas::kBGRA_Premul_Config8888:
+            a = static_cast<U8CPU>(c[3]);
+            r = static_cast<U8CPU>(c[2]);
+            g = static_cast<U8CPU>(c[1]);
+            b = static_cast<U8CPU>(c[0]);
+            break;
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            *premul = true; // fallthru
+        case SkCanvas::kRGBA_Premul_Config8888:
+            a = static_cast<U8CPU>(c[3]);
+            r = static_cast<U8CPU>(c[0]);
+            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);
+        g = SkMulDiv255Ceiling(g, a);
+        b = SkMulDiv255Ceiling(b, a);
+    }
+    return SkPackARGB32(a, r, g, b);
+}
+
+void fillCanvas(SkCanvas* canvas) {
+    static SkBitmap bmp;
+    if (bmp.isNull()) {
+        bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
+        SkDEBUGCODE(bool alloc =) bmp.allocPixels();
+        SkASSERT(alloc);
+        SkAutoLockPixels alp(bmp);
+        intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
+        for (int y = 0; y < DEV_H; ++y) {
+            for (int x = 0; x < DEV_W; ++x) {
+                SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
+                *pixel = getCanvasColor(x, y);
+            }
+        }
+    }
+    canvas->save();
+    canvas->setMatrix(SkMatrix::I());
+    canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
+    SkPaint paint;
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    canvas->drawBitmap(bmp, 0, 0, &paint);
+    canvas->restore();
+}
+
+void fillBitmap(SkBitmap* bitmap) {
+    SkASSERT(bitmap->lockPixelsAreWritable());
+    SkAutoLockPixels alp(*bitmap);
+    int w = bitmap->width();
+    int h = bitmap->height();
+    intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
+    for (int y = 0; y < h; ++y) {
+        for (int x = 0; x < w; ++x) {
+            SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel());
+            *pixel = getBitmapColor(x, y, w, h);
+        }
+    }
+}
+
+bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
+    if (!didPremulConversion) {
+        return a == b;
+    }
+    int32_t aA = static_cast<int32_t>(SkGetPackedA32(a));
+    int32_t aR = static_cast<int32_t>(SkGetPackedR32(a));
+    int32_t aG = static_cast<int32_t>(SkGetPackedG32(a));
+    int32_t aB = SkGetPackedB32(a);
+
+    int32_t bA = static_cast<int32_t>(SkGetPackedA32(b));
+    int32_t bR = static_cast<int32_t>(SkGetPackedR32(b));
+    int32_t bG = static_cast<int32_t>(SkGetPackedG32(b));
+    int32_t bB = static_cast<int32_t>(SkGetPackedB32(b));
+
+    return aA == bA &&
+           SkAbs32(aR - bR) <= 1 &&
+           SkAbs32(aG - bG) <= 1 &&
+           SkAbs32(aB - bB) <= 1;
+}
+
+// checks the bitmap contains correct pixels after the readPixels
+// if the bitmap was prefilled with pixels it checks that these weren't
+// overwritten in the area outside the readPixels.
+bool checkRead(skiatest::Reporter* reporter,
+               const SkBitmap& bitmap,
+               int x, int y,
+               bool checkCanvasPixels,
+               bool checkBitmapPixels,
+               SkCanvas::Config8888 config8888) {
+    SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
+    SkASSERT(!bitmap.isNull());
+    SkASSERT(checkCanvasPixels || checkBitmapPixels);
+
+    int bw = bitmap.width();
+    int bh = bitmap.height();
+
+    SkIRect srcRect = SkIRect::MakeXYWH(x, y, bw, bh);
+    SkIRect clippedSrcRect = DEV_RECT;
+    if (!clippedSrcRect.intersect(srcRect)) {
+        clippedSrcRect.setEmpty();
+    }
+    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)) {
+                if (checkCanvasPixels) {
+                    SkPMColor canvasPixel = getCanvasColor(devx, devy);
+                    bool didPremul;
+                    SkPMColor pmPixel = convertConfig8888ToPMColor(config8888, pixel, &didPremul);
+                    bool check;
+                    REPORTER_ASSERT(reporter, check = checkPixel(pmPixel, canvasPixel, didPremul));
+                    if (!check) {
+                        return false;
+                    }
+                }
+            } else if (checkBitmapPixels) {
+                REPORTER_ASSERT(reporter, getBitmapColor(bx, by, bw, bh) == pixel);
+                if (getBitmapColor(bx, by, bw, bh) != pixel) {
+                    return false;
+                }
+            }
+        }
+    }
+    return true;
+}
+
+enum BitmapInit {
+    kFirstBitmapInit = 0,
+
+    kNoPixels_BitmapInit = kFirstBitmapInit,
+    kTight_BitmapInit,
+    kRowBytes_BitmapInit,
+
+    kBitmapInitCnt
+};
+
+BitmapInit nextBMI(BitmapInit bmi) {
+    int x = bmi;
+    return static_cast<BitmapInit>(++x);
+}
+
+
+void init_bitmap(SkBitmap* bitmap, const SkIRect& rect, BitmapInit init) {
+    int w = rect.width();
+    int h = rect.height();
+    int rowBytes = 0;
+    bool alloc = true;
+    switch (init) {
+        case kNoPixels_BitmapInit:
+            alloc = false;
+        case kTight_BitmapInit:
+            break;
+        case kRowBytes_BitmapInit:
+            rowBytes = w * sizeof(SkPMColor) + 16 * sizeof(SkPMColor);
+            break;
+        default:
+            SkASSERT(0);
+            break;
+    }
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h, rowBytes);
+    if (alloc) {
+        bitmap->allocPixels();
+    }
+}
+
+void ReadPixelsTest(skiatest::Reporter* reporter, GrContext* context) {
+    const SkIRect testRects[] = {
+        // entire thing
+        DEV_RECT,
+        // larger on all sides
+        SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
+        // fully contained
+        SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
+        // outside top left
+        SkIRect::MakeLTRB(-10, -10, -1, -1),
+        // touching top left corner
+        SkIRect::MakeLTRB(-10, -10, 0, 0),
+        // overlapping top left corner
+        SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
+        // overlapping top left and top right corners
+        SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
+        // touching entire top edge
+        SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
+        // overlapping top right corner
+        SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
+        // contained in x, overlapping top edge
+        SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
+        // outside top right corner
+        SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
+        // touching top right corner
+        SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
+        // overlapping top left and bottom left corners
+        SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
+        // touching entire left edge
+        SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
+        // overlapping bottom left corner
+        SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
+        // contained in y, overlapping left edge
+        SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
+        // outside bottom left corner
+        SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
+        // touching bottom left corner
+        SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
+        // overlapping bottom left and bottom right corners
+        SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
+        // touching entire left edge
+        SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
+        // overlapping bottom right corner
+        SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
+        // overlapping top right and bottom right corners
+        SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
+    };
+
+    for (int dtype = 0; dtype < 2; ++dtype) {
+        SkAutoTUnref<SkDevice> device;
+        if (0 == dtype) {
+            device.reset(new SkDevice(SkBitmap::kARGB_8888_Config,
+                                          DEV_W,
+                                          DEV_H,
+                                          false));
+        } else {
+// GPU device known not to work in the fixed pt build.
+#if defined(SK_SCALAR_IS_FIXED) || !SK_SUPPORT_GPU
+            continue;
+#else
+            device.reset(new SkGpuDevice(context,
+                                             SkBitmap::kARGB_8888_Config,
+                                             DEV_W,
+                                             DEV_H));
+#endif
+        }
+        SkCanvas canvas(device);
+        fillCanvas(&canvas);
+
+        static const SkCanvas::Config8888 gReadConfigs[] = {
+            SkCanvas::kNative_Premul_Config8888,
+            SkCanvas::kNative_Unpremul_Config8888,
+/**
+ * There is a bug in Ganesh (http://code.google.com/p/skia/issues/detail?id=438)
+ * that causes the readback of pixels from BGRA canvas to an RGBA bitmap to
+ * fail. This should be removed as soon as the issue above is resolved.
+ */
+#if !defined(SK_BUILD_FOR_ANDROID)
+            SkCanvas::kBGRA_Premul_Config8888,
+            SkCanvas::kBGRA_Unpremul_Config8888,
+#endif
+            SkCanvas::kRGBA_Premul_Config8888,
+            SkCanvas::kRGBA_Unpremul_Config8888,
+        };
+        for (size_t rect = 0; rect < SK_ARRAY_COUNT(testRects); ++rect) {
+            const SkIRect& srcRect = testRects[rect];
+            for (BitmapInit bmi = kFirstBitmapInit;
+                 bmi < kBitmapInitCnt;
+                 bmi = nextBMI(bmi)) {
+                for (size_t c = 0; c < SK_ARRAY_COUNT(gReadConfigs); ++c) {
+                    SkCanvas::Config8888 config8888 = gReadConfigs[c];
+                    SkBitmap bmp;
+                    init_bitmap(&bmp, srcRect, bmi);
+
+                    // if the bitmap has pixels allocated before the readPixels,
+                    // note that and fill them with pattern
+                    bool startsWithPixels = !bmp.isNull();
+                    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,
+                                  success, startsWithPixels, config8888);
+                    } else {
+                        // if we had no pixels beforehand and the readPixels
+                        // failed then our bitmap should still not have pixels
+                        REPORTER_ASSERT(reporter, bmp.isNull());
+                    }
+                }
+                // check the old webkit version of readPixels that clips the
+                // bitmap size
+                SkBitmap wkbmp;
+                bool success = canvas.readPixels(srcRect, &wkbmp);
+                SkIRect clippedRect = DEV_RECT;
+                if (clippedRect.intersect(srcRect)) {
+                    REPORTER_ASSERT(reporter, success);
+                    checkRead(reporter, wkbmp, clippedRect.fLeft,
+                              clippedRect.fTop, true, false,
+                              SkCanvas::kNative_Premul_Config8888);
+                } else {
+                    REPORTER_ASSERT(reporter, !success);
+                }
+            }
+        }
+    }
+}
+}
+
+#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
new file mode 100644
index 0000000..13f2fc4
--- /dev/null
+++ b/tests/Reader32Test.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 "SkReader32.h"
+#include "Test.h"
+
+static void assert_eof(skiatest::Reporter* reporter, const SkReader32& reader) {
+    REPORTER_ASSERT(reporter, reader.eof());
+    REPORTER_ASSERT(reporter, reader.size() == reader.offset());
+    REPORTER_ASSERT(reporter, (const char*)reader.peek() ==
+                    (const char*)reader.base() + reader.size());
+}
+
+static void assert_start(skiatest::Reporter* reporter, const SkReader32& reader) {
+    REPORTER_ASSERT(reporter, 0 == reader.offset());
+    REPORTER_ASSERT(reporter, reader.size() == reader.available());
+    REPORTER_ASSERT(reporter, reader.isAvailable(reader.size()));
+    REPORTER_ASSERT(reporter, !reader.isAvailable(reader.size() + 1));
+    REPORTER_ASSERT(reporter, reader.peek() == reader.base());
+}
+
+static void assert_empty(skiatest::Reporter* reporter, const SkReader32& reader) {
+    REPORTER_ASSERT(reporter, 0 == reader.size());
+    REPORTER_ASSERT(reporter, 0 == reader.offset());
+    REPORTER_ASSERT(reporter, 0 == reader.available());
+    REPORTER_ASSERT(reporter, !reader.isAvailable(1));
+    assert_eof(reporter, reader);
+    assert_start(reporter, reader);
+}
+
+static void Tests(skiatest::Reporter* reporter) {
+    SkReader32 reader;
+    assert_empty(reporter, reader);
+    REPORTER_ASSERT(reporter, NULL == reader.base());
+    REPORTER_ASSERT(reporter, NULL == reader.peek());
+
+    size_t i;
+
+    const int32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+    const SkScalar data2[] = { 0, SK_Scalar1, -SK_Scalar1, SK_Scalar1/2 };
+    const size_t bufsize = sizeof(data) > sizeof(data2) ?
+      sizeof(data) : sizeof(data2);
+    char buffer[bufsize];
+
+    reader.setMemory(data, sizeof(data));
+    for (i = 0; i < SK_ARRAY_COUNT(data); ++i) {
+        REPORTER_ASSERT(reporter, sizeof(data) == reader.size());
+        REPORTER_ASSERT(reporter, i*4 == reader.offset());
+        REPORTER_ASSERT(reporter, (const void*)data == reader.base());
+        REPORTER_ASSERT(reporter, (const void*)&data[i] == reader.peek());
+        REPORTER_ASSERT(reporter, data[i] == reader.readInt());
+    }
+    assert_eof(reporter, reader);
+    reader.rewind();
+    assert_start(reporter, reader);
+    reader.read(buffer, sizeof(data));
+    REPORTER_ASSERT(reporter, !memcmp(data, buffer, sizeof(data)));
+
+    reader.setMemory(data2, sizeof(data2));
+    for (i = 0; i < SK_ARRAY_COUNT(data2); ++i) {
+        REPORTER_ASSERT(reporter, sizeof(data2) == reader.size());
+        REPORTER_ASSERT(reporter, i*4 == reader.offset());
+        REPORTER_ASSERT(reporter, (const void*)data2 == reader.base());
+        REPORTER_ASSERT(reporter, (const void*)&data2[i] == reader.peek());
+        REPORTER_ASSERT(reporter, data2[i] == reader.readScalar());
+    }
+    assert_eof(reporter, reader);
+    reader.rewind();
+    assert_start(reporter, reader);
+    reader.read(buffer, sizeof(data2));
+    REPORTER_ASSERT(reporter, !memcmp(data2, buffer, sizeof(data2)));
+
+    reader.setMemory(NULL, 0);
+    assert_empty(reporter, reader);
+    REPORTER_ASSERT(reporter, NULL == reader.base());
+    REPORTER_ASSERT(reporter, NULL == reader.peek());
+}
+
+#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
new file mode 100644
index 0000000..38a990a
--- /dev/null
+++ b/tests/RefDictTest.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 "Test.h"
+#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;
+
+    REPORTER_ASSERT(reporter, NULL == dict.find(NULL));
+    REPORTER_ASSERT(reporter, NULL == dict.find("foo"));
+    REPORTER_ASSERT(reporter, NULL == dict.find("bar"));
+
+    dict.set("foo", &data0);
+    REPORTER_ASSERT(reporter, &data0 == dict.find("foo"));
+    REPORTER_ASSERT(reporter, 2 == data0.getRefCnt());
+
+    dict.set("foo", &data0);
+    REPORTER_ASSERT(reporter, &data0 == dict.find("foo"));
+    REPORTER_ASSERT(reporter, 2 == data0.getRefCnt());
+
+    dict.set("foo", &data1);
+    REPORTER_ASSERT(reporter, &data1 == dict.find("foo"));
+    REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
+    REPORTER_ASSERT(reporter, 2 == data1.getRefCnt());
+
+    dict.set("foo", NULL);
+    REPORTER_ASSERT(reporter, NULL == dict.find("foo"));
+    REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
+    REPORTER_ASSERT(reporter, 1 == data1.getRefCnt());
+
+    dict.set("foo", &data0);
+    dict.set("bar", &data1);
+    REPORTER_ASSERT(reporter, &data0 == dict.find("foo"));
+    REPORTER_ASSERT(reporter, &data1 == dict.find("bar"));
+    REPORTER_ASSERT(reporter, 2 == data0.getRefCnt());
+    REPORTER_ASSERT(reporter, 2 == data1.getRefCnt());
+
+    dict.set("foo", &data1);
+    REPORTER_ASSERT(reporter, &data1 == dict.find("foo"));
+    REPORTER_ASSERT(reporter, &data1 == dict.find("bar"));
+    REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
+    REPORTER_ASSERT(reporter, 3 == data1.getRefCnt());
+
+    dict.removeAll();
+    REPORTER_ASSERT(reporter, NULL == dict.find("foo"));
+    REPORTER_ASSERT(reporter, NULL == dict.find("bar"));
+    REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
+    REPORTER_ASSERT(reporter, 1 == data1.getRefCnt());
+
+    {
+        SkRefDict d;
+        REPORTER_ASSERT(reporter, NULL == d.find("foo"));
+        REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
+        d.set("foo", &data0);
+        REPORTER_ASSERT(reporter, &data0 == d.find("foo"));
+        REPORTER_ASSERT(reporter, 2 == data0.getRefCnt());
+        // let d go out of scope still with a ref on data0
+    }
+    // be sure d's destructor lowered data0's owner count back to 1
+    REPORTER_ASSERT(reporter, 1 == data0.getRefCnt());
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("RefDict", RefDictTestClass, TestRefDict)
diff --git a/tests/RegionTest.cpp b/tests/RegionTest.cpp
new file mode 100644
index 0000000..8e66502
--- /dev/null
+++ b/tests/RegionTest.cpp
@@ -0,0 +1,191 @@
+
+/*
+ * Copyright 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 "SkRegion.h"
+#include "SkRandom.h"
+
+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;
+    rect->set(rand.nextU() >> shift, rand.nextU() >> shift,
+              rand.nextU() >> shift, rand.nextU() >> shift);
+    rect->sort();
+}
+
+static bool test_rects(const SkIRect rect[], int count) {
+    SkRegion rgn0, rgn1;
+
+    for (int i = 0; i < count; i++) {
+        rgn0.op(rect[i], SkRegion::kUnion_Op);
+    }
+    rgn1.setRects(rect, count);
+
+    if (rgn0 != rgn1) {
+        SkDebugf("\n");
+        for (int i = 0; i < count; i++) {
+            SkDebugf(" { %d, %d, %d, %d },\n",
+                     rect[i].fLeft, rect[i].fTop,
+                     rect[i].fRight, rect[i].fBottom);
+        }
+        SkDebugf("\n");
+        return false;
+    }
+    return true;
+}
+
+static void TestRegion(skiatest::Reporter* reporter) {
+    const SkIRect r2[] = {
+        { 0, 0, 1, 1 },
+        { 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 },
+        { 4, 0, 5, 1 },
+        { 6, 0, 7, 4 },
+    };
+    REPORTER_ASSERT(reporter, test_rects(rects, SK_ARRAY_COUNT(rects)));
+
+    SkRandom rand;
+    for (int i = 0; i < 1000; i++) {
+        SkRegion rgn0, rgn1;
+
+        const int N = 8;
+        SkIRect rect[N];
+        for (int j = 0; j < N; j++) {
+            rand_rect(&rect[j], rand);
+        }
+        REPORTER_ASSERT(reporter, test_rects(rect, N));
+    }
+
+    test_proc(reporter, contains_proc);
+    test_proc(reporter, intersects_proc);
+    test_empties(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Region", RegionTestClass, TestRegion)
diff --git a/tests/ScalarTest.cpp b/tests/ScalarTest.cpp
new file mode 100644
index 0000000..801872e
--- /dev/null
+++ b/tests/ScalarTest.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright 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 "SkFloatingPoint.h"
+#include "SkMath.h"
+#include "SkPoint.h"
+#include "SkRandom.h"
+#include "SkRect.h"
+
+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
+    int exponent = bits << 1 >> 24;
+    return exponent != 0xFF;
+}
+
+static bool isFinite_float(float x) {
+    return SkToBool(sk_float_isfinite(x));
+}
+
+static bool isFinite_mulzero(float x) {
+    float y = x * 0;
+    return y == y;
+}
+
+// return true if the float is finite
+typedef bool (*IsFiniteProc1)(float);
+
+static bool isFinite2_and(float x, float y, IsFiniteProc1 proc) {
+    return proc(x) && proc(y);
+}
+
+static bool isFinite2_mulzeroadd(float x, float y, IsFiniteProc1 proc) {
+    return proc(x * 0 + y * 0);
+}
+
+// return true if both floats are finite
+typedef bool (*IsFiniteProc2)(float, float, IsFiniteProc1);
+
+enum FloatClass {
+    kFinite,
+    kInfinite,
+    kNaN
+};
+
+static void test_floatclass(skiatest::Reporter* reporter, float value, FloatClass fc) {
+    // our sk_float_is... function may return int instead of bool,
+    // hence the double ! to turn it into a bool
+    REPORTER_ASSERT(reporter, !!sk_float_isfinite(value) == (fc == kFinite));
+    REPORTER_ASSERT(reporter, !!sk_float_isinf(value) == (fc == kInfinite));
+    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) {
+    struct Rec {
+        float   fValue;
+        bool    fIsFinite;
+    };
+
+    float max = 3.402823466e+38f;
+    float inf = max * max;
+    float nan = inf * 0;
+
+    test_floatclass(reporter,    0, kFinite);
+    test_floatclass(reporter,  max, kFinite);
+    test_floatclass(reporter, -max, kFinite);
+    test_floatclass(reporter,  inf, kInfinite);
+    test_floatclass(reporter, -inf, kInfinite);
+    test_floatclass(reporter,  nan, kNaN);
+    test_floatclass(reporter, -nan, kNaN);
+
+    const Rec data[] = {
+        {   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[] = {
+        isFinite_int,
+        isFinite_float,
+        isFinite_mulzero
+    };
+    const IsFiniteProc2 gProc2[] = {
+        isFinite2_and,
+        isFinite2_mulzeroadd
+    };
+
+    size_t i, n = SK_ARRAY_COUNT(data);
+
+    for (i = 0; i < n; ++i) {
+        for (size_t k = 0; k < SK_ARRAY_COUNT(gProc1); ++k) {
+            const Rec& rec = data[i];
+            bool finite = gProc1[k](rec.fValue);
+            REPORTER_ASSERT(reporter, rec.fIsFinite == finite);
+        }
+    }
+
+    for (i = 0; i < n; ++i) {
+        const Rec& rec0 = data[i];
+        for (size_t j = 0; j < n; ++j) {
+            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;
+                    REPORTER_ASSERT(reporter, finite2 == finite);
+                }
+            }
+        }
+    }
+
+    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
new file mode 100644
index 0000000..0af25d1
--- /dev/null
+++ b/tests/ShaderOpacityTest.cpp
@@ -0,0 +1,119 @@
+
+/*
+ * Copyright 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 "SkShader.h"
+#include "SkGradientShader.h"
+#include "SkColorShader.h"
+
+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::kClamp_TileMode, SkShader::kClamp_TileMode);
+    REPORTER_ASSERT(reporter, shader);
+    REPORTER_ASSERT(reporter, !shader->isOpaque());
+    shader->unref();
+
+    // From this point on, we have pixels
+    bmp.allocPixels();
+
+    // test 2: not opaque by default
+    shader = SkShader::CreateBitmapShader(bmp,
+        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    REPORTER_ASSERT(reporter, shader);
+    REPORTER_ASSERT(reporter, !shader->isOpaque());
+    shader->unref();
+
+    // test 3: explicitly opaque
+    bmp.setIsOpaque(true);
+    shader = SkShader::CreateBitmapShader(bmp,
+        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    REPORTER_ASSERT(reporter, shader);
+    REPORTER_ASSERT(reporter, shader->isOpaque());
+    shader->unref();
+
+    // test 4: explicitly not opaque
+    bmp.setIsOpaque(false);
+    shader = SkShader::CreateBitmapShader(bmp,
+        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
+    REPORTER_ASSERT(reporter, shader);
+    REPORTER_ASSERT(reporter, !shader->isOpaque());
+    shader->unref();
+
+}
+
+static void test_gradient(skiatest::Reporter* reporter)
+{
+    SkPoint pts[2];
+    pts[0].iset(0, 0);
+    pts[1].iset(1, 0);
+    SkColor colors[2];
+    SkScalar pos[2] = {SkIntToScalar(0), SkIntToScalar(1)};
+    int count = 2;
+    SkShader::TileMode mode = SkShader::kClamp_TileMode;
+
+    // test 1: all opaque
+    colors[0] = SkColorSetARGB(0xFF, 0, 0, 0);
+    colors[1] = SkColorSetARGB(0xFF, 0, 0, 0);
+    SkShader* grad = SkGradientShader::CreateLinear(pts, colors, pos, count,
+                                                    mode);
+    REPORTER_ASSERT(reporter, grad);
+    REPORTER_ASSERT(reporter, grad->isOpaque());
+    grad->unref();
+
+    // test 2: all 0 alpha
+    colors[0] = SkColorSetARGB(0, 0, 0, 0);
+    colors[1] = SkColorSetARGB(0, 0, 0, 0);
+    grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode);
+    REPORTER_ASSERT(reporter, grad);
+    REPORTER_ASSERT(reporter, !grad->isOpaque());
+    grad->unref();
+
+    // test 3: one opaque, one transparent
+    colors[0] = SkColorSetARGB(0xFF, 0, 0, 0);
+    colors[1] = SkColorSetARGB(0x40, 0, 0, 0);
+    grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode);
+    REPORTER_ASSERT(reporter, grad);
+    REPORTER_ASSERT(reporter, !grad->isOpaque());
+    grad->unref();
+
+    // test 4: test 3, swapped
+    colors[0] = SkColorSetARGB(0x40, 0, 0, 0);
+    colors[1] = SkColorSetARGB(0xFF, 0, 0, 0);
+    grad = SkGradientShader::CreateLinear(pts, colors, pos, count, mode);
+    REPORTER_ASSERT(reporter, grad);
+    REPORTER_ASSERT(reporter, !grad->isOpaque());
+    grad->unref();
+}
+
+static void test_color(skiatest::Reporter* reporter)
+{
+    SkColorShader colorShader1(SkColorSetARGB(0,0,0,0));
+    REPORTER_ASSERT(reporter, !colorShader1.isOpaque());
+    SkColorShader colorShader2(SkColorSetARGB(0xFF,0,0,0));
+    REPORTER_ASSERT(reporter, colorShader2.isOpaque());
+    SkColorShader colorShader3(SkColorSetARGB(0x7F,0,0,0));
+    REPORTER_ASSERT(reporter, !colorShader3.isOpaque());
+
+    // 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());
+}
+
+static void test_shader_opacity(skiatest::Reporter* reporter)
+{
+    test_gradient(reporter);
+    test_color(reporter);
+    test_bitmap(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("ShaderOpacity", ShaderOpacityTestClass, test_shader_opacity)
diff --git a/tests/Sk64Test.cpp b/tests/Sk64Test.cpp
new file mode 100644
index 0000000..50b7ec7
--- /dev/null
+++ b/tests/Sk64Test.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 "Test.h"
+#include "SkRandom.h"
+#include <math.h>
+
+struct BoolTable {
+    int8_t  zero, pos, neg, toBool, sign;
+};
+
+static void bool_table_test(skiatest::Reporter* reporter,
+                            const Sk64& a, const BoolTable& table)
+{
+    REPORTER_ASSERT(reporter, a.isZero() != a.nonZero());
+
+    REPORTER_ASSERT(reporter, !a.isZero() == !table.zero);
+    REPORTER_ASSERT(reporter, !a.isPos() == !table.pos);
+    REPORTER_ASSERT(reporter, !a.isNeg() == !table.neg);
+    REPORTER_ASSERT(reporter, a.getSign() == table.sign);
+}
+
+#ifdef SkLONGLONG
+    static SkLONGLONG asLL(const Sk64& a)
+    {
+        return ((SkLONGLONG)a.fHi << 32) | a.fLo;
+    }
+#endif
+
+static void TestSk64(skiatest::Reporter* reporter) {
+    enum BoolTests {
+        kZero_BoolTest,
+        kPos_BoolTest,
+        kNeg_BoolTest
+    };
+    static const BoolTable gBoolTable[] = {
+        { 1, 0, 0, 0, 0 },
+        { 0, 1, 0, 1, 1 },
+        { 0, 0, 1, 1, -1 }
+    };
+
+    Sk64    a, b, c;
+
+    a.fHi = a.fLo = 0;
+    b.set(0);
+    c.setZero();
+    REPORTER_ASSERT(reporter, a == b);
+    REPORTER_ASSERT(reporter, a == c);
+    bool_table_test(reporter, a, gBoolTable[kZero_BoolTest]);
+
+    a.fHi = 0;  a.fLo = 5;
+    b.set(5);
+    REPORTER_ASSERT(reporter, a == b);
+    REPORTER_ASSERT(reporter, a.is32() && a.get32() == 5 && !a.is64());
+    bool_table_test(reporter, a, gBoolTable[kPos_BoolTest]);
+
+    a.fHi = -1; a.fLo = (uint32_t)-5;
+    b.set(-5);
+    REPORTER_ASSERT(reporter, a == b);
+    REPORTER_ASSERT(reporter, a.is32() && a.get32() == -5 && !a.is64());
+    bool_table_test(reporter, a, gBoolTable[kNeg_BoolTest]);
+
+    a.setZero();
+    b.set(6);
+    c.set(-6);
+    REPORTER_ASSERT(reporter, a != b && b != c && a != c);
+    REPORTER_ASSERT(reporter, !(a == b) && !(a == b) && !(a == b));
+    REPORTER_ASSERT(reporter, a < b && b > a && a <= b && b >= a);
+    REPORTER_ASSERT(reporter, c < a && a > c && c <= a && a >= c);
+    REPORTER_ASSERT(reporter, c < b && b > c && c <= b && b >= c);
+
+    // Now test add/sub
+
+    SkRandom    rand;
+    int         i;
+
+    for (i = 0; i < 1000; i++)
+    {
+        int aa = rand.nextS() >> 1;
+        int bb = rand.nextS() >> 1;
+        a.set(aa);
+        b.set(bb);
+        REPORTER_ASSERT(reporter, a.get32() == aa && b.get32() == bb);
+        c = a; c.add(bb);
+        REPORTER_ASSERT(reporter, c.get32() == aa + bb);
+        c = a; c.add(-bb);
+        REPORTER_ASSERT(reporter, c.get32() == aa - bb);
+        c = a; c.add(b);
+        REPORTER_ASSERT(reporter, c.get32() == aa + bb);
+        c = a; c.sub(b);
+        REPORTER_ASSERT(reporter, c.get32() == aa - bb);
+    }
+
+#ifdef SkLONGLONG
+    for (i = 0; i < 1000; i++)
+    {
+        rand.next64(&a); //a.fHi >>= 1; // avoid overflow
+        rand.next64(&b); //b.fHi >>= 1; // avoid overflow
+
+        if (!(i & 3))   // want to explicitly test these cases
+        {
+            a.fLo = 0;
+            b.fLo = 0;
+        }
+        else if (!(i & 7))  // want to explicitly test these cases
+        {
+            a.fHi = 0;
+            b.fHi = 0;
+        }
+
+        SkLONGLONG aa = asLL(a);
+        SkLONGLONG bb = asLL(b);
+
+        REPORTER_ASSERT(reporter, (a < b) == (aa < bb));
+        REPORTER_ASSERT(reporter, (a <= b) == (aa <= bb));
+        REPORTER_ASSERT(reporter, (a > b) == (aa > bb));
+        REPORTER_ASSERT(reporter, (a >= b) == (aa >= bb));
+        REPORTER_ASSERT(reporter, (a == b) == (aa == bb));
+        REPORTER_ASSERT(reporter, (a != b) == (aa != bb));
+
+        c = a; c.add(b);
+        REPORTER_ASSERT(reporter, asLL(c) == aa + bb);
+        c = a; c.sub(b);
+        REPORTER_ASSERT(reporter, asLL(c) == aa - bb);
+        c = a; c.rsub(b);
+        REPORTER_ASSERT(reporter, asLL(c) == bb - aa);
+        c = a; c.negate();
+        REPORTER_ASSERT(reporter, asLL(c) == -aa);
+
+        int bits = rand.nextU() & 63;
+        c = a; c.shiftLeft(bits);
+        REPORTER_ASSERT(reporter, asLL(c) == (aa << bits));
+        c = a; c.shiftRight(bits);
+        REPORTER_ASSERT(reporter, asLL(c) == (aa >> bits));
+        c = a; c.roundRight(bits);
+
+        SkLONGLONG tmp;
+
+        tmp = aa;
+        if (bits > 0)
+            tmp += (SkLONGLONG)1 << (bits - 1);
+        REPORTER_ASSERT(reporter, asLL(c) == (tmp >> bits));
+
+        c.setMul(a.fHi, b.fHi);
+        tmp = (SkLONGLONG)a.fHi * b.fHi;
+        REPORTER_ASSERT(reporter, asLL(c) == tmp);
+    }
+
+
+    for (i = 0; i < 100000; i++)
+    {
+        Sk64    wide;
+        int32_t denom = rand.nextS();
+
+        while (denom == 0)
+            denom = rand.nextS();
+        wide.setMul(rand.nextS(), rand.nextS());
+        SkLONGLONG check = wide.getLongLong();
+
+        wide.div(denom, Sk64::kTrunc_DivOption);
+        check /= denom;
+        SkLONGLONG w = wide.getLongLong();
+
+        REPORTER_ASSERT(reporter, check == w);
+
+        wide.setMul(rand.nextS(), rand.nextS());
+        wide.abs();
+        denom = wide.getSqrt();
+        int32_t ck = (int32_t)sqrt((double)wide.getLongLong());
+        int diff = denom - ck;
+        REPORTER_ASSERT(reporter, SkAbs32(diff) <= 1);
+
+        wide.setMul(rand.nextS(), rand.nextS());
+        Sk64    dwide;
+        dwide.setMul(rand.nextS(), rand.nextS());
+        SkFixed fixdiv = wide.getFixedDiv(dwide);
+        double dnumer = (double)wide.getLongLong();
+        double ddenom = (double)dwide.getLongLong();
+        double ddiv = dnumer / ddenom;
+        SkFixed dfixdiv;
+        if (ddiv >= (double)SK_MaxS32 / (double)SK_Fixed1)
+            dfixdiv = SK_MaxS32;
+        else if (ddiv <= -(double)SK_MaxS32 / (double)SK_Fixed1)
+            dfixdiv = SK_MinS32;
+        else
+            dfixdiv = SkFloatToFixed(dnumer / ddenom);
+        diff = fixdiv - dfixdiv;
+
+        if (SkAbs32(diff) > 1) {
+            SkDebugf(" %d === numer %g denom %g div %g xdiv %x fxdiv %x\n",
+                     i, dnumer, ddenom, ddiv, dfixdiv, fixdiv);
+        }
+        REPORTER_ASSERT(reporter, SkAbs32(diff) <= 1);
+    }
+#endif
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Sk64", Sk64TestClass, TestSk64)
diff --git a/tests/SortTest.cpp b/tests/SortTest.cpp
new file mode 100644
index 0000000..65c4863
--- /dev/null
+++ b/tests/SortTest.cpp
@@ -0,0 +1,87 @@
+
+/*
+ * Copyright 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 "SkRandom.h"
+#include "SkChecksum.h"
+#include "SkTSort.h"
+
+// assert that as we change values (from 0 to non-zero) in our buffer, we
+// get a different value
+static void test_checksum(skiatest::Reporter* reporter, size_t size) {
+    SkAutoMalloc storage(size);
+    uint32_t*    ptr = (uint32_t*)storage.get();
+    char*        cptr = (char*)ptr;
+
+    sk_bzero(ptr, size);
+    uint32_t prev = 0;
+    for (size_t i = 0; i < size; ++i) {
+        cptr[i] = 0x5B; // just need some non-zero value here
+        uint32_t curr = SkChecksum::Compute(ptr, size);
+        REPORTER_ASSERT(reporter, prev != curr);
+        prev = curr;
+    }
+}
+
+static void test_checksum(skiatest::Reporter* reporter) {
+    REPORTER_ASSERT(reporter, SkChecksum::Compute(NULL, 0) == 0);
+
+    for (size_t size = 4; size <= 1000; size += 4) {
+        test_checksum(reporter, size);
+    }
+}
+
+extern "C" {
+    static int compare_int(const void* a, const void* b) {
+        return *(const int*)a - *(const int*)b;
+    }
+}
+
+static void rand_array(SkRandom& rand, int array[], int n) {
+    for (int j = 0; j < n; j++) {
+        array[j] = rand.nextS() & 0xFF;
+    }
+}
+
+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]) {
+            SkString str;
+           str.printf("%sSort [%d] failed %d %d", label, n,
+                      array[j-1], array[j]);
+            reporter->reportFailed(str);
+        }
+    }
+}
+
+static void TestSort(skiatest::Reporter* reporter) {
+    int         array[500];
+    SkRandom    rand;
+
+    for (int i = 0; i < 10000; i++) {
+        int count = rand.nextRangeU(1, SK_ARRAY_COUNT(array));
+
+        rand_array(rand, array, count);
+        SkTHeapSort<int>(array, count);
+        check_sort(reporter, "Heap", array, count);
+
+        rand_array(rand, array, count);
+        SkTQSort<int>(array, array + count - 1);
+        check_sort(reporter, "Quick", array, count);
+    }
+    if (false) { // avoid bit rot, suppress warning
+        compare_int(array, array);
+    }
+
+    test_checksum(reporter);
+}
+
+// need tests for SkStrSearch
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Sort", SortTestClass, TestSort)
diff --git a/tests/SrcOverTest.cpp b/tests/SrcOverTest.cpp
new file mode 100644
index 0000000..d1e65a9
--- /dev/null
+++ b/tests/SrcOverTest.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 "Test.h"
+#include "SkColorPriv.h"
+#include "SkXfermode.h"
+
+// our std SkAlpha255To256
+static int test_srcover0(unsigned dst, unsigned alpha) {
+    return alpha + SkAlphaMul(dst, SkAlpha255To256(255 - alpha));
+}
+
+// faster hack +1
+static int test_srcover1(unsigned dst, unsigned alpha) {
+    return alpha + SkAlphaMul(dst, 256 - alpha);
+}
+
+// slower "correct"
+static int test_srcover2(unsigned dst, unsigned alpha) {
+    return alpha + SkMulDiv255Round(dst, 255 - alpha);
+}
+
+static void test_srcover_hack(skiatest::Reporter* reporter) {
+    /*  Here's the idea. Can we ensure that when we blend on top of an opaque
+        dst, that the result always stay's opaque (i.e. exactly 255)?
+     */
+
+    unsigned i;
+    int opaqueCounter0 = 0;
+    int opaqueCounter1 = 0;
+    int opaqueCounter2 = 0;
+    for (i = 0; i <= 255; i++) {
+        unsigned result0 = test_srcover0(0xFF, i);
+        unsigned result1 = test_srcover1(0xFF, i);
+        unsigned result2 = test_srcover2(0xFF, i);
+        opaqueCounter0 += (result0 == 0xFF);
+        opaqueCounter1 += (result1 == 0xFF);
+        opaqueCounter2 += (result2 == 0xFF);
+    }
+#if 0
+    SkDebugf("---- opaque test: [%d %d %d]\n",
+             opaqueCounter0, opaqueCounter1, opaqueCounter2);
+#endif
+    // we acknowledge that technique0 does not always return opaque
+    REPORTER_ASSERT(reporter, opaqueCounter0 == 256);
+    REPORTER_ASSERT(reporter, opaqueCounter1 == 256);
+    REPORTER_ASSERT(reporter, opaqueCounter2 == 256);
+
+    // Now ensure that we never over/underflow a byte
+    for (i = 0; i <= 255; i++) {
+        for (unsigned dst = 0; dst <= 255; dst++) {
+            unsigned r0 = test_srcover0(dst, i);
+            unsigned r1 = test_srcover1(dst, i);
+            unsigned r2 = test_srcover2(dst, i);
+            unsigned max = SkMax32(dst, i);
+            // ignore the known failure
+            if (dst != 255) {
+                REPORTER_ASSERT(reporter, r0 <= 255 && r0 >= max);
+            }
+            REPORTER_ASSERT(reporter, r1 <= 255 && r1 >= max);
+            REPORTER_ASSERT(reporter, r2 <= 255 && r2 >= max);
+
+#if 0
+            // this shows where r1 (faster) differs from r2 (more exact)
+            if (r1 != r2) {
+                SkDebugf("--- dst=%d i=%d r1=%d r2=%d exact=%g\n",
+                         dst, i, r1, r2, i + dst - dst*i/255.0f);
+            }
+#endif
+        }
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("SrcOver", SrcOverTestClass, test_srcover_hack)
diff --git a/tests/StreamTest.cpp b/tests/StreamTest.cpp
new file mode 100644
index 0000000..b5e3cd9
--- /dev/null
+++ b/tests/StreamTest.cpp
@@ -0,0 +1,140 @@
+
+/*
+ * Copyright 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 "SkRandom.h"
+#include "SkStream.h"
+#include "SkData.h"
+
+#define MAX_SIZE    (256 * 1024)
+
+static void random_fill(SkRandom& rand, void* buffer, size_t size) {
+    char* p = (char*)buffer;
+    char* stop = p + size;
+    while (p < stop) {
+        *p++ = (char)(rand.nextU() >> 8);
+    }
+}
+
+static void test_buffer(skiatest::Reporter* reporter) {
+    SkRandom rand;
+    SkAutoMalloc am(MAX_SIZE * 2);
+    char* storage = (char*)am.get();
+    char* storage2 = storage + MAX_SIZE;
+
+    random_fill(rand, storage, MAX_SIZE);
+
+    for (int sizeTimes = 0; sizeTimes < 100; sizeTimes++) {
+        int size = rand.nextU() % MAX_SIZE;
+        if (size == 0) {
+            size = MAX_SIZE;
+        }
+        for (int times = 0; times < 100; times++) {
+            int bufferSize = 1 + (rand.nextU() & 0xFFFF);
+            SkMemoryStream mstream(storage, size);
+            SkBufferStream bstream(&mstream, bufferSize);
+
+            int bytesRead = 0;
+            while (bytesRead < size) {
+                int s = 17 + (rand.nextU() & 0xFFFF);
+                int ss = bstream.read(storage2, s);
+                REPORTER_ASSERT(reporter, ss > 0 && ss <= s);
+                REPORTER_ASSERT(reporter, bytesRead + ss <= size);
+                REPORTER_ASSERT(reporter,
+                                memcmp(storage + bytesRead, storage2, ss) == 0);
+                bytesRead += ss;
+            }
+            REPORTER_ASSERT(reporter, bytesRead == size);
+        }
+    }
+}
+
+static void TestRStream(skiatest::Reporter* reporter) {
+    static const char s[] =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+    char            copy[sizeof(s)];
+    SkRandom        rand;
+
+    for (int i = 0; i < 65; i++) {
+        char*           copyPtr = copy;
+        SkMemoryStream  mem(s, sizeof(s));
+        SkBufferStream  buff(&mem, i);
+
+        do {
+            copyPtr += buff.read(copyPtr, rand.nextU() & 15);
+        } while (copyPtr < copy + sizeof(s));
+        REPORTER_ASSERT(reporter, copyPtr == copy + sizeof(s));
+        REPORTER_ASSERT(reporter, memcmp(s, copy, sizeof(s)) == 0);
+    }
+    test_buffer(reporter);
+}
+
+static void TestWStream(skiatest::Reporter* reporter) {
+    SkDynamicMemoryWStream  ds;
+    const char s[] = "abcdefghijklmnopqrstuvwxyz";
+    int i;
+    for (i = 0; i < 100; i++) {
+        REPORTER_ASSERT(reporter, ds.write(s, 26));
+    }
+    REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
+    char* dst = new char[100 * 26 + 1];
+    dst[100*26] = '*';
+    ds.copyTo(dst);
+    REPORTER_ASSERT(reporter, dst[100*26] == '*');
+//     char* p = dst;
+    for (i = 0; i < 100; i++) {
+        REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0);
+    }
+
+    {
+        SkData* data = ds.copyToData();
+        REPORTER_ASSERT(reporter, 100 * 26 == data->size());
+        REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0);
+        data->unref();
+    }
+    delete[] dst;
+}
+
+static void TestPackedUInt(skiatest::Reporter* reporter) {
+    // we know that packeduint tries to write 1, 2 or 4 bytes for the length,
+    // so we test values around each of those transitions (and a few others)
+    const size_t sizes[] = {
+        0, 1, 2, 0xFC, 0xFD, 0xFE, 0xFF, 0x100, 0x101, 32767, 32768, 32769,
+        0xFFFD, 0xFFFE, 0xFFFF, 0x10000, 0x10001,
+        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();
+        if (sizes[i] != n) {
+            SkDebugf("-- %d: sizes:%x n:%x\n", i, sizes[i], n);
+        }
+        REPORTER_ASSERT(reporter, sizes[i] == n);
+    }
+}
+
+static void TestStreams(skiatest::Reporter* reporter) {
+    TestRStream(reporter);
+    TestWStream(reporter);
+    TestPackedUInt(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Stream", StreamTestClass, TestStreams)
diff --git a/tests/StringTest.cpp b/tests/StringTest.cpp
new file mode 100644
index 0000000..5ae718f
--- /dev/null
+++ b/tests/StringTest.cpp
@@ -0,0 +1,155 @@
+
+/*
+ * Copyright 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 "SkString.h"
+#include <stdarg.h>
+
+
+// Windows vsnprintf doesn't 0-terminate safely), but is so far
+// encapsulated in SkString that we can't test it directly.
+
+#ifdef SK_BUILD_FOR_WIN
+    #define VSNPRINTF(buffer, size, format, args)   \
+        vsnprintf_s(buffer, size, _TRUNCATE, format, args)
+#else
+    #define VSNPRINTF   vsnprintf
+#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)
+
+static void printfAnalog(char* buffer, int size, const char format[], ...) {
+    ARGS_TO_BUFFER(format, buffer, size);
+}
+
+
+
+static void TestString(skiatest::Reporter* reporter) {
+    SkString    a;
+    SkString    b((size_t)0);
+    SkString    c("");
+    SkString    d(NULL, 0);
+
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    REPORTER_ASSERT(reporter, a == b && a == c && a == d);
+
+    a.set("hello");
+    b.set("hellox", 5);
+    c.set(a);
+    d.resize(5);
+    memcpy(d.writable_str(), "helloz", 5);
+
+    REPORTER_ASSERT(reporter, !a.isEmpty());
+    REPORTER_ASSERT(reporter, a.size() == 5);
+    REPORTER_ASSERT(reporter, a == b && a == c && a == d);
+    REPORTER_ASSERT(reporter, a.equals("hello", 5));
+    REPORTER_ASSERT(reporter, a.equals("hello"));
+    REPORTER_ASSERT(reporter, !a.equals("help"));
+
+    REPORTER_ASSERT(reporter,  a.startsWith("hell"));
+    REPORTER_ASSERT(reporter, !a.startsWith( "ell"));
+    REPORTER_ASSERT(reporter,  a.startsWith(""));
+    REPORTER_ASSERT(reporter,  a.endsWith("llo"));
+    REPORTER_ASSERT(reporter, !a.endsWith("ll" ));
+    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(""));
+
+    SkString    e(a);
+    SkString    f("hello");
+    SkString    g("helloz", 5);
+
+    REPORTER_ASSERT(reporter, a == e && a == f && a == g);
+
+    b.set("world");
+    c = b;
+    REPORTER_ASSERT(reporter, a != b && a != c && b == c);
+
+    a.append(" world");
+    e.append("worldz", 5);
+    e.insert(5, " ");
+    f.set("world");
+    f.prepend("hello ");
+    REPORTER_ASSERT(reporter, a.equals("hello world") && a == e && a == f);
+
+    a.reset();
+    b.resize(0);
+    REPORTER_ASSERT(reporter, a.isEmpty() && b.isEmpty() && a == b);
+
+    a.set("a");
+    a.set("ab");
+    a.set("abc");
+    a.set("abcd");
+
+    a.set("");
+    a.appendS64(72036854775808LL, 0);
+    REPORTER_ASSERT(reporter, a.equals("72036854775808"));
+
+    a.set("");
+    a.appendS64(-1844674407370LL, 0);
+    REPORTER_ASSERT(reporter, a.equals("-1844674407370"));
+
+    a.set("");
+    a.appendS64(73709551616LL, 15);
+    REPORTER_ASSERT(reporter, a.equals("000073709551616"));
+
+    a.set("");
+    a.appendS64(-429496729612LL, 15);
+    REPORTER_ASSERT(reporter, a.equals("-000429496729612"));
+
+    static const struct {
+        SkScalar    fValue;
+        const char* fString;
+    } gRec[] = {
+        { 0,            "0" },
+        { SK_Scalar1,   "1" },
+        { -SK_Scalar1,  "-1" },
+        { SK_Scalar1/2, "0.5" },
+#ifdef SK_SCALAR_IS_FLOAT
+  #ifdef SK_BUILD_FOR_WIN
+        { 3.4028234e38f,   "3.4028235e+038" },
+        { -3.4028234e38f, "-3.4028235e+038" },
+  #else
+        { 3.4028234e38f,   "3.4028235e+38" },
+        { -3.4028234e38f, "-3.4028235e+38" },
+  #endif
+#endif
+    };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+        a.reset();
+        a.appendScalar(gRec[i].fValue);
+        REPORTER_ASSERT(reporter, a.size() <= SkStrAppendScalar_MaxSize);
+//        SkDebugf(" received <%s> expected <%s>\n", a.c_str(), gRec[i].fString);
+        REPORTER_ASSERT(reporter, a.equals(gRec[i].fString));
+    }
+
+    REPORTER_ASSERT(reporter, SkStringPrintf("%i", 0).equals("0"));
+
+    char buffer [40];
+    memset(buffer, 'a', 40);
+    REPORTER_ASSERT(reporter, buffer[18] == 'a');
+    REPORTER_ASSERT(reporter, buffer[19] == 'a');
+    REPORTER_ASSERT(reporter, buffer[20] == 'a');
+    printfAnalog(buffer, 20, "%30d", 0);
+    REPORTER_ASSERT(reporter, buffer[18] == ' ');
+    REPORTER_ASSERT(reporter, buffer[19] == 0);
+    REPORTER_ASSERT(reporter, buffer[20] == 'a');
+
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("String", StringTestClass, TestString)
diff --git a/tests/TDLinkedListTest.cpp b/tests/TDLinkedListTest.cpp
new file mode 100644
index 0000000..8df39b8
--- /dev/null
+++ b/tests/TDLinkedListTest.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 "Test.h"
+#include "SkTDLinkedList.h"
+
+class ListElement {
+public:
+    ListElement(int id) : fID(id) {
+    }
+
+    int fID;
+
+private:
+    SK_DEFINE_DLINKEDLIST_INTERFACE(ListElement);
+};
+
+static void CheckList(const SkTDLinkedList<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
+    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 TestTDLinkedList(skiatest::Reporter* reporter) {
+    SkTDLinkedList<ListElement> list;
+    ListElement elements[4] = {
+        ListElement(0),
+        ListElement(1),
+        ListElement(2),
+        ListElement(3),
+    };
+
+    // list should be empty to start with
+    CheckList(list, reporter, true, 0, false, false, false, false, elements);
+
+    list.addToHead(&elements[0]);
+
+    CheckList(list, reporter, false, 1, true, false, false, false, elements);
+
+    list.addToHead(&elements[1]);
+    list.addToHead(&elements[2]);
+    list.addToHead(&elements[3]);
+
+    CheckList(list, reporter, false, 4, true, true, true, true, elements);
+
+    // test out iterators
+    SkTDLinkedList<ListElement>::Iter iter;
+
+    ListElement* cur = iter.init(list, SkTDLinkedList<ListElement>::Iter::kHead_IterStart);
+    for (int i = 0; NULL != cur; ++i, cur = iter.next()) {
+        REPORTER_ASSERT(reporter, cur->fID == 3-i);
+    }
+
+    cur = iter.init(list, SkTDLinkedList<ListElement>::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]);
+
+    CheckList(list, reporter, false, 1, false, false, true, false, elements);
+
+    // remove last element
+    list.remove(&elements[2]);
+
+    // list should be empty again
+    CheckList(list, reporter, true, 0, false, false, false, false, elements);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("TDLinkedList", TestTDLinkedListClass, TestTDLinkedList)
diff --git a/tests/TLSTest.cpp b/tests/TLSTest.cpp
new file mode 100644
index 0000000..5fa0903
--- /dev/null
+++ b/tests/TLSTest.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 "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_measuretext(skiatest::Reporter* reporter) {
+    SkThread* threads[8];
+    int N = SK_ARRAY_COUNT(threads);
+    int i;
+
+    for (i = 0; i < N; ++i) {
+        threads[i] = new SkThread(thread_main);
+    }
+
+    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 void TestTLS(skiatest::Reporter* reporter) {
+    test_measuretext(reporter);
+}
+
+#include "TestClassDef.h"
+// 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')
+// DEFINE_TESTCLASS("TLS", TLSClass, TestTLS)
diff --git a/tests/Test.cpp b/tests/Test.cpp
new file mode 100644
index 0000000..deb8348
--- /dev/null
+++ b/tests/Test.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 "Test.h"
+
+#include "SkTLazy.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "gl/SkNativeGLContext.h"
+#else
+class GrContext;
+#endif
+
+SK_DEFINE_INST_COUNT(skiatest::Reporter)
+
+using namespace skiatest;
+
+Reporter::Reporter() {
+    this->resetReporting();
+}
+
+void Reporter::resetReporting() {
+    fCurrTest = NULL;
+    fTestCount = 0;
+    sk_bzero(fResultCount, sizeof(fResultCount));
+}
+
+void Reporter::startTest(Test* test) {
+    SkASSERT(NULL == fCurrTest);
+    fCurrTest = test;
+    this->onStart(test);
+    fTestCount += 1;
+    fCurrTestSuccess = true;    // we're optimistic
+}
+
+void Reporter::report(const char desc[], Result result) {
+    if (NULL == desc) {
+        desc = "<no description>";
+    }
+    this->onReport(desc, result);
+    fResultCount[result] += 1;
+    if (kFailed == result) {
+        fCurrTestSuccess = false;
+    }
+}
+
+void Reporter::endTest(Test* test) {
+    SkASSERT(test == fCurrTest);
+    this->onEnd(test);
+    fCurrTest = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Test::Test() : fReporter(NULL) {}
+
+Test::~Test() {
+    SkSafeUnref(fReporter);
+}
+
+void Test::setReporter(Reporter* r) {
+    SkRefCnt_SafeAssign(fReporter, r);
+}
+
+const char* Test::getName() {
+    if (fName.size() == 0) {
+        this->onGetName(&fName);
+    }
+    return fName.c_str();
+}
+
+bool Test::run() {
+    fReporter->startTest(this);
+    this->onRun(fReporter);
+    fReporter->endTest(this);
+    return fReporter->getCurrSuccess();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+GrContext* GpuTest::GetContext() {
+#if SK_SUPPORT_GPU
+    // preserve this order, we want gGrContext destroyed after gEGLContext
+    static SkTLazy<SkNativeGLContext> gGLContext;
+    static SkAutoTUnref<GrContext> gGrContext;
+
+    if (NULL == gGrContext.get()) {
+        gGLContext.init();
+        if (gGLContext.get()->init(800, 600)) {
+            GrPlatform3DContext ctx = reinterpret_cast<GrPlatform3DContext>(gGLContext.get()->gl());
+            gGrContext.reset(GrContext::Create(kOpenGL_Shaders_GrEngine, ctx));
+        }
+    }
+    if (gGLContext.get()) {
+        gGLContext.get()->makeCurrent();
+    }
+    return gGrContext.get();
+#else
+    return NULL;
+#endif
+}
+
diff --git a/tests/Test.h b/tests/Test.h
new file mode 100644
index 0000000..f87a7a0
--- /dev/null
+++ b/tests/Test.h
@@ -0,0 +1,134 @@
+
+/*
+ * 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 skiatest_Test_DEFINED
+#define skiatest_Test_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkString.h"
+#include "SkTRegistry.h"
+
+class GrContext;
+class SkGLContext;
+
+namespace skiatest {
+
+    class Test;
+
+    class Reporter : public SkRefCnt {
+    public:
+        SK_DECLARE_INST_COUNT(Reporter)
+        Reporter();
+
+        enum Result {
+            kPassed,    // must begin with 0
+            kFailed,
+            /////
+            kLastResult = kFailed
+        };
+
+        void resetReporting();
+        int countTests() const { return fTestCount; }
+        int countResults(Result r) {
+            SkASSERT((unsigned)r <= kLastResult);
+            return fResultCount[r];
+        }
+
+        void startTest(Test*);
+        void report(const char testDesc[], Result);
+        void endTest(Test*);
+
+        // helpers for tests
+        void assertTrue(bool cond, const char desc[]) {
+            if (!cond) {
+                this->report(desc, kFailed);
+            }
+        }
+        void assertFalse(bool cond, const char desc[]) {
+            if (cond) {
+                this->report(desc, kFailed);
+            }
+        }
+        void reportFailed(const char desc[]) {
+            this->report(desc, kFailed);
+        }
+        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) {}
+        virtual void onEnd(Test*) {}
+
+    private:
+        Test* fCurrTest;
+        int fTestCount;
+        int fResultCount[kLastResult+1];
+        bool fCurrTestSuccess;
+
+        typedef SkRefCnt INHERITED;
+    };
+
+    class Test {
+    public:
+        Test();
+        virtual ~Test();
+
+        Reporter* getReporter() const { return fReporter; }
+        void setReporter(Reporter*);
+
+        const char* getName();
+        bool run(); // returns true on success
+
+    protected:
+        virtual void onGetName(SkString*) = 0;
+        virtual void onRun(Reporter*) = 0;
+
+    private:
+        Reporter*   fReporter;
+        SkString    fName;
+    };
+
+    class GpuTest : public Test{
+    public:
+        GpuTest() : Test() {
+            fContext = GetContext();
+        }
+        static GrContext* GetContext();
+    protected:
+        GrContext* fContext;
+    private:
+    };
+
+    typedef SkTRegistry<Test*, void*> TestRegistry;
+}
+
+#define REPORTER_ASSERT(r, cond)                                        \
+    do {                                                                \
+        if (!(cond)) {                                                  \
+            SkString desc;                                              \
+            desc.printf("%s:%d: %s", __FILE__, __LINE__, #cond);        \
+            r->reportFailed(desc);                                      \
+        }                                                               \
+    } while(0)
+
+#define REPORTER_ASSERT_MESSAGE(r, cond, message)                            \
+    do {                                                                     \
+        if (!(cond)) {                                                       \
+            SkString desc;                                                   \
+            desc.printf("%s %s:%d: %s", message, __FILE__, __LINE__, #cond); \
+            r->reportFailed(desc);                                           \
+        }                                                                    \
+    } while(0)
+
+
+#endif
diff --git a/tests/TestClassDef.h b/tests/TestClassDef.h
new file mode 100644
index 0000000..ffef2a1
--- /dev/null
+++ b/tests/TestClassDef.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.
+ */
+/*  This file is meant to be included by .cpp files, so it can spew out a
+    customized class + global definition.
+
+    e.g.
+    #include "TestClassDef.h"
+    DEFINE_TESTCLASS("MyTest", MyTestClass, MyTestFunction)
+
+    where MyTestFunction is declared as
+
+        void MyTestFunction(skiatest::Reporter*)
+*/
+
+#define DEFINE_TESTCLASS(uiname, classname, function)                       \
+    namespace skiatest {                                                    \
+        class classname : public Test {                                     \
+        public:                                                             \
+            static Test* Factory(void*) { return SkNEW(classname); }        \
+        protected:                                                          \
+            virtual void onGetName(SkString* name) { name->set(uiname); }   \
+            virtual void onRun(Reporter* reporter) { function(reporter); }  \
+        };                                                                  \
+        static TestRegistry gReg(classname::Factory);                       \
+    }
+
+#define DEFINE_GPUTESTCLASS(uiname, classname, function)                    \
+    namespace skiatest {                                                    \
+        class classname : public GpuTest {                                  \
+        public:                                                             \
+            static Test* Factory(void*) { return SkNEW(classname); }        \
+        protected:                                                          \
+            virtual void onGetName(SkString* name) { name->set(uiname); }   \
+            virtual void onRun(Reporter* reporter) {                        \
+                if (fContext) {                                             \
+                    function(reporter, fContext);                           \
+                }                                                           \
+            }                                                               \
+        };                                                                  \
+        static TestRegistry gReg(classname::Factory);                       \
+    }
diff --git a/tests/TestSize.cpp b/tests/TestSize.cpp
new file mode 100644
index 0000000..6a9a887
--- /dev/null
+++ b/tests/TestSize.cpp
@@ -0,0 +1,67 @@
+
+/*
+ * Copyright 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 "SkSize.h"
+
+static void TestISize(skiatest::Reporter* reporter) {
+    SkISize  a, b;
+
+    a.set(0, 0);
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    a.set(5, -5);
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    a.clampNegToZero();
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    b.set(5, 0);
+    REPORTER_ASSERT(reporter, a == b);
+
+    a.set(3, 5);
+    REPORTER_ASSERT(reporter, !a.isEmpty());
+    b = a;
+    REPORTER_ASSERT(reporter, !b.isEmpty());
+    REPORTER_ASSERT(reporter, a == b);
+    REPORTER_ASSERT(reporter, !(a != b));
+    REPORTER_ASSERT(reporter,
+                    a.fWidth == b.fWidth && a.fHeight == b.fHeight);
+}
+
+static void TestSize(skiatest::Reporter* reporter) {
+    TestISize(reporter);
+
+    SkSize a, b;
+    int ix = 5;
+    int iy = 3;
+    SkScalar x = SkIntToScalar(ix);
+    SkScalar y = SkIntToScalar(iy);
+
+    a.set(0, 0);
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    a.set(x, -x);
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    a.clampNegToZero();
+    REPORTER_ASSERT(reporter, a.isEmpty());
+    b.set(x, 0);
+    REPORTER_ASSERT(reporter, a == b);
+
+    a.set(y, x);
+    REPORTER_ASSERT(reporter, !a.isEmpty());
+    b = a;
+    REPORTER_ASSERT(reporter, !b.isEmpty());
+    REPORTER_ASSERT(reporter, a == b);
+    REPORTER_ASSERT(reporter, !(a != b));
+    REPORTER_ASSERT(reporter,
+                    a.fWidth == b.fWidth && a.fHeight == b.fHeight);
+
+    SkISize ia;
+    ia.set(ix, iy);
+    a.set(x, y);
+    REPORTER_ASSERT(reporter, a.toRound() == ia);
+};
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Size", TestSizeClass, TestSize)
diff --git a/tests/TestXCode/Tests.xcodeproj/project.pbxproj b/tests/TestXCode/Tests.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..af9dfd7
--- /dev/null
+++ b/tests/TestXCode/Tests.xcodeproj/project.pbxproj
@@ -0,0 +1,371 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 44;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		00857F860F56F8EE0078BE26 /* libcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00857F730F56F71B0078BE26 /* libcore.a */; };
+		00857F920F56F9170078BE26 /* libmaccore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00857F910F56F9150078BE26 /* libmaccore.a */; };
+		00857FAA0F56F9620078BE26 /* Test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00857FA80F56F9620078BE26 /* Test.cpp */; };
+		00857FAB0F56F9620078BE26 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00857FA90F56F9620078BE26 /* main.cpp */; };
+		00857FB70F56FD340078BE26 /* MathTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00857FB60F56FD340078BE26 /* MathTest.cpp */; };
+		008634DC0F579B7A0044DA64 /* PackBitsTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 008634DB0F579B7A0044DA64 /* PackBitsTest.cpp */; };
+		008634F10F579E410044DA64 /* MatrixTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 008634F00F579E410044DA64 /* MatrixTest.cpp */; };
+		0086350F0F57A3140044DA64 /* UtilsTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0086350E0F57A3140044DA64 /* UtilsTest.cpp */; };
+		009CC7840F5DAE2B002185BE /* SrcOverTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 009CC7830F5DAE2B002185BE /* SrcOverTest.cpp */; };
+		009CC7880F5DAF16002185BE /* ClipCubicTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 009CC7870F5DAF16002185BE /* ClipCubicTest.cpp */; };
+		00A9BF860F584CF30091AD2D /* Sk64Test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A9BF850F584CF30091AD2D /* Sk64Test.cpp */; };
+		00A9BFA30F584E150091AD2D /* StringTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A9BFA20F584E150091AD2D /* StringTest.cpp */; };
+		00A9BFBC0F5851570091AD2D /* GeometryTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 00A9BFBB0F5851570091AD2D /* GeometryTest.cpp */; };
+		276D93080F5B9FEA0081B3B9 /* PathTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276D93070F5B9FEA0081B3B9 /* PathTest.cpp */; };
+		27C9A9C70F6222EE00E9C93D /* PathMeasureTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27C9A9C40F6222EE00E9C93D /* PathMeasureTest.cpp */; };
+		27C9A9C80F6222EE00E9C93D /* SortTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27C9A9C50F6222EE00E9C93D /* SortTest.cpp */; };
+		27C9A9C90F6222EE00E9C93D /* StreamTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27C9A9C60F6222EE00E9C93D /* StreamTest.cpp */; };
+		8DD76F6A0486A84900D96B5E /* Tests.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859E8B029090EE04C91782 /* Tests.1 */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		00857F720F56F71B0078BE26 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 00857F6B0F56F71B0078BE26 /* core.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = D2AAC046055464E500DB518D;
+			remoteInfo = core;
+		};
+		00857F900F56F9150078BE26 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 00857F890F56F9150078BE26 /* maccore.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = D2AAC046055464E500DB518D;
+			remoteInfo = maccore;
+		};
+		0086351C0F57A51A0044DA64 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 00857F6B0F56F71B0078BE26 /* core.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = D2AAC045055464E500DB518D;
+			remoteInfo = core;
+		};
+		0086351E0F57A5200044DA64 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 00857F890F56F9150078BE26 /* maccore.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = D2AAC045055464E500DB518D;
+			remoteInfo = maccore;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		8DD76F690486A84900D96B5E /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 8;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+				8DD76F6A0486A84900D96B5E /* Tests.1 in CopyFiles */,
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		00857F630F56F4220078BE26 /* Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Test.h; path = ../Test.h; sourceTree = SOURCE_ROOT; };
+		00857F6B0F56F71B0078BE26 /* core.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = core.xcodeproj; path = ../../xcode/core/core.xcodeproj; sourceTree = SOURCE_ROOT; };
+		00857F890F56F9150078BE26 /* maccore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = maccore.xcodeproj; path = ../../xcode/maccore/maccore.xcodeproj; sourceTree = SOURCE_ROOT; };
+		00857FA80F56F9620078BE26 /* Test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Test.cpp; path = ../Test.cpp; sourceTree = SOURCE_ROOT; };
+		00857FA90F56F9620078BE26 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = ../main.cpp; sourceTree = SOURCE_ROOT; };
+		00857FB60F56FD340078BE26 /* MathTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MathTest.cpp; path = ../MathTest.cpp; sourceTree = SOURCE_ROOT; };
+		008634DB0F579B7A0044DA64 /* PackBitsTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PackBitsTest.cpp; path = ../PackBitsTest.cpp; sourceTree = SOURCE_ROOT; };
+		008634F00F579E410044DA64 /* MatrixTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MatrixTest.cpp; path = ../MatrixTest.cpp; sourceTree = SOURCE_ROOT; };
+		0086350E0F57A3140044DA64 /* UtilsTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UtilsTest.cpp; path = ../UtilsTest.cpp; sourceTree = SOURCE_ROOT; };
+		009CC7830F5DAE2B002185BE /* SrcOverTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SrcOverTest.cpp; path = ../SrcOverTest.cpp; sourceTree = SOURCE_ROOT; };
+		009CC7870F5DAF16002185BE /* ClipCubicTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClipCubicTest.cpp; path = ../ClipCubicTest.cpp; sourceTree = SOURCE_ROOT; };
+		00A9BF850F584CF30091AD2D /* Sk64Test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sk64Test.cpp; path = ../Sk64Test.cpp; sourceTree = SOURCE_ROOT; };
+		00A9BFA20F584E150091AD2D /* StringTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringTest.cpp; path = ../StringTest.cpp; sourceTree = SOURCE_ROOT; };
+		00A9BFA60F584F200091AD2D /* TestClassDef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestClassDef.h; path = ../TestClassDef.h; sourceTree = SOURCE_ROOT; };
+		00A9BFBB0F5851570091AD2D /* GeometryTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GeometryTest.cpp; path = ../GeometryTest.cpp; sourceTree = SOURCE_ROOT; };
+		276D93070F5B9FEA0081B3B9 /* PathTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PathTest.cpp; path = ../PathTest.cpp; sourceTree = SOURCE_ROOT; };
+		27C9A9C40F6222EE00E9C93D /* PathMeasureTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PathMeasureTest.cpp; path = ../PathMeasureTest.cpp; sourceTree = SOURCE_ROOT; };
+		27C9A9C50F6222EE00E9C93D /* SortTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SortTest.cpp; path = ../SortTest.cpp; sourceTree = SOURCE_ROOT; };
+		27C9A9C60F6222EE00E9C93D /* StreamTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamTest.cpp; path = ../StreamTest.cpp; sourceTree = SOURCE_ROOT; };
+		8DD76F6C0486A84900D96B5E /* Tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Tests; sourceTree = BUILT_PRODUCTS_DIR; };
+		C6859E8B029090EE04C91782 /* Tests.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = Tests.1; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		8DD76F660486A84900D96B5E /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				00857F860F56F8EE0078BE26 /* libcore.a in Frameworks */,
+				00857F920F56F9170078BE26 /* libmaccore.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		00857F6C0F56F71B0078BE26 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				00857F730F56F71B0078BE26 /* libcore.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		00857F8A0F56F9150078BE26 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				00857F910F56F9150078BE26 /* libmaccore.a */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		08FB7794FE84155DC02AAC07 /* Tests */ = {
+			isa = PBXGroup;
+			children = (
+				00857F890F56F9150078BE26 /* maccore.xcodeproj */,
+				00857F6B0F56F71B0078BE26 /* core.xcodeproj */,
+				08FB7795FE84155DC02AAC07 /* Source */,
+				C6859E8C029090F304C91782 /* Documentation */,
+				1AB674ADFE9D54B511CA2CBB /* Products */,
+			);
+			name = Tests;
+			sourceTree = "<group>";
+		};
+		08FB7795FE84155DC02AAC07 /* Source */ = {
+			isa = PBXGroup;
+			children = (
+				27C9A9C40F6222EE00E9C93D /* PathMeasureTest.cpp */,
+				27C9A9C50F6222EE00E9C93D /* SortTest.cpp */,
+				27C9A9C60F6222EE00E9C93D /* StreamTest.cpp */,
+				009CC7870F5DAF16002185BE /* ClipCubicTest.cpp */,
+				276D93070F5B9FEA0081B3B9 /* PathTest.cpp */,
+				00A9BF850F584CF30091AD2D /* Sk64Test.cpp */,
+				00A9BFBB0F5851570091AD2D /* GeometryTest.cpp */,
+				00A9BFA20F584E150091AD2D /* StringTest.cpp */,
+				00857FA80F56F9620078BE26 /* Test.cpp */,
+				00857FA90F56F9620078BE26 /* main.cpp */,
+				00857F630F56F4220078BE26 /* Test.h */,
+				00857FB60F56FD340078BE26 /* MathTest.cpp */,
+				0086350E0F57A3140044DA64 /* UtilsTest.cpp */,
+				008634F00F579E410044DA64 /* MatrixTest.cpp */,
+				00A9BFA60F584F200091AD2D /* TestClassDef.h */,
+				008634DB0F579B7A0044DA64 /* PackBitsTest.cpp */,
+				009CC7830F5DAE2B002185BE /* SrcOverTest.cpp */,
+			);
+			name = Source;
+			sourceTree = "<group>";
+		};
+		1AB674ADFE9D54B511CA2CBB /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				8DD76F6C0486A84900D96B5E /* Tests */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		C6859E8C029090F304C91782 /* Documentation */ = {
+			isa = PBXGroup;
+			children = (
+				C6859E8B029090EE04C91782 /* Tests.1 */,
+			);
+			name = Documentation;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		8DD76F620486A84900D96B5E /* Tests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "Tests" */;
+			buildPhases = (
+				8DD76F640486A84900D96B5E /* Sources */,
+				8DD76F660486A84900D96B5E /* Frameworks */,
+				8DD76F690486A84900D96B5E /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				0086351D0F57A51A0044DA64 /* PBXTargetDependency */,
+				0086351F0F57A5200044DA64 /* PBXTargetDependency */,
+			);
+			name = Tests;
+			productInstallPath = "$(HOME)/bin";
+			productName = Tests;
+			productReference = 8DD76F6C0486A84900D96B5E /* Tests */;
+			productType = "com.apple.product-type.tool";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		08FB7793FE84155DC02AAC07 /* Project object */ = {
+			isa = PBXProject;
+			buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "Tests" */;
+			compatibilityVersion = "Xcode 3.0";
+			hasScannedForEncodings = 1;
+			mainGroup = 08FB7794FE84155DC02AAC07 /* Tests */;
+			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = 00857F6C0F56F71B0078BE26 /* Products */;
+					ProjectRef = 00857F6B0F56F71B0078BE26 /* core.xcodeproj */;
+				},
+				{
+					ProductGroup = 00857F8A0F56F9150078BE26 /* Products */;
+					ProjectRef = 00857F890F56F9150078BE26 /* maccore.xcodeproj */;
+				},
+			);
+			projectRoot = "";
+			targets = (
+				8DD76F620486A84900D96B5E /* Tests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXReferenceProxy section */
+		00857F730F56F71B0078BE26 /* libcore.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = libcore.a;
+			remoteRef = 00857F720F56F71B0078BE26 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		00857F910F56F9150078BE26 /* libmaccore.a */ = {
+			isa = PBXReferenceProxy;
+			fileType = archive.ar;
+			path = libmaccore.a;
+			remoteRef = 00857F900F56F9150078BE26 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
+/* Begin PBXSourcesBuildPhase section */
+		8DD76F640486A84900D96B5E /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				00857FAA0F56F9620078BE26 /* Test.cpp in Sources */,
+				00857FAB0F56F9620078BE26 /* main.cpp in Sources */,
+				00857FB70F56FD340078BE26 /* MathTest.cpp in Sources */,
+				008634DC0F579B7A0044DA64 /* PackBitsTest.cpp in Sources */,
+				008634F10F579E410044DA64 /* MatrixTest.cpp in Sources */,
+				0086350F0F57A3140044DA64 /* UtilsTest.cpp in Sources */,
+				00A9BF860F584CF30091AD2D /* Sk64Test.cpp in Sources */,
+				00A9BFA30F584E150091AD2D /* StringTest.cpp in Sources */,
+				00A9BFBC0F5851570091AD2D /* GeometryTest.cpp in Sources */,
+				276D93080F5B9FEA0081B3B9 /* PathTest.cpp in Sources */,
+				009CC7840F5DAE2B002185BE /* SrcOverTest.cpp in Sources */,
+				009CC7880F5DAF16002185BE /* ClipCubicTest.cpp in Sources */,
+				27C9A9C70F6222EE00E9C93D /* PathMeasureTest.cpp in Sources */,
+				27C9A9C80F6222EE00E9C93D /* SortTest.cpp in Sources */,
+				27C9A9C90F6222EE00E9C93D /* StreamTest.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		0086351D0F57A51A0044DA64 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = core;
+			targetProxy = 0086351C0F57A51A0044DA64 /* PBXContainerItemProxy */;
+		};
+		0086351F0F57A5200044DA64 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = maccore;
+			targetProxy = 0086351E0F57A5200044DA64 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+		1DEB923208733DC60010E9CD /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_ENABLE_FIX_AND_CONTINUE = YES;
+				GCC_MODEL_TUNING = G5;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"_GLIBCXX_DEBUG=1",
+					"_GLIBCXX_DEBUG_PEDANTIC=1",
+				);
+				INSTALL_PATH = /usr/local/bin;
+				PRODUCT_NAME = Tests;
+			};
+			name = Debug;
+		};
+		1DEB923308733DC60010E9CD /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_MODEL_TUNING = G5;
+				INSTALL_PATH = /usr/local/bin;
+				PRODUCT_NAME = Tests;
+			};
+			name = Release;
+		};
+		1DEB923608733DC60010E9CD /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(NATIVE_ARCH)";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = "../../../**";
+				ONLY_ACTIVE_ARCH = NO;
+				PREBINDING = NO;
+				PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
+				SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+				VALID_ARCHS = "i386 x86_64";
+			};
+			name = Debug;
+		};
+		1DEB923708733DC60010E9CD /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(NATIVE_ARCH)";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = "../../../**";
+				ONLY_ACTIVE_ARCH = NO;
+				PREBINDING = NO;
+				PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO;
+				SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk";
+				VALID_ARCHS = "i386 x86_64";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "Tests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1DEB923208733DC60010E9CD /* Debug */,
+				1DEB923308733DC60010E9CD /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "Tests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1DEB923608733DC60010E9CD /* Debug */,
+				1DEB923708733DC60010E9CD /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}
diff --git a/tests/ToUnicode.cpp b/tests/ToUnicode.cpp
new file mode 100644
index 0000000..ea9e258
--- /dev/null
+++ b/tests/ToUnicode.cpp
@@ -0,0 +1,131 @@
+
+/*
+ * 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 "Test.h"
+#include "SkData.h"
+#include "SkPDFTypes.h"
+#include "SkPDFFont.h"
+#include "SkStream.h"
+
+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()) {
+        return false;
+    }
+    if (len != strlen(buffer)) {
+        return false;
+    }
+    return memcmp(data->bytes() + offset, buffer, len) == 0;
+}
+
+void append_cmap_sections(const SkTDArray<SkUnichar>& glyphToUnicode,
+                          const SkPDFGlyphSet* subset,
+                          SkDynamicMemoryWStream* cmap);
+
+static void TestToUnicode(skiatest::Reporter* reporter) {
+    SkTDArray<SkUnichar> glyphToUnicode;
+    SkTDArray<uint16_t> glyphsInSubset;
+    SkPDFGlyphSet subset;
+
+    glyphToUnicode.push(0);  // 0
+    glyphToUnicode.push(0);  // 1
+    glyphToUnicode.push(0);  // 2
+    glyphsInSubset.push(3);
+    glyphToUnicode.push(0x20);  // 3
+    glyphsInSubset.push(4);
+    glyphToUnicode.push(0x25);  // 4
+    glyphsInSubset.push(5);
+    glyphToUnicode.push(0x27);  // 5
+    glyphsInSubset.push(6);
+    glyphToUnicode.push(0x28);  // 6
+    glyphsInSubset.push(7);
+    glyphToUnicode.push(0x29);  // 7
+    glyphsInSubset.push(8);
+    glyphToUnicode.push(0x2F);  // 8
+    glyphsInSubset.push(9);
+    glyphToUnicode.push(0x33);  // 9
+    glyphToUnicode.push(0);  // 10
+    glyphsInSubset.push(11);
+    glyphToUnicode.push(0x35);  // 11
+    glyphsInSubset.push(12);
+    glyphToUnicode.push(0x36);  // 12
+    for (uint16_t i = 13; i < 0xFE; ++i) {
+        glyphToUnicode.push(0);  // Zero from index 0x9 to 0xFD
+    }
+    glyphsInSubset.push(0xFE);
+    glyphToUnicode.push(0x1010);
+    glyphsInSubset.push(0xFF);
+    glyphToUnicode.push(0x1011);
+    glyphsInSubset.push(0x100);
+    glyphToUnicode.push(0x1012);
+    glyphsInSubset.push(0x101);
+    glyphToUnicode.push(0x1013);
+
+    SkDynamicMemoryWStream buffer;
+    subset.set(glyphsInSubset.begin(), glyphsInSubset.count());
+    append_cmap_sections(glyphToUnicode, &subset, &buffer);
+
+    char expectedResult[] =
+"4 beginbfchar\n\
+<0003> <0020>\n\
+<0004> <0025>\n\
+<0008> <002F>\n\
+<0009> <0033>\n\
+endbfchar\n\
+4 beginbfrange\n\
+<0005> <0007> <0027>\n\
+<000B> <000C> <0035>\n\
+<00FE> <00FF> <1010>\n\
+<0100> <0101> <1012>\n\
+endbfrange\n";
+
+    REPORTER_ASSERT(reporter, stream_equals(buffer, 0, expectedResult,
+                                            buffer.getOffset()));
+
+    glyphToUnicode.reset();
+    glyphsInSubset.reset();
+    SkPDFGlyphSet subset2;
+
+    // Test mapping:
+    //           I  n  s  t  a  l
+    // Glyph id 2c 51 56 57 44 4f
+    // Unicode  49 6e 73 74 61 6c
+    for (size_t i = 0; i < 100; ++i) {
+      glyphToUnicode.push(i + 29);
+    }
+
+    glyphsInSubset.push(0x2C);
+    glyphsInSubset.push(0x44);
+    glyphsInSubset.push(0x4F);
+    glyphsInSubset.push(0x51);
+    glyphsInSubset.push(0x56);
+    glyphsInSubset.push(0x57);
+
+    SkDynamicMemoryWStream buffer2;
+    subset2.set(glyphsInSubset.begin(), glyphsInSubset.count());
+    append_cmap_sections(glyphToUnicode, &subset2, &buffer2);
+
+    char expectedResult2[] =
+"4 beginbfchar\n\
+<002C> <0049>\n\
+<0044> <0061>\n\
+<004F> <006C>\n\
+<0051> <006E>\n\
+endbfchar\n\
+1 beginbfrange\n\
+<0056> <0057> <0073>\n\
+endbfrange\n";
+
+    REPORTER_ASSERT(reporter, stream_equals(buffer2, 0, expectedResult2,
+                                            buffer2.getOffset()));
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("ToUnicode", ToUnicodeTestClass, TestToUnicode)
diff --git a/tests/TriangulationTest.cpp b/tests/TriangulationTest.cpp
new file mode 100644
index 0000000..8d692c7
--- /dev/null
+++ b/tests/TriangulationTest.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 "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
new file mode 100644
index 0000000..ec9a8bc
--- /dev/null
+++ b/tests/UnicodeTest.cpp
@@ -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.
+ */
+#include "Test.h"
+#include "SkPaint.h"
+#include "SkUtils.h"
+
+// Unicode Variation Selector ranges: inclusive
+#define UVS_MIN0    0x180B
+#define UVS_MAX0    0x180D
+#define UVS_MIN1    0xFE00
+#define UVS_MAX1    0xFE0F
+#define UVS_MIN2    0xE0100
+#define UVS_MAX2    0xE01EF
+
+static bool isUVS(SkUnichar uni) {
+    return (uni >= UVS_MIN0 && uni <= UVS_MAX0) ||
+           (uni >= UVS_MIN1 && uni <= UVS_MAX1) ||
+           (uni >= UVS_MIN2 && uni <= UVS_MAX2);
+}
+
+static void test_uvs(skiatest::Reporter* reporter) {
+    // [min, max], [min, max] ... inclusive
+    static const SkUnichar gRanges[] = {
+        UVS_MIN0, UVS_MAX0, UVS_MIN1, UVS_MAX1, UVS_MIN2, UVS_MAX2
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRanges); i += 2) {
+        for (SkUnichar uni = gRanges[i] - 8; uni <= gRanges[i+1] + 8; ++uni) {
+            bool uvs0 = isUVS(uni);
+            bool uvs1 = SkUnichar_IsVariationSelector(uni);
+            REPORTER_ASSERT(reporter, uvs0 == uvs1);
+        }
+    }
+}
+
+// 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"
+DEFINE_TESTCLASS("Unicode", TestUnicodeClass, TestUnicode)
diff --git a/tests/UtilsTest.cpp b/tests/UtilsTest.cpp
new file mode 100644
index 0000000..1c2f870
--- /dev/null
+++ b/tests/UtilsTest.cpp
@@ -0,0 +1,186 @@
+
+/*
+ * Copyright 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 "SkRandom.h"
+#include "SkRefCnt.h"
+#include "SkTSearch.h"
+#include "SkTSort.h"
+#include "SkUtils.h"
+
+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;
+};
+
+SK_DEFINE_INST_COUNT(RefClass)
+
+static void test_refptr(skiatest::Reporter* reporter) {
+    RefClass* r0 = new RefClass(0);
+
+    SkRefPtr<RefClass> rc0;
+    REPORTER_ASSERT(reporter, rc0.get() == NULL);
+    REPORTER_ASSERT(reporter, !rc0);
+
+    SkRefPtr<RefClass> rc1;
+    REPORTER_ASSERT(reporter, rc0 == rc1);
+    REPORTER_ASSERT(reporter, rc0.get() != r0);
+
+    rc0 = r0;
+    REPORTER_ASSERT(reporter, rc0);
+    REPORTER_ASSERT(reporter, rc0 != rc1);
+    REPORTER_ASSERT(reporter, rc0.get() == r0);
+
+    rc1 = rc0;
+    REPORTER_ASSERT(reporter, rc1);
+    REPORTER_ASSERT(reporter, rc0 == rc1);
+    REPORTER_ASSERT(reporter, rc0.get() == r0);
+
+    rc0 = NULL;
+    REPORTER_ASSERT(reporter, rc0.get() == NULL);
+    REPORTER_ASSERT(reporter, !rc0);
+    REPORTER_ASSERT(reporter, rc0 != rc1);
+
+    r0->unref();
+}
+
+static void test_autounref(skiatest::Reporter* reporter) {
+    RefClass obj(0);
+    REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
+
+    SkAutoTUnref<RefClass> tmp(&obj);
+    REPORTER_ASSERT(reporter, &obj == tmp.get());
+    REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
+
+    REPORTER_ASSERT(reporter, &obj == tmp.detach());
+    REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
+    REPORTER_ASSERT(reporter, NULL == tmp.detach());
+    REPORTER_ASSERT(reporter, NULL == tmp.get());
+
+    obj.ref();
+    REPORTER_ASSERT(reporter, 2 == obj.getRefCnt());
+    {
+        SkAutoTUnref<RefClass> tmp2(&obj);
+    }
+    REPORTER_ASSERT(reporter, 1 == obj.getRefCnt());
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define kSEARCH_COUNT   91
+
+static void test_search(skiatest::Reporter* reporter) {
+    int         i, array[kSEARCH_COUNT];
+    SkRandom    rand;
+
+    for (i = 0; i < kSEARCH_COUNT; i++) {
+        array[i] = rand.nextS();
+    }
+
+    SkTHeapSort<int>(array, kSEARCH_COUNT);
+    // make sure we got sorted properly
+    for (i = 1; i < kSEARCH_COUNT; i++) {
+        REPORTER_ASSERT(reporter, array[i-1] <= array[i]);
+    }
+
+    // make sure we can find all of our values
+    for (i = 0; i < kSEARCH_COUNT; i++) {
+        int index = SkTSearch<int>(array, kSEARCH_COUNT, array[i], sizeof(int));
+        REPORTER_ASSERT(reporter, index == i);
+    }
+
+    // make sure that random values are either found, or the correct
+    // insertion index is returned
+    for (i = 0; i < 10000; i++) {
+        int value = rand.nextS();
+        int index = SkTSearch<int>(array, kSEARCH_COUNT, value, sizeof(int));
+
+        if (index >= 0) {
+            REPORTER_ASSERT(reporter,
+                            index < kSEARCH_COUNT && array[index] == value);
+        } else {
+            index = ~index;
+            REPORTER_ASSERT(reporter, index <= kSEARCH_COUNT);
+            if (index < kSEARCH_COUNT) {
+                REPORTER_ASSERT(reporter, value < array[index]);
+                if (index > 0) {
+                    REPORTER_ASSERT(reporter, value > array[index - 1]);
+                }
+            } else {
+                // we should append the new value
+                REPORTER_ASSERT(reporter, value > array[kSEARCH_COUNT - 1]);
+            }
+        }
+    }
+}
+
+static void test_utf16(skiatest::Reporter* reporter) {
+    static const SkUnichar gUni[] = {
+        0x10000, 0x18080, 0x20202, 0xFFFFF, 0x101234
+    };
+
+    uint16_t buf[2];
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gUni); i++) {
+        size_t count = SkUTF16_FromUnichar(gUni[i], buf);
+        REPORTER_ASSERT(reporter, count == 2);
+        size_t count2 = SkUTF16_CountUnichars(buf, 2);
+        REPORTER_ASSERT(reporter, count2 == 1);
+        const uint16_t* ptr = buf;
+        SkUnichar c = SkUTF16_NextUnichar(&ptr);
+        REPORTER_ASSERT(reporter, c == gUni[i]);
+        REPORTER_ASSERT(reporter, ptr - buf == 2);
+    }
+}
+
+static void TestUTF(skiatest::Reporter* reporter) {
+    static const struct {
+        const char* fUtf8;
+        SkUnichar   fUni;
+    } gTest[] = {
+        { "a",                  'a' },
+        { "\x7f",               0x7f },
+        { "\xC2\x80",           0x80 },
+        { "\xC3\x83",           (3 << 6) | 3    },
+        { "\xDF\xBF",           0x7ff },
+        { "\xE0\xA0\x80",       0x800 },
+        { "\xE0\xB0\xB8",       0xC38 },
+        { "\xE3\x83\x83",       (3 << 12) | (3 << 6) | 3    },
+        { "\xEF\xBF\xBF",       0xFFFF },
+        { "\xF0\x90\x80\x80",   0x10000 },
+        { "\xF3\x83\x83\x83",   (3 << 18) | (3 << 12) | (3 << 6) | 3    }
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTest); i++) {
+        const char* p = gTest[i].fUtf8;
+        int         n = SkUTF8_CountUnichars(p);
+        SkUnichar   u0 = SkUTF8_ToUnichar(gTest[i].fUtf8);
+        SkUnichar   u1 = SkUTF8_NextUnichar(&p);
+
+        REPORTER_ASSERT(reporter, n == 1);
+        REPORTER_ASSERT(reporter, u0 == u1);
+        REPORTER_ASSERT(reporter, u0 == gTest[i].fUni);
+        REPORTER_ASSERT(reporter,
+                        p - gTest[i].fUtf8 == (int)strlen(gTest[i].fUtf8));
+    }
+
+    test_utf16(reporter);
+    test_search(reporter);
+    test_refptr(reporter);
+    test_autounref(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Utils", UtfTestClass, TestUTF)
diff --git a/tests/WArrayTest.cpp b/tests/WArrayTest.cpp
new file mode 100644
index 0000000..4a5b879
--- /dev/null
+++ b/tests/WArrayTest.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright 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 the implementation so we can make an appropriate template instance.
+#include "SkAdvancedTypefaceMetrics.h"
+
+using namespace skia_advanced_typeface_metrics_utils;
+
+namespace {
+
+// Negative values and zeros in a range plus trailing zeros.
+//                        0  1   2  3  4  5  6  7  8  9 10 11 12 13 14
+const int16_t data1[] = {-1, 0, -3, 4, 5, 6, 7, 0, 0, 0, 8, 0, 0, 0, 0};
+const char* expected1 = "0[-1 0 -3 4 5 6 7 0 0 0 8]";
+
+// Run with leading and trailing zeros.
+// Test rules: d         0  1  2    3    4    5    6    7    8    9 10 11
+const int16_t data2[] = {0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 0, 0};
+const char* expected2 = "3 9 100";
+
+// Removing 0's from a range.
+// Test rules: a         0  1  2  3  4  5  6  7  8  9 10 11
+const int16_t data3[] = {1, 2, 0, 0, 0, 3, 4, 0, 0, 0, 0, 5};
+const char* expected3 = "0[1 2 0 0 0 3 4] 11[5]";
+
+// Removing 0's from a run/range and between runs.
+// Test rules: a, b      0  1  2  3  4  5  6  7  8  9 10 11 12 14 15
+const int16_t data4[] = {1, 0, 0, 0, 1, 2, 2, 2, 3, 0, 0, 0, 0, 3, 4};
+const char* expected4 = "0[1 0 0 0 1] 5 7 2 8[3] 13[3 4]";
+
+// Runs that starts outside a range.
+// Test rules: a, e      0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17
+const int16_t data5[] = {1, 1, 2, 3, 0, 0, 0, 0, 5, 5, 6, 7, 0, 0, 0, 0, 8, 0};
+const char* expected5 = "0 1 1 2[2 3] 8 9 5 10[6 7] 16[8]";
+
+// Zeros and runs that should be broken out.
+// Test rules: a, b, e   0  1  2  3  4  5  6  7  8  9 10 11 12 13
+const int16_t data6[] = {1, 0, 0, 0, 0, 1, 2, 3, 3, 4, 5, 5, 5, 6};
+const char* expected6 = "0[1] 5[1 2 3 3 4] 10 12 5 13[6]";
+
+// Don't cares that aren't enough to break out a run.
+// Test rules: c         0  1   2   3  4  5
+const int16_t data7[] = {1, 2, 10, 11, 2, 3};
+const char* expected7 = "0[1 2 10 11 2 3]";
+const uint32_t subset7[] = {0, 1, 4, 5};
+const char* expectedSubset7 = "0[1 2 0 0 2 3]";
+
+// Don't cares that are enough to break out a run.
+// Test rules: c         0  1   2   3  4   5  6
+const int16_t data8[] = {1, 2, 10, 11, 12, 2, 3};
+const char* expected8 = "0[1 2 10 11 12 2 3]";
+const uint32_t subset8[] = {0, 1, 5, 6};
+const char* expectedSubset8 = "0[1] 1 5 2 6[3]";
+
+// Leading don't cares.
+// Test rules: d         0  1   2  3  4
+const int16_t data9[] = {1, 1, 10, 2, 3};
+const char* expected9 = "0 1 1 2[10 2 3]";
+const uint32_t subset9[] = {0, 1, 3, 4};
+const char* expectedSubset9 = "0 1 1 3[2 3]";
+
+// Almost run of don't cares inside a range.
+// Test rules: c          0  1   2   3   4  5
+const int16_t data10[] = {1, 2, 10, 11, 12, 3};
+const char* expected10 = "0[1 2 10 11 12 3]";
+const uint32_t subset10[] = {0, 1, 5};
+const char* expectedSubset10 = "0[1 2 0 0 0 3]";
+
+// Run of don't cares inside a range.
+// Test rules: c          0  1   2   3   4   5  6
+const int16_t data11[] = {1, 2, 10, 11, 12, 13, 3};
+const char* expected11 = "0[1 2 10 11 12 13 3]";
+const uint32_t subset11[] = {0, 1, 6};
+const char* expectedSubset11 = "0[1 2] 6[3]";
+
+// Almost run within a range with leading don't cares.
+// Test rules: c          0   1   2  3   4   5  6
+const int16_t data12[] = {1, 10, 11, 2, 12, 13, 3};
+const char* expected12 = "0[1 10 11 2 12 13 3]";
+const uint32_t subset12[] = {0, 3, 6};
+const char* expectedSubset12 = "0[1 0 0 2 0 0 3]";
+
+// Run within a range with leading don't cares.
+// Test rules: c          0   1   2  3  4   5   6  7
+const int16_t data13[] = {1, 10, 11, 2, 2, 12, 13, 3};
+const char* expected13 = "0[1 10 11 2 2 12 13 3]";
+const uint32_t subset13[] = {0, 3, 4, 7};
+const char* expectedSubset13 = "0[1] 1 6 2 7[3]";
+
+// Enough don't cares to breakup something.
+// Test rules: a          0  1  2  3  4  5
+const int16_t data14[] = {1, 0, 0, 0, 0, 2};
+const char* expected14 = "0[1] 5[2]";
+const uint32_t subset14[] = {0, 5};
+const char* expectedSubset14 = "0[1] 5[2]";
+
+}
+
+static SkString stringify_advance_data(SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* data) {
+    SkString result;
+    bool leadingSpace = false;
+    while (data != NULL) {
+      if (leadingSpace) {
+        result.append(" ");
+      } else {
+        leadingSpace = true;
+      }
+      switch(data->fType) {
+        case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRun:
+          result.appendf("%d %d %d", data->fStartId, data->fEndId, data->fAdvance[0]);
+          break;
+        case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRange:
+          result.appendf("%d[", data->fStartId);
+          for (int i = 0; i < data->fAdvance.count(); ++i) {
+            if (i > 0) {
+              result.append(" ");
+            }
+            result.appendf("%d", data->fAdvance[i]);
+          }
+          result.append("]");
+          break;
+        case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kDefault:
+          result.appendf("<Default=%d>", data->fAdvance[0]);
+          break;
+      }
+      data = data->fNext.get();
+    }
+    return result;
+}
+
+class TestWData {
+  public:
+    TestWData(skiatest::Reporter* reporter,
+              const int16_t advances[], int advanceLen,
+              const uint32_t subset[], int subsetLen,
+              const char* expected)
+            : fAdvances(advances)
+            , fAdvancesLen(advanceLen)
+            , fSubset(subset)
+            , fSubsetLen(subsetLen)
+            , fExpected(expected) {
+        REPORTER_ASSERT(reporter, RunTest());
+    }
+
+  private:
+    const int16_t* fAdvances;
+    const int fAdvancesLen;
+    const uint32_t* fSubset;
+    const int fSubsetLen;
+    const char* fExpected;
+
+    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;
+        }
+        return false;
+    }
+
+    bool RunTest() {
+        SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t> > result;
+        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());
+            return false;
+        }
+        return true;
+    }
+};
+
+static void TestWArray(skiatest::Reporter* reporter) {
+    TestWData(reporter, data1, SK_ARRAY_COUNT(data1), NULL, 0, expected1);
+    TestWData(reporter, data2, SK_ARRAY_COUNT(data2), NULL, 0, expected2);
+    TestWData(reporter, data3, SK_ARRAY_COUNT(data3), NULL, 0, expected3);
+    TestWData(reporter, data4, SK_ARRAY_COUNT(data4), NULL, 0, expected4);
+    TestWData(reporter, data5, SK_ARRAY_COUNT(data5), NULL, 0, expected5);
+    TestWData(reporter, data6, SK_ARRAY_COUNT(data6), NULL, 0, expected6);
+    TestWData(reporter, data7, SK_ARRAY_COUNT(data7), NULL, 0, expected7);
+    TestWData(reporter, data7, SK_ARRAY_COUNT(data7), subset7,
+              SK_ARRAY_COUNT(subset7), expectedSubset7);
+    TestWData(reporter, data8, SK_ARRAY_COUNT(data8), NULL, 0, expected8);
+    TestWData(reporter, data8, SK_ARRAY_COUNT(data8), subset8,
+              SK_ARRAY_COUNT(subset8), expectedSubset8);
+    TestWData(reporter, data9, SK_ARRAY_COUNT(data9), NULL, 0, expected9);
+    TestWData(reporter, data9, SK_ARRAY_COUNT(data9), subset9,
+              SK_ARRAY_COUNT(subset9), expectedSubset9);
+    TestWData(reporter, data10, SK_ARRAY_COUNT(data10), NULL, 0, expected10);
+    TestWData(reporter, data10, SK_ARRAY_COUNT(data10), subset10,
+              SK_ARRAY_COUNT(subset10), expectedSubset10);
+    TestWData(reporter, data11, SK_ARRAY_COUNT(data11), NULL, 0, expected11);
+    TestWData(reporter, data11, SK_ARRAY_COUNT(data11), subset11,
+              SK_ARRAY_COUNT(subset11), expectedSubset11);
+    TestWData(reporter, data12, SK_ARRAY_COUNT(data12), NULL, 0, expected12);
+    TestWData(reporter, data12, SK_ARRAY_COUNT(data12), subset12,
+              SK_ARRAY_COUNT(subset12), expectedSubset12);
+    TestWData(reporter, data13, SK_ARRAY_COUNT(data13), NULL, 0, expected13);
+    TestWData(reporter, data13, SK_ARRAY_COUNT(data13), subset13,
+              SK_ARRAY_COUNT(subset13), expectedSubset13);
+    TestWData(reporter, data14, SK_ARRAY_COUNT(data14), NULL, 0, expected14);
+    TestWData(reporter, data14, SK_ARRAY_COUNT(data14), subset14,
+              SK_ARRAY_COUNT(subset14), expectedSubset14);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("WArray", WArrayTest, TestWArray)
diff --git a/tests/WritePixelsTest.cpp b/tests/WritePixelsTest.cpp
new file mode 100644
index 0000000..f5c4175
--- /dev/null
+++ b/tests/WritePixelsTest.cpp
@@ -0,0 +1,440 @@
+
+/*
+ * Copyright 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 "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,
+                                                DEV_H * SK_Scalar1);
+static const U8CPU DEV_PAD = 0xee;
+
+namespace {
+SkPMColor getCanvasColor(int x, int y) {
+    SkASSERT(x >= 0 && x < DEV_W);
+    SkASSERT(y >= 0 && y < DEV_H);
+
+    U8CPU r = x;
+    U8CPU g = y;
+    U8CPU b = 0xc;
+
+    U8CPU a = 0x0;
+    switch ((x+y) % 5) {
+        case 0:
+            a = 0xff;
+            break;
+        case 1:
+            a = 0x80;
+            break;
+        case 2:
+            a = 0xCC;
+            break;
+        case 3:
+            a = 0x00;
+            break;
+        case 4:
+            a = 0x01;
+            break;
+    }
+    return SkPremultiplyARGBInline(a, r, g, b);
+}
+
+bool config8888IsPremul(SkCanvas::Config8888 config8888) {
+    switch (config8888) {
+        case SkCanvas::kNative_Premul_Config8888:
+        case SkCanvas::kBGRA_Premul_Config8888:
+        case SkCanvas::kRGBA_Premul_Config8888:
+            return true;
+        case SkCanvas::kNative_Unpremul_Config8888:
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            return false;
+        default:
+            SkASSERT(0);
+            return false;
+    }
+}
+
+// assumes any premu/.unpremul has been applied
+uint32_t packConfig8888(SkCanvas::Config8888 config8888,
+                        U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+    uint32_t r32;
+    uint8_t* result = reinterpret_cast<uint8_t*>(&r32);
+    switch (config8888) {
+        case SkCanvas::kNative_Premul_Config8888:
+        case SkCanvas::kNative_Unpremul_Config8888:
+            r32 = SkPackARGB32NoCheck(a, r, g, b);
+            break;
+        case SkCanvas::kBGRA_Premul_Config8888:
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            result[0] = b;
+            result[1] = g;
+            result[2] = r;
+            result[3] = a;
+            break;
+        case SkCanvas::kRGBA_Premul_Config8888:
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            result[0] = r;
+            result[1] = g;
+            result[2] = b;
+            result[3] = a;
+            break;
+        default:
+            SkASSERT(0);
+            return 0;
+    }
+    return r32;
+}
+
+uint32_t getBitmapColor(int x, int y, int w, int h, SkCanvas::Config8888 config8888) {
+    int n = y * w + x;
+    U8CPU b = n & 0xff;
+    U8CPU g = (n >> 8) & 0xff;
+    U8CPU r = (n >> 16) & 0xff;
+    U8CPU a = 0;
+    switch ((x+y) % 5) {
+        case 4:
+            a = 0xff;
+            break;
+        case 3:
+            a = 0x80;
+            break;
+        case 2:
+            a = 0xCC;
+            break;
+        case 1:
+            a = 0x01;
+            break;
+        case 0:
+            a = 0x00;
+            break;
+    }
+    if (config8888IsPremul(config8888)) {
+        r = SkMulDiv255Ceiling(r, a);
+        g = SkMulDiv255Ceiling(g, a);
+        b = SkMulDiv255Ceiling(b, a);
+    }
+    return packConfig8888(config8888, a, r, g , b);
+}
+
+void fillCanvas(SkCanvas* canvas) {
+    static SkBitmap bmp;
+    if (bmp.isNull()) {
+        bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
+        bool alloc = bmp.allocPixels();
+        SkASSERT(alloc);
+        SkAutoLockPixels alp(bmp);
+        intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
+        for (int y = 0; y < DEV_H; ++y) {
+            for (int x = 0; x < DEV_W; ++x) {
+                SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
+                *pixel = getCanvasColor(x, y);
+            }
+        }
+    }
+    canvas->save();
+    canvas->setMatrix(SkMatrix::I());
+    canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
+    SkPaint paint;
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    canvas->drawBitmap(bmp, 0, 0, &paint);
+    canvas->restore();
+}
+
+SkPMColor convertConfig8888ToPMColor(SkCanvas::Config8888 config8888,
+                                     uint32_t color,
+                                     bool* premul) {
+    const uint8_t* c = reinterpret_cast<uint8_t*>(&color);
+    U8CPU a,r,g,b;
+    *premul = false;
+    switch (config8888) {
+        case SkCanvas::kNative_Premul_Config8888:
+            return color;
+        case SkCanvas::kNative_Unpremul_Config8888:
+            *premul = true;
+            a = SkGetPackedA32(color);
+            r = SkGetPackedR32(color);
+            g = SkGetPackedG32(color);
+            b = SkGetPackedB32(color);
+            break;
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            *premul = true; // fallthru
+        case SkCanvas::kBGRA_Premul_Config8888:
+            a = static_cast<U8CPU>(c[3]);
+            r = static_cast<U8CPU>(c[2]);
+            g = static_cast<U8CPU>(c[1]);
+            b = static_cast<U8CPU>(c[0]);
+            break;
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            *premul = true; // fallthru
+        case SkCanvas::kRGBA_Premul_Config8888:
+            a = static_cast<U8CPU>(c[3]);
+            r = static_cast<U8CPU>(c[0]);
+            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);
+        g = SkMulDiv255Ceiling(g, a);
+        b = SkMulDiv255Ceiling(b, a);
+    }
+    return SkPackARGB32(a, r, g, b);
+}
+
+bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
+    if (!didPremulConversion) {
+        return a == b;
+    }
+    int32_t aA = static_cast<int32_t>(SkGetPackedA32(a));
+    int32_t aR = static_cast<int32_t>(SkGetPackedR32(a));
+    int32_t aG = static_cast<int32_t>(SkGetPackedG32(a));
+    int32_t aB = SkGetPackedB32(a);
+
+    int32_t bA = static_cast<int32_t>(SkGetPackedA32(b));
+    int32_t bR = static_cast<int32_t>(SkGetPackedR32(b));
+    int32_t bG = static_cast<int32_t>(SkGetPackedG32(b));
+    int32_t bB = static_cast<int32_t>(SkGetPackedB32(b));
+
+    return aA == bA &&
+           SkAbs32(aR - bR) <= 1 &&
+           SkAbs32(aG - bG) <= 1 &&
+           SkAbs32(aB - bB) <= 1;
+}
+
+bool checkWrite(skiatest::Reporter* reporter,
+                SkCanvas* canvas,
+                const SkBitmap& bitmap,
+                int writeX, int writeY,
+                SkCanvas::Config8888 config8888) {
+    SkDevice* dev = canvas->getDevice();
+    if (!dev) {
+        return false;
+    }
+    SkBitmap devBmp = dev->accessBitmap(false);
+    if (devBmp.width() != DEV_W ||
+        devBmp.height() != DEV_H ||
+        devBmp.config() != SkBitmap::kARGB_8888_Config ||
+        devBmp.isNull()) {
+        return false;
+    }
+    SkAutoLockPixels alp(devBmp);
+
+    intptr_t canvasPixels = reinterpret_cast<intptr_t>(devBmp.getPixels());
+    size_t canvasRowBytes = devBmp.rowBytes();
+    SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height());
+    for (int cy = 0; cy < DEV_H; ++cy) {
+        const SkPMColor* canvasRow = reinterpret_cast<const SkPMColor*>(canvasPixels);
+        for (int cx = 0; cx < DEV_W; ++cx) {
+            SkPMColor canvasPixel = canvasRow[cx];
+            if (writeRect.contains(cx, cy)) {
+                int bx = cx - writeX;
+                int by = cy - writeY;
+                uint32_t bmpColor8888 = getBitmapColor(bx, by, bitmap.width(), bitmap.height(), config8888);
+                bool mul;
+                SkPMColor bmpPMColor = convertConfig8888ToPMColor(config8888, bmpColor8888, &mul);
+                bool check;
+                REPORTER_ASSERT(reporter, check = checkPixel(bmpPMColor, canvasPixel, mul));
+                if (!check) {
+                    return false;
+                }
+            } else {
+                bool check;
+                SkPMColor testColor = getCanvasColor(cx, cy);
+                REPORTER_ASSERT(reporter, check = (canvasPixel == testColor));
+                if (!check) {
+                    return false;
+                }
+            }
+        }
+        if (cy != DEV_H -1) {
+            const char* pad = reinterpret_cast<const char*>(canvasPixels + 4 * DEV_W);
+            for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) {
+                bool check;
+                REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD)));
+                if (!check) {
+                    return false;
+                }
+            }
+        }
+        canvasPixels += canvasRowBytes;
+    }
+
+    return true;
+}
+
+enum DevType {
+    kRaster_DevType,
+#if SK_SUPPORT_GPU
+    kGpu_DevType,
+#endif
+};
+
+struct CanvasConfig {
+    DevType fDevType;
+    bool fTightRowBytes;
+};
+
+static const CanvasConfig gCanvasConfigs[] = {
+    {kRaster_DevType, true},
+    {kRaster_DevType, false},
+#if SK_SUPPORT_GPU && defined(SK_SCALAR_IS_FLOAT)
+    {kGpu_DevType, true}, // row bytes has no meaning on gpu devices
+#endif
+};
+
+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()) {
+                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());
+            }
+            return new SkDevice(bmp);
+        }
+#if SK_SUPPORT_GPU
+        case kGpu_DevType:
+            return new SkGpuDevice(grCtx, SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
+#endif
+    }
+    return NULL;
+}
+
+bool setupBitmap(SkBitmap* bitmap,
+              SkCanvas::Config8888 config8888,
+              int w, int h,
+              bool tightRowBytes) {
+    size_t rowBytes = tightRowBytes ? 0 : 4 * w + 60;
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h, rowBytes);
+    if (!bitmap->allocPixels()) {
+        return false;
+    }
+    SkAutoLockPixels alp(*bitmap);
+    intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
+    for (int y = 0; y < h; ++y) {
+        for (int x = 0; x < w; ++x) {
+            uint32_t* pixel = reinterpret_cast<uint32_t*>(pixels + y * bitmap->rowBytes() + x * 4);
+            *pixel = getBitmapColor(x, y, w, h, config8888);
+        }
+    }
+    return true;
+}
+
+void WritePixelsTest(skiatest::Reporter* reporter, GrContext* context) {
+    SkCanvas canvas;
+
+    const SkIRect testRects[] = {
+        // entire thing
+        DEV_RECT,
+        // larger on all sides
+        SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
+        // fully contained
+        SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
+        // outside top left
+        SkIRect::MakeLTRB(-10, -10, -1, -1),
+        // touching top left corner
+        SkIRect::MakeLTRB(-10, -10, 0, 0),
+        // overlapping top left corner
+        SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
+        // overlapping top left and top right corners
+        SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
+        // touching entire top edge
+        SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
+        // overlapping top right corner
+        SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
+        // contained in x, overlapping top edge
+        SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
+        // outside top right corner
+        SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
+        // touching top right corner
+        SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
+        // overlapping top left and bottom left corners
+        SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
+        // touching entire left edge
+        SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
+        // overlapping bottom left corner
+        SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
+        // contained in y, overlapping left edge
+        SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
+        // outside bottom left corner
+        SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
+        // touching bottom left corner
+        SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
+        // overlapping bottom left and bottom right corners
+        SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
+        // touching entire left edge
+        SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
+        // overlapping bottom right corner
+        SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
+        // overlapping top right and bottom right corners
+        SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gCanvasConfigs); ++i) {
+        SkAutoTUnref<SkDevice> device(createDevice(gCanvasConfigs[i], context));
+        SkCanvas canvas(device);
+
+        static const SkCanvas::Config8888 gSrcConfigs[] = {
+            SkCanvas::kNative_Premul_Config8888,
+            SkCanvas::kNative_Unpremul_Config8888,
+            SkCanvas::kBGRA_Premul_Config8888,
+            SkCanvas::kBGRA_Unpremul_Config8888,
+            SkCanvas::kRGBA_Premul_Config8888,
+            SkCanvas::kRGBA_Unpremul_Config8888,
+        };
+        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(gSrcConfigs); ++c) {
+                    fillCanvas(&canvas);
+                    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));
+                }
+            }
+        }
+    }
+}
+}
+
+#ifndef SK_BUILD_FOR_ANDROID
+#include "TestClassDef.h"
+DEFINE_GPUTESTCLASS("WritePixels", WritePixelsTestClass, WritePixelsTest)
+#endif
diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp
new file mode 100644
index 0000000..4715f7a
--- /dev/null
+++ b/tests/Writer32Test.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 "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) {
+        REPORTER_ASSERT(reporter, i*4 == writer->size());
+        writer->write32(data[i]);
+        uint32_t* addr = writer->peek32(i * 4);
+        REPORTER_ASSERT(reporter, data[i] == *addr);
+    }
+
+    char buffer[sizeof(data)];
+    REPORTER_ASSERT(reporter, sizeof(buffer) == writer->size());
+    writer->flatten(buffer);
+    REPORTER_ASSERT(reporter, !memcmp(data, buffer, sizeof(buffer)));
+}
+
+static void test2(skiatest::Reporter* reporter, SkWriter32* writer) {
+    static const char gStr[] = "abcdefghimjklmnopqrstuvwxyz";
+    size_t i;
+
+    size_t len = 0;
+    for (i = 0; i <= 26; ++i) {
+        len += SkWriter32::WriteStringSize(gStr, i);
+        writer->writeString(gStr, i);
+    }
+    REPORTER_ASSERT(reporter, writer->size() == len);
+
+    SkAutoMalloc storage(len);
+    writer->flatten(storage.get());
+
+    SkReader32 reader;
+    reader.setMemory(storage.get(), len);
+    for (i = 0; i <= 26; ++i) {
+        REPORTER_ASSERT(reporter, !reader.eof());
+        const char* str = reader.readString(&len);
+        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
+    {
+        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);
+    }
+
+    // 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
new file mode 100644
index 0000000..0b7012d
--- /dev/null
+++ b/tests/XfermodeTest.cpp
@@ -0,0 +1,72 @@
+
+/*
+ * Copyright 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 "SkColor.h"
+#include "SkXfermode.h"
+
+static SkPMColor bogusXfermodeProc(SkPMColor src, SkPMColor dst) {
+    return 42;
+}
+
+#define ILLEGAL_MODE    ((SkXfermode::Mode)-1)
+
+static void test_asMode(skiatest::Reporter* reporter) {
+    for (int mode = 0; mode <= SkXfermode::kLastMode; mode++) {
+        SkXfermode* xfer = SkXfermode::Create((SkXfermode::Mode) mode);
+
+        SkXfermode::Mode reportedMode = ILLEGAL_MODE;
+        REPORTER_ASSERT(reporter, reportedMode != mode);
+
+        // test IsMode
+        REPORTER_ASSERT(reporter, SkXfermode::IsMode(xfer, &reportedMode));
+        REPORTER_ASSERT(reporter, reportedMode == mode);
+
+        // repeat that test, but with asMode instead
+        if (xfer) {
+            reportedMode = (SkXfermode::Mode) -1;
+            REPORTER_ASSERT(reporter, xfer->asMode(&reportedMode));
+            REPORTER_ASSERT(reporter, reportedMode == mode);
+            xfer->unref();
+        } else {
+            REPORTER_ASSERT(reporter, SkXfermode::kSrcOver_Mode == mode);
+        }
+    }
+
+    SkXfermode* bogusXfer = new SkProcXfermode(bogusXfermodeProc);
+    SkXfermode::Mode reportedMode = (SkXfermode::Mode) -1;
+    REPORTER_ASSERT(reporter, !bogusXfer->asMode(&reportedMode));
+    REPORTER_ASSERT(reporter, reportedMode == -1);
+    REPORTER_ASSERT(reporter, !SkXfermode::IsMode(bogusXfer, &reportedMode));
+    REPORTER_ASSERT(reporter, reportedMode == -1);
+    bogusXfer->unref();
+}
+
+static void test_IsMode(skiatest::Reporter* reporter) {
+    REPORTER_ASSERT(reporter, SkXfermode::IsMode(NULL,
+                                                 SkXfermode::kSrcOver_Mode));
+
+    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);
+
+        if (SkXfermode::kSrcOver_Mode != mode) {
+            REPORTER_ASSERT(reporter, !SkXfermode::IsMode(NULL, mode));
+        }
+    }
+}
+
+static void test_xfermodes(skiatest::Reporter* reporter) {
+    test_asMode(reporter);
+    test_IsMode(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Xfermode", XfermodeTestClass, test_xfermodes)
diff --git a/tests/skia_test.cpp b/tests/skia_test.cpp
new file mode 100644
index 0000000..f884a64
--- /dev/null
+++ b/tests/skia_test.cpp
@@ -0,0 +1,198 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#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
+template TestRegistry* TestRegistry::gHead;
+
+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;
+};
+
+static const char* result2string(Reporter::Result result) {
+    return result == Reporter::kPassed ? "passed" : "FAILED";
+}
+
+class DebugfReporter : public Reporter {
+public:
+    DebugfReporter(bool androidMode) : fAndroidMode(androidMode) {}
+
+    void setIndexOfTotal(int index, int total) {
+        fIndex = index;
+        fTotal = total;
+    }
+protected:
+    virtual void onStart(Test* test) {
+        this->dumpState(test, kStarting_State);
+    }
+    virtual void onReport(const char desc[], Reporter::Result result) {
+        if (!fAndroidMode) {
+            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");
+            }
+        }
+    }
+
+    int fIndex, fTotal;
+    bool fAndroidMode;
+};
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+#ifdef 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) {
+            ++argv;
+            if (argv < stop && **argv) {
+                matchStr = *argv;
+            }
+        }
+    }
+
+    {
+        SkString header("Skia UnitTests:");
+        if (matchStr) {
+            header.appendf(" --match %s", matchStr);
+        }
+#ifdef SK_DEBUG
+        header.append(" SK_DEBUG");
+#else
+        header.append(" SK_RELEASE");
+#endif
+#ifdef SK_SCALAR_IS_FIXED
+        header.append(" SK_SCALAR_IS_FIXED");
+#else
+        header.append(" SK_SCALAR_IS_FLOAT");
+#endif
+        if (!androidMode) {
+            SkDebugf("%s\n", header.c_str());
+        }
+    }
+
+    DebugfReporter reporter(androidMode);
+    Iter iter(&reporter);
+    Test* test;
+
+    const int count = Iter::Count();
+    int index = 0;
+    int failCount = 0;
+    int skipCount = 0;
+    while ((test = iter.next()) != NULL) {
+        reporter.setIndexOfTotal(index, count);
+        if (NULL != matchStr && !strstr(test->getName(), matchStr)) {
+            ++skipCount;
+        } else {
+            if (!test->run()) {
+                ++failCount;
+            }
+        }
+        SkDELETE(test);
+        index += 1;
+    }
+
+    if (!androidMode) {
+        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();
+
+    return (failCount == 0) ? 0 : 1;
+}
+
+#if !defined SK_BUILD_FOR_IOS
+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/bson_c/APACHE-2.0.txt b/third_party/bson_c/APACHE-2.0.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/third_party/bson_c/APACHE-2.0.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/bson_c/src/bson.c b/third_party/bson_c/src/bson.c
new file mode 100644
index 0000000..788bc2f
--- /dev/null
+++ b/third_party/bson_c/src/bson.c
@@ -0,0 +1,1063 @@
+/* bson.c */
+
+/*    Copyright 2009, 2010 10gen 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <limits.h>
+
+#include "bson.h"
+#include "encoding.h"
+
+const int initialBufferSize = 128;
+
+/* only need one of these */
+static const int zero = 0;
+
+/* Custom standard function pointers. */
+void *( *bson_malloc_func )( size_t ) = malloc;
+void *( *bson_realloc_func )( void *, size_t ) = realloc;
+void  ( *bson_free )( void * ) = free;
+#ifdef R_SAFETY_NET
+bson_printf_func bson_printf;
+#else
+bson_printf_func bson_printf = printf;
+#endif
+bson_fprintf_func bson_fprintf = fprintf;
+bson_sprintf_func bson_sprintf = sprintf;
+
+static int _bson_errprintf( const char *, ... );
+bson_printf_func bson_errprintf = _bson_errprintf;
+
+/* ObjectId fuzz functions. */
+static int ( *oid_fuzz_func )( void ) = NULL;
+static int ( *oid_inc_func )( void )  = NULL;
+
+/* ----------------------------
+   READING
+   ------------------------------ */
+
+MONGO_EXPORT bson* bson_create() {
+	return (bson*)bson_malloc(sizeof(bson));
+}
+
+MONGO_EXPORT void bson_dispose(bson* b) {
+	bson_free(b);
+}
+
+bson *bson_empty( bson *obj ) {
+    static char *data = "\005\0\0\0\0";
+    bson_init_data( obj, data );
+    obj->finished = 1;
+    obj->err = 0;
+    obj->stackPos = 0;
+    return obj;
+}
+
+MONGO_EXPORT int bson_copy( bson *out, const bson *in ) {
+    if ( !out ) return BSON_ERROR;
+    if ( !in->finished ) return BSON_ERROR;
+    bson_init_size( out, bson_size( in ) );
+    memcpy( out->data, in->data, bson_size( in ) );
+    out->finished = 1;
+
+    return BSON_OK;
+}
+
+int bson_init_data( bson *b, char *data ) {
+    b->data = data;
+    return BSON_OK;
+}
+
+int bson_init_finished_data( bson *b, char *data ) {
+    bson_init_data( b, data );
+    b->finished = 1;
+    return BSON_OK;
+}
+
+static void _bson_reset( bson *b ) {
+    b->finished = 0;
+    b->stackPos = 0;
+    b->err = 0;
+    b->errstr = NULL;
+}
+
+MONGO_EXPORT int bson_size( const bson *b ) {
+    int i;
+    if ( ! b || ! b->data )
+        return 0;
+    bson_little_endian32( &i, b->data );
+    return i;
+}
+
+MONGO_EXPORT int bson_buffer_size( const bson *b ) {
+    return (b->cur - b->data + 1);
+}
+
+
+const char *bson_data( bson *b ) {
+    return (const char *)b->data;
+}
+
+static char hexbyte( char hex ) {
+    switch ( hex ) {
+    case '0':
+        return 0x0;
+    case '1':
+        return 0x1;
+    case '2':
+        return 0x2;
+    case '3':
+        return 0x3;
+    case '4':
+        return 0x4;
+    case '5':
+        return 0x5;
+    case '6':
+        return 0x6;
+    case '7':
+        return 0x7;
+    case '8':
+        return 0x8;
+    case '9':
+        return 0x9;
+    case 'a':
+    case 'A':
+        return 0xa;
+    case 'b':
+    case 'B':
+        return 0xb;
+    case 'c':
+    case 'C':
+        return 0xc;
+    case 'd':
+    case 'D':
+        return 0xd;
+    case 'e':
+    case 'E':
+        return 0xe;
+    case 'f':
+    case 'F':
+        return 0xf;
+    default:
+        return 0x0; /* something smarter? */
+    }
+}
+
+MONGO_EXPORT void bson_oid_from_string( bson_oid_t *oid, const char *str ) {
+    int i;
+    for ( i=0; i<12; i++ ) {
+        oid->bytes[i] = ( hexbyte( str[2*i] ) << 4 ) | hexbyte( str[2*i + 1] );
+    }
+}
+
+MONGO_EXPORT void bson_oid_to_string( const bson_oid_t *oid, char *str ) {
+    static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+    int i;
+    for ( i=0; i<12; i++ ) {
+        str[2*i]     = hex[( oid->bytes[i] & 0xf0 ) >> 4];
+        str[2*i + 1] = hex[ oid->bytes[i] & 0x0f      ];
+    }
+    str[24] = '\0';
+}
+
+void bson_set_oid_fuzz( int ( *func )( void ) ) {
+    oid_fuzz_func = func;
+}
+
+void bson_set_oid_inc( int ( *func )( void ) ) {
+    oid_inc_func = func;
+}
+
+MONGO_EXPORT void bson_oid_gen( bson_oid_t *oid ) {
+    static int incr = 0;
+    static int fuzz = 0;
+    int i;
+    int t = time( NULL );
+
+    if( oid_inc_func )
+        i = oid_inc_func();
+    else
+        i = incr++;
+
+    if ( !fuzz ) {
+        if ( oid_fuzz_func )
+            fuzz = oid_fuzz_func();
+        else {
+            srand( t );
+            fuzz = rand();
+        }
+    }
+
+    bson_big_endian32( &oid->ints[0], &t );
+    oid->ints[1] = fuzz;
+    bson_big_endian32( &oid->ints[2], &i );
+}
+
+time_t bson_oid_generated_time( bson_oid_t *oid ) {
+    time_t out;
+    bson_big_endian32( &out, &oid->ints[0] );
+
+    return out;
+}
+
+void bson_print( bson *b ) {
+    bson_print_raw( b->data , 0 );
+}
+
+void bson_print_raw( const char *data , int depth ) {
+    bson_iterator i;
+    const char *key;
+    int temp;
+    bson_timestamp_t ts;
+    char oidhex[25];
+    bson scope;
+    bson_iterator_from_buffer( &i, data );
+
+    while ( bson_iterator_next( &i ) ) {
+        bson_type t = bson_iterator_type( &i );
+        if ( t == 0 )
+            break;
+        key = bson_iterator_key( &i );
+
+        for ( temp=0; temp<=depth; temp++ )
+            bson_printf( "\t" );
+        bson_printf( "%s : %d \t " , key , t );
+        switch ( t ) {
+        case BSON_DOUBLE:
+            bson_printf( "%f" , bson_iterator_double( &i ) );
+            break;
+        case BSON_STRING:
+            bson_printf( "%s" , bson_iterator_string( &i ) );
+            break;
+        case BSON_SYMBOL:
+            bson_printf( "SYMBOL: %s" , bson_iterator_string( &i ) );
+            break;
+        case BSON_OID:
+            bson_oid_to_string( bson_iterator_oid( &i ), oidhex );
+            bson_printf( "%s" , oidhex );
+            break;
+        case BSON_BOOL:
+            bson_printf( "%s" , bson_iterator_bool( &i ) ? "true" : "false" );
+            break;
+        case BSON_DATE:
+            bson_printf( "%ld" , ( long int )bson_iterator_date( &i ) );
+            break;
+        case BSON_BINDATA:
+            bson_printf( "BSON_BINDATA" );
+            break;
+        case BSON_UNDEFINED:
+            bson_printf( "BSON_UNDEFINED" );
+            break;
+        case BSON_NULL:
+            bson_printf( "BSON_NULL" );
+            break;
+        case BSON_REGEX:
+            bson_printf( "BSON_REGEX: %s", bson_iterator_regex( &i ) );
+            break;
+        case BSON_CODE:
+            bson_printf( "BSON_CODE: %s", bson_iterator_code( &i ) );
+            break;
+        case BSON_CODEWSCOPE:
+            bson_printf( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) );
+            bson_init( &scope );
+            bson_iterator_code_scope( &i, &scope );
+            bson_printf( "\n\t SCOPE: " );
+            bson_print( &scope );
+            break;
+        case BSON_INT:
+            bson_printf( "%d" , bson_iterator_int( &i ) );
+            break;
+        case BSON_LONG:
+            bson_printf( "%lld" , ( uint64_t )bson_iterator_long( &i ) );
+            break;
+        case BSON_TIMESTAMP:
+            ts = bson_iterator_timestamp( &i );
+            bson_printf( "i: %d, t: %d", ts.i, ts.t );
+            break;
+        case BSON_OBJECT:
+        case BSON_ARRAY:
+            bson_printf( "\n" );
+            bson_print_raw( bson_iterator_value( &i ) , depth + 1 );
+            break;
+        default:
+            bson_errprintf( "can't print type : %d\n" , t );
+        }
+        bson_printf( "\n" );
+    }
+}
+
+/* ----------------------------
+   ITERATOR
+   ------------------------------ */
+
+MONGO_EXPORT bson_iterator* bson_iterator_create() {
+    return (bson_iterator*)malloc(sizeof(bson_iterator*));
+}
+
+MONGO_EXPORT void bson_iterator_dispose(bson_iterator* i) {
+    free(i);
+}
+
+MONGO_EXPORT void bson_iterator_init( bson_iterator *i, const bson *b ) {
+    i->cur = b->data + 4;
+    i->first = 1;
+}
+
+void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ) {
+    i->cur = buffer + 4;
+    i->first = 1;
+}
+
+MONGO_EXPORT bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ) {
+    bson_iterator_init( it, (bson *)obj );
+    while( bson_iterator_next( it ) ) {
+        if ( strcmp( name, bson_iterator_key( it ) ) == 0 )
+            break;
+    }
+    return bson_iterator_type( it );
+}
+
+bson_bool_t bson_iterator_more( const bson_iterator *i ) {
+    return *( i->cur );
+}
+
+MONGO_EXPORT bson_type bson_iterator_next( bson_iterator *i ) {
+    int ds;
+
+    if ( i->first ) {
+        i->first = 0;
+        return ( bson_type )( *i->cur );
+    }
+
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_EOO:
+        return BSON_EOO; /* don't advance */
+    case BSON_UNDEFINED:
+    case BSON_NULL:
+        ds = 0;
+        break;
+    case BSON_BOOL:
+        ds = 1;
+        break;
+    case BSON_INT:
+        ds = 4;
+        break;
+    case BSON_LONG:
+    case BSON_DOUBLE:
+    case BSON_TIMESTAMP:
+    case BSON_DATE:
+        ds = 8;
+        break;
+    case BSON_OID:
+        ds = 12;
+        break;
+    case BSON_STRING:
+    case BSON_SYMBOL:
+    case BSON_CODE:
+        ds = 4 + bson_iterator_int_raw( i );
+        break;
+    case BSON_BINDATA:
+        ds = 5 + bson_iterator_int_raw( i );
+        break;
+    case BSON_OBJECT:
+    case BSON_ARRAY:
+    case BSON_CODEWSCOPE:
+        ds = bson_iterator_int_raw( i );
+        break;
+    case BSON_DBREF:
+        ds = 4+12 + bson_iterator_int_raw( i );
+        break;
+    case BSON_REGEX: {
+        const char *s = bson_iterator_value( i );
+        const char *p = s;
+        p += strlen( p )+1;
+        p += strlen( p )+1;
+        ds = p-s;
+        break;
+    }
+
+    default: {
+        char msg[] = "unknown type: 000000000000";
+        bson_numstr( msg+14, ( unsigned )( i->cur[0] ) );
+        bson_fatal_msg( 0, msg );
+        return 0;
+    }
+    }
+
+    i->cur += 1 + strlen( i->cur + 1 ) + 1 + ds;
+
+    return ( bson_type )( *i->cur );
+}
+
+MONGO_EXPORT bson_type bson_iterator_type( const bson_iterator *i ) {
+    return ( bson_type )i->cur[0];
+}
+
+MONGO_EXPORT const char *bson_iterator_key( const bson_iterator *i ) {
+    return i->cur + 1;
+}
+
+const char *bson_iterator_value( const bson_iterator *i ) {
+    const char *t = i->cur + 1;
+    t += strlen( t ) + 1;
+    return t;
+}
+
+/* types */
+
+int bson_iterator_int_raw( const bson_iterator *i ) {
+    int out;
+    bson_little_endian32( &out, bson_iterator_value( i ) );
+    return out;
+}
+
+double bson_iterator_double_raw( const bson_iterator *i ) {
+    double out;
+    bson_little_endian64( &out, bson_iterator_value( i ) );
+    return out;
+}
+
+int64_t bson_iterator_long_raw( const bson_iterator *i ) {
+    int64_t out;
+    bson_little_endian64( &out, bson_iterator_value( i ) );
+    return out;
+}
+
+bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ) {
+    return bson_iterator_value( i )[0];
+}
+
+MONGO_EXPORT bson_oid_t *bson_iterator_oid( const bson_iterator *i ) {
+    return ( bson_oid_t * )bson_iterator_value( i );
+}
+
+MONGO_EXPORT int bson_iterator_int( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_INT:
+        return bson_iterator_int_raw( i );
+    case BSON_LONG:
+        return bson_iterator_long_raw( i );
+    case BSON_DOUBLE:
+        return bson_iterator_double_raw( i );
+    default:
+        return 0;
+    }
+}
+
+MONGO_EXPORT double bson_iterator_double( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_INT:
+        return bson_iterator_int_raw( i );
+    case BSON_LONG:
+        return bson_iterator_long_raw( i );
+    case BSON_DOUBLE:
+        return bson_iterator_double_raw( i );
+    default:
+        return 0;
+    }
+}
+
+MONGO_EXPORT int64_t bson_iterator_long( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_INT:
+        return bson_iterator_int_raw( i );
+    case BSON_LONG:
+        return bson_iterator_long_raw( i );
+    case BSON_DOUBLE:
+        return bson_iterator_double_raw( i );
+    default:
+        return 0;
+    }
+}
+
+bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ) {
+    bson_timestamp_t ts;
+    bson_little_endian32( &( ts.i ), bson_iterator_value( i ) );
+    bson_little_endian32( &( ts.t ), bson_iterator_value( i ) + 4 );
+    return ts;
+}
+
+
+MONGO_EXPORT int bson_iterator_timestamp_time( const bson_iterator *i ) {
+    int time;
+    bson_little_endian32( &time, bson_iterator_value( i ) + 4 );
+    return time;
+}
+
+
+MONGO_EXPORT int bson_iterator_timestamp_increment( const bson_iterator *i ) {
+    int increment;
+    bson_little_endian32( &increment, bson_iterator_value( i ) );
+    return increment;
+}
+
+
+MONGO_EXPORT bson_bool_t bson_iterator_bool( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_BOOL:
+        return bson_iterator_bool_raw( i );
+    case BSON_INT:
+        return bson_iterator_int_raw( i ) != 0;
+    case BSON_LONG:
+        return bson_iterator_long_raw( i ) != 0;
+    case BSON_DOUBLE:
+        return bson_iterator_double_raw( i ) != 0;
+    case BSON_EOO:
+    case BSON_NULL:
+        return 0;
+    default:
+        return 1;
+    }
+}
+
+MONGO_EXPORT const char *bson_iterator_string( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_STRING:
+    case BSON_SYMBOL:
+        return bson_iterator_value( i ) + 4;
+    default:
+        return "";
+    }
+}
+
+int bson_iterator_string_len( const bson_iterator *i ) {
+    return bson_iterator_int_raw( i );
+}
+
+MONGO_EXPORT const char *bson_iterator_code( const bson_iterator *i ) {
+    switch ( bson_iterator_type( i ) ) {
+    case BSON_STRING:
+    case BSON_CODE:
+        return bson_iterator_value( i ) + 4;
+    case BSON_CODEWSCOPE:
+        return bson_iterator_value( i ) + 8;
+    default:
+        return NULL;
+    }
+}
+
+MONGO_EXPORT void bson_iterator_code_scope( const bson_iterator *i, bson *scope ) {
+    if ( bson_iterator_type( i ) == BSON_CODEWSCOPE ) {
+        int code_len;
+        bson_little_endian32( &code_len, bson_iterator_value( i )+4 );
+        bson_init_data( scope, ( void * )( bson_iterator_value( i )+8+code_len ) );
+        _bson_reset( scope );
+        scope->finished = 1;
+    } else {
+        bson_empty( scope );
+    }
+}
+
+MONGO_EXPORT bson_date_t bson_iterator_date( const bson_iterator *i ) {
+    return bson_iterator_long_raw( i );
+}
+
+time_t bson_iterator_time_t( const bson_iterator *i ) {
+    return bson_iterator_date( i ) / 1000;
+}
+
+MONGO_EXPORT int bson_iterator_bin_len( const bson_iterator *i ) {
+    return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
+           ? bson_iterator_int_raw( i ) - 4
+           : bson_iterator_int_raw( i );
+}
+
+MONGO_EXPORT char bson_iterator_bin_type( const bson_iterator *i ) {
+    return bson_iterator_value( i )[4];
+}
+
+MONGO_EXPORT const char *bson_iterator_bin_data( const bson_iterator *i ) {
+    return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD )
+           ? bson_iterator_value( i ) + 9
+           : bson_iterator_value( i ) + 5;
+}
+
+MONGO_EXPORT const char *bson_iterator_regex( const bson_iterator *i ) {
+    return bson_iterator_value( i );
+}
+
+MONGO_EXPORT const char *bson_iterator_regex_opts( const bson_iterator *i ) {
+    const char *p = bson_iterator_value( i );
+    return p + strlen( p ) + 1;
+
+}
+
+void bson_iterator_subobject( const bson_iterator *i, bson *sub ) {
+    bson_init_data( sub, ( char * )bson_iterator_value( i ) );
+    _bson_reset( sub );
+    sub->finished = 1;
+}
+
+MONGO_EXPORT void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ) {
+    bson_iterator_from_buffer( sub, bson_iterator_value( i ) );
+}
+
+/* ----------------------------
+   BUILDING
+   ------------------------------ */
+
+static void _bson_init_size( bson *b, int size ) {
+    if( size == 0 )
+        b->data = NULL;
+    else
+        b->data = ( char * )bson_malloc( size );
+    b->dataSize = size;
+    b->cur = b->data + 4;
+    _bson_reset( b );
+}
+
+MONGO_EXPORT void bson_init( bson *b ) {
+    _bson_init_size( b, initialBufferSize );
+}
+
+void bson_init_size( bson *b, int size ) {
+    _bson_init_size( b, size );
+}
+
+void bson_append_byte( bson *b, char c ) {
+    b->cur[0] = c;
+    b->cur++;
+}
+
+void bson_append( bson *b, const void *data, int len ) {
+    memcpy( b->cur , data , len );
+    b->cur += len;
+}
+
+void bson_append32( bson *b, const void *data ) {
+    bson_little_endian32( b->cur, data );
+    b->cur += 4;
+}
+
+void bson_append64( bson *b, const void *data ) {
+    bson_little_endian64( b->cur, data );
+    b->cur += 8;
+}
+
+int bson_ensure_space( bson *b, const int bytesNeeded ) {
+    int pos = b->cur - b->data;
+    char *orig = b->data;
+    int new_size;
+
+    if ( pos + bytesNeeded <= b->dataSize )
+        return BSON_OK;
+
+    new_size = 1.5 * ( b->dataSize + bytesNeeded );
+
+    if( new_size < b->dataSize ) {
+        if( ( b->dataSize + bytesNeeded ) < INT_MAX )
+            new_size = INT_MAX;
+        else {
+            b->err = BSON_SIZE_OVERFLOW;
+            return BSON_ERROR;
+        }
+    }
+
+    b->data = bson_realloc( b->data, new_size );
+    if ( !b->data )
+        bson_fatal_msg( !!b->data, "realloc() failed" );
+
+    b->dataSize = new_size;
+    b->cur += b->data - orig;
+
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_finish( bson *b ) {
+    int i;
+
+    if( b->err & BSON_NOT_UTF8 )
+        return BSON_ERROR;
+
+    if ( ! b->finished ) {
+        if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
+        bson_append_byte( b, 0 );
+        i = b->cur - b->data;
+        bson_little_endian32( b->data, &i );
+        b->finished = 1;
+    }
+
+    return BSON_OK;
+}
+
+MONGO_EXPORT void bson_destroy( bson *b ) {
+    if (b) {
+        bson_free( b->data );
+        b->err = 0;
+        b->data = 0;
+        b->cur = 0;
+        b->finished = 1;
+    }
+}
+
+static int bson_append_estart( bson *b, int type, const char *name, const int dataSize ) {
+    const int len = strlen( name ) + 1;
+
+    if ( b->finished ) {
+        b->err |= BSON_ALREADY_FINISHED;
+        return BSON_ERROR;
+    }
+
+    if ( bson_ensure_space( b, 1 + len + dataSize ) == BSON_ERROR ) {
+        return BSON_ERROR;
+    }
+
+    if( bson_check_field_name( b, ( const char * )name, len - 1 ) == BSON_ERROR ) {
+        bson_builder_error( b );
+        return BSON_ERROR;
+    }
+
+    bson_append_byte( b, ( char )type );
+    bson_append( b, name, len );
+    return BSON_OK;
+}
+
+/* ----------------------------
+   BUILDING TYPES
+   ------------------------------ */
+
+MONGO_EXPORT int bson_append_int( bson *b, const char *name, const int i ) {
+    if ( bson_append_estart( b, BSON_INT, name, 4 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append32( b , &i );
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_long( bson *b, const char *name, const int64_t i ) {
+    if ( bson_append_estart( b , BSON_LONG, name, 8 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append64( b , &i );
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_double( bson *b, const char *name, const double d ) {
+    if ( bson_append_estart( b, BSON_DOUBLE, name, 8 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append64( b , &d );
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_bool( bson *b, const char *name, const bson_bool_t i ) {
+    if ( bson_append_estart( b, BSON_BOOL, name, 1 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append_byte( b , i != 0 );
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_null( bson *b, const char *name ) {
+    if ( bson_append_estart( b , BSON_NULL, name, 0 ) == BSON_ERROR )
+        return BSON_ERROR;
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_undefined( bson *b, const char *name ) {
+    if ( bson_append_estart( b, BSON_UNDEFINED, name, 0 ) == BSON_ERROR )
+        return BSON_ERROR;
+    return BSON_OK;
+}
+
+int bson_append_string_base( bson *b, const char *name,
+                             const char *value, int len, bson_type type ) {
+
+    int sl = len + 1;
+    if ( bson_check_string( b, ( const char * )value, sl - 1 ) == BSON_ERROR )
+        return BSON_ERROR;
+    if ( bson_append_estart( b, type, name, 4 + sl ) == BSON_ERROR ) {
+        return BSON_ERROR;
+    }
+    bson_append32( b , &sl );
+    bson_append( b , value , sl - 1 );
+    bson_append( b , "\0" , 1 );
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_string( bson *b, const char *name, const char *value ) {
+    return bson_append_string_base( b, name, value, strlen ( value ), BSON_STRING );
+}
+
+MONGO_EXPORT int bson_append_symbol( bson *b, const char *name, const char *value ) {
+    return bson_append_string_base( b, name, value, strlen ( value ), BSON_SYMBOL );
+}
+
+MONGO_EXPORT int bson_append_code( bson *b, const char *name, const char *value ) {
+    return bson_append_string_base( b, name, value, strlen ( value ), BSON_CODE );
+}
+
+int bson_append_string_n( bson *b, const char *name, const char *value, int len ) {
+    return bson_append_string_base( b, name, value, len, BSON_STRING );
+}
+
+int bson_append_symbol_n( bson *b, const char *name, const char *value, int len ) {
+    return bson_append_string_base( b, name, value, len, BSON_SYMBOL );
+}
+
+int bson_append_code_n( bson *b, const char *name, const char *value, int len ) {
+    return bson_append_string_base( b, name, value, len, BSON_CODE );
+}
+
+int bson_append_code_w_scope_n( bson *b, const char *name,
+                                const char *code, int len, const bson *scope ) {
+
+    int sl = len + 1;
+    int size = 4 + 4 + sl + bson_size( scope );
+    if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append32( b, &size );
+    bson_append32( b, &sl );
+    bson_append( b, code, sl );
+    bson_append( b, scope->data, bson_size( scope ) );
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ) {
+    return bson_append_code_w_scope_n( b, name, code, strlen ( code ), scope );
+}
+
+MONGO_EXPORT int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ) {
+    if ( type == BSON_BIN_BINARY_OLD ) {
+        int subtwolen = len + 4;
+        if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+4+len ) == BSON_ERROR )
+            return BSON_ERROR;
+        bson_append32( b, &subtwolen );
+        bson_append_byte( b, type );
+        bson_append32( b, &len );
+        bson_append( b, str, len );
+    } else {
+        if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+len ) == BSON_ERROR )
+            return BSON_ERROR;
+        bson_append32( b, &len );
+        bson_append_byte( b, type );
+        bson_append( b, str, len );
+    }
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ) {
+    if ( bson_append_estart( b, BSON_OID, name, 12 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append( b , oid , 12 );
+    return BSON_OK;
+}
+
+int bson_append_new_oid( bson *b, const char *name ) {
+    bson_oid_t oid;
+    bson_oid_gen( &oid );
+    return bson_append_oid( b, name, &oid );
+}
+
+MONGO_EXPORT int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ) {
+    const int plen = strlen( pattern )+1;
+    const int olen = strlen( opts )+1;
+    if ( bson_append_estart( b, BSON_REGEX, name, plen + olen ) == BSON_ERROR )
+        return BSON_ERROR;
+    if ( bson_check_string( b, pattern, plen - 1 ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append( b , pattern , plen );
+    bson_append( b , opts , olen );
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_bson( bson *b, const char *name, const bson *bson ) {
+    if ( bson_append_estart( b, BSON_OBJECT, name, bson_size( bson ) ) == BSON_ERROR )
+        return BSON_ERROR;
+    bson_append( b , bson->data , bson_size( bson ) );
+    return BSON_OK;
+}
+
+int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ) {
+    bson_iterator next = *elem;
+    int size;
+
+    bson_iterator_next( &next );
+    size = next.cur - elem->cur;
+
+    if ( name_or_null == NULL ) {
+        if( bson_ensure_space( b, size ) == BSON_ERROR )
+            return BSON_ERROR;
+        bson_append( b, elem->cur, size );
+    } else {
+        int data_size = size - 2 - strlen( bson_iterator_key( elem ) );
+        bson_append_estart( b, elem->cur[0], name_or_null, data_size );
+        bson_append( b, bson_iterator_value( elem ), data_size );
+    }
+
+    return BSON_OK;
+}
+
+int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ) {
+    if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
+
+    bson_append32( b , &( ts->i ) );
+    bson_append32( b , &( ts->t ) );
+
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_timestamp2( bson *b, const char *name, int time, int increment ) {
+    if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
+
+    bson_append32( b , &increment );
+    bson_append32( b , &time );
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_date( bson *b, const char *name, bson_date_t millis ) {
+    if ( bson_append_estart( b, BSON_DATE, name, 8 ) == BSON_ERROR ) return BSON_ERROR;
+    bson_append64( b , &millis );
+    return BSON_OK;
+}
+
+int bson_append_time_t( bson *b, const char *name, time_t secs ) {
+    return bson_append_date( b, name, ( bson_date_t )secs * 1000 );
+}
+
+MONGO_EXPORT int bson_append_start_object( bson *b, const char *name ) {
+    if ( bson_append_estart( b, BSON_OBJECT, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
+    b->stack[ b->stackPos++ ] = b->cur - b->data;
+    bson_append32( b , &zero );
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_start_array( bson *b, const char *name ) {
+    if ( bson_append_estart( b, BSON_ARRAY, name, 5 ) == BSON_ERROR ) return BSON_ERROR;
+    b->stack[ b->stackPos++ ] = b->cur - b->data;
+    bson_append32( b , &zero );
+    return BSON_OK;
+}
+
+MONGO_EXPORT int bson_append_finish_object( bson *b ) {
+    char *start;
+    int i;
+    if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR;
+    bson_append_byte( b , 0 );
+
+    start = b->data + b->stack[ --b->stackPos ];
+    i = b->cur - start;
+    bson_little_endian32( start, &i );
+
+    return BSON_OK;
+}
+
+MONGO_EXPORT double bson_int64_to_double( int64_t i64 ) {
+  return (double)i64;
+}
+
+int bson_append_finish_array( bson *b ) {
+    return bson_append_finish_object( b );
+}
+
+/* Error handling and allocators. */
+
+static bson_err_handler err_handler = NULL;
+
+MONGO_EXPORT bson_err_handler set_bson_err_handler( bson_err_handler func ) {
+    bson_err_handler old = err_handler;
+    err_handler = func;
+    return old;
+}
+
+void *bson_malloc( int size ) {
+    void *p;
+    p = bson_malloc_func( size );
+    bson_fatal_msg( !!p, "malloc() failed" );
+    return p;
+}
+
+void *bson_realloc( void *ptr, int size ) {
+    void *p;
+    p = bson_realloc_func( ptr, size );
+    bson_fatal_msg( !!p, "realloc() failed" );
+    return p;
+}
+
+int _bson_errprintf( const char *format, ... ) {
+    va_list ap;
+    int ret;
+    va_start( ap, format );
+#ifndef R_SAFETY_NET
+    ret = vfprintf( stderr, format, ap );
+#endif
+    va_end( ap );
+
+    return ret;
+}
+
+/**
+ * This method is invoked when a non-fatal bson error is encountered.
+ * Calls the error handler if available.
+ *
+ *  @param
+ */
+void bson_builder_error( bson *b ) {
+    if( err_handler )
+        err_handler( "BSON error." );
+}
+
+void bson_fatal( int ok ) {
+    bson_fatal_msg( ok, "" );
+}
+
+void bson_fatal_msg( int ok , const char *msg ) {
+    if ( ok )
+        return;
+
+    if ( err_handler ) {
+        err_handler( msg );
+    }
+#ifndef R_SAFETY_NET
+    bson_errprintf( "error: %s\n" , msg );
+    exit( -5 );
+#endif
+}
+
+
+/* Efficiently copy an integer to a string. */
+extern const char bson_numstrs[1000][4];
+
+void bson_numstr( char *str, int i ) {
+    if( i < 1000 )
+        memcpy( str, bson_numstrs[i], 4 );
+    else
+        bson_sprintf( str,"%d", i );
+}
+
+MONGO_EXPORT void bson_swap_endian64( void *outp, const void *inp ) {
+    const char *in = ( const char * )inp;
+    char *out = ( char * )outp;
+
+    out[0] = in[7];
+    out[1] = in[6];
+    out[2] = in[5];
+    out[3] = in[4];
+    out[4] = in[3];
+    out[5] = in[2];
+    out[6] = in[1];
+    out[7] = in[0];
+
+}
+
+MONGO_EXPORT void bson_swap_endian32( void *outp, const void *inp ) {
+    const char *in = ( const char * )inp;
+    char *out = ( char * )outp;
+
+    out[0] = in[3];
+    out[1] = in[2];
+    out[2] = in[1];
+    out[3] = in[0];
+}
diff --git a/third_party/bson_c/src/bson.h b/third_party/bson_c/src/bson.h
new file mode 100644
index 0000000..b8c5ba2
--- /dev/null
+++ b/third_party/bson_c/src/bson.h
@@ -0,0 +1,1036 @@
+/**
+ * @file bson.h
+ * @brief BSON Declarations
+ */
+
+/*    Copyright 2009-2011 10gen 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.
+ */
+
+#ifndef BSON_H_
+#define BSON_H_
+
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef __GNUC__
+    #define MONGO_INLINE static __inline__
+    #define MONGO_EXPORT
+#else
+    #define MONGO_INLINE static
+    #ifdef MONGO_STATIC_BUILD
+        #define MONGO_EXPORT
+    #elif defined(MONGO_DLL_BUILD)
+        #define MONGO_EXPORT __declspec(dllexport)
+    #else
+        #define MONGO_EXPORT __declspec(dllimport)
+    #endif
+#endif
+
+#ifdef __cplusplus
+#define MONGO_EXTERN_C_START extern "C" {
+#define MONGO_EXTERN_C_END }
+#else
+#define MONGO_EXTERN_C_START
+#define MONGO_EXTERN_C_END
+#endif
+
+#if defined(MONGO_HAVE_STDINT) || __STDC_VERSION__ >= 199901L
+#include <stdint.h>
+#elif defined(MONGO_HAVE_UNISTD)
+#include <unistd.h>
+#elif defined(MONGO_USE__INT64)
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#elif defined(MONGO_USE_LONG_LONG_INT)
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#else
+#error Must compile with c99 or define MONGO_HAVE_STDINT, MONGO_HAVE_UNISTD, MONGO_USE__INT64, or MONGO_USE_LONG_INT.
+#endif
+
+#ifdef MONGO_BIG_ENDIAN
+#define bson_little_endian64(out, in) ( bson_swap_endian64(out, in) )
+#define bson_little_endian32(out, in) ( bson_swap_endian32(out, in) )
+#define bson_big_endian64(out, in) ( memcpy(out, in, 8) )
+#define bson_big_endian32(out, in) ( memcpy(out, in, 4) )
+#else
+#define bson_little_endian64(out, in) ( memcpy(out, in, 8) )
+#define bson_little_endian32(out, in) ( memcpy(out, in, 4) )
+#define bson_big_endian64(out, in) ( bson_swap_endian64(out, in) )
+#define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) )
+#endif
+
+MONGO_EXTERN_C_START
+
+#define BSON_OK 0
+#define BSON_ERROR -1
+
+enum bson_error_t {
+    BSON_SIZE_OVERFLOW = 1 /**< Trying to create a BSON object larger than INT_MAX. */
+};
+
+enum bson_validity_t {
+    BSON_VALID = 0,                 /**< BSON is valid and UTF-8 compliant. */
+    BSON_NOT_UTF8 = ( 1<<1 ),       /**< A key or a string is not valid UTF-8. */
+    BSON_FIELD_HAS_DOT = ( 1<<2 ),  /**< Warning: key contains '.' character. */
+    BSON_FIELD_INIT_DOLLAR = ( 1<<3 ), /**< Warning: key starts with '$' character. */
+    BSON_ALREADY_FINISHED = ( 1<<4 )  /**< Trying to modify a finished BSON object. */
+};
+
+enum bson_binary_subtype_t {
+    BSON_BIN_BINARY = 0,
+    BSON_BIN_FUNC = 1,
+    BSON_BIN_BINARY_OLD = 2,
+    BSON_BIN_UUID = 3,
+    BSON_BIN_MD5 = 5,
+    BSON_BIN_USER = 128
+};
+
+typedef enum {
+    BSON_EOO = 0,
+    BSON_DOUBLE = 1,
+    BSON_STRING = 2,
+    BSON_OBJECT = 3,
+    BSON_ARRAY = 4,
+    BSON_BINDATA = 5,
+    BSON_UNDEFINED = 6,
+    BSON_OID = 7,
+    BSON_BOOL = 8,
+    BSON_DATE = 9,
+    BSON_NULL = 10,
+    BSON_REGEX = 11,
+    BSON_DBREF = 12, /**< Deprecated. */
+    BSON_CODE = 13,
+    BSON_SYMBOL = 14,
+    BSON_CODEWSCOPE = 15,
+    BSON_INT = 16,
+    BSON_TIMESTAMP = 17,
+    BSON_LONG = 18
+} bson_type;
+
+typedef int bson_bool_t;
+
+typedef struct {
+    const char *cur;
+    bson_bool_t first;
+} bson_iterator;
+
+typedef struct {
+    char *data;
+    char *cur;
+    int dataSize;
+    bson_bool_t finished;
+    int stack[32];
+    int stackPos;
+    int err; /**< Bitfield representing errors or warnings on this buffer */
+    char *errstr; /**< A string representation of the most recent error or warning. */
+} bson;
+
+#pragma pack(1)
+typedef union {
+    char bytes[12];
+    int ints[3];
+} bson_oid_t;
+#pragma pack()
+
+typedef int64_t bson_date_t; /* milliseconds since epoch UTC */
+
+typedef struct {
+    int i; /* increment */
+    int t; /* time in seconds */
+} bson_timestamp_t;
+
+/* ----------------------------
+   READING
+   ------------------------------ */
+
+MONGO_EXPORT bson* bson_create();
+MONGO_EXPORT void  bson_dispose(bson* b);
+/**
+ * Size of a BSON object.
+ *
+ * @param b the BSON object.
+ *
+ * @return the size.
+ */
+MONGO_EXPORT int bson_size( const bson *b );
+MONGO_EXPORT int bson_buffer_size( const bson *b );
+
+/**
+ * Print a string representation of a BSON object.
+ *
+ * @param b the BSON object to print.
+ */
+void bson_print( bson *b );
+
+/**
+ * Return a pointer to the raw buffer stored by this bson object.
+ *
+ * @param b a BSON object
+ */
+const char *bson_data( bson *b );
+
+/**
+ * Print a string representation of a BSON object.
+ *
+ * @param bson the raw data to print.
+ * @param depth the depth to recurse the object.x
+ */
+void bson_print_raw( const char *bson , int depth );
+
+/**
+ * Advance a bson_iterator to the named field.
+ *
+ * @param it the bson_iterator to use.
+ * @param obj the BSON object to use.
+ * @param name the name of the field to find.
+ *
+ * @return the type of the found object or BSON_EOO if it is not found.
+ */
+MONGO_EXPORT bson_type bson_find( bson_iterator *it, const bson *obj, const char *name );
+
+
+MONGO_EXPORT bson_iterator* bson_iterator_create();
+MONGO_EXPORT void bson_iterator_dispose(bson_iterator*);
+/**
+ * Initialize a bson_iterator.
+ *
+ * @param i the bson_iterator to initialize.
+ * @param bson the BSON object to associate with the iterator.
+ */
+MONGO_EXPORT void bson_iterator_init( bson_iterator *i , const bson *b );
+
+/**
+ * Initialize a bson iterator from a const char* buffer. Note
+ * that this is mostly used internally.
+ *
+ * @param i the bson_iterator to initialize.
+ * @param buffer the buffer to point to.
+ */
+void bson_iterator_from_buffer( bson_iterator *i, const char *buffer );
+
+/* more returns true for eoo. best to loop with bson_iterator_next(&it) */
+/**
+ * Check to see if the bson_iterator has more data.
+ *
+ * @param i the iterator.
+ *
+ * @return  returns true if there is more data.
+ */
+bson_bool_t bson_iterator_more( const bson_iterator *i );
+
+/**
+ * Point the iterator at the next BSON object.
+ *
+ * @param i the bson_iterator.
+ *
+ * @return the type of the next BSON object.
+ */
+MONGO_EXPORT bson_type bson_iterator_next( bson_iterator *i );
+
+/**
+ * Get the type of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return  the type of the current BSON object.
+ */
+MONGO_EXPORT bson_type bson_iterator_type( const bson_iterator *i );
+
+/**
+ * Get the key of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the key of the current BSON object.
+ */
+MONGO_EXPORT const char *bson_iterator_key( const bson_iterator *i );
+
+/**
+ * Get the value of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return  the value of the current BSON object.
+ */
+const char *bson_iterator_value( const bson_iterator *i );
+
+/* these convert to the right type (return 0 if non-numeric) */
+/**
+ * Get the double value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return  the value of the current BSON object.
+ */
+MONGO_EXPORT double bson_iterator_double( const bson_iterator *i );
+
+/**
+ * Get the int value of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return  the value of the current BSON object.
+ */
+MONGO_EXPORT int bson_iterator_int( const bson_iterator *i );
+
+/**
+ * Get the long value of the BSON object currently pointed to by the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+MONGO_EXPORT int64_t bson_iterator_long( const bson_iterator *i );
+
+/* return the bson timestamp as a whole or in parts */
+/**
+ * Get the timestamp value of the BSON object currently pointed to by
+ * the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i );
+MONGO_EXPORT int bson_iterator_timestamp_time( const bson_iterator *i );
+MONGO_EXPORT int bson_iterator_timestamp_increment( const bson_iterator *i );
+
+/**
+ * Get the boolean value of the BSON object currently pointed to by
+ * the iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+/* false: boolean false, 0 in any type, or null */
+/* true: anything else (even empty strings and objects) */
+MONGO_EXPORT bson_bool_t bson_iterator_bool( const bson_iterator *i );
+
+/**
+ * Get the double value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+/* these assume you are using the right type */
+double bson_iterator_double_raw( const bson_iterator *i );
+
+/**
+ * Get the int value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+int bson_iterator_int_raw( const bson_iterator *i );
+
+/**
+ * Get the long value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+int64_t bson_iterator_long_raw( const bson_iterator *i );
+
+/**
+ * Get the bson_bool_t value of the BSON object currently pointed to by the
+ * iterator. Assumes the correct type is used.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+bson_bool_t bson_iterator_bool_raw( const bson_iterator *i );
+
+/**
+ * Get the bson_oid_t value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON object.
+ */
+MONGO_EXPORT bson_oid_t *bson_iterator_oid( const bson_iterator *i );
+
+/**
+ * Get the string value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return  the value of the current BSON object.
+ */
+/* these can also be used with bson_code and bson_symbol*/
+MONGO_EXPORT const char *bson_iterator_string( const bson_iterator *i );
+
+/**
+ * Get the string length of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the length of the current BSON object.
+ */
+int bson_iterator_string_len( const bson_iterator *i );
+
+/**
+ * Get the code value of the BSON object currently pointed to by the
+ * iterator. Works with bson_code, bson_codewscope, and BSON_STRING
+ * returns NULL for everything else.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the code value of the current BSON object.
+ */
+/* works with bson_code, bson_codewscope, and BSON_STRING */
+/* returns NULL for everything else */
+MONGO_EXPORT const char *bson_iterator_code( const bson_iterator *i );
+
+/**
+ * Calls bson_empty on scope if not a bson_codewscope
+ *
+ * @param i the bson_iterator.
+ * @param scope the bson scope.
+ */
+/* calls bson_empty on scope if not a bson_codewscope */
+MONGO_EXPORT void bson_iterator_code_scope( const bson_iterator *i, bson *scope );
+
+/**
+ * Get the date value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the date value of the current BSON object.
+ */
+/* both of these only work with bson_date */
+MONGO_EXPORT bson_date_t bson_iterator_date( const bson_iterator *i );
+
+/**
+ * Get the time value of the BSON object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the time value of the current BSON object.
+ */
+time_t bson_iterator_time_t( const bson_iterator *i );
+
+/**
+ * Get the length of the BSON binary object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the length of the current BSON binary object.
+ */
+MONGO_EXPORT int bson_iterator_bin_len( const bson_iterator *i );
+
+/**
+ * Get the type of the BSON binary object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the type of the current BSON binary object.
+ */
+MONGO_EXPORT char bson_iterator_bin_type( const bson_iterator *i );
+
+/**
+ * Get the value of the BSON binary object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON binary object.
+ */
+MONGO_EXPORT const char *bson_iterator_bin_data( const bson_iterator *i );
+
+/**
+ * Get the value of the BSON regex object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator
+ *
+ * @return the value of the current BSON regex object.
+ */
+MONGO_EXPORT const char *bson_iterator_regex( const bson_iterator *i );
+
+/**
+ * Get the options of the BSON regex object currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator.
+ *
+ * @return the options of the current BSON regex object.
+ */
+MONGO_EXPORT const char *bson_iterator_regex_opts( const bson_iterator *i );
+
+/* these work with BSON_OBJECT and BSON_ARRAY */
+/**
+ * Get the BSON subobject currently pointed to by the
+ * iterator.
+ *
+ * @param i the bson_iterator.
+ * @param sub the BSON subobject destination.
+ */
+void bson_iterator_subobject( const bson_iterator *i, bson *sub );
+
+/**
+ * Get a bson_iterator that on the BSON subobject.
+ *
+ * @param i the bson_iterator.
+ * @param sub the iterator to point at the BSON subobject.
+ */
+MONGO_EXPORT void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub );
+
+/* str must be at least 24 hex chars + null byte */
+/**
+ * Create a bson_oid_t from a string.
+ *
+ * @param oid the bson_oid_t destination.
+ * @param str a null terminated string comprised of at least 24 hex chars.
+ */
+MONGO_EXPORT void bson_oid_from_string( bson_oid_t *oid, const char *str );
+
+/**
+ * Create a string representation of the bson_oid_t.
+ *
+ * @param oid the bson_oid_t source.
+ * @param str the string representation destination.
+ */
+MONGO_EXPORT void bson_oid_to_string( const bson_oid_t *oid, char *str );
+
+/**
+ * Create a bson_oid object.
+ *
+ * @param oid the destination for the newly created bson_oid_t.
+ */
+MONGO_EXPORT void bson_oid_gen( bson_oid_t *oid );
+
+/**
+ * Set a function to be used to generate the second four bytes
+ * of an object id.
+ *
+ * @param func a pointer to a function that returns an int.
+ */
+void bson_set_oid_fuzz( int ( *func )( void ) );
+
+/**
+ * Set a function to be used to generate the incrementing part
+ * of an object id (last four bytes). If you need thread-safety
+ * in generating object ids, you should set this function.
+ *
+ * @param func a pointer to a function that returns an int.
+ */
+void bson_set_oid_inc( int ( *func )( void ) );
+
+/**
+ * Get the time a bson_oid_t was created.
+ *
+ * @param oid the bson_oid_t.
+ */
+time_t bson_oid_generated_time( bson_oid_t *oid ); /* Gives the time the OID was created */
+
+/* ----------------------------
+   BUILDING
+   ------------------------------ */
+
+/**
+ *  Initialize a new bson object. If not created
+ *  with bson_new, you must initialize each new bson
+ *  object using this function.
+ *
+ *  @note When finished, you must pass the bson object to
+ *      bson_destroy( ).
+ */
+MONGO_EXPORT void bson_init( bson *b );
+
+/**
+ * Initialize a BSON object, and point its data
+ * pointer to the provided char*.
+ *
+ * @param b the BSON object to initialize.
+ * @param data the raw BSON data.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_init_data( bson *b , char *data );
+int bson_init_finished_data( bson *b, char *data ) ;
+
+/**
+ * Initialize a BSON object, and set its
+ * buffer to the given size.
+ *
+ * @param b the BSON object to initialize.
+ * @param size the initial size of the buffer.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+void bson_init_size( bson *b, int size );
+
+/**
+ * Grow a bson object.
+ *
+ * @param b the bson to grow.
+ * @param bytesNeeded the additional number of bytes needed.
+ *
+ * @return BSON_OK or BSON_ERROR with the bson error object set.
+ *   Exits if allocation fails.
+ */
+int bson_ensure_space( bson *b, const int bytesNeeded );
+
+/**
+ * Finalize a bson object.
+ *
+ * @param b the bson object to finalize.
+ *
+ * @return the standard error code. To deallocate memory,
+ *   call bson_destroy on the bson object.
+ */
+MONGO_EXPORT int bson_finish( bson *b );
+
+/**
+ * Destroy a bson object.
+ *
+ * @param b the bson object to destroy.
+ *
+ */
+MONGO_EXPORT void bson_destroy( bson *b );
+
+/**
+ * Returns a pointer to a static empty BSON object.
+ *
+ * @param obj the BSON object to initialize.
+ *
+ * @return the empty initialized BSON object.
+ */
+/* returns pointer to static empty bson object */
+bson *bson_empty( bson *obj );
+
+/**
+ * Make a complete copy of the a BSON object.
+ * The source bson object must be in a finished
+ * state; otherwise, the copy will fail.
+ *
+ * @param out the copy destination BSON object.
+ * @param in the copy source BSON object.
+ */
+MONGO_EXPORT int bson_copy( bson *out, const bson *in ); /* puts data in new buffer. NOOP if out==NULL */
+
+/**
+ * Append a previously created bson_oid_t to a bson object.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the bson_oid_t.
+ * @param oid the bson_oid_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid );
+
+/**
+ * Append a bson_oid_t to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the bson_oid_t.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_new_oid( bson *b, const char *name );
+
+/**
+ * Append an int to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the int.
+ * @param i the int to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_int( bson *b, const char *name, const int i );
+
+/**
+ * Append an long to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the long.
+ * @param i the long to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_long( bson *b, const char *name, const int64_t i );
+
+/**
+ * Append an double to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the double.
+ * @param d the double to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_double( bson *b, const char *name, const double d );
+
+/**
+ * Append a string to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the string.
+ * @param str the string to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+*/
+MONGO_EXPORT int bson_append_string( bson *b, const char *name, const char *str );
+
+/**
+ * Append len bytes of a string to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the string.
+ * @param str the string to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_string_n( bson *b, const char *name, const char *str, int len );
+
+/**
+ * Append a symbol to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the symbol.
+ * @param str the symbol to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_symbol( bson *b, const char *name, const char *str );
+
+/**
+ * Append len bytes of a symbol to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the symbol.
+ * @param str the symbol to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_symbol_n( bson *b, const char *name, const char *str, int len );
+
+/**
+ * Append code to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the code to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_code( bson *b, const char *name, const char *str );
+
+/**
+ * Append len bytes of code to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the code to append.
+ * @param len the number of bytes from str to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_code_n( bson *b, const char *name, const char *str, int len );
+
+/**
+ * Append code to a bson with scope.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the string to append.
+ * @param scope a BSON object containing the scope.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope );
+
+/**
+ * Append len bytes of code to a bson with scope.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the code.
+ * @param str the string to append.
+ * @param len the number of bytes from str to append.
+ * @param scope a BSON object containing the scope.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_code_w_scope_n( bson *b, const char *name, const char *code, int size, const bson *scope );
+
+/**
+ * Append binary data to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the data.
+ * @param type the binary data type.
+ * @param str the binary data.
+ * @param len the length of the data.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_binary( bson *b, const char *name, char type, const char *str, int len );
+
+/**
+ * Append a bson_bool_t to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the boolean value.
+ * @param v the bson_bool_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_bool( bson *b, const char *name, const bson_bool_t v );
+
+/**
+ * Append a null value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the null value.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_null( bson *b, const char *name );
+
+/**
+ * Append an undefined value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the undefined value.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_undefined( bson *b, const char *name );
+
+/**
+ * Append a regex value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the regex value.
+ * @param pattern the regex pattern to append.
+ * @param the regex options.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts );
+
+/**
+ * Append bson data to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the bson data.
+ * @param bson the bson object to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_bson( bson *b, const char *name, const bson *bson );
+
+/**
+ * Append a BSON element to a bson from the current point of an iterator.
+ *
+ * @param b the bson to append to.
+ * @param name_or_null the key for the BSON element, or NULL.
+ * @param elem the bson_iterator.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem );
+
+/**
+ * Append a bson_timestamp_t value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the timestampe value.
+ * @param ts the bson_timestamp_t value to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts );
+MONGO_EXPORT int bson_append_timestamp2( bson *b, const char *name, int time, int increment );
+
+/* these both append a bson_date */
+/**
+ * Append a bson_date_t value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the date value.
+ * @param millis the bson_date_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_date( bson *b, const char *name, bson_date_t millis );
+
+/**
+ * Append a time_t value to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the key for the date value.
+ * @param secs the time_t to append.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_time_t( bson *b, const char *name, time_t secs );
+
+/**
+ * Start appending a new object to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the name of the new object.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_start_object( bson *b, const char *name );
+
+/**
+ * Start appending a new array to a bson.
+ *
+ * @param b the bson to append to.
+ * @param name the name of the new array.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_start_array( bson *b, const char *name );
+
+/**
+ * Finish appending a new object or array to a bson.
+ *
+ * @param b the bson to append to.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+MONGO_EXPORT int bson_append_finish_object( bson *b );
+
+/**
+ * Finish appending a new object or array to a bson. This
+ * is simply an alias for bson_append_finish_object.
+ *
+ * @param b the bson to append to.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+int bson_append_finish_array( bson *b );
+
+void bson_numstr( char *str, int i );
+
+void bson_incnumstr( char *str );
+
+/* Error handling and standard library function over-riding. */
+/* -------------------------------------------------------- */
+
+/* bson_err_handlers shouldn't return!!! */
+typedef void( *bson_err_handler )( const char *errmsg );
+
+typedef int (*bson_printf_func)( const char *, ... );
+typedef int (*bson_fprintf_func)( FILE *, const char *, ... );
+typedef int (*bson_sprintf_func)( char *, const char *, ... );
+
+extern void *( *bson_malloc_func )( size_t );
+extern void *( *bson_realloc_func )( void *, size_t );
+extern void ( *bson_free )( void * );
+
+extern bson_printf_func bson_printf;
+extern bson_fprintf_func bson_fprintf;
+extern bson_sprintf_func bson_sprintf;
+
+extern bson_printf_func bson_errprintf;
+
+/**
+ * Allocates memory and checks return value, exiting fatally if malloc() fails.
+ *
+ * @param size bytes to allocate.
+ *
+ * @return a pointer to the allocated memory.
+ *
+ * @sa malloc(3)
+ */
+void *bson_malloc( int size );
+
+/**
+ * Changes the size of allocated memory and checks return value,
+ * exiting fatally if realloc() fails.
+ *
+ * @param ptr pointer to the space to reallocate.
+ * @param size bytes to allocate.
+ *
+ * @return a pointer to the allocated memory.
+ *
+ * @sa realloc()
+ */
+void *bson_realloc( void *ptr, int size );
+
+/**
+ * Set a function for error handling.
+ *
+ * @param func a bson_err_handler function.
+ *
+ * @return the old error handling function, or NULL.
+ */
+MONGO_EXPORT bson_err_handler set_bson_err_handler( bson_err_handler func );
+
+/* does nothing if ok != 0 */
+/**
+ * Exit fatally.
+ *
+ * @param ok exits if ok is equal to 0.
+ */
+void bson_fatal( int ok );
+
+/**
+ * Exit fatally with an error message.
+  *
+ * @param ok exits if ok is equal to 0.
+ * @param msg prints to stderr before exiting.
+ */
+void bson_fatal_msg( int ok, const char *msg );
+
+/**
+ * Invoke the error handler, but do not exit.
+ *
+ * @param b the buffer object.
+ */
+void bson_builder_error( bson *b );
+
+/**
+ * Cast an int64_t to double. This is necessary for embedding in
+ * certain environments.
+ *
+ */
+MONGO_EXPORT double bson_int64_to_double( int64_t i64 );
+
+MONGO_EXPORT void bson_swap_endian32( void *outp, const void *inp );
+MONGO_EXPORT void bson_swap_endian64( void *outp, const void *inp );
+
+MONGO_EXTERN_C_END
+#endif
diff --git a/third_party/bson_c/src/encoding.c b/third_party/bson_c/src/encoding.c
new file mode 100644
index 0000000..8d2da15
--- /dev/null
+++ b/third_party/bson_c/src/encoding.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2009-2011 10gen, 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.
+ */
+
+/*
+ * Portions Copyright 2001 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+
+#include "bson.h"
+#include "encoding.h"
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ */
+static const char trailingBytesForUTF8[256] = {
+    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,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,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,0,0,0,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * The length can be set by:
+ *  length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns 0.  The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+static int isLegalUTF8( const unsigned char *source, int length ) {
+    unsigned char a;
+    const unsigned char *srcptr = source + length;
+    switch ( length ) {
+    default:
+        return 0;
+        /* Everything else falls through when "true"... */
+    case 4:
+        if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
+    case 3:
+        if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0;
+    case 2:
+        if ( ( a = ( *--srcptr ) ) > 0xBF ) return 0;
+        switch ( *source ) {
+            /* no fall-through in this inner switch */
+        case 0xE0:
+            if ( a < 0xA0 ) return 0;
+            break;
+        case 0xF0:
+            if ( a < 0x90 ) return 0;
+            break;
+        case 0xF4:
+            if ( a > 0x8F ) return 0;
+            break;
+        default:
+            if ( a < 0x80 ) return 0;
+        }
+    case 1:
+        if ( *source >= 0x80 && *source < 0xC2 ) return 0;
+        if ( *source > 0xF4 ) return 0;
+    }
+    return 1;
+}
+
+static int bson_validate_string( bson *b, const unsigned char *string,
+                                 const int length, const char check_utf8, const char check_dot,
+                                 const char check_dollar ) {
+
+    int position = 0;
+    int sequence_length = 1;
+
+    if( check_dollar && string[0] == '$' ) {
+        b->err |= BSON_FIELD_INIT_DOLLAR;
+    }
+
+    while ( position < length ) {
+        if ( check_dot && *( string + position ) == '.' ) {
+            b->err |= BSON_FIELD_HAS_DOT;
+        }
+
+        if ( check_utf8 ) {
+            sequence_length = trailingBytesForUTF8[*( string + position )] + 1;
+            if ( ( position + sequence_length ) > length ) {
+                b->err |= BSON_NOT_UTF8;
+                return BSON_ERROR;
+            }
+            if ( !isLegalUTF8( string + position, sequence_length ) ) {
+                b->err |= BSON_NOT_UTF8;
+                return BSON_ERROR;
+            }
+        }
+        position += sequence_length;
+    }
+
+    return BSON_OK;
+}
+
+
+int bson_check_string( bson *b, const char *string,
+                       const int length ) {
+
+    return bson_validate_string( b, ( const unsigned char * )string, length, 1, 0, 0 );
+}
+
+int bson_check_field_name( bson *b, const char *string,
+                           const int length ) {
+
+    return bson_validate_string( b, ( const unsigned char * )string, length, 1, 1, 1 );
+}
diff --git a/third_party/bson_c/src/encoding.h b/third_party/bson_c/src/encoding.h
new file mode 100644
index 0000000..86d5e8e
--- /dev/null
+++ b/third_party/bson_c/src/encoding.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2009-2011 10gen, 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.
+ */
+
+#ifndef BSON_ENCODING_H_
+#define BSON_ENCODING_H_
+
+MONGO_EXTERN_C_START
+
+/**
+ * Check that a field name is valid UTF8, does not start with a '$',
+ * and contains no '.' characters. Set bson bit field appropriately.
+ * Note that we don't need to check for '\0' because we're using
+ * strlen(3), which stops at '\0'.
+ *
+ * @param b The bson object to which field name will be appended.
+ * @param string The field name as char*.
+ * @param length The length of the field name.
+ *
+ * @return BSON_OK if valid UTF8 and BSON_ERROR if not. All BSON strings must be
+ *     valid UTF8. This function will also check whether the string
+ *     contains '.' or starts with '$', since the validity of this depends on context.
+ *     Set the value of b->err appropriately.
+ */
+int bson_check_field_name( bson *b, const char *string,
+                           const int length );
+
+/**
+ * Check that a string is valid UTF8. Sets the buffer bit field appropriately.
+ *
+ * @param b The bson object to which string will be appended.
+ * @param string The string to check.
+ * @param length The length of the string.
+ *
+ * @return BSON_OK if valid UTF-8; otherwise, BSON_ERROR.
+ *     Sets b->err on error.
+ */
+bson_bool_t bson_check_string( bson *b, const char *string,
+                               const int length );
+
+MONGO_EXTERN_C_END
+#endif
diff --git a/third_party/bson_c/src/numbers.c b/third_party/bson_c/src/numbers.c
new file mode 100644
index 0000000..a63e3d7
--- /dev/null
+++ b/third_party/bson_c/src/numbers.c
@@ -0,0 +1,127 @@
+/*    Copyright 2009-2011 10gen 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.
+ */
+
+/* all the numbers that fit in a 4 byte string */
+const char bson_numstrs[1000][4] = {
+    "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", "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", "52", "53", "54", "55", "56", "57", "58", "59",
+    "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
+    "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
+    "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
+    "90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
+
+    "100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
+    "110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
+    "120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
+    "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
+    "140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
+    "150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
+    "160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
+    "170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
+    "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
+    "190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
+
+    "200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
+    "210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
+    "220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
+    "230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
+    "240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
+    "250", "251", "252", "253", "254", "255", "256", "257", "258", "259",
+    "260", "261", "262", "263", "264", "265", "266", "267", "268", "269",
+    "270", "271", "272", "273", "274", "275", "276", "277", "278", "279",
+    "280", "281", "282", "283", "284", "285", "286", "287", "288", "289",
+    "290", "291", "292", "293", "294", "295", "296", "297", "298", "299",
+
+    "300", "301", "302", "303", "304", "305", "306", "307", "308", "309",
+    "310", "311", "312", "313", "314", "315", "316", "317", "318", "319",
+    "320", "321", "322", "323", "324", "325", "326", "327", "328", "329",
+    "330", "331", "332", "333", "334", "335", "336", "337", "338", "339",
+    "340", "341", "342", "343", "344", "345", "346", "347", "348", "349",
+    "350", "351", "352", "353", "354", "355", "356", "357", "358", "359",
+    "360", "361", "362", "363", "364", "365", "366", "367", "368", "369",
+    "370", "371", "372", "373", "374", "375", "376", "377", "378", "379",
+    "380", "381", "382", "383", "384", "385", "386", "387", "388", "389",
+    "390", "391", "392", "393", "394", "395", "396", "397", "398", "399",
+
+    "400", "401", "402", "403", "404", "405", "406", "407", "408", "409",
+    "410", "411", "412", "413", "414", "415", "416", "417", "418", "419",
+    "420", "421", "422", "423", "424", "425", "426", "427", "428", "429",
+    "430", "431", "432", "433", "434", "435", "436", "437", "438", "439",
+    "440", "441", "442", "443", "444", "445", "446", "447", "448", "449",
+    "450", "451", "452", "453", "454", "455", "456", "457", "458", "459",
+    "460", "461", "462", "463", "464", "465", "466", "467", "468", "469",
+    "470", "471", "472", "473", "474", "475", "476", "477", "478", "479",
+    "480", "481", "482", "483", "484", "485", "486", "487", "488", "489",
+    "490", "491", "492", "493", "494", "495", "496", "497", "498", "499",
+
+    "500", "501", "502", "503", "504", "505", "506", "507", "508", "509",
+    "510", "511", "512", "513", "514", "515", "516", "517", "518", "519",
+    "520", "521", "522", "523", "524", "525", "526", "527", "528", "529",
+    "530", "531", "532", "533", "534", "535", "536", "537", "538", "539",
+    "540", "541", "542", "543", "544", "545", "546", "547", "548", "549",
+    "550", "551", "552", "553", "554", "555", "556", "557", "558", "559",
+    "560", "561", "562", "563", "564", "565", "566", "567", "568", "569",
+    "570", "571", "572", "573", "574", "575", "576", "577", "578", "579",
+    "580", "581", "582", "583", "584", "585", "586", "587", "588", "589",
+    "590", "591", "592", "593", "594", "595", "596", "597", "598", "599",
+
+    "600", "601", "602", "603", "604", "605", "606", "607", "608", "609",
+    "610", "611", "612", "613", "614", "615", "616", "617", "618", "619",
+    "620", "621", "622", "623", "624", "625", "626", "627", "628", "629",
+    "630", "631", "632", "633", "634", "635", "636", "637", "638", "639",
+    "640", "641", "642", "643", "644", "645", "646", "647", "648", "649",
+    "650", "651", "652", "653", "654", "655", "656", "657", "658", "659",
+    "660", "661", "662", "663", "664", "665", "666", "667", "668", "669",
+    "670", "671", "672", "673", "674", "675", "676", "677", "678", "679",
+    "680", "681", "682", "683", "684", "685", "686", "687", "688", "689",
+    "690", "691", "692", "693", "694", "695", "696", "697", "698", "699",
+
+    "700", "701", "702", "703", "704", "705", "706", "707", "708", "709",
+    "710", "711", "712", "713", "714", "715", "716", "717", "718", "719",
+    "720", "721", "722", "723", "724", "725", "726", "727", "728", "729",
+    "730", "731", "732", "733", "734", "735", "736", "737", "738", "739",
+    "740", "741", "742", "743", "744", "745", "746", "747", "748", "749",
+    "750", "751", "752", "753", "754", "755", "756", "757", "758", "759",
+    "760", "761", "762", "763", "764", "765", "766", "767", "768", "769",
+    "770", "771", "772", "773", "774", "775", "776", "777", "778", "779",
+    "780", "781", "782", "783", "784", "785", "786", "787", "788", "789",
+    "790", "791", "792", "793", "794", "795", "796", "797", "798", "799",
+
+    "800", "801", "802", "803", "804", "805", "806", "807", "808", "809",
+    "810", "811", "812", "813", "814", "815", "816", "817", "818", "819",
+    "820", "821", "822", "823", "824", "825", "826", "827", "828", "829",
+    "830", "831", "832", "833", "834", "835", "836", "837", "838", "839",
+    "840", "841", "842", "843", "844", "845", "846", "847", "848", "849",
+    "850", "851", "852", "853", "854", "855", "856", "857", "858", "859",
+    "860", "861", "862", "863", "864", "865", "866", "867", "868", "869",
+    "870", "871", "872", "873", "874", "875", "876", "877", "878", "879",
+    "880", "881", "882", "883", "884", "885", "886", "887", "888", "889",
+    "890", "891", "892", "893", "894", "895", "896", "897", "898", "899",
+
+    "900", "901", "902", "903", "904", "905", "906", "907", "908", "909",
+    "910", "911", "912", "913", "914", "915", "916", "917", "918", "919",
+    "920", "921", "922", "923", "924", "925", "926", "927", "928", "929",
+    "930", "931", "932", "933", "934", "935", "936", "937", "938", "939",
+    "940", "941", "942", "943", "944", "945", "946", "947", "948", "949",
+    "950", "951", "952", "953", "954", "955", "956", "957", "958", "959",
+    "960", "961", "962", "963", "964", "965", "966", "967", "968", "969",
+    "970", "971", "972", "973", "974", "975", "976", "977", "978", "979",
+    "980", "981", "982", "983", "984", "985", "986", "987", "988", "989",
+    "990", "991", "992", "993", "994", "995", "996", "997", "998", "999",
+};
diff --git a/third_party/bson_c/test/bson_subobject_test.c b/third_party/bson_c/test/bson_subobject_test.c
new file mode 100644
index 0000000..06d49c9
--- /dev/null
+++ b/third_party/bson_c/test/bson_subobject_test.c
@@ -0,0 +1,50 @@
+#include "test.h"
+#include "bson.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+int main() {
+    bson_iterator it[1], it2[1];
+    bson b[1];
+    bson sub[1];
+    bson copy[1];
+    bson_type type;
+
+    bson_init( b );
+    bson_append_string( b, "foo", "hello" );
+
+    {
+        bson_append_start_object( b, "o" );
+          bson_append_string( b, "bar", "goodbye" );
+        bson_append_finish_object( b );
+    }
+
+    bson_iterator_init( it, b );
+
+    bson_iterator_next( it );
+    type = bson_iterator_next( it );
+
+    ASSERT( BSON_OBJECT == type );
+
+    bson_iterator_subobject( it, sub );
+    ASSERT( sub->finished == 1 );
+
+    bson_iterator_init( it2, sub );
+
+    type = bson_iterator_next( it2 );
+    ASSERT( BSON_STRING == type );
+    type = bson_iterator_next( it2 );
+    ASSERT( BSON_EOO == type );
+
+    bson_copy( copy, sub );
+
+    ASSERT( 1 == copy->finished );
+    ASSERT( 0 == copy->stackPos );
+    ASSERT( 0 == copy->err );
+
+    bson_destroy( b );
+
+    return 0;
+}
+
diff --git a/third_party/bson_c/test/bson_test.c b/third_party/bson_c/test/bson_test.c
new file mode 100644
index 0000000..f0687a1
--- /dev/null
+++ b/third_party/bson_c/test/bson_test.c
@@ -0,0 +1,259 @@
+#include "test.h"
+#include "bson.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+int test_bson_generic() {
+
+   bson_iterator it, it2, it3;
+   bson_oid_t oid;
+   bson_timestamp_t ts;
+   bson_timestamp_t ts_result;
+   bson b[1];
+   bson copy[1];
+   bson scope[1];
+
+   ts.i = 1;
+   ts.t = 2;
+
+   bson_init( b );
+   bson_append_double( b, "d", 3.14 );
+   bson_append_string( b, "s", "hello" );
+   bson_append_string_n( b, "s_n", "goodbye cruel world", 7 );
+
+   {
+       bson_append_start_object( b, "o" );
+       bson_append_start_array( b, "a" );
+       bson_append_binary( b, "0", 8, "w\0rld", 5 );
+       bson_append_finish_object( b );
+       bson_append_finish_object( b );
+   }
+
+   bson_append_undefined( b, "u" );
+
+   bson_oid_from_string( &oid, "010203040506070809101112" );
+   ASSERT( !memcmp( oid.bytes, "\x001\x002\x003\x004\x005\x006\x007\x008\x009\x010\x011\x012", 12 ) );
+   bson_append_oid( b, "oid", &oid );
+
+   bson_append_bool( b, "b", 1 );
+   bson_append_date( b, "date", 0x0102030405060708 );
+   bson_append_null( b, "n" );
+   bson_append_regex( b, "r", "^asdf", "imx" );
+   /* no dbref test (deprecated) */
+   bson_append_code( b, "c", "function(){}" );
+   bson_append_code_n( b, "c_n", "function(){}garbage", 12 );
+   bson_append_symbol( b, "symbol", "symbol" );
+   bson_append_symbol_n( b, "symbol_n", "symbol and garbage", 6 );
+
+   {
+       bson_init( scope );
+       bson_append_int( scope, "i", 123 );
+       bson_finish( scope );
+
+       bson_append_code_w_scope( b, "cws", "function(){return i}", scope );
+       bson_destroy( scope );
+   }
+
+   bson_append_timestamp( b, "timestamp", &ts );
+   bson_append_long( b, "l", 0x1122334455667788 );
+
+   /* Ensure that we can't copy a non-finished object. */
+   ASSERT( bson_copy( copy, b ) == BSON_ERROR );
+
+   bson_finish( b );
+
+   ASSERT( b->err == BSON_VALID );
+
+   /* Test append after finish. */
+   ASSERT( bson_append_string( b, "foo", "bar" ) == BSON_ERROR );
+   ASSERT( b->err & BSON_ALREADY_FINISHED );
+
+   ASSERT( bson_copy( copy, b ) == BSON_OK );
+
+   ASSERT( 1 == copy->finished );
+   ASSERT( 0 == copy->err );
+
+   bson_print( b );
+
+   bson_iterator_init( &it, b );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_DOUBLE );
+   ASSERT( bson_iterator_type( &it ) == BSON_DOUBLE );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "d" ) );
+   ASSERT( bson_iterator_double( &it ) == 3.14 );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_STRING );
+   ASSERT( bson_iterator_type( &it ) == BSON_STRING );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "s" ) );
+   ASSERT( !strcmp( bson_iterator_string( &it ), "hello" ) );
+   ASSERT( strcmp( bson_iterator_string( &it ), "" ) );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_STRING );
+   ASSERT( bson_iterator_type( &it ) == BSON_STRING );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "s_n" ) );
+   ASSERT( !strcmp( bson_iterator_string( &it ), "goodbye" ) );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_OBJECT );
+   ASSERT( bson_iterator_type( &it ) == BSON_OBJECT );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "o" ) );
+   bson_iterator_subiterator( &it, &it2 );
+
+   ASSERT( bson_iterator_more( &it2 ) );
+   ASSERT( bson_iterator_next( &it2 ) == BSON_ARRAY );
+   ASSERT( bson_iterator_type( &it2 ) == BSON_ARRAY );
+   ASSERT( !strcmp( bson_iterator_key( &it2 ), "a" ) );
+   bson_iterator_subiterator( &it2, &it3 );
+
+   ASSERT( bson_iterator_more( &it3 ) );
+   ASSERT( bson_iterator_next( &it3 ) == BSON_BINDATA );
+   ASSERT( bson_iterator_type( &it3 ) == BSON_BINDATA );
+   ASSERT( !strcmp( bson_iterator_key( &it3 ), "0" ) );
+   ASSERT( bson_iterator_bin_type( &it3 ) == 8 );
+   ASSERT( bson_iterator_bin_len( &it3 ) == 5 );
+   ASSERT( !memcmp( bson_iterator_bin_data( &it3 ), "w\0rld", 5 ) );
+
+   ASSERT( bson_iterator_more( &it3 ) );
+   ASSERT( bson_iterator_next( &it3 ) == BSON_EOO );
+   ASSERT( bson_iterator_type( &it3 ) == BSON_EOO );
+   ASSERT( !bson_iterator_more( &it3 ) );
+
+   ASSERT( bson_iterator_more( &it2 ) );
+   ASSERT( bson_iterator_next( &it2 ) == BSON_EOO );
+   ASSERT( bson_iterator_type( &it2 ) == BSON_EOO );
+   ASSERT( !bson_iterator_more( &it2 ) );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_UNDEFINED );
+   ASSERT( bson_iterator_type( &it ) == BSON_UNDEFINED );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "u" ) );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_OID );
+   ASSERT( bson_iterator_type( &it ) == BSON_OID );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "oid" ) );
+   ASSERT( !memcmp( bson_iterator_oid( &it )->bytes, "\x001\x002\x003\x004\x005\x006\x007\x008\x009\x010\x011\x012", 12 ) );
+   ASSERT( bson_iterator_oid( &it )->ints[0] == oid.ints[0] );
+   ASSERT( bson_iterator_oid( &it )->ints[1] == oid.ints[1] );
+   ASSERT( bson_iterator_oid( &it )->ints[2] == oid.ints[2] );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_BOOL );
+   ASSERT( bson_iterator_type( &it ) == BSON_BOOL );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "b" ) );
+   ASSERT( bson_iterator_bool( &it ) == 1 );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_DATE );
+   ASSERT( bson_iterator_type( &it ) == BSON_DATE );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "date" ) );
+   ASSERT( bson_iterator_date( &it ) == 0x0102030405060708 );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_NULL );
+   ASSERT( bson_iterator_type( &it ) == BSON_NULL );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "n" ) );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_REGEX );
+   ASSERT( bson_iterator_type( &it ) == BSON_REGEX );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "r" ) );
+   ASSERT( !strcmp( bson_iterator_regex( &it ), "^asdf" ) );
+   ASSERT( !strcmp( bson_iterator_regex_opts( &it ), "imx" ) );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_CODE );
+   ASSERT( bson_iterator_type( &it ) == BSON_CODE );
+   ASSERT( !strcmp( bson_iterator_code(&it), "function(){}") );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "c" ) );
+   ASSERT( !strcmp( bson_iterator_string( &it ), "" ) );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_CODE );
+   ASSERT( bson_iterator_type( &it ) == BSON_CODE );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "c_n" ) );
+   ASSERT( !strcmp( bson_iterator_string( &it ), "" ) );
+   ASSERT( !strcmp( bson_iterator_code( &it ), "function(){}" ) );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_SYMBOL );
+   ASSERT( bson_iterator_type( &it ) == BSON_SYMBOL );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "symbol" ) );
+   ASSERT( !strcmp( bson_iterator_string( &it ), "symbol" ) );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_SYMBOL );
+   ASSERT( bson_iterator_type( &it ) == BSON_SYMBOL );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "symbol_n" ) );
+   ASSERT( !strcmp( bson_iterator_string( &it ), "symbol" ) );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_CODEWSCOPE );
+   ASSERT( bson_iterator_type( &it ) == BSON_CODEWSCOPE );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "cws" ) );
+   ASSERT( !strcmp( bson_iterator_code( &it ), "function(){return i}" ) );
+
+   {
+       bson scope;
+       bson_iterator_code_scope( &it, &scope );
+       bson_iterator_init( &it2, &scope );
+
+       ASSERT( bson_iterator_more( &it2 ) );
+       ASSERT( bson_iterator_next( &it2 ) == BSON_INT );
+       ASSERT( bson_iterator_type( &it2 ) == BSON_INT );
+       ASSERT( !strcmp( bson_iterator_key( &it2 ), "i" ) );
+       ASSERT( bson_iterator_int( &it2 ) == 123 );
+
+       ASSERT( bson_iterator_more( &it2 ) );
+       ASSERT( bson_iterator_next( &it2 ) == BSON_EOO );
+       ASSERT( bson_iterator_type( &it2 ) == BSON_EOO );
+       ASSERT( !bson_iterator_more( &it2 ) );
+   }
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_TIMESTAMP );
+   ASSERT( bson_iterator_type( &it ) == BSON_TIMESTAMP );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "timestamp" ) );
+   ts_result = bson_iterator_timestamp( &it );
+   ASSERT( ts_result.i == 1 );
+   ASSERT( ts_result.t == 2 );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_LONG );
+   ASSERT( bson_iterator_type( &it ) == BSON_LONG );
+   ASSERT( !strcmp( bson_iterator_key( &it ), "l" ) );
+   ASSERT( bson_iterator_long( &it ) == 0x1122334455667788 );
+
+   ASSERT( bson_iterator_more( &it ) );
+   ASSERT( bson_iterator_next( &it ) == BSON_EOO );
+   ASSERT( bson_iterator_type( &it ) == BSON_EOO );
+   ASSERT( !bson_iterator_more( &it ) );
+
+   bson_destroy( b );
+}
+
+int test_bson_iterator() {
+    bson b[1];
+    bson_iterator i[1];
+
+    bson_iterator_init( i, bson_empty( b ) );
+    bson_iterator_next( i );
+    bson_iterator_type( i );
+
+    bson_find( i, bson_empty( b ), "foo" );
+
+    return 0;
+}
+
+int main() {
+
+  test_bson_generic();
+  test_bson_iterator();
+
+  return 0;
+}
+
diff --git a/third_party/externals/README b/third_party/externals/README
new file mode 100644
index 0000000..9b613be
--- /dev/null
+++ b/third_party/externals/README
@@ -0,0 +1,16 @@
+Subversion uses this directory to map in "external" repositories upon which
+we depend.
+
+To see the mappings, cd into this directory and then run:
+svn propget svn:externals .
+
+To modify the mappings, cd into this directory and run:
+svn propedit svn:externals .
+
+See http://svnbook.red-bean.com/en/1.5/svn.advanced.externals.html for
+more info.
+
+
+NOTE: This set of dependencies must be manually kept in sync with the gclient
+DEPS; the output of "svn propget svn:externals ." should always be equivalent
+to the content of the "DEPS" file within trunk.
diff --git a/third_party/harfbuzz/.gitignore b/third_party/harfbuzz/.gitignore
new file mode 100644
index 0000000..572243f
--- /dev/null
+++ b/third_party/harfbuzz/.gitignore
@@ -0,0 +1,20 @@
+INSTALL
+Makefile.in
+aclocal.m4
+autom4te.cache
+config.guess
+config.h.in
+config.sub
+config.h
+configure
+depcomp
+install-sh
+ltmain.sh
+missing
+Makefile
+config.status
+config.log
+libtool
+stamp-h1
+compile
+build
diff --git a/third_party/harfbuzz/AUTHORS b/third_party/harfbuzz/AUTHORS
new file mode 100644
index 0000000..023488a
--- /dev/null
+++ b/third_party/harfbuzz/AUTHORS
@@ -0,0 +1,6 @@
+David Turner
+Werner Lemberg
+Owen Taylor
+Behdad Esfahbod
+Lars Knoll
+Simon Hausmann
diff --git a/third_party/harfbuzz/COPYING b/third_party/harfbuzz/COPYING
new file mode 100644
index 0000000..820a9e6
--- /dev/null
+++ b/third_party/harfbuzz/COPYING
@@ -0,0 +1,24 @@
+HarfBuzz was previously licensed under different licenses.  This was
+changed in January 2008.  If you need to relicense your old copies,
+consult the announcement of the license change on the internet.
+Other than that, each copy of HarfBuzz is licensed under the COPYING
+file included with it.  The actual license follows:
+
+
+Permission is hereby granted, without written agreement and without
+license or royalty fees, to use, copy, modify, and distribute this
+software and its documentation for any purpose, provided that the
+above copyright notice and the following two paragraphs appear in
+all copies of this software.
+
+IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
diff --git a/third_party/harfbuzz/ChangeLog b/third_party/harfbuzz/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/harfbuzz/ChangeLog
diff --git a/third_party/harfbuzz/Makefile.am b/third_party/harfbuzz/Makefile.am
new file mode 100644
index 0000000..776b947
--- /dev/null
+++ b/third_party/harfbuzz/Makefile.am
@@ -0,0 +1,2 @@
+
+SUBDIRS = src tests
diff --git a/third_party/harfbuzz/NEWS b/third_party/harfbuzz/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/harfbuzz/NEWS
diff --git a/third_party/harfbuzz/README b/third_party/harfbuzz/README
new file mode 100644
index 0000000..ca2546a
--- /dev/null
+++ b/third_party/harfbuzz/README
@@ -0,0 +1,7 @@
+This is HarfBuzz, an OpenType Layout engine library.
+
+To report bugs or post to discussion mailing list, see:
+
+	http://freedesktop.org/wiki/Software/HarfBuzz
+
+For license information, see the file COPYING. 
diff --git a/third_party/harfbuzz/README.google b/third_party/harfbuzz/README.google
new file mode 100644
index 0000000..74eeb2e
--- /dev/null
+++ b/third_party/harfbuzz/README.google
@@ -0,0 +1,9 @@
+Harfbuzz
+
+http://freedesktop.org/wiki/Software/HarfBuzz
+
+This code was taken from b0d396aa88b3cdf8cea896bfeeba197656e1cdb1
+(git://anongit.freedesktop.org/harfbuzz)
+
+The patch in chromium.patch was applied on top of this; I will talk with
+upstream about it.
diff --git a/third_party/harfbuzz/autogen.sh b/third_party/harfbuzz/autogen.sh
new file mode 100755
index 0000000..7fa1c3d
--- /dev/null
+++ b/third_party/harfbuzz/autogen.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+set -e
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+PROJECT=harfbuzz
+TEST_TYPE=-f
+FILE=src/harfbuzz.h
+ACLOCAL=${ACLOCAL-aclocal}
+LIBTOOLIZE=${LIBTOOLIZE-libtoolize}
+AUTOMAKE=${AUTOMAKE-automake}
+AUTOHEADER=${AUTOHEADER-autoheader}
+AUTOCONF=${AUTOCONF-autoconf}
+LIBTOOLIZE_FLAGS="--copy --force"
+
+DIE=0
+
+have_libtool=false
+if $LIBTOOLIZE --version < /dev/null > /dev/null 2>&1 ; then
+	libtool_version=`$LIBTOOLIZE --version | sed 's/^[^0-9]*\([0-9].[0-9.]*\).*/\1/'`
+	case $libtool_version in
+	    1.4*|1.5*|1.6*|1.7*|2*)
+		have_libtool=true
+		;;
+	esac
+fi
+if $have_libtool ; then : ; else
+	echo
+	echo "You must have libtool 1.4 installed to compile $PROJECT."
+	echo "Install the appropriate package for your distribution,"
+	echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+	DIE=1
+fi
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+	echo
+	echo "You must have autoconf installed to compile $PROJECT."
+	echo "libtool the appropriate package for your distribution,"
+	echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+	DIE=1
+}
+
+have_automake=false
+need_libtoolize=true
+if $AUTOMAKE --version < /dev/null > /dev/null 2>&1 ; then
+	automake_version=`$AUTOMAKE --version | grep 'automake (GNU automake)' | sed 's/^[^0-9]*\(.*\)/\1/'`
+	case $automake_version in
+	   1.2*|1.3*|1.4) 
+		;;
+	   1.4*)
+	   	have_automake=true
+	        need_libtoolize=false
+		;;
+	   *)
+		have_automake=true
+		;;
+	esac
+fi
+if $have_automake ; then : ; else
+	echo
+	echo "You must have automake 1.4-p1 installed to compile $PROJECT."
+	echo "Get ftp://ftp.gnu.org/pub/gnu/automake/automake-1.4-p1.tar.gz"
+	echo "(or a newer version if it is available)"
+	DIE=1
+fi
+
+if test "$DIE" -eq 1; then
+	exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+	echo "You must run this script in the top-level $PROJECT directory"
+	exit 1
+}
+
+if test -z "$AUTOGEN_SUBDIR_MODE"; then
+        if test -z "$*"; then
+                echo "I am going to run ./configure with no arguments - if you wish "
+                echo "to pass any to it, please specify them on the $0 command line."
+        fi
+fi
+
+echo Running $ACLOCAL $ACLOCAL_FLAGS
+$ACLOCAL $ACLOCAL_FLAGS
+
+# optionally run autoheader
+if $AUTOHEADER --version  < /dev/null > /dev/null 2>&1; then
+	echo Running $AUTOHEADER
+	$AUTOHEADER
+fi
+
+case $need_libtoolize in
+   true)
+   	echo Running $LIBTOOLIZE $LIBTOOLIZE_FLAGS
+   	$LIBTOOLIZE $LIBTOOLIZE_FLAGS
+	;;
+esac
+
+echo Running $AUTOMAKE -a $am_opt
+$AUTOMAKE -a $am_opt
+echo Running $AUTOCONF
+$AUTOCONF
+cd $ORIGDIR
+
+if test -z "$AUTOGEN_SUBDIR_MODE"; then
+	echo Running $srcdir/configure "$@"
+        $srcdir/configure "$@"
+
+        echo 
+        echo "Now type 'make' to compile $PROJECT."
+fi
diff --git a/third_party/harfbuzz/chromium.patch b/third_party/harfbuzz/chromium.patch
new file mode 100644
index 0000000..0f20dfd
--- /dev/null
+++ b/third_party/harfbuzz/chromium.patch
@@ -0,0 +1,37 @@
+diff --git a/contrib/harfbuzz-unicode.c b/contrib/harfbuzz-unicode.c
+index 51dd4ea..cb7a85b 100644
+--- a/contrib/harfbuzz-unicode.c
++++ b/contrib/harfbuzz-unicode.c
+@@ -171,7 +171,10 @@ hb_utf16_script_run_prev(unsigned *num_code_points, HB_ScriptItem *output,
+         current_script = script;
+         continue;
+       } else if (script == HB_Script_Inherited) {
+-        current_script = script;
++        // Just assume that whatever follows this combining character is within
++        // the same script.  This is incorrect if you had language1 + combining
++        // char + language 2, but that is rare and this code is suspicious
++        // anyway.
+         continue;
+       } else {
+         *iter = prev_iter;
+diff --git a/src/harfbuzz-shaper.cpp b/src/harfbuzz-shaper.cpp
+index f3ec8e1..2b0dfde 100644
+--- a/src/harfbuzz-shaper.cpp
++++ b/src/harfbuzz-shaper.cpp
+@@ -433,7 +433,7 @@ void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
+ 
+     // ### zeroWidth and justification are missing here!!!!!
+ 
+-    assert(item->num_glyphs <= length);
++    assert(length <= item->num_glyphs);
+ 
+ //     qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
+     HB_GlyphAttributes *attributes = item->attributes;
+@@ -451,7 +451,6 @@ void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
+         }
+         ++glyph_pos;
+     }
+-    assert(glyph_pos == item->num_glyphs);
+ 
+     // first char in a run is never (treated as) a mark
+     int cStart = 0;
diff --git a/third_party/harfbuzz/config.h b/third_party/harfbuzz/config.h
new file mode 100644
index 0000000..2e856ee
--- /dev/null
+++ b/third_party/harfbuzz/config.h
@@ -0,0 +1,60 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "harfbuzz"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION ""
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "0.1"
diff --git a/third_party/harfbuzz/configure.ac b/third_party/harfbuzz/configure.ac
new file mode 100644
index 0000000..8519279
--- /dev/null
+++ b/third_party/harfbuzz/configure.ac
@@ -0,0 +1,54 @@
+AC_INIT(README)
+AM_INIT_AUTOMAKE(harfbuzz, 0.1)
+AC_PROG_CC
+AC_PROG_CXX
+AM_PROG_LIBTOOL
+PKG_PROG_PKG_CONFIG
+AM_CONFIG_HEADER(config.h)
+
+if test "x$ac_compiler_gnu" = xyes; then
+    CFLAGS="$CFLAGS -Wall -W -pedantic -ansi"
+    CXXFLAGS="$CXXFLAGS -Wall -W"
+fi
+
+AC_PATH_PROG(ft_config,freetype-config,no)
+if test "$ft_config" = "no"; then
+    AC_MSG_ERROR([You must have freetype installed; see http://www.freetype.org/])
+fi
+
+FREETYPE_CFLAGS="`$ft_config --cflags`"
+FREETYPE_LIBS="`$ft_config --libs`"
+
+AC_SUBST(FREETYPE_LIBS)
+AC_SUBST(FREETYPE_CFLAGS)
+
+AC_ARG_ENABLE(qt, AS_HELP_STRING([--disable-qt], [Build Qt support (default: auto)]), [QT=$enableval], [QT=auto])
+
+if test "x$QT" = xauto; then
+    PKG_CHECK_MODULES(QT, [QtGui >= 4.3], [QT=yes], [QT=no])
+fi
+if test "x$QT" = xyes; then
+    PKG_CHECK_MODULES(QT_GUI, [QtGui >= 4.3])
+    PKG_CHECK_MODULES(QT_QTEST, [QtTest >= 4.3])
+
+    _PKG_CONFIG(QT_INCDIR, [variable=includedir], [QtGui >= 4.3])
+    QT_GUI_CFLAGS="$QT_GUI_CFLAGS -I$pkg_cv_QT_INCDIR/../Qt"
+
+    AC_SUBST(QT_GUI_CFLAGS)
+    AC_SUBST(QT_GUI_LIBS)
+    AC_SUBST(QT_QTEST_CFLAGS)
+    AC_SUBST(QT_QTEST_LIBS)
+
+    _PKG_CONFIG(QT_MOC, [variable=moc_location], [QtGui >= 4.3])
+    QT_MOC=$pkg_cv_QT_MOC
+    AC_SUBST(QT_MOC)
+fi
+AM_CONDITIONAL(QT, [test "x$QT" = xyes])
+
+AC_OUTPUT([
+Makefile
+src/Makefile
+tests/Makefile
+tests/linebreaking/Makefile
+tests/shaping/Makefile
+])
diff --git a/third_party/harfbuzz/contrib/README b/third_party/harfbuzz/contrib/README
new file mode 100644
index 0000000..074cc52
--- /dev/null
+++ b/third_party/harfbuzz/contrib/README
@@ -0,0 +1,9 @@
+Harfbuzz requires several functions to be defined in order to work with the
+platform's Unicode tables etc.
+
+If you are building on top of Qt4 you should look at the code in the tests/
+directory for examples of how to hook up Qt4 functions to Harfbuzz.
+
+Otherwise, this directory contains examples of using downloaded Unicode tables
+and/or glib to host Harfbuzz. You should read the README file in tables/ for how
+to build the header files for some of the Unicode tables.
diff --git a/third_party/harfbuzz/contrib/harfbuzz-freetype.c b/third_party/harfbuzz/contrib/harfbuzz-freetype.c
new file mode 100644
index 0000000..a2962df
--- /dev/null
+++ b/third_party/harfbuzz/contrib/harfbuzz-freetype.c
@@ -0,0 +1,149 @@
+#include <stdint.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TABLES_H
+
+#if 0
+#include <freetype/freetype.h>
+#include <freetype/tttables.h>
+#endif
+
+#include <harfbuzz-shaper.h>
+#include "harfbuzz-unicode.h"
+
+static HB_Bool
+hb_freetype_string_to_glyphs(HB_Font font,
+                             const HB_UChar16 *chars, hb_uint32 len,
+                             HB_Glyph *glyphs, hb_uint32 *numGlyphs,
+                             HB_Bool is_rtl) {
+  FT_Face face = (FT_Face) font->userData;
+  if (len > *numGlyphs)
+    return 0;
+
+  size_t i = 0, j = 0;
+  while (i < len) {
+    const uint32_t cp = utf16_to_code_point(chars, len, &i);
+    glyphs[j++] = FT_Get_Char_Index(face, cp);
+  }
+
+  *numGlyphs = j;
+
+  return 1;
+}
+
+static void
+hb_freetype_advances_get(HB_Font font, const HB_Glyph *glyphs, hb_uint32 len,
+                         HB_Fixed *advances, int flags) {
+  FT_Face face = (FT_Face) font->userData;
+
+  hb_uint32 i;
+  for (i = 0; i < len; ++i) {
+    const FT_Error error = FT_Load_Glyph(face, glyphs[i], FT_LOAD_DEFAULT);
+    if (error) {
+      advances[i] = 0;
+      continue;
+    }
+
+    advances[i] = face->glyph->advance.x;
+  }
+}
+
+static HB_Bool
+hb_freetype_can_render(HB_Font font, const HB_UChar16 *chars, hb_uint32 len) {
+  FT_Face face = (FT_Face)font->userData;
+
+  size_t i = 0;
+  while (i < len) {
+    const uint32_t cp = utf16_to_code_point(chars, len, &i);
+    if (FT_Get_Char_Index(face, cp) == 0)
+      return 0;
+  }
+
+  return 1;
+}
+
+static HB_Error
+hb_freetype_outline_point_get(HB_Font font, HB_Glyph glyph, int flags,
+                              hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos,
+                              hb_uint32 *n_points) {
+  HB_Error error = HB_Err_Ok;
+  FT_Face face = (FT_Face) font->userData;
+
+  int load_flags = (flags & HB_ShaperFlag_UseDesignMetrics) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT;
+
+  if ((error = (HB_Error) FT_Load_Glyph(face, glyph, load_flags)))
+    return error;
+
+  if (face->glyph->format != ft_glyph_format_outline)
+    return (HB_Error)HB_Err_Invalid_SubTable;
+
+  *n_points = face->glyph->outline.n_points;
+  if (!(*n_points))
+    return HB_Err_Ok;
+
+  if (point > *n_points)
+    return (HB_Error)HB_Err_Invalid_SubTable;
+
+  *xpos = face->glyph->outline.points[point].x;
+  *ypos = face->glyph->outline.points[point].y;
+
+  return HB_Err_Ok;
+}
+
+static void
+hb_freetype_glyph_metrics_get(HB_Font font, HB_Glyph glyph,
+                              HB_GlyphMetrics *metrics) {
+  FT_Face face = (FT_Face) font->userData;
+
+  const FT_Error error = FT_Load_Glyph(face, glyph, FT_LOAD_DEFAULT);
+  if (error) {
+    metrics->x = metrics->y = metrics->width = metrics->height = 0;
+    metrics->xOffset = metrics->yOffset = 0;
+    return;
+  }
+
+  const FT_Glyph_Metrics *ftmetrics = &face->glyph->metrics;
+  metrics->width = ftmetrics->width;
+  metrics->height = ftmetrics->height;
+  metrics->x = ftmetrics->horiAdvance;
+  metrics->y = 0;  // unclear what this is
+  metrics->xOffset = ftmetrics->horiBearingX;
+  metrics->yOffset = ftmetrics->horiBearingY;
+}
+
+static HB_Fixed
+hb_freetype_font_metric_get(HB_Font font, HB_FontMetric metric) {
+  FT_Face face = (FT_Face) font->userData;
+
+  switch (metric) {
+  case HB_FontAscent:
+    // Note that we aren't scanning the VDMX table which we probably would in
+    // an ideal world.
+    return face->ascender;
+  default:
+    return 0;
+  }
+}
+
+const HB_FontClass hb_freetype_class = {
+  hb_freetype_string_to_glyphs,
+  hb_freetype_advances_get,
+  hb_freetype_can_render,
+  hb_freetype_outline_point_get,
+  hb_freetype_glyph_metrics_get,
+  hb_freetype_font_metric_get,
+};
+
+HB_Error
+hb_freetype_table_sfnt_get(void *voidface, const HB_Tag tag, HB_Byte *buffer, HB_UInt *len) {
+  FT_Face face = (FT_Face) voidface;
+  FT_ULong ftlen = *len;
+
+  if (!FT_IS_SFNT(face))
+    return HB_Err_Invalid_Argument;
+
+  const FT_Error error = FT_Load_Sfnt_Table(face, tag, 0, buffer, &ftlen);
+  *len = ftlen;
+  return (HB_Error) error;
+}
diff --git a/third_party/harfbuzz/contrib/harfbuzz-freetype.h b/third_party/harfbuzz/contrib/harfbuzz-freetype.h
new file mode 100644
index 0000000..628be16
--- /dev/null
+++ b/third_party/harfbuzz/contrib/harfbuzz-freetype.h
@@ -0,0 +1,9 @@
+#ifndef HB_FREETYPE_H_
+#define HB_FREETYPE_H_
+
+extern const HB_FontClass hb_freetype_class;
+
+HB_Error hb_freetype_table_sfnt_get(void *voidface, const HB_Tag tag,
+                                    HB_Byte *buffer, HB_UInt *len);
+
+#endif  // HB_FREETYPE_H_
diff --git a/third_party/harfbuzz/contrib/harfbuzz-unicode-glib.c b/third_party/harfbuzz/contrib/harfbuzz-unicode-glib.c
new file mode 100644
index 0000000..6a13433
--- /dev/null
+++ b/third_party/harfbuzz/contrib/harfbuzz-unicode-glib.c
@@ -0,0 +1,169 @@
+#include "harfbuzz-external.h"
+
+#include <glib.h>
+
+static int
+hb_category_for_char(HB_UChar32 ch) {
+  switch (g_unichar_type(ch)) {
+    case G_UNICODE_CONTROL:
+      return HB_Other_Control;
+    case G_UNICODE_FORMAT:
+      return HB_Other_Format;
+    case G_UNICODE_UNASSIGNED:
+      return HB_Other_NotAssigned;
+    case G_UNICODE_PRIVATE_USE:
+      return HB_Other_PrivateUse;
+    case G_UNICODE_SURROGATE:
+      return HB_Other_Surrogate;
+    case G_UNICODE_LOWERCASE_LETTER:
+      return HB_Letter_Lowercase;
+    case G_UNICODE_MODIFIER_LETTER:
+      return HB_Letter_Modifier;
+    case G_UNICODE_OTHER_LETTER:
+      return HB_Letter_Other;
+    case G_UNICODE_TITLECASE_LETTER:
+      return HB_Letter_Titlecase;
+    case G_UNICODE_UPPERCASE_LETTER:
+      return HB_Letter_Uppercase;
+    case G_UNICODE_COMBINING_MARK:
+      return HB_Mark_SpacingCombining;
+    case G_UNICODE_ENCLOSING_MARK:
+      return HB_Mark_Enclosing;
+    case G_UNICODE_NON_SPACING_MARK:
+      return HB_Mark_NonSpacing;
+    case G_UNICODE_DECIMAL_NUMBER:
+      return HB_Number_DecimalDigit;
+    case G_UNICODE_LETTER_NUMBER:
+      return HB_Number_Letter;
+    case G_UNICODE_OTHER_NUMBER:
+      return HB_Number_Other;
+    case G_UNICODE_CONNECT_PUNCTUATION:
+      return HB_Punctuation_Connector;
+    case G_UNICODE_DASH_PUNCTUATION:
+      return HB_Punctuation_Dash;
+    case G_UNICODE_CLOSE_PUNCTUATION:
+      return HB_Punctuation_Close;
+    case G_UNICODE_FINAL_PUNCTUATION:
+      return HB_Punctuation_FinalQuote;
+    case G_UNICODE_INITIAL_PUNCTUATION:
+      return HB_Punctuation_InitialQuote;
+    case G_UNICODE_OTHER_PUNCTUATION:
+      return HB_Punctuation_Other;
+    case G_UNICODE_OPEN_PUNCTUATION:
+      return HB_Punctuation_Open;
+    case G_UNICODE_CURRENCY_SYMBOL:
+      return HB_Symbol_Currency;
+    case G_UNICODE_MODIFIER_SYMBOL:
+      return HB_Symbol_Modifier;
+    case G_UNICODE_MATH_SYMBOL:
+      return HB_Symbol_Math;
+    case G_UNICODE_OTHER_SYMBOL:
+      return HB_Symbol_Other;
+    case G_UNICODE_LINE_SEPARATOR:
+      return HB_Separator_Line;
+    case G_UNICODE_PARAGRAPH_SEPARATOR:
+      return HB_Separator_Paragraph;
+    case G_UNICODE_SPACE_SEPARATOR:
+      return HB_Separator_Space;
+    default:
+      return HB_Symbol_Other;
+  }
+}
+
+HB_LineBreakClass
+HB_GetLineBreakClass(HB_UChar32 ch) {
+  switch (g_unichar_break_type(ch)) {
+    case G_UNICODE_BREAK_MANDATORY:
+      return HB_LineBreak_BK;
+    case G_UNICODE_BREAK_CARRIAGE_RETURN:
+      return HB_LineBreak_CR;
+    case G_UNICODE_BREAK_LINE_FEED:
+      return HB_LineBreak_LF;
+    case G_UNICODE_BREAK_COMBINING_MARK:
+      return HB_LineBreak_CM;
+    case G_UNICODE_BREAK_SURROGATE:
+      return HB_LineBreak_SG;
+    case G_UNICODE_BREAK_ZERO_WIDTH_SPACE:
+      return HB_LineBreak_ZW;
+    case G_UNICODE_BREAK_INSEPARABLE:
+      return HB_LineBreak_IN;
+    case G_UNICODE_BREAK_NON_BREAKING_GLUE:
+      return HB_LineBreak_GL;
+    case G_UNICODE_BREAK_CONTINGENT:
+      return HB_LineBreak_AL;
+    case G_UNICODE_BREAK_SPACE:
+      return HB_LineBreak_SP;
+    case G_UNICODE_BREAK_AFTER:
+      return HB_LineBreak_BA;
+    case G_UNICODE_BREAK_BEFORE:
+      return HB_LineBreak_BB;
+    case G_UNICODE_BREAK_BEFORE_AND_AFTER:
+      return HB_LineBreak_B2;
+    case G_UNICODE_BREAK_HYPHEN:
+      return HB_LineBreak_HY;
+    case G_UNICODE_BREAK_NON_STARTER:
+      return HB_LineBreak_NS;
+    case G_UNICODE_BREAK_OPEN_PUNCTUATION:
+      return HB_LineBreak_OP;
+    case G_UNICODE_BREAK_CLOSE_PUNCTUATION:
+      return HB_LineBreak_CL;
+    case G_UNICODE_BREAK_QUOTATION:
+      return HB_LineBreak_QU;
+    case G_UNICODE_BREAK_EXCLAMATION:
+      return HB_LineBreak_EX;
+    case G_UNICODE_BREAK_IDEOGRAPHIC:
+      return HB_LineBreak_ID;
+    case G_UNICODE_BREAK_NUMERIC:
+      return HB_LineBreak_NU;
+    case G_UNICODE_BREAK_INFIX_SEPARATOR:
+      return HB_LineBreak_IS;
+    case G_UNICODE_BREAK_SYMBOL:
+      return HB_LineBreak_SY;
+    case G_UNICODE_BREAK_ALPHABETIC:
+      return HB_LineBreak_AL;
+    case G_UNICODE_BREAK_PREFIX:
+      return HB_LineBreak_PR;
+    case G_UNICODE_BREAK_POSTFIX:
+      return HB_LineBreak_PO;
+    case G_UNICODE_BREAK_COMPLEX_CONTEXT:
+      return HB_LineBreak_SA;
+    case G_UNICODE_BREAK_AMBIGUOUS:
+      return HB_LineBreak_AL;
+    case G_UNICODE_BREAK_UNKNOWN:
+      return HB_LineBreak_AL;
+    case G_UNICODE_BREAK_NEXT_LINE:
+      return HB_LineBreak_AL;
+    case G_UNICODE_BREAK_WORD_JOINER:
+      return HB_LineBreak_WJ;
+    case G_UNICODE_BREAK_HANGUL_L_JAMO:
+      return HB_LineBreak_JL;
+    case G_UNICODE_BREAK_HANGUL_V_JAMO:
+      return HB_LineBreak_JV;
+    case G_UNICODE_BREAK_HANGUL_T_JAMO:
+      return HB_LineBreak_JT;
+    case G_UNICODE_BREAK_HANGUL_LV_SYLLABLE:
+      return HB_LineBreak_H2;
+    case G_UNICODE_BREAK_HANGUL_LVT_SYLLABLE:
+      return HB_LineBreak_H3;
+    default:
+      return HB_LineBreak_AL;
+  }
+}
+
+int
+HB_GetUnicodeCharCombiningClass(HB_UChar32 ch) {
+  return g_unichar_combining_class(ch);
+}
+
+void
+HB_GetUnicodeCharProperties(HB_UChar32 ch,
+                            HB_CharCategory *category,
+                            int *combiningClass) {
+  *category = hb_category_for_char(ch);
+  *combiningClass = g_unichar_combining_class(ch);
+}
+
+HB_CharCategory
+HB_GetUnicodeCharCategory(HB_UChar32 ch) {
+  return hb_category_for_char(ch);
+}
diff --git a/third_party/harfbuzz/contrib/harfbuzz-unicode-tables.c b/third_party/harfbuzz/contrib/harfbuzz-unicode-tables.c
new file mode 100644
index 0000000..3c3fead
--- /dev/null
+++ b/third_party/harfbuzz/contrib/harfbuzz-unicode-tables.c
@@ -0,0 +1,84 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <harfbuzz-external.h>
+
+#include "tables/category-properties.h"
+#include "tables/combining-properties.h"
+
+HB_LineBreakClass
+HB_GetLineBreakClass(HB_UChar32 ch) {
+  abort();
+  return 0;
+}
+
+static int
+combining_property_cmp(const void *vkey, const void *vcandidate) {
+  const uint32_t key = (uint32_t) (intptr_t) vkey;
+  const struct combining_property *candidate = vcandidate;
+
+  if (key < candidate->range_start) {
+    return -1;
+  } else if (key > candidate->range_end) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+static int
+code_point_to_combining_class(HB_UChar32 cp) {
+  const void *vprop = bsearch((void *) (intptr_t) cp, combining_properties,
+                              combining_properties_count,
+                              sizeof(struct combining_property),
+                              combining_property_cmp);
+  if (!vprop)
+    return 0;
+
+  return ((const struct combining_property *) vprop)->klass;
+}
+
+int
+HB_GetUnicodeCharCombiningClass(HB_UChar32 ch) {
+  return code_point_to_combining_class(ch);
+  return 0;
+}
+
+static int
+category_property_cmp(const void *vkey, const void *vcandidate) {
+  const uint32_t key = (uint32_t) (intptr_t) vkey;
+  const struct category_property *candidate = vcandidate;
+
+  if (key < candidate->range_start) {
+    return -1;
+  } else if (key > candidate->range_end) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+static HB_CharCategory
+code_point_to_category(HB_UChar32 cp) {
+  const void *vprop = bsearch((void *) (intptr_t) cp, category_properties,
+                              category_properties_count,
+                              sizeof(struct category_property),
+                              category_property_cmp);
+  if (!vprop)
+    return HB_NoCategory;
+
+  return ((const struct category_property *) vprop)->category;
+}
+
+void
+HB_GetUnicodeCharProperties(HB_UChar32 ch,
+                            HB_CharCategory *category,
+                            int *combiningClass) {
+  *category = code_point_to_category(ch);
+  *combiningClass = code_point_to_combining_class(ch);
+}
+
+HB_CharCategory
+HB_GetUnicodeCharCategory(HB_UChar32 ch) {
+  return code_point_to_category(ch);
+}
diff --git a/third_party/harfbuzz/contrib/harfbuzz-unicode.c b/third_party/harfbuzz/contrib/harfbuzz-unicode.c
new file mode 100644
index 0000000..f2185dc
--- /dev/null
+++ b/third_party/harfbuzz/contrib/harfbuzz-unicode.c
@@ -0,0 +1,287 @@
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <harfbuzz-external.h>
+#include <harfbuzz-impl.h>
+#include <harfbuzz-shaper.h>
+#include "harfbuzz-unicode.h"
+
+#include "tables/grapheme-break-properties.h"
+#include "tables/mirroring-properties.h"
+#include "tables/script-properties.h"
+
+uint32_t
+utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) {
+  const uint16_t v = chars[(*iter)++];
+  if (HB_IsHighSurrogate(v)) {
+    // surrogate pair
+    if (*iter >= len) {
+      // the surrogate is incomplete.
+      return HB_InvalidCodePoint;
+    }
+    const uint16_t v2 = chars[(*iter)++];
+    if (!HB_IsLowSurrogate(v2)) {
+      // invalidate surrogate pair.
+      return HB_InvalidCodePoint;
+    }
+
+    return HB_SurrogateToUcs4(v, v2);
+  }
+
+  if (HB_IsLowSurrogate(v)) {
+    // this isn't a valid code point
+    return HB_InvalidCodePoint;
+  }
+
+  return v;
+}
+
+uint32_t
+utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) {
+  const uint16_t v = chars[(*iter)--];
+  if (HB_IsLowSurrogate(v)) {
+    // surrogate pair
+    if (*iter < 0) {
+      // the surrogate is incomplete.
+      return HB_InvalidCodePoint;
+    }
+    const uint16_t v2 = chars[(*iter)--];
+    if (!HB_IsHighSurrogate(v2)) {
+      // invalidate surrogate pair.
+      return HB_InvalidCodePoint;
+    }
+
+    return HB_SurrogateToUcs4(v2, v);
+  }
+
+  if (HB_IsHighSurrogate(v)) {
+    // this isn't a valid code point
+    return HB_InvalidCodePoint;
+  }
+
+  return v;
+}
+
+static int
+script_property_cmp(const void *vkey, const void *vcandidate) {
+  const uint32_t key = (uint32_t) (intptr_t) vkey;
+  const struct script_property *candidate = vcandidate;
+
+  if (key < candidate->range_start) {
+    return -1;
+  } else if (key > candidate->range_end) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+HB_Script
+code_point_to_script(uint32_t cp) {
+  const void *vprop = bsearch((void *) (intptr_t) cp, script_properties,
+                              script_properties_count,
+                              sizeof(struct script_property),
+                              script_property_cmp);
+  if (!vprop)
+    return HB_Script_Common;
+
+  return ((const struct script_property *) vprop)->script;
+}
+
+char
+hb_utf16_script_run_next(unsigned *num_code_points, HB_ScriptItem *output,
+                         const uint16_t *chars, size_t len, ssize_t *iter) {
+  if (*iter == len)
+    return 0;
+
+  output->pos = *iter;
+  const uint32_t init_cp = utf16_to_code_point(chars, len, iter);
+  unsigned cps = 1;
+  if (init_cp == HB_InvalidCodePoint)
+    return 0;
+  const HB_Script init_script = code_point_to_script(init_cp);
+  HB_Script current_script = init_script;
+  output->script = init_script;
+
+  for (;;) {
+    if (*iter == len)
+      break;
+    const ssize_t prev_iter = *iter;
+    const uint32_t cp = utf16_to_code_point(chars, len, iter);
+    if (cp == HB_InvalidCodePoint)
+      return 0;
+    cps++;
+    const HB_Script script = code_point_to_script(cp);
+
+    if (script != current_script) {
+      if (current_script == init_script == HB_Script_Inherited) {
+        // If we started off as inherited, we take whatever we can find.
+        output->script = script;
+        current_script = script;
+        continue;
+      } else if (script == HB_Script_Inherited) {
+        continue;
+      } else {
+        *iter = prev_iter;
+        cps--;
+        break;
+      }
+    }
+  }
+
+  if (output->script == HB_Script_Inherited)
+    output->script = HB_Script_Common;
+
+  output->length = *iter - output->pos;
+  if (num_code_points)
+    *num_code_points = cps;
+  return 1;
+}
+
+char
+hb_utf16_script_run_prev(unsigned *num_code_points, HB_ScriptItem *output,
+                         const uint16_t *chars, size_t len, ssize_t *iter) {
+  if (*iter == (size_t) -1)
+    return 0;
+
+  const size_t ending_index = *iter;
+  const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter);
+  unsigned cps = 1;
+  if (init_cp == HB_InvalidCodePoint)
+    return 0;
+  const HB_Script init_script = code_point_to_script(init_cp);
+  HB_Script current_script = init_script;
+  output->script = init_script;
+
+  for (;;) {
+    if (*iter < 0)
+      break;
+    const ssize_t prev_iter = *iter;
+    const uint32_t cp = utf16_to_code_point_prev(chars, len, iter);
+    if (cp == HB_InvalidCodePoint)
+      return 0;
+    cps++;
+    const HB_Script script = code_point_to_script(cp);
+
+    if (script != current_script) {
+      if (current_script == init_script == HB_Script_Inherited) {
+        // If we started off as inherited, we take whatever we can find.
+        output->script = script;
+        current_script = script;
+        continue;
+      } else if (script == HB_Script_Inherited) {
+        // Just assume that whatever follows this combining character is within
+        // the same script.  This is incorrect if you had language1 + combining
+        // char + language 2, but that is rare and this code is suspicious
+        // anyway.
+        continue;
+      } else {
+        *iter = prev_iter;
+        cps--;
+        break;
+      }
+    }
+  }
+
+  if (output->script == HB_Script_Inherited)
+    output->script = HB_Script_Common;
+
+  output->pos = *iter + 1;
+  output->length = ending_index - *iter;
+  if (num_code_points)
+    *num_code_points = cps;
+  return 1;
+}
+
+static int
+grapheme_break_property_cmp(const void *vkey, const void *vcandidate) {
+  const uint32_t key = (uint32_t) (intptr_t) vkey;
+  const struct grapheme_break_property *candidate = vcandidate;
+
+  if (key < candidate->range_start) {
+    return -1;
+  } else if (key > candidate->range_end) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+HB_GraphemeClass
+HB_GetGraphemeClass(HB_UChar32 ch) {
+  const void *vprop = bsearch((void *) (intptr_t) ch, grapheme_break_properties,
+                              grapheme_break_properties_count,
+                              sizeof(struct grapheme_break_property),
+                              grapheme_break_property_cmp);
+  if (!vprop)
+    return HB_Grapheme_Other;
+
+  return ((const struct grapheme_break_property *) vprop)->klass;
+}
+
+HB_WordClass
+HB_GetWordClass(HB_UChar32 ch) {
+  abort();
+  return 0;
+}
+
+HB_SentenceClass
+HB_GetSentenceClass(HB_UChar32 ch) {
+  abort();
+  return 0;
+}
+
+void
+HB_GetGraphemeAndLineBreakClass(HB_UChar32 ch, HB_GraphemeClass *gclass, HB_LineBreakClass *breakclass) {
+  *gclass = HB_GetGraphemeClass(ch);
+  *breakclass = HB_GetLineBreakClass(ch);
+}
+
+static int
+mirroring_property_cmp(const void *vkey, const void *vcandidate) {
+  const uint32_t key = (uint32_t) (intptr_t) vkey;
+  const struct mirroring_property *candidate = vcandidate;
+
+  if (key < candidate->a) {
+    return -1;
+  } else if (key > candidate->a) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+HB_UChar16
+HB_GetMirroredChar(HB_UChar16 ch) {
+  const void *mprop = bsearch((void *) (intptr_t) ch, mirroring_properties,
+                              mirroring_properties_count,
+                              sizeof(struct mirroring_property),
+                              mirroring_property_cmp);
+  if (!mprop)
+    return ch;
+
+  return ((const struct mirroring_property *) mprop)->b;
+}
+
+void *
+HB_Library_Resolve(const char *library, const char *symbol) {
+  abort();
+  return NULL;
+}
+
+void *
+HB_TextCodecForMib(int mib) {
+  abort();
+  return NULL;
+}
+
+char *
+HB_TextCodec_ConvertFromUnicode(void *codec, const HB_UChar16 *unicode, hb_uint32 length, hb_uint32 *outputLength) {
+  abort();
+  return NULL;
+}
+
+void
+HB_TextCodec_FreeResult(char *v) {
+  abort();
+}
diff --git a/third_party/harfbuzz/contrib/harfbuzz-unicode.h b/third_party/harfbuzz/contrib/harfbuzz-unicode.h
new file mode 100644
index 0000000..f28b3c3
--- /dev/null
+++ b/third_party/harfbuzz/contrib/harfbuzz-unicode.h
@@ -0,0 +1,54 @@
+#ifndef SCRIPT_IDENTIFY_H_
+#define SCRIPT_IDENTIFY_H_
+
+#include <stdint.h>
+
+#include <harfbuzz-shaper.h>
+
+static const uint32_t HB_InvalidCodePoint = 0xffffffffu;
+
+// -----------------------------------------------------------------------------
+// Return the next Unicode code point from a UTF-16 vector
+//   chars: a pointer to @len words
+//   iter: (input/output) an index into @chars. This is updated.
+//   returns: HB_InvalidCodePoint on error and the code point otherwise.
+// -----------------------------------------------------------------------------
+uint32_t utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter);
+
+// -----------------------------------------------------------------------------
+// Like the above, except that the code points are traversed backwards. Thus,
+// on the first call, |iter| should be |len| - 1.
+// -----------------------------------------------------------------------------
+uint32_t utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter);
+
+// -----------------------------------------------------------------------------
+// Return the script of the given code point
+// -----------------------------------------------------------------------------
+HB_Script code_point_to_script(uint32_t cp);
+
+// -----------------------------------------------------------------------------
+// Find the next script run in a UTF-16 string.
+//
+// A script run is a subvector of codepoints, all of which are in the same
+// script. A run will never cut a surrogate pair in half at either end.
+//
+// num_code_points: (output, maybe NULL) the number of code points in the run
+// output: (output) the @pos, @length and @script fields are set on success
+// chars: the UTF-16 string
+// len: the length of @chars, in words
+// iter: (in/out) the current index into the string. This should be 0 for the
+//   first call and is updated on exit.
+//
+// returns: non-zero if a script run was found and returned.
+// -----------------------------------------------------------------------------
+char hb_utf16_script_run_next(unsigned *num_code_points, HB_ScriptItem *output,
+                              const uint16_t *chars, size_t len, ssize_t *iter);
+
+// -----------------------------------------------------------------------------
+// This is the same as above, except that the input is traversed backwards.
+// Thus, on the first call, |iter| should be |len| - 1.
+// -----------------------------------------------------------------------------
+char hb_utf16_script_run_prev(unsigned *num_code_points, HB_ScriptItem *output,
+                              const uint16_t *chars, size_t len, ssize_t *iter);
+
+#endif
diff --git a/third_party/harfbuzz/contrib/tables/BidiMirroring.txt b/third_party/harfbuzz/contrib/tables/BidiMirroring.txt
new file mode 100644
index 0000000..64d29e4
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/BidiMirroring.txt
@@ -0,0 +1,588 @@
+# BidiMirroring-5.1.0.txt
+# Date: 2008-03-28, 10:22:00 PDT [KW]
+#
+# Bidi_Mirroring_Glyph Property
+# 
+# This file is an informative contributory data file in the
+# Unicode Character Database.
+#
+# Copyright (c) 1991-2008 Unicode, Inc.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+#
+# This data file lists characters that have the mirrored property
+# where there is another Unicode character that typically has a glyph
+# that is the mirror image of the original character's glyph.
+# The repertoire covered by the file is Unicode 5.1.0.
+# 
+# The file contains a list of lines with mappings from one code point
+# to another one for character-based mirroring.
+# Note that for "real" mirroring, a rendering engine needs to select
+# appropriate alternative glyphs, and that many Unicode characters do not
+# have a mirror-image Unicode character.
+# 
+# Each mapping line contains two fields, separated by a semicolon (';').
+# Each of the two fields contains a code point represented as a
+# variable-length hexadecimal value with 4 to 6 digits.
+# A comment indicates where the characters are "BEST FIT" mirroring.
+# 
+# Code points with the "mirrored" property but no appropriate mirrors are
+# listed as comments at the end of the file.
+# 
+# For information on bidi mirroring, see UAX #9: Bidirectional Algorithm,
+# at http://www.unicode.org/unicode/reports/tr9/
+# 
+# This file was originally created by Markus Scherer.
+# Extended for Unicode 3.2, 4.0, 4.1, 5.0, and 5.1 by Ken Whistler.
+# 
+# ############################################################
+
+0028; 0029 # LEFT PARENTHESIS
+0029; 0028 # RIGHT PARENTHESIS
+003C; 003E # LESS-THAN SIGN
+003E; 003C # GREATER-THAN SIGN
+005B; 005D # LEFT SQUARE BRACKET
+005D; 005B # RIGHT SQUARE BRACKET
+007B; 007D # LEFT CURLY BRACKET
+007D; 007B # RIGHT CURLY BRACKET
+00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0F3A; 0F3B # TIBETAN MARK GUG RTAGS GYON
+0F3B; 0F3A # TIBETAN MARK GUG RTAGS GYAS
+0F3C; 0F3D # TIBETAN MARK ANG KHANG GYON
+0F3D; 0F3C # TIBETAN MARK ANG KHANG GYAS
+169B; 169C # OGHAM FEATHER MARK
+169C; 169B # OGHAM REVERSED FEATHER MARK
+2039; 203A # SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+203A; 2039 # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+2045; 2046 # LEFT SQUARE BRACKET WITH QUILL
+2046; 2045 # RIGHT SQUARE BRACKET WITH QUILL
+207D; 207E # SUPERSCRIPT LEFT PARENTHESIS
+207E; 207D # SUPERSCRIPT RIGHT PARENTHESIS
+208D; 208E # SUBSCRIPT LEFT PARENTHESIS
+208E; 208D # SUBSCRIPT RIGHT PARENTHESIS
+2208; 220B # ELEMENT OF
+2209; 220C # NOT AN ELEMENT OF
+220A; 220D # SMALL ELEMENT OF
+220B; 2208 # CONTAINS AS MEMBER
+220C; 2209 # DOES NOT CONTAIN AS MEMBER
+220D; 220A # SMALL CONTAINS AS MEMBER
+2215; 29F5 # DIVISION SLASH
+223C; 223D # TILDE OPERATOR
+223D; 223C # REVERSED TILDE
+2243; 22CD # ASYMPTOTICALLY EQUAL TO
+2252; 2253 # APPROXIMATELY EQUAL TO OR THE IMAGE OF
+2253; 2252 # IMAGE OF OR APPROXIMATELY EQUAL TO
+2254; 2255 # COLON EQUALS
+2255; 2254 # EQUALS COLON
+2264; 2265 # LESS-THAN OR EQUAL TO
+2265; 2264 # GREATER-THAN OR EQUAL TO
+2266; 2267 # LESS-THAN OVER EQUAL TO
+2267; 2266 # GREATER-THAN OVER EQUAL TO
+2268; 2269 # [BEST FIT] LESS-THAN BUT NOT EQUAL TO
+2269; 2268 # [BEST FIT] GREATER-THAN BUT NOT EQUAL TO
+226A; 226B # MUCH LESS-THAN
+226B; 226A # MUCH GREATER-THAN
+226E; 226F # [BEST FIT] NOT LESS-THAN
+226F; 226E # [BEST FIT] NOT GREATER-THAN
+2270; 2271 # [BEST FIT] NEITHER LESS-THAN NOR EQUAL TO
+2271; 2270 # [BEST FIT] NEITHER GREATER-THAN NOR EQUAL TO
+2272; 2273 # [BEST FIT] LESS-THAN OR EQUIVALENT TO
+2273; 2272 # [BEST FIT] GREATER-THAN OR EQUIVALENT TO
+2274; 2275 # [BEST FIT] NEITHER LESS-THAN NOR EQUIVALENT TO
+2275; 2274 # [BEST FIT] NEITHER GREATER-THAN NOR EQUIVALENT TO
+2276; 2277 # LESS-THAN OR GREATER-THAN
+2277; 2276 # GREATER-THAN OR LESS-THAN
+2278; 2279 # [BEST FIT] NEITHER LESS-THAN NOR GREATER-THAN
+2279; 2278 # [BEST FIT] NEITHER GREATER-THAN NOR LESS-THAN
+227A; 227B # PRECEDES
+227B; 227A # SUCCEEDS
+227C; 227D # PRECEDES OR EQUAL TO
+227D; 227C # SUCCEEDS OR EQUAL TO
+227E; 227F # [BEST FIT] PRECEDES OR EQUIVALENT TO
+227F; 227E # [BEST FIT] SUCCEEDS OR EQUIVALENT TO
+2280; 2281 # [BEST FIT] DOES NOT PRECEDE
+2281; 2280 # [BEST FIT] DOES NOT SUCCEED
+2282; 2283 # SUBSET OF
+2283; 2282 # SUPERSET OF
+2284; 2285 # [BEST FIT] NOT A SUBSET OF
+2285; 2284 # [BEST FIT] NOT A SUPERSET OF
+2286; 2287 # SUBSET OF OR EQUAL TO
+2287; 2286 # SUPERSET OF OR EQUAL TO
+2288; 2289 # [BEST FIT] NEITHER A SUBSET OF NOR EQUAL TO
+2289; 2288 # [BEST FIT] NEITHER A SUPERSET OF NOR EQUAL TO
+228A; 228B # [BEST FIT] SUBSET OF WITH NOT EQUAL TO
+228B; 228A # [BEST FIT] SUPERSET OF WITH NOT EQUAL TO
+228F; 2290 # SQUARE IMAGE OF
+2290; 228F # SQUARE ORIGINAL OF
+2291; 2292 # SQUARE IMAGE OF OR EQUAL TO
+2292; 2291 # SQUARE ORIGINAL OF OR EQUAL TO
+2298; 29B8 # CIRCLED DIVISION SLASH
+22A2; 22A3 # RIGHT TACK
+22A3; 22A2 # LEFT TACK
+22A6; 2ADE # ASSERTION
+22A8; 2AE4 # TRUE
+22A9; 2AE3 # FORCES
+22AB; 2AE5 # DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+22B0; 22B1 # PRECEDES UNDER RELATION
+22B1; 22B0 # SUCCEEDS UNDER RELATION
+22B2; 22B3 # NORMAL SUBGROUP OF
+22B3; 22B2 # CONTAINS AS NORMAL SUBGROUP
+22B4; 22B5 # NORMAL SUBGROUP OF OR EQUAL TO
+22B5; 22B4 # CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
+22B6; 22B7 # ORIGINAL OF
+22B7; 22B6 # IMAGE OF
+22C9; 22CA # LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
+22CA; 22C9 # RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
+22CB; 22CC # LEFT SEMIDIRECT PRODUCT
+22CC; 22CB # RIGHT SEMIDIRECT PRODUCT
+22CD; 2243 # REVERSED TILDE EQUALS
+22D0; 22D1 # DOUBLE SUBSET
+22D1; 22D0 # DOUBLE SUPERSET
+22D6; 22D7 # LESS-THAN WITH DOT
+22D7; 22D6 # GREATER-THAN WITH DOT
+22D8; 22D9 # VERY MUCH LESS-THAN
+22D9; 22D8 # VERY MUCH GREATER-THAN
+22DA; 22DB # LESS-THAN EQUAL TO OR GREATER-THAN
+22DB; 22DA # GREATER-THAN EQUAL TO OR LESS-THAN
+22DC; 22DD # EQUAL TO OR LESS-THAN
+22DD; 22DC # EQUAL TO OR GREATER-THAN
+22DE; 22DF # EQUAL TO OR PRECEDES
+22DF; 22DE # EQUAL TO OR SUCCEEDS
+22E0; 22E1 # [BEST FIT] DOES NOT PRECEDE OR EQUAL
+22E1; 22E0 # [BEST FIT] DOES NOT SUCCEED OR EQUAL
+22E2; 22E3 # [BEST FIT] NOT SQUARE IMAGE OF OR EQUAL TO
+22E3; 22E2 # [BEST FIT] NOT SQUARE ORIGINAL OF OR EQUAL TO
+22E4; 22E5 # [BEST FIT] SQUARE IMAGE OF OR NOT EQUAL TO
+22E5; 22E4 # [BEST FIT] SQUARE ORIGINAL OF OR NOT EQUAL TO
+22E6; 22E7 # [BEST FIT] LESS-THAN BUT NOT EQUIVALENT TO
+22E7; 22E6 # [BEST FIT] GREATER-THAN BUT NOT EQUIVALENT TO
+22E8; 22E9 # [BEST FIT] PRECEDES BUT NOT EQUIVALENT TO
+22E9; 22E8 # [BEST FIT] SUCCEEDS BUT NOT EQUIVALENT TO
+22EA; 22EB # [BEST FIT] NOT NORMAL SUBGROUP OF
+22EB; 22EA # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP
+22EC; 22ED # [BEST FIT] NOT NORMAL SUBGROUP OF OR EQUAL TO
+22ED; 22EC # [BEST FIT] DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+22F0; 22F1 # UP RIGHT DIAGONAL ELLIPSIS
+22F1; 22F0 # DOWN RIGHT DIAGONAL ELLIPSIS
+22F2; 22FA # ELEMENT OF WITH LONG HORIZONTAL STROKE
+22F3; 22FB # ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+22F4; 22FC # SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+22F6; 22FD # ELEMENT OF WITH OVERBAR
+22F7; 22FE # SMALL ELEMENT OF WITH OVERBAR
+22FA; 22F2 # CONTAINS WITH LONG HORIZONTAL STROKE
+22FB; 22F3 # CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+22FC; 22F4 # SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+22FD; 22F6 # CONTAINS WITH OVERBAR
+22FE; 22F7 # SMALL CONTAINS WITH OVERBAR
+2308; 2309 # LEFT CEILING
+2309; 2308 # RIGHT CEILING
+230A; 230B # LEFT FLOOR
+230B; 230A # RIGHT FLOOR
+2329; 232A # LEFT-POINTING ANGLE BRACKET
+232A; 2329 # RIGHT-POINTING ANGLE BRACKET
+2768; 2769 # MEDIUM LEFT PARENTHESIS ORNAMENT
+2769; 2768 # MEDIUM RIGHT PARENTHESIS ORNAMENT
+276A; 276B # MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT
+276B; 276A # MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT
+276C; 276D # MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT
+276D; 276C # MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT
+276E; 276F # HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT
+276F; 276E # HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT
+2770; 2771 # HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT
+2771; 2770 # HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT
+2772; 2773 # LIGHT LEFT TORTOISE SHELL BRACKET
+2773; 2772 # LIGHT RIGHT TORTOISE SHELL BRACKET
+2774; 2775 # MEDIUM LEFT CURLY BRACKET ORNAMENT
+2775; 2774 # MEDIUM RIGHT CURLY BRACKET ORNAMENT
+27C3; 27C4 # OPEN SUBSET
+27C4; 27C3 # OPEN SUPERSET
+27C5; 27C6 # LEFT S-SHAPED BAG DELIMITER
+27C6; 27C5 # RIGHT S-SHAPED BAG DELIMITER
+27C8; 27C9 # REVERSE SOLIDUS PRECEDING SUBSET
+27C9; 27C8 # SUPERSET PRECEDING SOLIDUS
+27D5; 27D6 # LEFT OUTER JOIN
+27D6; 27D5 # RIGHT OUTER JOIN
+27DD; 27DE # LONG RIGHT TACK
+27DE; 27DD # LONG LEFT TACK
+27E2; 27E3 # WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK
+27E3; 27E2 # WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK
+27E4; 27E5 # WHITE SQUARE WITH LEFTWARDS TICK
+27E5; 27E4 # WHITE SQUARE WITH RIGHTWARDS TICK
+27E6; 27E7 # MATHEMATICAL LEFT WHITE SQUARE BRACKET
+27E7; 27E6 # MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+27E8; 27E9 # MATHEMATICAL LEFT ANGLE BRACKET
+27E9; 27E8 # MATHEMATICAL RIGHT ANGLE BRACKET
+27EA; 27EB # MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
+27EB; 27EA # MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
+27EC; 27ED # MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
+27ED; 27EC # MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
+27EE; 27EF # MATHEMATICAL LEFT FLATTENED PARENTHESIS
+27EF; 27EE # MATHEMATICAL RIGHT FLATTENED PARENTHESIS
+2983; 2984 # LEFT WHITE CURLY BRACKET
+2984; 2983 # RIGHT WHITE CURLY BRACKET
+2985; 2986 # LEFT WHITE PARENTHESIS
+2986; 2985 # RIGHT WHITE PARENTHESIS
+2987; 2988 # Z NOTATION LEFT IMAGE BRACKET
+2988; 2987 # Z NOTATION RIGHT IMAGE BRACKET
+2989; 298A # Z NOTATION LEFT BINDING BRACKET
+298A; 2989 # Z NOTATION RIGHT BINDING BRACKET
+298B; 298C # LEFT SQUARE BRACKET WITH UNDERBAR
+298C; 298B # RIGHT SQUARE BRACKET WITH UNDERBAR
+298D; 2990 # LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
+298E; 298F # RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+298F; 298E # LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+2990; 298D # RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
+2991; 2992 # LEFT ANGLE BRACKET WITH DOT
+2992; 2991 # RIGHT ANGLE BRACKET WITH DOT
+2993; 2994 # LEFT ARC LESS-THAN BRACKET
+2994; 2993 # RIGHT ARC GREATER-THAN BRACKET
+2995; 2996 # DOUBLE LEFT ARC GREATER-THAN BRACKET
+2996; 2995 # DOUBLE RIGHT ARC LESS-THAN BRACKET
+2997; 2998 # LEFT BLACK TORTOISE SHELL BRACKET
+2998; 2997 # RIGHT BLACK TORTOISE SHELL BRACKET
+29B8; 2298 # CIRCLED REVERSE SOLIDUS
+29C0; 29C1 # CIRCLED LESS-THAN
+29C1; 29C0 # CIRCLED GREATER-THAN
+29C4; 29C5 # SQUARED RISING DIAGONAL SLASH
+29C5; 29C4 # SQUARED FALLING DIAGONAL SLASH
+29CF; 29D0 # LEFT TRIANGLE BESIDE VERTICAL BAR
+29D0; 29CF # VERTICAL BAR BESIDE RIGHT TRIANGLE
+29D1; 29D2 # BOWTIE WITH LEFT HALF BLACK
+29D2; 29D1 # BOWTIE WITH RIGHT HALF BLACK
+29D4; 29D5 # TIMES WITH LEFT HALF BLACK
+29D5; 29D4 # TIMES WITH RIGHT HALF BLACK
+29D8; 29D9 # LEFT WIGGLY FENCE
+29D9; 29D8 # RIGHT WIGGLY FENCE
+29DA; 29DB # LEFT DOUBLE WIGGLY FENCE
+29DB; 29DA # RIGHT DOUBLE WIGGLY FENCE
+29F5; 2215 # REVERSE SOLIDUS OPERATOR
+29F8; 29F9 # BIG SOLIDUS
+29F9; 29F8 # BIG REVERSE SOLIDUS
+29FC; 29FD # LEFT-POINTING CURVED ANGLE BRACKET
+29FD; 29FC # RIGHT-POINTING CURVED ANGLE BRACKET
+2A2B; 2A2C # MINUS SIGN WITH FALLING DOTS
+2A2C; 2A2B # MINUS SIGN WITH RISING DOTS
+2A2D; 2A2E # PLUS SIGN IN LEFT HALF CIRCLE
+2A2E; 2A2D # PLUS SIGN IN RIGHT HALF CIRCLE
+2A34; 2A35 # MULTIPLICATION SIGN IN LEFT HALF CIRCLE
+2A35; 2A34 # MULTIPLICATION SIGN IN RIGHT HALF CIRCLE
+2A3C; 2A3D # INTERIOR PRODUCT
+2A3D; 2A3C # RIGHTHAND INTERIOR PRODUCT
+2A64; 2A65 # Z NOTATION DOMAIN ANTIRESTRICTION
+2A65; 2A64 # Z NOTATION RANGE ANTIRESTRICTION
+2A79; 2A7A # LESS-THAN WITH CIRCLE INSIDE
+2A7A; 2A79 # GREATER-THAN WITH CIRCLE INSIDE
+2A7D; 2A7E # LESS-THAN OR SLANTED EQUAL TO
+2A7E; 2A7D # GREATER-THAN OR SLANTED EQUAL TO
+2A7F; 2A80 # LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
+2A80; 2A7F # GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
+2A81; 2A82 # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
+2A82; 2A81 # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
+2A83; 2A84 # LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT
+2A84; 2A83 # GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT
+2A8B; 2A8C # LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
+2A8C; 2A8B # GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
+2A91; 2A92 # LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL
+2A92; 2A91 # GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL
+2A93; 2A94 # LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL
+2A94; 2A93 # GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL
+2A95; 2A96 # SLANTED EQUAL TO OR LESS-THAN
+2A96; 2A95 # SLANTED EQUAL TO OR GREATER-THAN
+2A97; 2A98 # SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE
+2A98; 2A97 # SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE
+2A99; 2A9A # DOUBLE-LINE EQUAL TO OR LESS-THAN
+2A9A; 2A99 # DOUBLE-LINE EQUAL TO OR GREATER-THAN
+2A9B; 2A9C # DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN
+2A9C; 2A9B # DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN
+2AA1; 2AA2 # DOUBLE NESTED LESS-THAN
+2AA2; 2AA1 # DOUBLE NESTED GREATER-THAN
+2AA6; 2AA7 # LESS-THAN CLOSED BY CURVE
+2AA7; 2AA6 # GREATER-THAN CLOSED BY CURVE
+2AA8; 2AA9 # LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
+2AA9; 2AA8 # GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
+2AAA; 2AAB # SMALLER THAN
+2AAB; 2AAA # LARGER THAN
+2AAC; 2AAD # SMALLER THAN OR EQUAL TO
+2AAD; 2AAC # LARGER THAN OR EQUAL TO
+2AAF; 2AB0 # PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
+2AB0; 2AAF # SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
+2AB3; 2AB4 # PRECEDES ABOVE EQUALS SIGN
+2AB4; 2AB3 # SUCCEEDS ABOVE EQUALS SIGN
+2ABB; 2ABC # DOUBLE PRECEDES
+2ABC; 2ABB # DOUBLE SUCCEEDS
+2ABD; 2ABE # SUBSET WITH DOT
+2ABE; 2ABD # SUPERSET WITH DOT
+2ABF; 2AC0 # SUBSET WITH PLUS SIGN BELOW
+2AC0; 2ABF # SUPERSET WITH PLUS SIGN BELOW
+2AC1; 2AC2 # SUBSET WITH MULTIPLICATION SIGN BELOW
+2AC2; 2AC1 # SUPERSET WITH MULTIPLICATION SIGN BELOW
+2AC3; 2AC4 # SUBSET OF OR EQUAL TO WITH DOT ABOVE
+2AC4; 2AC3 # SUPERSET OF OR EQUAL TO WITH DOT ABOVE
+2AC5; 2AC6 # SUBSET OF ABOVE EQUALS SIGN
+2AC6; 2AC5 # SUPERSET OF ABOVE EQUALS SIGN
+2ACD; 2ACE # SQUARE LEFT OPEN BOX OPERATOR
+2ACE; 2ACD # SQUARE RIGHT OPEN BOX OPERATOR
+2ACF; 2AD0 # CLOSED SUBSET
+2AD0; 2ACF # CLOSED SUPERSET
+2AD1; 2AD2 # CLOSED SUBSET OR EQUAL TO
+2AD2; 2AD1 # CLOSED SUPERSET OR EQUAL TO
+2AD3; 2AD4 # SUBSET ABOVE SUPERSET
+2AD4; 2AD3 # SUPERSET ABOVE SUBSET
+2AD5; 2AD6 # SUBSET ABOVE SUBSET
+2AD6; 2AD5 # SUPERSET ABOVE SUPERSET
+2ADE; 22A6 # SHORT LEFT TACK
+2AE3; 22A9 # DOUBLE VERTICAL BAR LEFT TURNSTILE
+2AE4; 22A8 # VERTICAL BAR DOUBLE LEFT TURNSTILE
+2AE5; 22AB # DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE
+2AEC; 2AED # DOUBLE STROKE NOT SIGN
+2AED; 2AEC # REVERSED DOUBLE STROKE NOT SIGN
+2AF7; 2AF8 # TRIPLE NESTED LESS-THAN
+2AF8; 2AF7 # TRIPLE NESTED GREATER-THAN
+2AF9; 2AFA # DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO
+2AFA; 2AF9 # DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO
+2E02; 2E03 # LEFT SUBSTITUTION BRACKET
+2E03; 2E02 # RIGHT SUBSTITUTION BRACKET
+2E04; 2E05 # LEFT DOTTED SUBSTITUTION BRACKET
+2E05; 2E04 # RIGHT DOTTED SUBSTITUTION BRACKET
+2E09; 2E0A # LEFT TRANSPOSITION BRACKET
+2E0A; 2E09 # RIGHT TRANSPOSITION BRACKET
+2E0C; 2E0D # LEFT RAISED OMISSION BRACKET
+2E0D; 2E0C # RIGHT RAISED OMISSION BRACKET
+2E1C; 2E1D # LEFT LOW PARAPHRASE BRACKET
+2E1D; 2E1C # RIGHT LOW PARAPHRASE BRACKET
+2E20; 2E21 # LEFT VERTICAL BAR WITH QUILL
+2E21; 2E20 # RIGHT VERTICAL BAR WITH QUILL
+2E22; 2E23 # TOP LEFT HALF BRACKET
+2E23; 2E22 # TOP RIGHT HALF BRACKET
+2E24; 2E25 # BOTTOM LEFT HALF BRACKET
+2E25; 2E24 # BOTTOM RIGHT HALF BRACKET
+2E26; 2E27 # LEFT SIDEWAYS U BRACKET
+2E27; 2E26 # RIGHT SIDEWAYS U BRACKET
+2E28; 2E29 # LEFT DOUBLE PARENTHESIS
+2E29; 2E28 # RIGHT DOUBLE PARENTHESIS
+3008; 3009 # LEFT ANGLE BRACKET
+3009; 3008 # RIGHT ANGLE BRACKET
+300A; 300B # LEFT DOUBLE ANGLE BRACKET
+300B; 300A # RIGHT DOUBLE ANGLE BRACKET
+300C; 300D # [BEST FIT] LEFT CORNER BRACKET
+300D; 300C # [BEST FIT] RIGHT CORNER BRACKET
+300E; 300F # [BEST FIT] LEFT WHITE CORNER BRACKET
+300F; 300E # [BEST FIT] RIGHT WHITE CORNER BRACKET
+3010; 3011 # LEFT BLACK LENTICULAR BRACKET
+3011; 3010 # RIGHT BLACK LENTICULAR BRACKET
+3014; 3015 # LEFT TORTOISE SHELL BRACKET
+3015; 3014 # RIGHT TORTOISE SHELL BRACKET
+3016; 3017 # LEFT WHITE LENTICULAR BRACKET
+3017; 3016 # RIGHT WHITE LENTICULAR BRACKET
+3018; 3019 # LEFT WHITE TORTOISE SHELL BRACKET
+3019; 3018 # RIGHT WHITE TORTOISE SHELL BRACKET
+301A; 301B # LEFT WHITE SQUARE BRACKET
+301B; 301A # RIGHT WHITE SQUARE BRACKET
+FE59; FE5A # SMALL LEFT PARENTHESIS
+FE5A; FE59 # SMALL RIGHT PARENTHESIS
+FE5B; FE5C # SMALL LEFT CURLY BRACKET
+FE5C; FE5B # SMALL RIGHT CURLY BRACKET
+FE5D; FE5E # SMALL LEFT TORTOISE SHELL BRACKET
+FE5E; FE5D # SMALL RIGHT TORTOISE SHELL BRACKET
+FE64; FE65 # SMALL LESS-THAN SIGN
+FE65; FE64 # SMALL GREATER-THAN SIGN
+FF08; FF09 # FULLWIDTH LEFT PARENTHESIS
+FF09; FF08 # FULLWIDTH RIGHT PARENTHESIS
+FF1C; FF1E # FULLWIDTH LESS-THAN SIGN
+FF1E; FF1C # FULLWIDTH GREATER-THAN SIGN
+FF3B; FF3D # FULLWIDTH LEFT SQUARE BRACKET
+FF3D; FF3B # FULLWIDTH RIGHT SQUARE BRACKET
+FF5B; FF5D # FULLWIDTH LEFT CURLY BRACKET
+FF5D; FF5B # FULLWIDTH RIGHT CURLY BRACKET
+FF5F; FF60 # FULLWIDTH LEFT WHITE PARENTHESIS
+FF60; FF5F # FULLWIDTH RIGHT WHITE PARENTHESIS
+FF62; FF63 # [BEST FIT] HALFWIDTH LEFT CORNER BRACKET
+FF63; FF62 # [BEST FIT] HALFWIDTH RIGHT CORNER BRACKET
+
+# The following characters have no appropriate mirroring character.
+# For these characters it is up to the rendering system
+#   to provide mirrored glyphs.
+
+# 2140; DOUBLE-STRUCK N-ARY SUMMATION
+# 2201; COMPLEMENT
+# 2202; PARTIAL DIFFERENTIAL
+# 2203; THERE EXISTS
+# 2204; THERE DOES NOT EXIST
+# 2211; N-ARY SUMMATION
+# 2216; SET MINUS
+# 221A; SQUARE ROOT
+# 221B; CUBE ROOT
+# 221C; FOURTH ROOT
+# 221D; PROPORTIONAL TO
+# 221F; RIGHT ANGLE
+# 2220; ANGLE
+# 2221; MEASURED ANGLE
+# 2222; SPHERICAL ANGLE
+# 2224; DOES NOT DIVIDE
+# 2226; NOT PARALLEL TO
+# 222B; INTEGRAL
+# 222C; DOUBLE INTEGRAL
+# 222D; TRIPLE INTEGRAL
+# 222E; CONTOUR INTEGRAL
+# 222F; SURFACE INTEGRAL
+# 2230; VOLUME INTEGRAL
+# 2231; CLOCKWISE INTEGRAL
+# 2232; CLOCKWISE CONTOUR INTEGRAL
+# 2233; ANTICLOCKWISE CONTOUR INTEGRAL
+# 2239; EXCESS
+# 223B; HOMOTHETIC
+# 223E; INVERTED LAZY S
+# 223F; SINE WAVE
+# 2240; WREATH PRODUCT
+# 2241; NOT TILDE
+# 2242; MINUS TILDE
+# 2244; NOT ASYMPTOTICALLY EQUAL TO
+# 2245; APPROXIMATELY EQUAL TO
+# 2246; APPROXIMATELY BUT NOT ACTUALLY EQUAL TO
+# 2247; NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+# 2248; ALMOST EQUAL TO
+# 2249; NOT ALMOST EQUAL TO
+# 224A; ALMOST EQUAL OR EQUAL TO
+# 224B; TRIPLE TILDE
+# 224C; ALL EQUAL TO
+# 225F; QUESTIONED EQUAL TO
+# 2260; NOT EQUAL TO
+# 2262; NOT IDENTICAL TO
+# 228C; MULTISET
+# 22A7; MODELS
+# 22AA; TRIPLE VERTICAL BAR RIGHT TURNSTILE
+# 22AC; DOES NOT PROVE
+# 22AD; NOT TRUE
+# 22AE; DOES NOT FORCE
+# 22AF; NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+# 22B8; MULTIMAP
+# 22BE; RIGHT ANGLE WITH ARC
+# 22BF; RIGHT TRIANGLE
+# 22F5; ELEMENT OF WITH DOT ABOVE
+# 22F8; ELEMENT OF WITH UNDERBAR
+# 22F9; ELEMENT OF WITH TWO HORIZONTAL STROKES
+# 22FF; Z NOTATION BAG MEMBERSHIP
+# 2320; TOP HALF INTEGRAL
+# 2321; BOTTOM HALF INTEGRAL
+# 27CC; LONG DIVISION
+# 27C0; THREE DIMENSIONAL ANGLE
+# 27D3; LOWER RIGHT CORNER WITH DOT
+# 27D4; UPPER LEFT CORNER WITH DOT
+# 27DC; LEFT MULTIMAP
+# 299B; MEASURED ANGLE OPENING LEFT
+# 299C; RIGHT ANGLE VARIANT WITH SQUARE
+# 299D; MEASURED RIGHT ANGLE WITH DOT
+# 299E; ANGLE WITH S INSIDE
+# 299F; ACUTE ANGLE
+# 29A0; SPHERICAL ANGLE OPENING LEFT
+# 29A1; SPHERICAL ANGLE OPENING UP
+# 29A2; TURNED ANGLE
+# 29A3; REVERSED ANGLE
+# 29A4; ANGLE WITH UNDERBAR
+# 29A5; REVERSED ANGLE WITH UNDERBAR
+# 29A6; OBLIQUE ANGLE OPENING UP
+# 29A7; OBLIQUE ANGLE OPENING DOWN
+# 29A8; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT
+# 29A9; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT
+# 29AA; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT
+# 29AB; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT
+# 29AC; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP
+# 29AD; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP
+# 29AE; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN
+# 29AF; MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN
+# 29C2; CIRCLE WITH SMALL CIRCLE TO THE RIGHT
+# 29C3; CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT
+# 29C9; TWO JOINED SQUARES
+# 29CE; RIGHT TRIANGLE ABOVE LEFT TRIANGLE
+# 29DC; INCOMPLETE INFINITY
+# 29E1; INCREASES AS
+# 29E3; EQUALS SIGN AND SLANTED PARALLEL
+# 29E4; EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE
+# 29E5; IDENTICAL TO AND SLANTED PARALLEL
+# 29E8; DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK
+# 29E9; DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK
+# 29F4; RULE-DELAYED
+# 29F6; SOLIDUS WITH OVERBAR
+# 29F7; REVERSE SOLIDUS WITH HORIZONTAL STROKE
+# 2A0A; MODULO TWO SUM
+# 2A0B; SUMMATION WITH INTEGRAL
+# 2A0C; QUADRUPLE INTEGRAL OPERATOR
+# 2A0D; FINITE PART INTEGRAL
+# 2A0E; INTEGRAL WITH DOUBLE STROKE
+# 2A0F; INTEGRAL AVERAGE WITH SLASH
+# 2A10; CIRCULATION FUNCTION
+# 2A11; ANTICLOCKWISE INTEGRATION
+# 2A12; LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE
+# 2A13; LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE
+# 2A14; LINE INTEGRATION NOT INCLUDING THE POLE
+# 2A15; INTEGRAL AROUND A POINT OPERATOR
+# 2A16; QUATERNION INTEGRAL OPERATOR
+# 2A17; INTEGRAL WITH LEFTWARDS ARROW WITH HOOK
+# 2A18; INTEGRAL WITH TIMES SIGN
+# 2A19; INTEGRAL WITH INTERSECTION
+# 2A1A; INTEGRAL WITH UNION
+# 2A1B; INTEGRAL WITH OVERBAR
+# 2A1C; INTEGRAL WITH UNDERBAR
+# 2A1E; LARGE LEFT TRIANGLE OPERATOR
+# 2A1F; Z NOTATION SCHEMA COMPOSITION
+# 2A20; Z NOTATION SCHEMA PIPING
+# 2A21; Z NOTATION SCHEMA PROJECTION
+# 2A24; PLUS SIGN WITH TILDE ABOVE
+# 2A26; PLUS SIGN WITH TILDE BELOW
+# 2A29; MINUS SIGN WITH COMMA ABOVE
+# 2A3E; Z NOTATION RELATIONAL COMPOSITION
+# 2A57; SLOPING LARGE OR
+# 2A58; SLOPING LARGE AND
+# 2A6A; TILDE OPERATOR WITH DOT ABOVE
+# 2A6B; TILDE OPERATOR WITH RISING DOTS
+# 2A6C; SIMILAR MINUS SIMILAR
+# 2A6D; CONGRUENT WITH DOT ABOVE
+# 2A6F; ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT
+# 2A70; APPROXIMATELY EQUAL OR EQUAL TO
+# 2A73; EQUALS SIGN ABOVE TILDE OPERATOR
+# 2A74; DOUBLE COLON EQUAL
+# 2A7B; LESS-THAN WITH QUESTION MARK ABOVE
+# 2A7C; GREATER-THAN WITH QUESTION MARK ABOVE
+# 2A85; LESS-THAN OR APPROXIMATE
+# 2A86; GREATER-THAN OR APPROXIMATE
+# 2A87; LESS-THAN AND SINGLE-LINE NOT EQUAL TO
+# 2A88; GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
+# 2A89; LESS-THAN AND NOT APPROXIMATE
+# 2A8A; GREATER-THAN AND NOT APPROXIMATE
+# 2A8D; LESS-THAN ABOVE SIMILAR OR EQUAL
+# 2A8E; GREATER-THAN ABOVE SIMILAR OR EQUAL
+# 2A8F; LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN
+# 2A90; GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN
+# 2A9D; SIMILAR OR LESS-THAN
+# 2A9E; SIMILAR OR GREATER-THAN
+# 2A9F; SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN
+# 2AA0; SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN
+# 2AA3; DOUBLE NESTED LESS-THAN WITH UNDERBAR
+# 2AB1; PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO
+# 2AB2; SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO
+# 2AB5; PRECEDES ABOVE NOT EQUAL TO
+# 2AB6; SUCCEEDS ABOVE NOT EQUAL TO
+# 2AB7; PRECEDES ABOVE ALMOST EQUAL TO
+# 2AB8; SUCCEEDS ABOVE ALMOST EQUAL TO
+# 2AB9; PRECEDES ABOVE NOT ALMOST EQUAL TO
+# 2ABA; SUCCEEDS ABOVE NOT ALMOST EQUAL TO
+# 2AC7; SUBSET OF ABOVE TILDE OPERATOR
+# 2AC8; SUPERSET OF ABOVE TILDE OPERATOR
+# 2AC9; SUBSET OF ABOVE ALMOST EQUAL TO
+# 2ACA; SUPERSET OF ABOVE ALMOST EQUAL TO
+# 2ACB; SUBSET OF ABOVE NOT EQUAL TO
+# 2ACC; SUPERSET OF ABOVE NOT EQUAL TO
+# 2ADC; FORKING
+# 2AE2; VERTICAL BAR TRIPLE RIGHT TURNSTILE
+# 2AE6; LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL
+# 2AEE; DOES NOT DIVIDE WITH REVERSED NEGATION SLASH
+# 2AF3; PARALLEL WITH TILDE OPERATOR
+# 2AFB; TRIPLE SOLIDUS BINARY RELATION
+# 2AFD; DOUBLE SOLIDUS OPERATOR
+# 1D6DB; MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
+# 1D715; MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL
+# 1D74F; MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL
+# 1D789; MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
+# 1D7C3; MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
+
+# EOF
diff --git a/third_party/harfbuzz/contrib/tables/DerivedCombiningClass.txt b/third_party/harfbuzz/contrib/tables/DerivedCombiningClass.txt
new file mode 100644
index 0000000..f30fb0b
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/DerivedCombiningClass.txt
@@ -0,0 +1,1881 @@
+# DerivedCombiningClass-5.1.0.txt
+# Date: 2008-03-20, 17:54:45 GMT [MD]
+#
+# Unicode Character Database
+# Copyright (c) 1991-2008 Unicode, Inc.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For documentation, see UCD.html
+
+# ================================================
+
+# Combining Class (listing UnicodeData.txt, field 3: see UCD.html)
+
+#  All code points not explicitly listed for Canonical_Combining_Class
+#  have the value Not_Reordered (0).
+
+# @missing: 0000..10FFFF; Not_Reordered
+
+# ================================================
+
+# Canonical_Combining_Class=Not_Reordered
+
+0000..001F    ; 0 # Cc  [32] <control-0000>..<control-001F>
+0020          ; 0 # Zs       SPACE
+0021..0023    ; 0 # Po   [3] EXCLAMATION MARK..NUMBER SIGN
+0024          ; 0 # Sc       DOLLAR SIGN
+0025..0027    ; 0 # Po   [3] PERCENT SIGN..APOSTROPHE
+0028          ; 0 # Ps       LEFT PARENTHESIS
+0029          ; 0 # Pe       RIGHT PARENTHESIS
+002A          ; 0 # Po       ASTERISK
+002B          ; 0 # Sm       PLUS SIGN
+002C          ; 0 # Po       COMMA
+002D          ; 0 # Pd       HYPHEN-MINUS
+002E..002F    ; 0 # Po   [2] FULL STOP..SOLIDUS
+0030..0039    ; 0 # Nd  [10] DIGIT ZERO..DIGIT NINE
+003A..003B    ; 0 # Po   [2] COLON..SEMICOLON
+003C..003E    ; 0 # Sm   [3] LESS-THAN SIGN..GREATER-THAN SIGN
+003F..0040    ; 0 # Po   [2] QUESTION MARK..COMMERCIAL AT
+0041..005A    ; 0 # L&  [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
+005B          ; 0 # Ps       LEFT SQUARE BRACKET
+005C          ; 0 # Po       REVERSE SOLIDUS
+005D          ; 0 # Pe       RIGHT SQUARE BRACKET
+005E          ; 0 # Sk       CIRCUMFLEX ACCENT
+005F          ; 0 # Pc       LOW LINE
+0060          ; 0 # Sk       GRAVE ACCENT
+0061..007A    ; 0 # L&  [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z
+007B          ; 0 # Ps       LEFT CURLY BRACKET
+007C          ; 0 # Sm       VERTICAL LINE
+007D          ; 0 # Pe       RIGHT CURLY BRACKET
+007E          ; 0 # Sm       TILDE
+007F..009F    ; 0 # Cc  [33] <control-007F>..<control-009F>
+00A0          ; 0 # Zs       NO-BREAK SPACE
+00A1          ; 0 # Po       INVERTED EXCLAMATION MARK
+00A2..00A5    ; 0 # Sc   [4] CENT SIGN..YEN SIGN
+00A6..00A7    ; 0 # So   [2] BROKEN BAR..SECTION SIGN
+00A8          ; 0 # Sk       DIAERESIS
+00A9          ; 0 # So       COPYRIGHT SIGN
+00AA          ; 0 # L&       FEMININE ORDINAL INDICATOR
+00AB          ; 0 # Pi       LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+00AC          ; 0 # Sm       NOT SIGN
+00AD          ; 0 # Cf       SOFT HYPHEN
+00AE          ; 0 # So       REGISTERED SIGN
+00AF          ; 0 # Sk       MACRON
+00B0          ; 0 # So       DEGREE SIGN
+00B1          ; 0 # Sm       PLUS-MINUS SIGN
+00B2..00B3    ; 0 # No   [2] SUPERSCRIPT TWO..SUPERSCRIPT THREE
+00B4          ; 0 # Sk       ACUTE ACCENT
+00B5          ; 0 # L&       MICRO SIGN
+00B6          ; 0 # So       PILCROW SIGN
+00B7          ; 0 # Po       MIDDLE DOT
+00B8          ; 0 # Sk       CEDILLA
+00B9          ; 0 # No       SUPERSCRIPT ONE
+00BA          ; 0 # L&       MASCULINE ORDINAL INDICATOR
+00BB          ; 0 # Pf       RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+00BC..00BE    ; 0 # No   [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS
+00BF          ; 0 # Po       INVERTED QUESTION MARK
+00C0..00D6    ; 0 # L&  [23] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER O WITH DIAERESIS
+00D7          ; 0 # Sm       MULTIPLICATION SIGN
+00D8..00F6    ; 0 # L&  [31] LATIN CAPITAL LETTER O WITH STROKE..LATIN SMALL LETTER O WITH DIAERESIS
+00F7          ; 0 # Sm       DIVISION SIGN
+00F8..01BA    ; 0 # L& [195] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER EZH WITH TAIL
+01BB          ; 0 # Lo       LATIN LETTER TWO WITH STROKE
+01BC..01BF    ; 0 # L&   [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
+01C0..01C3    ; 0 # Lo   [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
+01C4..0293    ; 0 # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL
+0294          ; 0 # Lo       LATIN LETTER GLOTTAL STOP
+0295..02AF    ; 0 # L&  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+02B0..02C1    ; 0 # Lm  [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
+02C2..02C5    ; 0 # Sk   [4] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER DOWN ARROWHEAD
+02C6..02D1    ; 0 # Lm  [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON
+02D2..02DF    ; 0 # Sk  [14] MODIFIER LETTER CENTRED RIGHT HALF RING..MODIFIER LETTER CROSS ACCENT
+02E0..02E4    ; 0 # Lm   [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
+02E5..02EB    ; 0 # Sk   [7] MODIFIER LETTER EXTRA-HIGH TONE BAR..MODIFIER LETTER YANG DEPARTING TONE MARK
+02EC          ; 0 # Lm       MODIFIER LETTER VOICING
+02ED          ; 0 # Sk       MODIFIER LETTER UNASPIRATED
+02EE          ; 0 # Lm       MODIFIER LETTER DOUBLE APOSTROPHE
+02EF..02FF    ; 0 # Sk  [17] MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER LETTER LOW LEFT ARROW
+034F          ; 0 # Mn       COMBINING GRAPHEME JOINER
+0370..0373    ; 0 # L&   [4] GREEK CAPITAL LETTER HETA..GREEK SMALL LETTER ARCHAIC SAMPI
+0374          ; 0 # Lm       GREEK NUMERAL SIGN
+0375          ; 0 # Sk       GREEK LOWER NUMERAL SIGN
+0376..0377    ; 0 # L&   [2] GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA..GREEK SMALL LETTER PAMPHYLIAN DIGAMMA
+037A          ; 0 # Lm       GREEK YPOGEGRAMMENI
+037B..037D    ; 0 # L&   [3] GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL
+037E          ; 0 # Po       GREEK QUESTION MARK
+0384..0385    ; 0 # Sk   [2] GREEK TONOS..GREEK DIALYTIKA TONOS
+0386          ; 0 # L&       GREEK CAPITAL LETTER ALPHA WITH TONOS
+0387          ; 0 # Po       GREEK ANO TELEIA
+0388..038A    ; 0 # L&   [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS
+038C          ; 0 # L&       GREEK CAPITAL LETTER OMICRON WITH TONOS
+038E..03A1    ; 0 # L&  [20] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK CAPITAL LETTER RHO
+03A3..03F5    ; 0 # L&  [83] GREEK CAPITAL LETTER SIGMA..GREEK LUNATE EPSILON SYMBOL
+03F6          ; 0 # Sm       GREEK REVERSED LUNATE EPSILON SYMBOL
+03F7..0481    ; 0 # L& [139] GREEK CAPITAL LETTER SHO..CYRILLIC SMALL LETTER KOPPA
+0482          ; 0 # So       CYRILLIC THOUSANDS SIGN
+0488..0489    ; 0 # Me   [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN
+048A..0523    ; 0 # L& [154] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK
+0531..0556    ; 0 # L&  [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
+0559          ; 0 # Lm       ARMENIAN MODIFIER LETTER LEFT HALF RING
+055A..055F    ; 0 # Po   [6] ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK
+0561..0587    ; 0 # L&  [39] ARMENIAN SMALL LETTER AYB..ARMENIAN SMALL LIGATURE ECH YIWN
+0589          ; 0 # Po       ARMENIAN FULL STOP
+058A          ; 0 # Pd       ARMENIAN HYPHEN
+05BE          ; 0 # Pd       HEBREW PUNCTUATION MAQAF
+05C0          ; 0 # Po       HEBREW PUNCTUATION PASEQ
+05C3          ; 0 # Po       HEBREW PUNCTUATION SOF PASUQ
+05C6          ; 0 # Po       HEBREW PUNCTUATION NUN HAFUKHA
+05D0..05EA    ; 0 # Lo  [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
+05F0..05F2    ; 0 # Lo   [3] HEBREW LIGATURE YIDDISH DOUBLE VAV..HEBREW LIGATURE YIDDISH DOUBLE YOD
+05F3..05F4    ; 0 # Po   [2] HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATION GERSHAYIM
+0600..0603    ; 0 # Cf   [4] ARABIC NUMBER SIGN..ARABIC SIGN SAFHA
+0606..0608    ; 0 # Sm   [3] ARABIC-INDIC CUBE ROOT..ARABIC RAY
+0609..060A    ; 0 # Po   [2] ARABIC-INDIC PER MILLE SIGN..ARABIC-INDIC PER TEN THOUSAND SIGN
+060B          ; 0 # Sc       AFGHANI SIGN
+060C..060D    ; 0 # Po   [2] ARABIC COMMA..ARABIC DATE SEPARATOR
+060E..060F    ; 0 # So   [2] ARABIC POETIC VERSE SIGN..ARABIC SIGN MISRA
+061B          ; 0 # Po       ARABIC SEMICOLON
+061E..061F    ; 0 # Po   [2] ARABIC TRIPLE DOT PUNCTUATION MARK..ARABIC QUESTION MARK
+0621..063F    ; 0 # Lo  [31] ARABIC LETTER HAMZA..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE
+0640          ; 0 # Lm       ARABIC TATWEEL
+0641..064A    ; 0 # Lo  [10] ARABIC LETTER FEH..ARABIC LETTER YEH
+0660..0669    ; 0 # Nd  [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE
+066A..066D    ; 0 # Po   [4] ARABIC PERCENT SIGN..ARABIC FIVE POINTED STAR
+066E..066F    ; 0 # Lo   [2] ARABIC LETTER DOTLESS BEH..ARABIC LETTER DOTLESS QAF
+0671..06D3    ; 0 # Lo  [99] ARABIC LETTER ALEF WASLA..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+06D4          ; 0 # Po       ARABIC FULL STOP
+06D5          ; 0 # Lo       ARABIC LETTER AE
+06DD          ; 0 # Cf       ARABIC END OF AYAH
+06DE          ; 0 # Me       ARABIC START OF RUB EL HIZB
+06E5..06E6    ; 0 # Lm   [2] ARABIC SMALL WAW..ARABIC SMALL YEH
+06E9          ; 0 # So       ARABIC PLACE OF SAJDAH
+06EE..06EF    ; 0 # Lo   [2] ARABIC LETTER DAL WITH INVERTED V..ARABIC LETTER REH WITH INVERTED V
+06F0..06F9    ; 0 # Nd  [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE
+06FA..06FC    ; 0 # Lo   [3] ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC LETTER GHAIN WITH DOT BELOW
+06FD..06FE    ; 0 # So   [2] ARABIC SIGN SINDHI AMPERSAND..ARABIC SIGN SINDHI POSTPOSITION MEN
+06FF          ; 0 # Lo       ARABIC LETTER HEH WITH INVERTED V
+0700..070D    ; 0 # Po  [14] SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN ASTERISCUS
+070F          ; 0 # Cf       SYRIAC ABBREVIATION MARK
+0710          ; 0 # Lo       SYRIAC LETTER ALAPH
+0712..072F    ; 0 # Lo  [30] SYRIAC LETTER BETH..SYRIAC LETTER PERSIAN DHALATH
+074D..07A5    ; 0 # Lo  [89] SYRIAC LETTER SOGDIAN ZHAIN..THAANA LETTER WAAVU
+07A6..07B0    ; 0 # Mn  [11] THAANA ABAFILI..THAANA SUKUN
+07B1          ; 0 # Lo       THAANA LETTER NAA
+07C0..07C9    ; 0 # Nd  [10] NKO DIGIT ZERO..NKO DIGIT NINE
+07CA..07EA    ; 0 # Lo  [33] NKO LETTER A..NKO LETTER JONA RA
+07F4..07F5    ; 0 # Lm   [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE
+07F6          ; 0 # So       NKO SYMBOL OO DENNEN
+07F7..07F9    ; 0 # Po   [3] NKO SYMBOL GBAKURUNEN..NKO EXCLAMATION MARK
+07FA          ; 0 # Lm       NKO LAJANYALAN
+0901..0902    ; 0 # Mn   [2] DEVANAGARI SIGN CANDRABINDU..DEVANAGARI SIGN ANUSVARA
+0903          ; 0 # Mc       DEVANAGARI SIGN VISARGA
+0904..0939    ; 0 # Lo  [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA
+093D          ; 0 # Lo       DEVANAGARI SIGN AVAGRAHA
+093E..0940    ; 0 # Mc   [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II
+0941..0948    ; 0 # Mn   [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI
+0949..094C    ; 0 # Mc   [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU
+0950          ; 0 # Lo       DEVANAGARI OM
+0958..0961    ; 0 # Lo  [10] DEVANAGARI LETTER QA..DEVANAGARI LETTER VOCALIC LL
+0962..0963    ; 0 # Mn   [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL
+0964..0965    ; 0 # Po   [2] DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA
+0966..096F    ; 0 # Nd  [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE
+0970          ; 0 # Po       DEVANAGARI ABBREVIATION SIGN
+0971          ; 0 # Lm       DEVANAGARI SIGN HIGH SPACING DOT
+0972          ; 0 # Lo       DEVANAGARI LETTER CANDRA A
+097B..097F    ; 0 # Lo   [5] DEVANAGARI LETTER GGA..DEVANAGARI LETTER BBA
+0981          ; 0 # Mn       BENGALI SIGN CANDRABINDU
+0982..0983    ; 0 # Mc   [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA
+0985..098C    ; 0 # Lo   [8] BENGALI LETTER A..BENGALI LETTER VOCALIC L
+098F..0990    ; 0 # Lo   [2] BENGALI LETTER E..BENGALI LETTER AI
+0993..09A8    ; 0 # Lo  [22] BENGALI LETTER O..BENGALI LETTER NA
+09AA..09B0    ; 0 # Lo   [7] BENGALI LETTER PA..BENGALI LETTER RA
+09B2          ; 0 # Lo       BENGALI LETTER LA
+09B6..09B9    ; 0 # Lo   [4] BENGALI LETTER SHA..BENGALI LETTER HA
+09BD          ; 0 # Lo       BENGALI SIGN AVAGRAHA
+09BE..09C0    ; 0 # Mc   [3] BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN II
+09C1..09C4    ; 0 # Mn   [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR
+09C7..09C8    ; 0 # Mc   [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI
+09CB..09CC    ; 0 # Mc   [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+09CE          ; 0 # Lo       BENGALI LETTER KHANDA TA
+09D7          ; 0 # Mc       BENGALI AU LENGTH MARK
+09DC..09DD    ; 0 # Lo   [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF..09E1    ; 0 # Lo   [3] BENGALI LETTER YYA..BENGALI LETTER VOCALIC LL
+09E2..09E3    ; 0 # Mn   [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL
+09E6..09EF    ; 0 # Nd  [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE
+09F0..09F1    ; 0 # Lo   [2] BENGALI LETTER RA WITH MIDDLE DIAGONAL..BENGALI LETTER RA WITH LOWER DIAGONAL
+09F2..09F3    ; 0 # Sc   [2] BENGALI RUPEE MARK..BENGALI RUPEE SIGN
+09F4..09F9    ; 0 # No   [6] BENGALI CURRENCY NUMERATOR ONE..BENGALI CURRENCY DENOMINATOR SIXTEEN
+09FA          ; 0 # So       BENGALI ISSHAR
+0A01..0A02    ; 0 # Mn   [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI
+0A03          ; 0 # Mc       GURMUKHI SIGN VISARGA
+0A05..0A0A    ; 0 # Lo   [6] GURMUKHI LETTER A..GURMUKHI LETTER UU
+0A0F..0A10    ; 0 # Lo   [2] GURMUKHI LETTER EE..GURMUKHI LETTER AI
+0A13..0A28    ; 0 # Lo  [22] GURMUKHI LETTER OO..GURMUKHI LETTER NA
+0A2A..0A30    ; 0 # Lo   [7] GURMUKHI LETTER PA..GURMUKHI LETTER RA
+0A32..0A33    ; 0 # Lo   [2] GURMUKHI LETTER LA..GURMUKHI LETTER LLA
+0A35..0A36    ; 0 # Lo   [2] GURMUKHI LETTER VA..GURMUKHI LETTER SHA
+0A38..0A39    ; 0 # Lo   [2] GURMUKHI LETTER SA..GURMUKHI LETTER HA
+0A3E..0A40    ; 0 # Mc   [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II
+0A41..0A42    ; 0 # Mn   [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU
+0A47..0A48    ; 0 # Mn   [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI
+0A4B..0A4C    ; 0 # Mn   [2] GURMUKHI VOWEL SIGN OO..GURMUKHI VOWEL SIGN AU
+0A51          ; 0 # Mn       GURMUKHI SIGN UDAAT
+0A59..0A5C    ; 0 # Lo   [4] GURMUKHI LETTER KHHA..GURMUKHI LETTER RRA
+0A5E          ; 0 # Lo       GURMUKHI LETTER FA
+0A66..0A6F    ; 0 # Nd  [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE
+0A70..0A71    ; 0 # Mn   [2] GURMUKHI TIPPI..GURMUKHI ADDAK
+0A72..0A74    ; 0 # Lo   [3] GURMUKHI IRI..GURMUKHI EK ONKAR
+0A75          ; 0 # Mn       GURMUKHI SIGN YAKASH
+0A81..0A82    ; 0 # Mn   [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA
+0A83          ; 0 # Mc       GUJARATI SIGN VISARGA
+0A85..0A8D    ; 0 # Lo   [9] GUJARATI LETTER A..GUJARATI VOWEL CANDRA E
+0A8F..0A91    ; 0 # Lo   [3] GUJARATI LETTER E..GUJARATI VOWEL CANDRA O
+0A93..0AA8    ; 0 # Lo  [22] GUJARATI LETTER O..GUJARATI LETTER NA
+0AAA..0AB0    ; 0 # Lo   [7] GUJARATI LETTER PA..GUJARATI LETTER RA
+0AB2..0AB3    ; 0 # Lo   [2] GUJARATI LETTER LA..GUJARATI LETTER LLA
+0AB5..0AB9    ; 0 # Lo   [5] GUJARATI LETTER VA..GUJARATI LETTER HA
+0ABD          ; 0 # Lo       GUJARATI SIGN AVAGRAHA
+0ABE..0AC0    ; 0 # Mc   [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II
+0AC1..0AC5    ; 0 # Mn   [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E
+0AC7..0AC8    ; 0 # Mn   [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI
+0AC9          ; 0 # Mc       GUJARATI VOWEL SIGN CANDRA O
+0ACB..0ACC    ; 0 # Mc   [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU
+0AD0          ; 0 # Lo       GUJARATI OM
+0AE0..0AE1    ; 0 # Lo   [2] GUJARATI LETTER VOCALIC RR..GUJARATI LETTER VOCALIC LL
+0AE2..0AE3    ; 0 # Mn   [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL
+0AE6..0AEF    ; 0 # Nd  [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE
+0AF1          ; 0 # Sc       GUJARATI RUPEE SIGN
+0B01          ; 0 # Mn       ORIYA SIGN CANDRABINDU
+0B02..0B03    ; 0 # Mc   [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA
+0B05..0B0C    ; 0 # Lo   [8] ORIYA LETTER A..ORIYA LETTER VOCALIC L
+0B0F..0B10    ; 0 # Lo   [2] ORIYA LETTER E..ORIYA LETTER AI
+0B13..0B28    ; 0 # Lo  [22] ORIYA LETTER O..ORIYA LETTER NA
+0B2A..0B30    ; 0 # Lo   [7] ORIYA LETTER PA..ORIYA LETTER RA
+0B32..0B33    ; 0 # Lo   [2] ORIYA LETTER LA..ORIYA LETTER LLA
+0B35..0B39    ; 0 # Lo   [5] ORIYA LETTER VA..ORIYA LETTER HA
+0B3D          ; 0 # Lo       ORIYA SIGN AVAGRAHA
+0B3E          ; 0 # Mc       ORIYA VOWEL SIGN AA
+0B3F          ; 0 # Mn       ORIYA VOWEL SIGN I
+0B40          ; 0 # Mc       ORIYA VOWEL SIGN II
+0B41..0B44    ; 0 # Mn   [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
+0B47..0B48    ; 0 # Mc   [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
+0B4B..0B4C    ; 0 # Mc   [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+0B56          ; 0 # Mn       ORIYA AI LENGTH MARK
+0B57          ; 0 # Mc       ORIYA AU LENGTH MARK
+0B5C..0B5D    ; 0 # Lo   [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0B5F..0B61    ; 0 # Lo   [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL
+0B62..0B63    ; 0 # Mn   [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
+0B66..0B6F    ; 0 # Nd  [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE
+0B70          ; 0 # So       ORIYA ISSHAR
+0B71          ; 0 # Lo       ORIYA LETTER WA
+0B82          ; 0 # Mn       TAMIL SIGN ANUSVARA
+0B83          ; 0 # Lo       TAMIL SIGN VISARGA
+0B85..0B8A    ; 0 # Lo   [6] TAMIL LETTER A..TAMIL LETTER UU
+0B8E..0B90    ; 0 # Lo   [3] TAMIL LETTER E..TAMIL LETTER AI
+0B92..0B95    ; 0 # Lo   [4] TAMIL LETTER O..TAMIL LETTER KA
+0B99..0B9A    ; 0 # Lo   [2] TAMIL LETTER NGA..TAMIL LETTER CA
+0B9C          ; 0 # Lo       TAMIL LETTER JA
+0B9E..0B9F    ; 0 # Lo   [2] TAMIL LETTER NYA..TAMIL LETTER TTA
+0BA3..0BA4    ; 0 # Lo   [2] TAMIL LETTER NNA..TAMIL LETTER TA
+0BA8..0BAA    ; 0 # Lo   [3] TAMIL LETTER NA..TAMIL LETTER PA
+0BAE..0BB9    ; 0 # Lo  [12] TAMIL LETTER MA..TAMIL LETTER HA
+0BBE..0BBF    ; 0 # Mc   [2] TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN I
+0BC0          ; 0 # Mn       TAMIL VOWEL SIGN II
+0BC1..0BC2    ; 0 # Mc   [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU
+0BC6..0BC8    ; 0 # Mc   [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI
+0BCA..0BCC    ; 0 # Mc   [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+0BD0          ; 0 # Lo       TAMIL OM
+0BD7          ; 0 # Mc       TAMIL AU LENGTH MARK
+0BE6..0BEF    ; 0 # Nd  [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE
+0BF0..0BF2    ; 0 # No   [3] TAMIL NUMBER TEN..TAMIL NUMBER ONE THOUSAND
+0BF3..0BF8    ; 0 # So   [6] TAMIL DAY SIGN..TAMIL AS ABOVE SIGN
+0BF9          ; 0 # Sc       TAMIL RUPEE SIGN
+0BFA          ; 0 # So       TAMIL NUMBER SIGN
+0C01..0C03    ; 0 # Mc   [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA
+0C05..0C0C    ; 0 # Lo   [8] TELUGU LETTER A..TELUGU LETTER VOCALIC L
+0C0E..0C10    ; 0 # Lo   [3] TELUGU LETTER E..TELUGU LETTER AI
+0C12..0C28    ; 0 # Lo  [23] TELUGU LETTER O..TELUGU LETTER NA
+0C2A..0C33    ; 0 # Lo  [10] TELUGU LETTER PA..TELUGU LETTER LLA
+0C35..0C39    ; 0 # Lo   [5] TELUGU LETTER VA..TELUGU LETTER HA
+0C3D          ; 0 # Lo       TELUGU SIGN AVAGRAHA
+0C3E..0C40    ; 0 # Mn   [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II
+0C41..0C44    ; 0 # Mc   [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR
+0C46..0C48    ; 0 # Mn   [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI
+0C4A..0C4C    ; 0 # Mn   [3] TELUGU VOWEL SIGN O..TELUGU VOWEL SIGN AU
+0C58..0C59    ; 0 # Lo   [2] TELUGU LETTER TSA..TELUGU LETTER DZA
+0C60..0C61    ; 0 # Lo   [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
+0C62..0C63    ; 0 # Mn   [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
+0C66..0C6F    ; 0 # Nd  [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
+0C78..0C7E    ; 0 # No   [7] TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR..TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR
+0C7F          ; 0 # So       TELUGU SIGN TUUMU
+0C82..0C83    ; 0 # Mc   [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA
+0C85..0C8C    ; 0 # Lo   [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L
+0C8E..0C90    ; 0 # Lo   [3] KANNADA LETTER E..KANNADA LETTER AI
+0C92..0CA8    ; 0 # Lo  [23] KANNADA LETTER O..KANNADA LETTER NA
+0CAA..0CB3    ; 0 # Lo  [10] KANNADA LETTER PA..KANNADA LETTER LLA
+0CB5..0CB9    ; 0 # Lo   [5] KANNADA LETTER VA..KANNADA LETTER HA
+0CBD          ; 0 # Lo       KANNADA SIGN AVAGRAHA
+0CBE          ; 0 # Mc       KANNADA VOWEL SIGN AA
+0CBF          ; 0 # Mn       KANNADA VOWEL SIGN I
+0CC0..0CC4    ; 0 # Mc   [5] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN VOCALIC RR
+0CC6          ; 0 # Mn       KANNADA VOWEL SIGN E
+0CC7..0CC8    ; 0 # Mc   [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+0CCA..0CCB    ; 0 # Mc   [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+0CCC          ; 0 # Mn       KANNADA VOWEL SIGN AU
+0CD5..0CD6    ; 0 # Mc   [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+0CDE          ; 0 # Lo       KANNADA LETTER FA
+0CE0..0CE1    ; 0 # Lo   [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
+0CE2..0CE3    ; 0 # Mn   [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
+0CE6..0CEF    ; 0 # Nd  [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
+0CF1..0CF2    ; 0 # So   [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
+0D02..0D03    ; 0 # Mc   [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA
+0D05..0D0C    ; 0 # Lo   [8] MALAYALAM LETTER A..MALAYALAM LETTER VOCALIC L
+0D0E..0D10    ; 0 # Lo   [3] MALAYALAM LETTER E..MALAYALAM LETTER AI
+0D12..0D28    ; 0 # Lo  [23] MALAYALAM LETTER O..MALAYALAM LETTER NA
+0D2A..0D39    ; 0 # Lo  [16] MALAYALAM LETTER PA..MALAYALAM LETTER HA
+0D3D          ; 0 # Lo       MALAYALAM SIGN AVAGRAHA
+0D3E..0D40    ; 0 # Mc   [3] MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN II
+0D41..0D44    ; 0 # Mn   [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR
+0D46..0D48    ; 0 # Mc   [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI
+0D4A..0D4C    ; 0 # Mc   [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+0D57          ; 0 # Mc       MALAYALAM AU LENGTH MARK
+0D60..0D61    ; 0 # Lo   [2] MALAYALAM LETTER VOCALIC RR..MALAYALAM LETTER VOCALIC LL
+0D62..0D63    ; 0 # Mn   [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL
+0D66..0D6F    ; 0 # Nd  [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE
+0D70..0D75    ; 0 # No   [6] MALAYALAM NUMBER TEN..MALAYALAM FRACTION THREE QUARTERS
+0D79          ; 0 # So       MALAYALAM DATE MARK
+0D7A..0D7F    ; 0 # Lo   [6] MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER CHILLU K
+0D82..0D83    ; 0 # Mc   [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA
+0D85..0D96    ; 0 # Lo  [18] SINHALA LETTER AYANNA..SINHALA LETTER AUYANNA
+0D9A..0DB1    ; 0 # Lo  [24] SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA LETTER DANTAJA NAYANNA
+0DB3..0DBB    ; 0 # Lo   [9] SINHALA LETTER SANYAKA DAYANNA..SINHALA LETTER RAYANNA
+0DBD          ; 0 # Lo       SINHALA LETTER DANTAJA LAYANNA
+0DC0..0DC6    ; 0 # Lo   [7] SINHALA LETTER VAYANNA..SINHALA LETTER FAYANNA
+0DCF..0DD1    ; 0 # Mc   [3] SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA
+0DD2..0DD4    ; 0 # Mn   [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA
+0DD6          ; 0 # Mn       SINHALA VOWEL SIGN DIGA PAA-PILLA
+0DD8..0DDF    ; 0 # Mc   [8] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA
+0DF2..0DF3    ; 0 # Mc   [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA
+0DF4          ; 0 # Po       SINHALA PUNCTUATION KUNDDALIYA
+0E01..0E30    ; 0 # Lo  [48] THAI CHARACTER KO KAI..THAI CHARACTER SARA A
+0E31          ; 0 # Mn       THAI CHARACTER MAI HAN-AKAT
+0E32..0E33    ; 0 # Lo   [2] THAI CHARACTER SARA AA..THAI CHARACTER SARA AM
+0E34..0E37    ; 0 # Mn   [4] THAI CHARACTER SARA I..THAI CHARACTER SARA UEE
+0E3F          ; 0 # Sc       THAI CURRENCY SYMBOL BAHT
+0E40..0E45    ; 0 # Lo   [6] THAI CHARACTER SARA E..THAI CHARACTER LAKKHANGYAO
+0E46          ; 0 # Lm       THAI CHARACTER MAIYAMOK
+0E47          ; 0 # Mn       THAI CHARACTER MAITAIKHU
+0E4C..0E4E    ; 0 # Mn   [3] THAI CHARACTER THANTHAKHAT..THAI CHARACTER YAMAKKAN
+0E4F          ; 0 # Po       THAI CHARACTER FONGMAN
+0E50..0E59    ; 0 # Nd  [10] THAI DIGIT ZERO..THAI DIGIT NINE
+0E5A..0E5B    ; 0 # Po   [2] THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT
+0E81..0E82    ; 0 # Lo   [2] LAO LETTER KO..LAO LETTER KHO SUNG
+0E84          ; 0 # Lo       LAO LETTER KHO TAM
+0E87..0E88    ; 0 # Lo   [2] LAO LETTER NGO..LAO LETTER CO
+0E8A          ; 0 # Lo       LAO LETTER SO TAM
+0E8D          ; 0 # Lo       LAO LETTER NYO
+0E94..0E97    ; 0 # Lo   [4] LAO LETTER DO..LAO LETTER THO TAM
+0E99..0E9F    ; 0 # Lo   [7] LAO LETTER NO..LAO LETTER FO SUNG
+0EA1..0EA3    ; 0 # Lo   [3] LAO LETTER MO..LAO LETTER LO LING
+0EA5          ; 0 # Lo       LAO LETTER LO LOOT
+0EA7          ; 0 # Lo       LAO LETTER WO
+0EAA..0EAB    ; 0 # Lo   [2] LAO LETTER SO SUNG..LAO LETTER HO SUNG
+0EAD..0EB0    ; 0 # Lo   [4] LAO LETTER O..LAO VOWEL SIGN A
+0EB1          ; 0 # Mn       LAO VOWEL SIGN MAI KAN
+0EB2..0EB3    ; 0 # Lo   [2] LAO VOWEL SIGN AA..LAO VOWEL SIGN AM
+0EB4..0EB7    ; 0 # Mn   [4] LAO VOWEL SIGN I..LAO VOWEL SIGN YY
+0EBB..0EBC    ; 0 # Mn   [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO
+0EBD          ; 0 # Lo       LAO SEMIVOWEL SIGN NYO
+0EC0..0EC4    ; 0 # Lo   [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI
+0EC6          ; 0 # Lm       LAO KO LA
+0ECC..0ECD    ; 0 # Mn   [2] LAO CANCELLATION MARK..LAO NIGGAHITA
+0ED0..0ED9    ; 0 # Nd  [10] LAO DIGIT ZERO..LAO DIGIT NINE
+0EDC..0EDD    ; 0 # Lo   [2] LAO HO NO..LAO HO MO
+0F00          ; 0 # Lo       TIBETAN SYLLABLE OM
+0F01..0F03    ; 0 # So   [3] TIBETAN MARK GTER YIG MGO TRUNCATED A..TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA
+0F04..0F12    ; 0 # Po  [15] TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK RGYA GRAM SHAD
+0F13..0F17    ; 0 # So   [5] TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN..TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS
+0F1A..0F1F    ; 0 # So   [6] TIBETAN SIGN RDEL DKAR GCIG..TIBETAN SIGN RDEL DKAR RDEL NAG
+0F20..0F29    ; 0 # Nd  [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE
+0F2A..0F33    ; 0 # No  [10] TIBETAN DIGIT HALF ONE..TIBETAN DIGIT HALF ZERO
+0F34          ; 0 # So       TIBETAN MARK BSDUS RTAGS
+0F36          ; 0 # So       TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN
+0F38          ; 0 # So       TIBETAN MARK CHE MGO
+0F3A          ; 0 # Ps       TIBETAN MARK GUG RTAGS GYON
+0F3B          ; 0 # Pe       TIBETAN MARK GUG RTAGS GYAS
+0F3C          ; 0 # Ps       TIBETAN MARK ANG KHANG GYON
+0F3D          ; 0 # Pe       TIBETAN MARK ANG KHANG GYAS
+0F3E..0F3F    ; 0 # Mc   [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES
+0F40..0F47    ; 0 # Lo   [8] TIBETAN LETTER KA..TIBETAN LETTER JA
+0F49..0F6C    ; 0 # Lo  [36] TIBETAN LETTER NYA..TIBETAN LETTER RRA
+0F73          ; 0 # Mn       TIBETAN VOWEL SIGN II
+0F75..0F79    ; 0 # Mn   [5] TIBETAN VOWEL SIGN UU..TIBETAN VOWEL SIGN VOCALIC LL
+0F7E          ; 0 # Mn       TIBETAN SIGN RJES SU NGA RO
+0F7F          ; 0 # Mc       TIBETAN SIGN RNAM BCAD
+0F81          ; 0 # Mn       TIBETAN VOWEL SIGN REVERSED II
+0F85          ; 0 # Po       TIBETAN MARK PALUTA
+0F88..0F8B    ; 0 # Lo   [4] TIBETAN SIGN LCE TSA CAN..TIBETAN SIGN GRU MED RGYINGS
+0F90..0F97    ; 0 # Mn   [8] TIBETAN SUBJOINED LETTER KA..TIBETAN SUBJOINED LETTER JA
+0F99..0FBC    ; 0 # Mn  [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA
+0FBE..0FC5    ; 0 # So   [8] TIBETAN KU RU KHA..TIBETAN SYMBOL RDO RJE
+0FC7..0FCC    ; 0 # So   [6] TIBETAN SYMBOL RDO RJE RGYA GRAM..TIBETAN SYMBOL NOR BU BZHI -KHYIL
+0FCE..0FCF    ; 0 # So   [2] TIBETAN SIGN RDEL NAG RDEL DKAR..TIBETAN SIGN RDEL NAG GSUM
+0FD0..0FD4    ; 0 # Po   [5] TIBETAN MARK BSKA- SHOG GI MGO RGYAN..TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA
+1000..102A    ; 0 # Lo  [43] MYANMAR LETTER KA..MYANMAR LETTER AU
+102B..102C    ; 0 # Mc   [2] MYANMAR VOWEL SIGN TALL AA..MYANMAR VOWEL SIGN AA
+102D..1030    ; 0 # Mn   [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU
+1031          ; 0 # Mc       MYANMAR VOWEL SIGN E
+1032..1036    ; 0 # Mn   [5] MYANMAR VOWEL SIGN AI..MYANMAR SIGN ANUSVARA
+1038          ; 0 # Mc       MYANMAR SIGN VISARGA
+103B..103C    ; 0 # Mc   [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA
+103D..103E    ; 0 # Mn   [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA
+103F          ; 0 # Lo       MYANMAR LETTER GREAT SA
+1040..1049    ; 0 # Nd  [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE
+104A..104F    ; 0 # Po   [6] MYANMAR SIGN LITTLE SECTION..MYANMAR SYMBOL GENITIVE
+1050..1055    ; 0 # Lo   [6] MYANMAR LETTER SHA..MYANMAR LETTER VOCALIC LL
+1056..1057    ; 0 # Mc   [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR
+1058..1059    ; 0 # Mn   [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
+105A..105D    ; 0 # Lo   [4] MYANMAR LETTER MON NGA..MYANMAR LETTER MON BBE
+105E..1060    ; 0 # Mn   [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
+1061          ; 0 # Lo       MYANMAR LETTER SGAW KAREN SHA
+1062..1064    ; 0 # Mc   [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO
+1065..1066    ; 0 # Lo   [2] MYANMAR LETTER WESTERN PWO KAREN THA..MYANMAR LETTER WESTERN PWO KAREN PWA
+1067..106D    ; 0 # Mc   [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5
+106E..1070    ; 0 # Lo   [3] MYANMAR LETTER EASTERN PWO KAREN NNA..MYANMAR LETTER EASTERN PWO KAREN GHWA
+1071..1074    ; 0 # Mn   [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE
+1075..1081    ; 0 # Lo  [13] MYANMAR LETTER SHAN KA..MYANMAR LETTER SHAN HA
+1082          ; 0 # Mn       MYANMAR CONSONANT SIGN SHAN MEDIAL WA
+1083..1084    ; 0 # Mc   [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E
+1085..1086    ; 0 # Mn   [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y
+1087..108C    ; 0 # Mc   [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
+108E          ; 0 # Lo       MYANMAR LETTER RUMAI PALAUNG FA
+108F          ; 0 # Mc       MYANMAR SIGN RUMAI PALAUNG TONE-5
+1090..1099    ; 0 # Nd  [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE
+109E..109F    ; 0 # So   [2] MYANMAR SYMBOL SHAN ONE..MYANMAR SYMBOL SHAN EXCLAMATION
+10A0..10C5    ; 0 # L&  [38] GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE
+10D0..10FA    ; 0 # Lo  [43] GEORGIAN LETTER AN..GEORGIAN LETTER AIN
+10FB          ; 0 # Po       GEORGIAN PARAGRAPH SEPARATOR
+10FC          ; 0 # Lm       MODIFIER LETTER GEORGIAN NAR
+1100..1159    ; 0 # Lo  [90] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG YEORINHIEUH
+115F..11A2    ; 0 # Lo  [68] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG SSANGARAEA
+11A8..11F9    ; 0 # Lo  [82] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG YEORINHIEUH
+1200..1248    ; 0 # Lo  [73] ETHIOPIC SYLLABLE HA..ETHIOPIC SYLLABLE QWA
+124A..124D    ; 0 # Lo   [4] ETHIOPIC SYLLABLE QWI..ETHIOPIC SYLLABLE QWE
+1250..1256    ; 0 # Lo   [7] ETHIOPIC SYLLABLE QHA..ETHIOPIC SYLLABLE QHO
+1258          ; 0 # Lo       ETHIOPIC SYLLABLE QHWA
+125A..125D    ; 0 # Lo   [4] ETHIOPIC SYLLABLE QHWI..ETHIOPIC SYLLABLE QHWE
+1260..1288    ; 0 # Lo  [41] ETHIOPIC SYLLABLE BA..ETHIOPIC SYLLABLE XWA
+128A..128D    ; 0 # Lo   [4] ETHIOPIC SYLLABLE XWI..ETHIOPIC SYLLABLE XWE
+1290..12B0    ; 0 # Lo  [33] ETHIOPIC SYLLABLE NA..ETHIOPIC SYLLABLE KWA
+12B2..12B5    ; 0 # Lo   [4] ETHIOPIC SYLLABLE KWI..ETHIOPIC SYLLABLE KWE
+12B8..12BE    ; 0 # Lo   [7] ETHIOPIC SYLLABLE KXA..ETHIOPIC SYLLABLE KXO
+12C0          ; 0 # Lo       ETHIOPIC SYLLABLE KXWA
+12C2..12C5    ; 0 # Lo   [4] ETHIOPIC SYLLABLE KXWI..ETHIOPIC SYLLABLE KXWE
+12C8..12D6    ; 0 # Lo  [15] ETHIOPIC SYLLABLE WA..ETHIOPIC SYLLABLE PHARYNGEAL O
+12D8..1310    ; 0 # Lo  [57] ETHIOPIC SYLLABLE ZA..ETHIOPIC SYLLABLE GWA
+1312..1315    ; 0 # Lo   [4] ETHIOPIC SYLLABLE GWI..ETHIOPIC SYLLABLE GWE
+1318..135A    ; 0 # Lo  [67] ETHIOPIC SYLLABLE GGA..ETHIOPIC SYLLABLE FYA
+1360          ; 0 # So       ETHIOPIC SECTION MARK
+1361..1368    ; 0 # Po   [8] ETHIOPIC WORDSPACE..ETHIOPIC PARAGRAPH SEPARATOR
+1369..137C    ; 0 # No  [20] ETHIOPIC DIGIT ONE..ETHIOPIC NUMBER TEN THOUSAND
+1380..138F    ; 0 # Lo  [16] ETHIOPIC SYLLABLE SEBATBEIT MWA..ETHIOPIC SYLLABLE PWE
+1390..1399    ; 0 # So  [10] ETHIOPIC TONAL MARK YIZET..ETHIOPIC TONAL MARK KURT
+13A0..13F4    ; 0 # Lo  [85] CHEROKEE LETTER A..CHEROKEE LETTER YV
+1401..166C    ; 0 # Lo [620] CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA
+166D..166E    ; 0 # Po   [2] CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP
+166F..1676    ; 0 # Lo   [8] CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS NNGAA
+1680          ; 0 # Zs       OGHAM SPACE MARK
+1681..169A    ; 0 # Lo  [26] OGHAM LETTER BEITH..OGHAM LETTER PEITH
+169B          ; 0 # Ps       OGHAM FEATHER MARK
+169C          ; 0 # Pe       OGHAM REVERSED FEATHER MARK
+16A0..16EA    ; 0 # Lo  [75] RUNIC LETTER FEHU FEOH FE F..RUNIC LETTER X
+16EB..16ED    ; 0 # Po   [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION
+16EE..16F0    ; 0 # Nl   [3] RUNIC ARLAUG SYMBOL..RUNIC BELGTHOR SYMBOL
+1700..170C    ; 0 # Lo  [13] TAGALOG LETTER A..TAGALOG LETTER YA
+170E..1711    ; 0 # Lo   [4] TAGALOG LETTER LA..TAGALOG LETTER HA
+1712..1713    ; 0 # Mn   [2] TAGALOG VOWEL SIGN I..TAGALOG VOWEL SIGN U
+1720..1731    ; 0 # Lo  [18] HANUNOO LETTER A..HANUNOO LETTER HA
+1732..1733    ; 0 # Mn   [2] HANUNOO VOWEL SIGN I..HANUNOO VOWEL SIGN U
+1735..1736    ; 0 # Po   [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION
+1740..1751    ; 0 # Lo  [18] BUHID LETTER A..BUHID LETTER HA
+1752..1753    ; 0 # Mn   [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
+1760..176C    ; 0 # Lo  [13] TAGBANWA LETTER A..TAGBANWA LETTER YA
+176E..1770    ; 0 # Lo   [3] TAGBANWA LETTER LA..TAGBANWA LETTER SA
+1772..1773    ; 0 # Mn   [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U
+1780..17B3    ; 0 # Lo  [52] KHMER LETTER KA..KHMER INDEPENDENT VOWEL QAU
+17B4..17B5    ; 0 # Cf   [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
+17B6          ; 0 # Mc       KHMER VOWEL SIGN AA
+17B7..17BD    ; 0 # Mn   [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA
+17BE..17C5    ; 0 # Mc   [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU
+17C6          ; 0 # Mn       KHMER SIGN NIKAHIT
+17C7..17C8    ; 0 # Mc   [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU
+17C9..17D1    ; 0 # Mn   [9] KHMER SIGN MUUSIKATOAN..KHMER SIGN VIRIAM
+17D3          ; 0 # Mn       KHMER SIGN BATHAMASAT
+17D4..17D6    ; 0 # Po   [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH
+17D7          ; 0 # Lm       KHMER SIGN LEK TOO
+17D8..17DA    ; 0 # Po   [3] KHMER SIGN BEYYAL..KHMER SIGN KOOMUUT
+17DB          ; 0 # Sc       KHMER CURRENCY SYMBOL RIEL
+17DC          ; 0 # Lo       KHMER SIGN AVAKRAHASANYA
+17E0..17E9    ; 0 # Nd  [10] KHMER DIGIT ZERO..KHMER DIGIT NINE
+17F0..17F9    ; 0 # No  [10] KHMER SYMBOL LEK ATTAK SON..KHMER SYMBOL LEK ATTAK PRAM-BUON
+1800..1805    ; 0 # Po   [6] MONGOLIAN BIRGA..MONGOLIAN FOUR DOTS
+1806          ; 0 # Pd       MONGOLIAN TODO SOFT HYPHEN
+1807..180A    ; 0 # Po   [4] MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER..MONGOLIAN NIRUGU
+180B..180D    ; 0 # Mn   [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
+180E          ; 0 # Zs       MONGOLIAN VOWEL SEPARATOR
+1810..1819    ; 0 # Nd  [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE
+1820..1842    ; 0 # Lo  [35] MONGOLIAN LETTER A..MONGOLIAN LETTER CHI
+1843          ; 0 # Lm       MONGOLIAN LETTER TODO LONG VOWEL SIGN
+1844..1877    ; 0 # Lo  [52] MONGOLIAN LETTER TODO E..MONGOLIAN LETTER MANCHU ZHA
+1880..18A8    ; 0 # Lo  [41] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER MANCHU ALI GALI BHA
+18AA          ; 0 # Lo       MONGOLIAN LETTER MANCHU ALI GALI LHA
+1900..191C    ; 0 # Lo  [29] LIMBU VOWEL-CARRIER LETTER..LIMBU LETTER HA
+1920..1922    ; 0 # Mn   [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U
+1923..1926    ; 0 # Mc   [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU
+1927..1928    ; 0 # Mn   [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O
+1929..192B    ; 0 # Mc   [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA
+1930..1931    ; 0 # Mc   [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA
+1932          ; 0 # Mn       LIMBU SMALL LETTER ANUSVARA
+1933..1938    ; 0 # Mc   [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA
+1940          ; 0 # So       LIMBU SIGN LOO
+1944..1945    ; 0 # Po   [2] LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK
+1946..194F    ; 0 # Nd  [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE
+1950..196D    ; 0 # Lo  [30] TAI LE LETTER KA..TAI LE LETTER AI
+1970..1974    ; 0 # Lo   [5] TAI LE LETTER TONE-2..TAI LE LETTER TONE-6
+1980..19A9    ; 0 # Lo  [42] NEW TAI LUE LETTER HIGH QA..NEW TAI LUE LETTER LOW XVA
+19B0..19C0    ; 0 # Mc  [17] NEW TAI LUE VOWEL SIGN VOWEL SHORTENER..NEW TAI LUE VOWEL SIGN IY
+19C1..19C7    ; 0 # Lo   [7] NEW TAI LUE LETTER FINAL V..NEW TAI LUE LETTER FINAL B
+19C8..19C9    ; 0 # Mc   [2] NEW TAI LUE TONE MARK-1..NEW TAI LUE TONE MARK-2
+19D0..19D9    ; 0 # Nd  [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE
+19DE..19DF    ; 0 # Po   [2] NEW TAI LUE SIGN LAE..NEW TAI LUE SIGN LAEV
+19E0..19FF    ; 0 # So  [32] KHMER SYMBOL PATHAMASAT..KHMER SYMBOL DAP-PRAM ROC
+1A00..1A16    ; 0 # Lo  [23] BUGINESE LETTER KA..BUGINESE LETTER HA
+1A19..1A1B    ; 0 # Mc   [3] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN AE
+1A1E..1A1F    ; 0 # Po   [2] BUGINESE PALLAWA..BUGINESE END OF SECTION
+1B00..1B03    ; 0 # Mn   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
+1B04          ; 0 # Mc       BALINESE SIGN BISAH
+1B05..1B33    ; 0 # Lo  [47] BALINESE LETTER AKARA..BALINESE LETTER HA
+1B35          ; 0 # Mc       BALINESE VOWEL SIGN TEDUNG
+1B36..1B3A    ; 0 # Mn   [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
+1B3B          ; 0 # Mc       BALINESE VOWEL SIGN RA REPA TEDUNG
+1B3C          ; 0 # Mn       BALINESE VOWEL SIGN LA LENGA
+1B3D..1B41    ; 0 # Mc   [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
+1B42          ; 0 # Mn       BALINESE VOWEL SIGN PEPET
+1B43          ; 0 # Mc       BALINESE VOWEL SIGN PEPET TEDUNG
+1B45..1B4B    ; 0 # Lo   [7] BALINESE LETTER KAF SASAK..BALINESE LETTER ASYURA SASAK
+1B50..1B59    ; 0 # Nd  [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE
+1B5A..1B60    ; 0 # Po   [7] BALINESE PANTI..BALINESE PAMENENG
+1B61..1B6A    ; 0 # So  [10] BALINESE MUSICAL SYMBOL DONG..BALINESE MUSICAL SYMBOL DANG GEDE
+1B74..1B7C    ; 0 # So   [9] BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG..BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING
+1B80..1B81    ; 0 # Mn   [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR
+1B82          ; 0 # Mc       SUNDANESE SIGN PANGWISAD
+1B83..1BA0    ; 0 # Lo  [30] SUNDANESE LETTER A..SUNDANESE LETTER HA
+1BA1          ; 0 # Mc       SUNDANESE CONSONANT SIGN PAMINGKAL
+1BA2..1BA5    ; 0 # Mn   [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU
+1BA6..1BA7    ; 0 # Mc   [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG
+1BA8..1BA9    ; 0 # Mn   [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG
+1BAE..1BAF    ; 0 # Lo   [2] SUNDANESE LETTER KHA..SUNDANESE LETTER SYA
+1BB0..1BB9    ; 0 # Nd  [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE
+1C00..1C23    ; 0 # Lo  [36] LEPCHA LETTER KA..LEPCHA LETTER A
+1C24..1C2B    ; 0 # Mc   [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
+1C2C..1C33    ; 0 # Mn   [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T
+1C34..1C35    ; 0 # Mc   [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
+1C36          ; 0 # Mn       LEPCHA SIGN RAN
+1C3B..1C3F    ; 0 # Po   [5] LEPCHA PUNCTUATION TA-ROL..LEPCHA PUNCTUATION TSHOOK
+1C40..1C49    ; 0 # Nd  [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE
+1C4D..1C4F    ; 0 # Lo   [3] LEPCHA LETTER TTA..LEPCHA LETTER DDA
+1C50..1C59    ; 0 # Nd  [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE
+1C5A..1C77    ; 0 # Lo  [30] OL CHIKI LETTER LA..OL CHIKI LETTER OH
+1C78..1C7D    ; 0 # Lm   [6] OL CHIKI MU TTUDDAG..OL CHIKI AHAD
+1C7E..1C7F    ; 0 # Po   [2] OL CHIKI PUNCTUATION MUCAAD..OL CHIKI PUNCTUATION DOUBLE MUCAAD
+1D00..1D2B    ; 0 # L&  [44] LATIN LETTER SMALL CAPITAL A..CYRILLIC LETTER SMALL CAPITAL EL
+1D2C..1D61    ; 0 # Lm  [54] MODIFIER LETTER CAPITAL A..MODIFIER LETTER SMALL CHI
+1D62..1D77    ; 0 # L&  [22] LATIN SUBSCRIPT SMALL LETTER I..LATIN SMALL LETTER TURNED G
+1D78          ; 0 # Lm       MODIFIER LETTER CYRILLIC EN
+1D79..1D9A    ; 0 # L&  [34] LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK
+1D9B..1DBF    ; 0 # Lm  [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA
+1E00..1F15    ; 0 # L& [278] LATIN CAPITAL LETTER A WITH RING BELOW..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+1F18..1F1D    ; 0 # L&   [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+1F20..1F45    ; 0 # L&  [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+1F48..1F4D    ; 0 # L&   [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+1F50..1F57    ; 0 # L&   [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F59          ; 0 # L&       GREEK CAPITAL LETTER UPSILON WITH DASIA
+1F5B          ; 0 # L&       GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+1F5D          ; 0 # L&       GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+1F5F..1F7D    ; 0 # L&  [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA
+1F80..1FB4    ; 0 # L&  [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+1FB6..1FBC    ; 0 # L&   [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FBD          ; 0 # Sk       GREEK KORONIS
+1FBE          ; 0 # L&       GREEK PROSGEGRAMMENI
+1FBF..1FC1    ; 0 # Sk   [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI
+1FC2..1FC4    ; 0 # L&   [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+1FC6..1FCC    ; 0 # L&   [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FCD..1FCF    ; 0 # Sk   [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI
+1FD0..1FD3    ; 0 # L&   [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FD6..1FDB    ; 0 # L&   [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA
+1FDD..1FDF    ; 0 # Sk   [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI
+1FE0..1FEC    ; 0 # L&  [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA
+1FED..1FEF    ; 0 # Sk   [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA
+1FF2..1FF4    ; 0 # L&   [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+1FF6..1FFC    ; 0 # L&   [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+1FFD..1FFE    ; 0 # Sk   [2] GREEK OXIA..GREEK DASIA
+2000..200A    ; 0 # Zs  [11] EN QUAD..HAIR SPACE
+200B..200F    ; 0 # Cf   [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
+2010..2015    ; 0 # Pd   [6] HYPHEN..HORIZONTAL BAR
+2016..2017    ; 0 # Po   [2] DOUBLE VERTICAL LINE..DOUBLE LOW LINE
+2018          ; 0 # Pi       LEFT SINGLE QUOTATION MARK
+2019          ; 0 # Pf       RIGHT SINGLE QUOTATION MARK
+201A          ; 0 # Ps       SINGLE LOW-9 QUOTATION MARK
+201B..201C    ; 0 # Pi   [2] SINGLE HIGH-REVERSED-9 QUOTATION MARK..LEFT DOUBLE QUOTATION MARK
+201D          ; 0 # Pf       RIGHT DOUBLE QUOTATION MARK
+201E          ; 0 # Ps       DOUBLE LOW-9 QUOTATION MARK
+201F          ; 0 # Pi       DOUBLE HIGH-REVERSED-9 QUOTATION MARK
+2020..2027    ; 0 # Po   [8] DAGGER..HYPHENATION POINT
+2028          ; 0 # Zl       LINE SEPARATOR
+2029          ; 0 # Zp       PARAGRAPH SEPARATOR
+202A..202E    ; 0 # Cf   [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
+202F          ; 0 # Zs       NARROW NO-BREAK SPACE
+2030..2038    ; 0 # Po   [9] PER MILLE SIGN..CARET
+2039          ; 0 # Pi       SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+203A          ; 0 # Pf       SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+203B..203E    ; 0 # Po   [4] REFERENCE MARK..OVERLINE
+203F..2040    ; 0 # Pc   [2] UNDERTIE..CHARACTER TIE
+2041..2043    ; 0 # Po   [3] CARET INSERTION POINT..HYPHEN BULLET
+2044          ; 0 # Sm       FRACTION SLASH
+2045          ; 0 # Ps       LEFT SQUARE BRACKET WITH QUILL
+2046          ; 0 # Pe       RIGHT SQUARE BRACKET WITH QUILL
+2047..2051    ; 0 # Po  [11] DOUBLE QUESTION MARK..TWO ASTERISKS ALIGNED VERTICALLY
+2052          ; 0 # Sm       COMMERCIAL MINUS SIGN
+2053          ; 0 # Po       SWUNG DASH
+2054          ; 0 # Pc       INVERTED UNDERTIE
+2055..205E    ; 0 # Po  [10] FLOWER PUNCTUATION MARK..VERTICAL FOUR DOTS
+205F          ; 0 # Zs       MEDIUM MATHEMATICAL SPACE
+2060..2064    ; 0 # Cf   [5] WORD JOINER..INVISIBLE PLUS
+206A..206F    ; 0 # Cf   [6] INHIBIT SYMMETRIC SWAPPING..NOMINAL DIGIT SHAPES
+2070          ; 0 # No       SUPERSCRIPT ZERO
+2071          ; 0 # L&       SUPERSCRIPT LATIN SMALL LETTER I
+2074..2079    ; 0 # No   [6] SUPERSCRIPT FOUR..SUPERSCRIPT NINE
+207A..207C    ; 0 # Sm   [3] SUPERSCRIPT PLUS SIGN..SUPERSCRIPT EQUALS SIGN
+207D          ; 0 # Ps       SUPERSCRIPT LEFT PARENTHESIS
+207E          ; 0 # Pe       SUPERSCRIPT RIGHT PARENTHESIS
+207F          ; 0 # L&       SUPERSCRIPT LATIN SMALL LETTER N
+2080..2089    ; 0 # No  [10] SUBSCRIPT ZERO..SUBSCRIPT NINE
+208A..208C    ; 0 # Sm   [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN
+208D          ; 0 # Ps       SUBSCRIPT LEFT PARENTHESIS
+208E          ; 0 # Pe       SUBSCRIPT RIGHT PARENTHESIS
+2090..2094    ; 0 # Lm   [5] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER SCHWA
+20A0..20B5    ; 0 # Sc  [22] EURO-CURRENCY SIGN..CEDI SIGN
+20DD..20E0    ; 0 # Me   [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH
+20E2..20E4    ; 0 # Me   [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE
+2100..2101    ; 0 # So   [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT
+2102          ; 0 # L&       DOUBLE-STRUCK CAPITAL C
+2103..2106    ; 0 # So   [4] DEGREE CELSIUS..CADA UNA
+2107          ; 0 # L&       EULER CONSTANT
+2108..2109    ; 0 # So   [2] SCRUPLE..DEGREE FAHRENHEIT
+210A..2113    ; 0 # L&  [10] SCRIPT SMALL G..SCRIPT SMALL L
+2114          ; 0 # So       L B BAR SYMBOL
+2115          ; 0 # L&       DOUBLE-STRUCK CAPITAL N
+2116..2118    ; 0 # So   [3] NUMERO SIGN..SCRIPT CAPITAL P
+2119..211D    ; 0 # L&   [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R
+211E..2123    ; 0 # So   [6] PRESCRIPTION TAKE..VERSICLE
+2124          ; 0 # L&       DOUBLE-STRUCK CAPITAL Z
+2125          ; 0 # So       OUNCE SIGN
+2126          ; 0 # L&       OHM SIGN
+2127          ; 0 # So       INVERTED OHM SIGN
+2128          ; 0 # L&       BLACK-LETTER CAPITAL Z
+2129          ; 0 # So       TURNED GREEK SMALL LETTER IOTA
+212A..212D    ; 0 # L&   [4] KELVIN SIGN..BLACK-LETTER CAPITAL C
+212E          ; 0 # So       ESTIMATED SYMBOL
+212F..2134    ; 0 # L&   [6] SCRIPT SMALL E..SCRIPT SMALL O
+2135..2138    ; 0 # Lo   [4] ALEF SYMBOL..DALET SYMBOL
+2139          ; 0 # L&       INFORMATION SOURCE
+213A..213B    ; 0 # So   [2] ROTATED CAPITAL Q..FACSIMILE SIGN
+213C..213F    ; 0 # L&   [4] DOUBLE-STRUCK SMALL PI..DOUBLE-STRUCK CAPITAL PI
+2140..2144    ; 0 # Sm   [5] DOUBLE-STRUCK N-ARY SUMMATION..TURNED SANS-SERIF CAPITAL Y
+2145..2149    ; 0 # L&   [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J
+214A          ; 0 # So       PROPERTY LINE
+214B          ; 0 # Sm       TURNED AMPERSAND
+214C..214D    ; 0 # So   [2] PER SIGN..AKTIESELSKAB
+214E          ; 0 # L&       TURNED SMALL F
+214F          ; 0 # So       SYMBOL FOR SAMARITAN SOURCE
+2153..215F    ; 0 # No  [13] VULGAR FRACTION ONE THIRD..FRACTION NUMERATOR ONE
+2160..2182    ; 0 # Nl  [35] ROMAN NUMERAL ONE..ROMAN NUMERAL TEN THOUSAND
+2183..2184    ; 0 # L&   [2] ROMAN NUMERAL REVERSED ONE HUNDRED..LATIN SMALL LETTER REVERSED C
+2185..2188    ; 0 # Nl   [4] ROMAN NUMERAL SIX LATE FORM..ROMAN NUMERAL ONE HUNDRED THOUSAND
+2190..2194    ; 0 # Sm   [5] LEFTWARDS ARROW..LEFT RIGHT ARROW
+2195..2199    ; 0 # So   [5] UP DOWN ARROW..SOUTH WEST ARROW
+219A..219B    ; 0 # Sm   [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE
+219C..219F    ; 0 # So   [4] LEFTWARDS WAVE ARROW..UPWARDS TWO HEADED ARROW
+21A0          ; 0 # Sm       RIGHTWARDS TWO HEADED ARROW
+21A1..21A2    ; 0 # So   [2] DOWNWARDS TWO HEADED ARROW..LEFTWARDS ARROW WITH TAIL
+21A3          ; 0 # Sm       RIGHTWARDS ARROW WITH TAIL
+21A4..21A5    ; 0 # So   [2] LEFTWARDS ARROW FROM BAR..UPWARDS ARROW FROM BAR
+21A6          ; 0 # Sm       RIGHTWARDS ARROW FROM BAR
+21A7..21AD    ; 0 # So   [7] DOWNWARDS ARROW FROM BAR..LEFT RIGHT WAVE ARROW
+21AE          ; 0 # Sm       LEFT RIGHT ARROW WITH STROKE
+21AF..21CD    ; 0 # So  [31] DOWNWARDS ZIGZAG ARROW..LEFTWARDS DOUBLE ARROW WITH STROKE
+21CE..21CF    ; 0 # Sm   [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE
+21D0..21D1    ; 0 # So   [2] LEFTWARDS DOUBLE ARROW..UPWARDS DOUBLE ARROW
+21D2          ; 0 # Sm       RIGHTWARDS DOUBLE ARROW
+21D3          ; 0 # So       DOWNWARDS DOUBLE ARROW
+21D4          ; 0 # Sm       LEFT RIGHT DOUBLE ARROW
+21D5..21F3    ; 0 # So  [31] UP DOWN DOUBLE ARROW..UP DOWN WHITE ARROW
+21F4..22FF    ; 0 # Sm [268] RIGHT ARROW WITH SMALL CIRCLE..Z NOTATION BAG MEMBERSHIP
+2300..2307    ; 0 # So   [8] DIAMETER SIGN..WAVY LINE
+2308..230B    ; 0 # Sm   [4] LEFT CEILING..RIGHT FLOOR
+230C..231F    ; 0 # So  [20] BOTTOM RIGHT CROP..BOTTOM RIGHT CORNER
+2320..2321    ; 0 # Sm   [2] TOP HALF INTEGRAL..BOTTOM HALF INTEGRAL
+2322..2328    ; 0 # So   [7] FROWN..KEYBOARD
+2329          ; 0 # Ps       LEFT-POINTING ANGLE BRACKET
+232A          ; 0 # Pe       RIGHT-POINTING ANGLE BRACKET
+232B..237B    ; 0 # So  [81] ERASE TO THE LEFT..NOT CHECK MARK
+237C          ; 0 # Sm       RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW
+237D..239A    ; 0 # So  [30] SHOULDERED OPEN BOX..CLEAR SCREEN SYMBOL
+239B..23B3    ; 0 # Sm  [25] LEFT PARENTHESIS UPPER HOOK..SUMMATION BOTTOM
+23B4..23DB    ; 0 # So  [40] TOP SQUARE BRACKET..FUSE
+23DC..23E1    ; 0 # Sm   [6] TOP PARENTHESIS..BOTTOM TORTOISE SHELL BRACKET
+23E2..23E7    ; 0 # So   [6] WHITE TRAPEZIUM..ELECTRICAL INTERSECTION
+2400..2426    ; 0 # So  [39] SYMBOL FOR NULL..SYMBOL FOR SUBSTITUTE FORM TWO
+2440..244A    ; 0 # So  [11] OCR HOOK..OCR DOUBLE BACKSLASH
+2460..249B    ; 0 # No  [60] CIRCLED DIGIT ONE..NUMBER TWENTY FULL STOP
+249C..24E9    ; 0 # So  [78] PARENTHESIZED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z
+24EA..24FF    ; 0 # No  [22] CIRCLED DIGIT ZERO..NEGATIVE CIRCLED DIGIT ZERO
+2500..25B6    ; 0 # So [183] BOX DRAWINGS LIGHT HORIZONTAL..BLACK RIGHT-POINTING TRIANGLE
+25B7          ; 0 # Sm       WHITE RIGHT-POINTING TRIANGLE
+25B8..25C0    ; 0 # So   [9] BLACK RIGHT-POINTING SMALL TRIANGLE..BLACK LEFT-POINTING TRIANGLE
+25C1          ; 0 # Sm       WHITE LEFT-POINTING TRIANGLE
+25C2..25F7    ; 0 # So  [54] BLACK LEFT-POINTING SMALL TRIANGLE..WHITE CIRCLE WITH UPPER RIGHT QUADRANT
+25F8..25FF    ; 0 # Sm   [8] UPPER LEFT TRIANGLE..LOWER RIGHT TRIANGLE
+2600..266E    ; 0 # So [111] BLACK SUN WITH RAYS..MUSIC NATURAL SIGN
+266F          ; 0 # Sm       MUSIC SHARP SIGN
+2670..269D    ; 0 # So  [46] WEST SYRIAC CROSS..OUTLINED WHITE STAR
+26A0..26BC    ; 0 # So  [29] WARNING SIGN..SESQUIQUADRATE
+26C0..26C3    ; 0 # So   [4] WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
+2701..2704    ; 0 # So   [4] UPPER BLADE SCISSORS..WHITE SCISSORS
+2706..2709    ; 0 # So   [4] TELEPHONE LOCATION SIGN..ENVELOPE
+270C..2727    ; 0 # So  [28] VICTORY HAND..WHITE FOUR POINTED STAR
+2729..274B    ; 0 # So  [35] STRESS OUTLINED WHITE STAR..HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK
+274D          ; 0 # So       SHADOWED WHITE CIRCLE
+274F..2752    ; 0 # So   [4] LOWER RIGHT DROP-SHADOWED WHITE SQUARE..UPPER RIGHT SHADOWED WHITE SQUARE
+2756          ; 0 # So       BLACK DIAMOND MINUS WHITE X
+2758..275E    ; 0 # So   [7] LIGHT VERTICAL BAR..HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT
+2761..2767    ; 0 # So   [7] CURVED STEM PARAGRAPH SIGN ORNAMENT..ROTATED FLORAL HEART BULLET
+2768          ; 0 # Ps       MEDIUM LEFT PARENTHESIS ORNAMENT
+2769          ; 0 # Pe       MEDIUM RIGHT PARENTHESIS ORNAMENT
+276A          ; 0 # Ps       MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT
+276B          ; 0 # Pe       MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT
+276C          ; 0 # Ps       MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT
+276D          ; 0 # Pe       MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT
+276E          ; 0 # Ps       HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT
+276F          ; 0 # Pe       HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT
+2770          ; 0 # Ps       HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT
+2771          ; 0 # Pe       HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT
+2772          ; 0 # Ps       LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
+2773          ; 0 # Pe       LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
+2774          ; 0 # Ps       MEDIUM LEFT CURLY BRACKET ORNAMENT
+2775          ; 0 # Pe       MEDIUM RIGHT CURLY BRACKET ORNAMENT
+2776..2793    ; 0 # No  [30] DINGBAT NEGATIVE CIRCLED DIGIT ONE..DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN
+2794          ; 0 # So       HEAVY WIDE-HEADED RIGHTWARDS ARROW
+2798..27AF    ; 0 # So  [24] HEAVY SOUTH EAST ARROW..NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27B1..27BE    ; 0 # So  [14] NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW..OPEN-OUTLINED RIGHTWARDS ARROW
+27C0..27C4    ; 0 # Sm   [5] THREE DIMENSIONAL ANGLE..OPEN SUPERSET
+27C5          ; 0 # Ps       LEFT S-SHAPED BAG DELIMITER
+27C6          ; 0 # Pe       RIGHT S-SHAPED BAG DELIMITER
+27C7..27CA    ; 0 # Sm   [4] OR WITH DOT INSIDE..VERTICAL BAR WITH HORIZONTAL STROKE
+27CC          ; 0 # Sm       LONG DIVISION
+27D0..27E5    ; 0 # Sm  [22] WHITE DIAMOND WITH CENTRED DOT..WHITE SQUARE WITH RIGHTWARDS TICK
+27E6          ; 0 # Ps       MATHEMATICAL LEFT WHITE SQUARE BRACKET
+27E7          ; 0 # Pe       MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+27E8          ; 0 # Ps       MATHEMATICAL LEFT ANGLE BRACKET
+27E9          ; 0 # Pe       MATHEMATICAL RIGHT ANGLE BRACKET
+27EA          ; 0 # Ps       MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
+27EB          ; 0 # Pe       MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
+27EC          ; 0 # Ps       MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
+27ED          ; 0 # Pe       MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
+27EE          ; 0 # Ps       MATHEMATICAL LEFT FLATTENED PARENTHESIS
+27EF          ; 0 # Pe       MATHEMATICAL RIGHT FLATTENED PARENTHESIS
+27F0..27FF    ; 0 # Sm  [16] UPWARDS QUADRUPLE ARROW..LONG RIGHTWARDS SQUIGGLE ARROW
+2800..28FF    ; 0 # So [256] BRAILLE PATTERN BLANK..BRAILLE PATTERN DOTS-12345678
+2900..2982    ; 0 # Sm [131] RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE..Z NOTATION TYPE COLON
+2983          ; 0 # Ps       LEFT WHITE CURLY BRACKET
+2984          ; 0 # Pe       RIGHT WHITE CURLY BRACKET
+2985          ; 0 # Ps       LEFT WHITE PARENTHESIS
+2986          ; 0 # Pe       RIGHT WHITE PARENTHESIS
+2987          ; 0 # Ps       Z NOTATION LEFT IMAGE BRACKET
+2988          ; 0 # Pe       Z NOTATION RIGHT IMAGE BRACKET
+2989          ; 0 # Ps       Z NOTATION LEFT BINDING BRACKET
+298A          ; 0 # Pe       Z NOTATION RIGHT BINDING BRACKET
+298B          ; 0 # Ps       LEFT SQUARE BRACKET WITH UNDERBAR
+298C          ; 0 # Pe       RIGHT SQUARE BRACKET WITH UNDERBAR
+298D          ; 0 # Ps       LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
+298E          ; 0 # Pe       RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+298F          ; 0 # Ps       LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+2990          ; 0 # Pe       RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
+2991          ; 0 # Ps       LEFT ANGLE BRACKET WITH DOT
+2992          ; 0 # Pe       RIGHT ANGLE BRACKET WITH DOT
+2993          ; 0 # Ps       LEFT ARC LESS-THAN BRACKET
+2994          ; 0 # Pe       RIGHT ARC GREATER-THAN BRACKET
+2995          ; 0 # Ps       DOUBLE LEFT ARC GREATER-THAN BRACKET
+2996          ; 0 # Pe       DOUBLE RIGHT ARC LESS-THAN BRACKET
+2997          ; 0 # Ps       LEFT BLACK TORTOISE SHELL BRACKET
+2998          ; 0 # Pe       RIGHT BLACK TORTOISE SHELL BRACKET
+2999..29D7    ; 0 # Sm  [63] DOTTED FENCE..BLACK HOURGLASS
+29D8          ; 0 # Ps       LEFT WIGGLY FENCE
+29D9          ; 0 # Pe       RIGHT WIGGLY FENCE
+29DA          ; 0 # Ps       LEFT DOUBLE WIGGLY FENCE
+29DB          ; 0 # Pe       RIGHT DOUBLE WIGGLY FENCE
+29DC..29FB    ; 0 # Sm  [32] INCOMPLETE INFINITY..TRIPLE PLUS
+29FC          ; 0 # Ps       LEFT-POINTING CURVED ANGLE BRACKET
+29FD          ; 0 # Pe       RIGHT-POINTING CURVED ANGLE BRACKET
+29FE..2AFF    ; 0 # Sm [258] TINY..N-ARY WHITE VERTICAL BAR
+2B00..2B2F    ; 0 # So  [48] NORTH EAST WHITE ARROW..WHITE VERTICAL ELLIPSE
+2B30..2B44    ; 0 # Sm  [21] LEFT ARROW WITH SMALL CIRCLE..RIGHTWARDS ARROW THROUGH SUPERSET
+2B45..2B46    ; 0 # So   [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW
+2B47..2B4C    ; 0 # Sm   [6] REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR
+2B50..2B54    ; 0 # So   [5] WHITE MEDIUM STAR..WHITE RIGHT-POINTING PENTAGON
+2C00..2C2E    ; 0 # L&  [47] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE
+2C30..2C5E    ; 0 # L&  [47] GLAGOLITIC SMALL LETTER AZU..GLAGOLITIC SMALL LETTER LATINATE MYSLITE
+2C60..2C6F    ; 0 # L&  [16] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN CAPITAL LETTER TURNED A
+2C71..2C7C    ; 0 # L&  [12] LATIN SMALL LETTER V WITH RIGHT HOOK..LATIN SUBSCRIPT SMALL LETTER J
+2C7D          ; 0 # Lm       MODIFIER LETTER CAPITAL V
+2C80..2CE4    ; 0 # L& [101] COPTIC CAPITAL LETTER ALFA..COPTIC SYMBOL KAI
+2CE5..2CEA    ; 0 # So   [6] COPTIC SYMBOL MI RO..COPTIC SYMBOL SHIMA SIMA
+2CF9..2CFC    ; 0 # Po   [4] COPTIC OLD NUBIAN FULL STOP..COPTIC OLD NUBIAN VERSE DIVIDER
+2CFD          ; 0 # No       COPTIC FRACTION ONE HALF
+2CFE..2CFF    ; 0 # Po   [2] COPTIC FULL STOP..COPTIC MORPHOLOGICAL DIVIDER
+2D00..2D25    ; 0 # L&  [38] GEORGIAN SMALL LETTER AN..GEORGIAN SMALL LETTER HOE
+2D30..2D65    ; 0 # Lo  [54] TIFINAGH LETTER YA..TIFINAGH LETTER YAZZ
+2D6F          ; 0 # Lm       TIFINAGH MODIFIER LETTER LABIALIZATION MARK
+2D80..2D96    ; 0 # Lo  [23] ETHIOPIC SYLLABLE LOA..ETHIOPIC SYLLABLE GGWE
+2DA0..2DA6    ; 0 # Lo   [7] ETHIOPIC SYLLABLE SSA..ETHIOPIC SYLLABLE SSO
+2DA8..2DAE    ; 0 # Lo   [7] ETHIOPIC SYLLABLE CCA..ETHIOPIC SYLLABLE CCO
+2DB0..2DB6    ; 0 # Lo   [7] ETHIOPIC SYLLABLE ZZA..ETHIOPIC SYLLABLE ZZO
+2DB8..2DBE    ; 0 # Lo   [7] ETHIOPIC SYLLABLE CCHA..ETHIOPIC SYLLABLE CCHO
+2DC0..2DC6    ; 0 # Lo   [7] ETHIOPIC SYLLABLE QYA..ETHIOPIC SYLLABLE QYO
+2DC8..2DCE    ; 0 # Lo   [7] ETHIOPIC SYLLABLE KYA..ETHIOPIC SYLLABLE KYO
+2DD0..2DD6    ; 0 # Lo   [7] ETHIOPIC SYLLABLE XYA..ETHIOPIC SYLLABLE XYO
+2DD8..2DDE    ; 0 # Lo   [7] ETHIOPIC SYLLABLE GYA..ETHIOPIC SYLLABLE GYO
+2E00..2E01    ; 0 # Po   [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER
+2E02          ; 0 # Pi       LEFT SUBSTITUTION BRACKET
+2E03          ; 0 # Pf       RIGHT SUBSTITUTION BRACKET
+2E04          ; 0 # Pi       LEFT DOTTED SUBSTITUTION BRACKET
+2E05          ; 0 # Pf       RIGHT DOTTED SUBSTITUTION BRACKET
+2E06..2E08    ; 0 # Po   [3] RAISED INTERPOLATION MARKER..DOTTED TRANSPOSITION MARKER
+2E09          ; 0 # Pi       LEFT TRANSPOSITION BRACKET
+2E0A          ; 0 # Pf       RIGHT TRANSPOSITION BRACKET
+2E0B          ; 0 # Po       RAISED SQUARE
+2E0C          ; 0 # Pi       LEFT RAISED OMISSION BRACKET
+2E0D          ; 0 # Pf       RIGHT RAISED OMISSION BRACKET
+2E0E..2E16    ; 0 # Po   [9] EDITORIAL CORONIS..DOTTED RIGHT-POINTING ANGLE
+2E17          ; 0 # Pd       DOUBLE OBLIQUE HYPHEN
+2E18..2E19    ; 0 # Po   [2] INVERTED INTERROBANG..PALM BRANCH
+2E1A          ; 0 # Pd       HYPHEN WITH DIAERESIS
+2E1B          ; 0 # Po       TILDE WITH RING ABOVE
+2E1C          ; 0 # Pi       LEFT LOW PARAPHRASE BRACKET
+2E1D          ; 0 # Pf       RIGHT LOW PARAPHRASE BRACKET
+2E1E..2E1F    ; 0 # Po   [2] TILDE WITH DOT ABOVE..TILDE WITH DOT BELOW
+2E20          ; 0 # Pi       LEFT VERTICAL BAR WITH QUILL
+2E21          ; 0 # Pf       RIGHT VERTICAL BAR WITH QUILL
+2E22          ; 0 # Ps       TOP LEFT HALF BRACKET
+2E23          ; 0 # Pe       TOP RIGHT HALF BRACKET
+2E24          ; 0 # Ps       BOTTOM LEFT HALF BRACKET
+2E25          ; 0 # Pe       BOTTOM RIGHT HALF BRACKET
+2E26          ; 0 # Ps       LEFT SIDEWAYS U BRACKET
+2E27          ; 0 # Pe       RIGHT SIDEWAYS U BRACKET
+2E28          ; 0 # Ps       LEFT DOUBLE PARENTHESIS
+2E29          ; 0 # Pe       RIGHT DOUBLE PARENTHESIS
+2E2A..2E2E    ; 0 # Po   [5] TWO DOTS OVER ONE DOT PUNCTUATION..REVERSED QUESTION MARK
+2E2F          ; 0 # Lm       VERTICAL TILDE
+2E30          ; 0 # Po       RING POINT
+2E80..2E99    ; 0 # So  [26] CJK RADICAL REPEAT..CJK RADICAL RAP
+2E9B..2EF3    ; 0 # So  [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE
+2F00..2FD5    ; 0 # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE
+2FF0..2FFB    ; 0 # So  [12] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID
+3000          ; 0 # Zs       IDEOGRAPHIC SPACE
+3001..3003    ; 0 # Po   [3] IDEOGRAPHIC COMMA..DITTO MARK
+3004          ; 0 # So       JAPANESE INDUSTRIAL STANDARD SYMBOL
+3005          ; 0 # Lm       IDEOGRAPHIC ITERATION MARK
+3006          ; 0 # Lo       IDEOGRAPHIC CLOSING MARK
+3007          ; 0 # Nl       IDEOGRAPHIC NUMBER ZERO
+3008          ; 0 # Ps       LEFT ANGLE BRACKET
+3009          ; 0 # Pe       RIGHT ANGLE BRACKET
+300A          ; 0 # Ps       LEFT DOUBLE ANGLE BRACKET
+300B          ; 0 # Pe       RIGHT DOUBLE ANGLE BRACKET
+300C          ; 0 # Ps       LEFT CORNER BRACKET
+300D          ; 0 # Pe       RIGHT CORNER BRACKET
+300E          ; 0 # Ps       LEFT WHITE CORNER BRACKET
+300F          ; 0 # Pe       RIGHT WHITE CORNER BRACKET
+3010          ; 0 # Ps       LEFT BLACK LENTICULAR BRACKET
+3011          ; 0 # Pe       RIGHT BLACK LENTICULAR BRACKET
+3012..3013    ; 0 # So   [2] POSTAL MARK..GETA MARK
+3014          ; 0 # Ps       LEFT TORTOISE SHELL BRACKET
+3015          ; 0 # Pe       RIGHT TORTOISE SHELL BRACKET
+3016          ; 0 # Ps       LEFT WHITE LENTICULAR BRACKET
+3017          ; 0 # Pe       RIGHT WHITE LENTICULAR BRACKET
+3018          ; 0 # Ps       LEFT WHITE TORTOISE SHELL BRACKET
+3019          ; 0 # Pe       RIGHT WHITE TORTOISE SHELL BRACKET
+301A          ; 0 # Ps       LEFT WHITE SQUARE BRACKET
+301B          ; 0 # Pe       RIGHT WHITE SQUARE BRACKET
+301C          ; 0 # Pd       WAVE DASH
+301D          ; 0 # Ps       REVERSED DOUBLE PRIME QUOTATION MARK
+301E..301F    ; 0 # Pe   [2] DOUBLE PRIME QUOTATION MARK..LOW DOUBLE PRIME QUOTATION MARK
+3020          ; 0 # So       POSTAL MARK FACE
+3021..3029    ; 0 # Nl   [9] HANGZHOU NUMERAL ONE..HANGZHOU NUMERAL NINE
+3030          ; 0 # Pd       WAVY DASH
+3031..3035    ; 0 # Lm   [5] VERTICAL KANA REPEAT MARK..VERTICAL KANA REPEAT MARK LOWER HALF
+3036..3037    ; 0 # So   [2] CIRCLED POSTAL MARK..IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL
+3038..303A    ; 0 # Nl   [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY
+303B          ; 0 # Lm       VERTICAL IDEOGRAPHIC ITERATION MARK
+303C          ; 0 # Lo       MASU MARK
+303D          ; 0 # Po       PART ALTERNATION MARK
+303E..303F    ; 0 # So   [2] IDEOGRAPHIC VARIATION INDICATOR..IDEOGRAPHIC HALF FILL SPACE
+3041..3096    ; 0 # Lo  [86] HIRAGANA LETTER SMALL A..HIRAGANA LETTER SMALL KE
+309B..309C    ; 0 # Sk   [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+309D..309E    ; 0 # Lm   [2] HIRAGANA ITERATION MARK..HIRAGANA VOICED ITERATION MARK
+309F          ; 0 # Lo       HIRAGANA DIGRAPH YORI
+30A0          ; 0 # Pd       KATAKANA-HIRAGANA DOUBLE HYPHEN
+30A1..30FA    ; 0 # Lo  [90] KATAKANA LETTER SMALL A..KATAKANA LETTER VO
+30FB          ; 0 # Po       KATAKANA MIDDLE DOT
+30FC..30FE    ; 0 # Lm   [3] KATAKANA-HIRAGANA PROLONGED SOUND MARK..KATAKANA VOICED ITERATION MARK
+30FF          ; 0 # Lo       KATAKANA DIGRAPH KOTO
+3105..312D    ; 0 # Lo  [41] BOPOMOFO LETTER B..BOPOMOFO LETTER IH
+3131..318E    ; 0 # Lo  [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE
+3190..3191    ; 0 # So   [2] IDEOGRAPHIC ANNOTATION LINKING MARK..IDEOGRAPHIC ANNOTATION REVERSE MARK
+3192..3195    ; 0 # No   [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC ANNOTATION FOUR MARK
+3196..319F    ; 0 # So  [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC ANNOTATION MAN MARK
+31A0..31B7    ; 0 # Lo  [24] BOPOMOFO LETTER BU..BOPOMOFO FINAL LETTER H
+31C0..31E3    ; 0 # So  [36] CJK STROKE T..CJK STROKE Q
+31F0..31FF    ; 0 # Lo  [16] KATAKANA LETTER SMALL KU..KATAKANA LETTER SMALL RO
+3200..321E    ; 0 # So  [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU
+3220..3229    ; 0 # No  [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN
+322A..3243    ; 0 # So  [26] PARENTHESIZED IDEOGRAPH MOON..PARENTHESIZED IDEOGRAPH REACH
+3250          ; 0 # So       PARTNERSHIP SIGN
+3251..325F    ; 0 # No  [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE
+3260..327F    ; 0 # So  [32] CIRCLED HANGUL KIYEOK..KOREAN STANDARD SYMBOL
+3280..3289    ; 0 # No  [10] CIRCLED IDEOGRAPH ONE..CIRCLED IDEOGRAPH TEN
+328A..32B0    ; 0 # So  [39] CIRCLED IDEOGRAPH MOON..CIRCLED IDEOGRAPH NIGHT
+32B1..32BF    ; 0 # No  [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY
+32C0..32FE    ; 0 # So  [63] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..CIRCLED KATAKANA WO
+3300..33FF    ; 0 # So [256] SQUARE APAATO..SQUARE GAL
+3400..4DB5    ; 0 # Lo [6582] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DB5
+4DC0..4DFF    ; 0 # So  [64] HEXAGRAM FOR THE CREATIVE HEAVEN..HEXAGRAM FOR BEFORE COMPLETION
+4E00..9FC3    ; 0 # Lo [20932] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FC3
+A000..A014    ; 0 # Lo  [21] YI SYLLABLE IT..YI SYLLABLE E
+A015          ; 0 # Lm       YI SYLLABLE WU
+A016..A48C    ; 0 # Lo [1143] YI SYLLABLE BIT..YI SYLLABLE YYR
+A490..A4C6    ; 0 # So  [55] YI RADICAL QOT..YI RADICAL KE
+A500..A60B    ; 0 # Lo [268] VAI SYLLABLE EE..VAI SYLLABLE NG
+A60C          ; 0 # Lm       VAI SYLLABLE LENGTHENER
+A60D..A60F    ; 0 # Po   [3] VAI COMMA..VAI QUESTION MARK
+A610..A61F    ; 0 # Lo  [16] VAI SYLLABLE NDOLE FA..VAI SYMBOL JONG
+A620..A629    ; 0 # Nd  [10] VAI DIGIT ZERO..VAI DIGIT NINE
+A62A..A62B    ; 0 # Lo   [2] VAI SYLLABLE NDOLE MA..VAI SYLLABLE NDOLE DO
+A640..A65F    ; 0 # L&  [32] CYRILLIC CAPITAL LETTER ZEMLYA..CYRILLIC SMALL LETTER YN
+A662..A66D    ; 0 # L&  [12] CYRILLIC CAPITAL LETTER SOFT DE..CYRILLIC SMALL LETTER DOUBLE MONOCULAR O
+A66E          ; 0 # Lo       CYRILLIC LETTER MULTIOCULAR O
+A670..A672    ; 0 # Me   [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN
+A673          ; 0 # Po       SLAVONIC ASTERISK
+A67E          ; 0 # Po       CYRILLIC KAVYKA
+A67F          ; 0 # Lm       CYRILLIC PAYEROK
+A680..A697    ; 0 # L&  [24] CYRILLIC CAPITAL LETTER DWE..CYRILLIC SMALL LETTER SHWE
+A700..A716    ; 0 # Sk  [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR
+A717..A71F    ; 0 # Lm   [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK
+A720..A721    ; 0 # Sk   [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE
+A722..A76F    ; 0 # L&  [78] LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF..LATIN SMALL LETTER CON
+A770          ; 0 # Lm       MODIFIER LETTER US
+A771..A787    ; 0 # L&  [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER INSULAR T
+A788          ; 0 # Lm       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
+A789..A78A    ; 0 # Sk   [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
+A78B..A78C    ; 0 # L&   [2] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER SALTILLO
+A7FB..A801    ; 0 # Lo   [7] LATIN EPIGRAPHIC LETTER REVERSED F..SYLOTI NAGRI LETTER I
+A802          ; 0 # Mn       SYLOTI NAGRI SIGN DVISVARA
+A803..A805    ; 0 # Lo   [3] SYLOTI NAGRI LETTER U..SYLOTI NAGRI LETTER O
+A807..A80A    ; 0 # Lo   [4] SYLOTI NAGRI LETTER KO..SYLOTI NAGRI LETTER GHO
+A80B          ; 0 # Mn       SYLOTI NAGRI SIGN ANUSVARA
+A80C..A822    ; 0 # Lo  [23] SYLOTI NAGRI LETTER CO..SYLOTI NAGRI LETTER HO
+A823..A824    ; 0 # Mc   [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
+A825..A826    ; 0 # Mn   [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E
+A827          ; 0 # Mc       SYLOTI NAGRI VOWEL SIGN OO
+A828..A82B    ; 0 # So   [4] SYLOTI NAGRI POETRY MARK-1..SYLOTI NAGRI POETRY MARK-4
+A840..A873    ; 0 # Lo  [52] PHAGS-PA LETTER KA..PHAGS-PA LETTER CANDRABINDU
+A874..A877    ; 0 # Po   [4] PHAGS-PA SINGLE HEAD MARK..PHAGS-PA MARK DOUBLE SHAD
+A880..A881    ; 0 # Mc   [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA
+A882..A8B3    ; 0 # Lo  [50] SAURASHTRA LETTER A..SAURASHTRA LETTER LLA
+A8B4..A8C3    ; 0 # Mc  [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU
+A8CE..A8CF    ; 0 # Po   [2] SAURASHTRA DANDA..SAURASHTRA DOUBLE DANDA
+A8D0..A8D9    ; 0 # Nd  [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE
+A900..A909    ; 0 # Nd  [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE
+A90A..A925    ; 0 # Lo  [28] KAYAH LI LETTER KA..KAYAH LI LETTER OO
+A926..A92A    ; 0 # Mn   [5] KAYAH LI VOWEL UE..KAYAH LI VOWEL O
+A92E..A92F    ; 0 # Po   [2] KAYAH LI SIGN CWI..KAYAH LI SIGN SHYA
+A930..A946    ; 0 # Lo  [23] REJANG LETTER KA..REJANG LETTER A
+A947..A951    ; 0 # Mn  [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
+A952          ; 0 # Mc       REJANG CONSONANT SIGN H
+A95F          ; 0 # Po       REJANG SECTION MARK
+AA00..AA28    ; 0 # Lo  [41] CHAM LETTER A..CHAM LETTER HA
+AA29..AA2E    ; 0 # Mn   [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
+AA2F..AA30    ; 0 # Mc   [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
+AA31..AA32    ; 0 # Mn   [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
+AA33..AA34    ; 0 # Mc   [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA
+AA35..AA36    ; 0 # Mn   [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA
+AA40..AA42    ; 0 # Lo   [3] CHAM LETTER FINAL K..CHAM LETTER FINAL NG
+AA43          ; 0 # Mn       CHAM CONSONANT SIGN FINAL NG
+AA44..AA4B    ; 0 # Lo   [8] CHAM LETTER FINAL CH..CHAM LETTER FINAL SS
+AA4C          ; 0 # Mn       CHAM CONSONANT SIGN FINAL M
+AA4D          ; 0 # Mc       CHAM CONSONANT SIGN FINAL H
+AA50..AA59    ; 0 # Nd  [10] CHAM DIGIT ZERO..CHAM DIGIT NINE
+AA5C..AA5F    ; 0 # Po   [4] CHAM PUNCTUATION SPIRAL..CHAM PUNCTUATION TRIPLE DANDA
+AC00..D7A3    ; 0 # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH
+E000..F8FF    ; 0 # Co [6400] <private-use-E000>..<private-use-F8FF>
+F900..FA2D    ; 0 # Lo [302] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA2D
+FA30..FA6A    ; 0 # Lo  [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A
+FA70..FAD9    ; 0 # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9
+FB00..FB06    ; 0 # L&   [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
+FB13..FB17    ; 0 # L&   [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
+FB1D          ; 0 # Lo       HEBREW LETTER YOD WITH HIRIQ
+FB1F..FB28    ; 0 # Lo  [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV
+FB29          ; 0 # Sm       HEBREW LETTER ALTERNATIVE PLUS SIGN
+FB2A..FB36    ; 0 # Lo  [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C    ; 0 # Lo   [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E          ; 0 # Lo       HEBREW LETTER MEM WITH DAGESH
+FB40..FB41    ; 0 # Lo   [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44    ; 0 # Lo   [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FBB1    ; 0 # Lo [108] HEBREW LETTER TSADI WITH DAGESH..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
+FBD3..FD3D    ; 0 # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+FD3E          ; 0 # Ps       ORNATE LEFT PARENTHESIS
+FD3F          ; 0 # Pe       ORNATE RIGHT PARENTHESIS
+FD50..FD8F    ; 0 # Lo  [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD92..FDC7    ; 0 # Lo  [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+FDF0..FDFB    ; 0 # Lo  [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
+FDFC          ; 0 # Sc       RIAL SIGN
+FDFD          ; 0 # So       ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM
+FE00..FE0F    ; 0 # Mn  [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
+FE10..FE16    ; 0 # Po   [7] PRESENTATION FORM FOR VERTICAL COMMA..PRESENTATION FORM FOR VERTICAL QUESTION MARK
+FE17          ; 0 # Ps       PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET
+FE18          ; 0 # Pe       PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET
+FE19          ; 0 # Po       PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS
+FE30          ; 0 # Po       PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+FE31..FE32    ; 0 # Pd   [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH
+FE33..FE34    ; 0 # Pc   [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
+FE35          ; 0 # Ps       PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS
+FE36          ; 0 # Pe       PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS
+FE37          ; 0 # Ps       PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET
+FE38          ; 0 # Pe       PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET
+FE39          ; 0 # Ps       PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET
+FE3A          ; 0 # Pe       PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET
+FE3B          ; 0 # Ps       PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET
+FE3C          ; 0 # Pe       PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET
+FE3D          ; 0 # Ps       PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET
+FE3E          ; 0 # Pe       PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET
+FE3F          ; 0 # Ps       PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET
+FE40          ; 0 # Pe       PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET
+FE41          ; 0 # Ps       PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET
+FE42          ; 0 # Pe       PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET
+FE43          ; 0 # Ps       PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET
+FE44          ; 0 # Pe       PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET
+FE45..FE46    ; 0 # Po   [2] SESAME DOT..WHITE SESAME DOT
+FE47          ; 0 # Ps       PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET
+FE48          ; 0 # Pe       PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET
+FE49..FE4C    ; 0 # Po   [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE
+FE4D..FE4F    ; 0 # Pc   [3] DASHED LOW LINE..WAVY LOW LINE
+FE50..FE52    ; 0 # Po   [3] SMALL COMMA..SMALL FULL STOP
+FE54..FE57    ; 0 # Po   [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK
+FE58          ; 0 # Pd       SMALL EM DASH
+FE59          ; 0 # Ps       SMALL LEFT PARENTHESIS
+FE5A          ; 0 # Pe       SMALL RIGHT PARENTHESIS
+FE5B          ; 0 # Ps       SMALL LEFT CURLY BRACKET
+FE5C          ; 0 # Pe       SMALL RIGHT CURLY BRACKET
+FE5D          ; 0 # Ps       SMALL LEFT TORTOISE SHELL BRACKET
+FE5E          ; 0 # Pe       SMALL RIGHT TORTOISE SHELL BRACKET
+FE5F..FE61    ; 0 # Po   [3] SMALL NUMBER SIGN..SMALL ASTERISK
+FE62          ; 0 # Sm       SMALL PLUS SIGN
+FE63          ; 0 # Pd       SMALL HYPHEN-MINUS
+FE64..FE66    ; 0 # Sm   [3] SMALL LESS-THAN SIGN..SMALL EQUALS SIGN
+FE68          ; 0 # Po       SMALL REVERSE SOLIDUS
+FE69          ; 0 # Sc       SMALL DOLLAR SIGN
+FE6A..FE6B    ; 0 # Po   [2] SMALL PERCENT SIGN..SMALL COMMERCIAL AT
+FE70..FE74    ; 0 # Lo   [5] ARABIC FATHATAN ISOLATED FORM..ARABIC KASRATAN ISOLATED FORM
+FE76..FEFC    ; 0 # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+FEFF          ; 0 # Cf       ZERO WIDTH NO-BREAK SPACE
+FF01..FF03    ; 0 # Po   [3] FULLWIDTH EXCLAMATION MARK..FULLWIDTH NUMBER SIGN
+FF04          ; 0 # Sc       FULLWIDTH DOLLAR SIGN
+FF05..FF07    ; 0 # Po   [3] FULLWIDTH PERCENT SIGN..FULLWIDTH APOSTROPHE
+FF08          ; 0 # Ps       FULLWIDTH LEFT PARENTHESIS
+FF09          ; 0 # Pe       FULLWIDTH RIGHT PARENTHESIS
+FF0A          ; 0 # Po       FULLWIDTH ASTERISK
+FF0B          ; 0 # Sm       FULLWIDTH PLUS SIGN
+FF0C          ; 0 # Po       FULLWIDTH COMMA
+FF0D          ; 0 # Pd       FULLWIDTH HYPHEN-MINUS
+FF0E..FF0F    ; 0 # Po   [2] FULLWIDTH FULL STOP..FULLWIDTH SOLIDUS
+FF10..FF19    ; 0 # Nd  [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
+FF1A..FF1B    ; 0 # Po   [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON
+FF1C..FF1E    ; 0 # Sm   [3] FULLWIDTH LESS-THAN SIGN..FULLWIDTH GREATER-THAN SIGN
+FF1F..FF20    ; 0 # Po   [2] FULLWIDTH QUESTION MARK..FULLWIDTH COMMERCIAL AT
+FF21..FF3A    ; 0 # L&  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z
+FF3B          ; 0 # Ps       FULLWIDTH LEFT SQUARE BRACKET
+FF3C          ; 0 # Po       FULLWIDTH REVERSE SOLIDUS
+FF3D          ; 0 # Pe       FULLWIDTH RIGHT SQUARE BRACKET
+FF3E          ; 0 # Sk       FULLWIDTH CIRCUMFLEX ACCENT
+FF3F          ; 0 # Pc       FULLWIDTH LOW LINE
+FF40          ; 0 # Sk       FULLWIDTH GRAVE ACCENT
+FF41..FF5A    ; 0 # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z
+FF5B          ; 0 # Ps       FULLWIDTH LEFT CURLY BRACKET
+FF5C          ; 0 # Sm       FULLWIDTH VERTICAL LINE
+FF5D          ; 0 # Pe       FULLWIDTH RIGHT CURLY BRACKET
+FF5E          ; 0 # Sm       FULLWIDTH TILDE
+FF5F          ; 0 # Ps       FULLWIDTH LEFT WHITE PARENTHESIS
+FF60          ; 0 # Pe       FULLWIDTH RIGHT WHITE PARENTHESIS
+FF61          ; 0 # Po       HALFWIDTH IDEOGRAPHIC FULL STOP
+FF62          ; 0 # Ps       HALFWIDTH LEFT CORNER BRACKET
+FF63          ; 0 # Pe       HALFWIDTH RIGHT CORNER BRACKET
+FF64..FF65    ; 0 # Po   [2] HALFWIDTH IDEOGRAPHIC COMMA..HALFWIDTH KATAKANA MIDDLE DOT
+FF66..FF6F    ; 0 # Lo  [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU
+FF70          ; 0 # Lm       HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
+FF71..FF9D    ; 0 # Lo  [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N
+FF9E..FF9F    ; 0 # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+FFA0..FFBE    ; 0 # Lo  [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH
+FFC2..FFC7    ; 0 # Lo   [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E
+FFCA..FFCF    ; 0 # Lo   [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE
+FFD2..FFD7    ; 0 # Lo   [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU
+FFDA..FFDC    ; 0 # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
+FFE0..FFE1    ; 0 # Sc   [2] FULLWIDTH CENT SIGN..FULLWIDTH POUND SIGN
+FFE2          ; 0 # Sm       FULLWIDTH NOT SIGN
+FFE3          ; 0 # Sk       FULLWIDTH MACRON
+FFE4          ; 0 # So       FULLWIDTH BROKEN BAR
+FFE5..FFE6    ; 0 # Sc   [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN
+FFE8          ; 0 # So       HALFWIDTH FORMS LIGHT VERTICAL
+FFE9..FFEC    ; 0 # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW
+FFED..FFEE    ; 0 # So   [2] HALFWIDTH BLACK SQUARE..HALFWIDTH WHITE CIRCLE
+FFF9..FFFB    ; 0 # Cf   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR
+FFFC..FFFD    ; 0 # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHARACTER
+10000..1000B  ; 0 # Lo  [12] LINEAR B SYLLABLE B008 A..LINEAR B SYLLABLE B046 JE
+1000D..10026  ; 0 # Lo  [26] LINEAR B SYLLABLE B036 JO..LINEAR B SYLLABLE B032 QO
+10028..1003A  ; 0 # Lo  [19] LINEAR B SYLLABLE B060 RA..LINEAR B SYLLABLE B042 WO
+1003C..1003D  ; 0 # Lo   [2] LINEAR B SYLLABLE B017 ZA..LINEAR B SYLLABLE B074 ZE
+1003F..1004D  ; 0 # Lo  [15] LINEAR B SYLLABLE B020 ZO..LINEAR B SYLLABLE B091 TWO
+10050..1005D  ; 0 # Lo  [14] LINEAR B SYMBOL B018..LINEAR B SYMBOL B089
+10080..100FA  ; 0 # Lo [123] LINEAR B IDEOGRAM B100 MAN..LINEAR B IDEOGRAM VESSEL B305
+10100..10101  ; 0 # Po   [2] AEGEAN WORD SEPARATOR LINE..AEGEAN WORD SEPARATOR DOT
+10102         ; 0 # So       AEGEAN CHECK MARK
+10107..10133  ; 0 # No  [45] AEGEAN NUMBER ONE..AEGEAN NUMBER NINETY THOUSAND
+10137..1013F  ; 0 # So   [9] AEGEAN WEIGHT BASE UNIT..AEGEAN MEASURE THIRD SUBUNIT
+10140..10174  ; 0 # Nl  [53] GREEK ACROPHONIC ATTIC ONE QUARTER..GREEK ACROPHONIC STRATIAN FIFTY MNAS
+10175..10178  ; 0 # No   [4] GREEK ONE HALF SIGN..GREEK THREE QUARTERS SIGN
+10179..10189  ; 0 # So  [17] GREEK YEAR SIGN..GREEK TRYBLION BASE SIGN
+1018A         ; 0 # No       GREEK ZERO SIGN
+10190..1019B  ; 0 # So  [12] ROMAN SEXTANS SIGN..ROMAN CENTURIAL SIGN
+101D0..101FC  ; 0 # So  [45] PHAISTOS DISC SIGN PEDESTRIAN..PHAISTOS DISC SIGN WAVY BAND
+10280..1029C  ; 0 # Lo  [29] LYCIAN LETTER A..LYCIAN LETTER X
+102A0..102D0  ; 0 # Lo  [49] CARIAN LETTER A..CARIAN LETTER UUU3
+10300..1031E  ; 0 # Lo  [31] OLD ITALIC LETTER A..OLD ITALIC LETTER UU
+10320..10323  ; 0 # No   [4] OLD ITALIC NUMERAL ONE..OLD ITALIC NUMERAL FIFTY
+10330..10340  ; 0 # Lo  [17] GOTHIC LETTER AHSA..GOTHIC LETTER PAIRTHRA
+10341         ; 0 # Nl       GOTHIC LETTER NINETY
+10342..10349  ; 0 # Lo   [8] GOTHIC LETTER RAIDA..GOTHIC LETTER OTHAL
+1034A         ; 0 # Nl       GOTHIC LETTER NINE HUNDRED
+10380..1039D  ; 0 # Lo  [30] UGARITIC LETTER ALPA..UGARITIC LETTER SSU
+1039F         ; 0 # Po       UGARITIC WORD DIVIDER
+103A0..103C3  ; 0 # Lo  [36] OLD PERSIAN SIGN A..OLD PERSIAN SIGN HA
+103C8..103CF  ; 0 # Lo   [8] OLD PERSIAN SIGN AURAMAZDAA..OLD PERSIAN SIGN BUUMISH
+103D0         ; 0 # Po       OLD PERSIAN WORD DIVIDER
+103D1..103D5  ; 0 # Nl   [5] OLD PERSIAN NUMBER ONE..OLD PERSIAN NUMBER HUNDRED
+10400..1044F  ; 0 # L&  [80] DESERET CAPITAL LETTER LONG I..DESERET SMALL LETTER EW
+10450..1049D  ; 0 # Lo  [78] SHAVIAN LETTER PEEP..OSMANYA LETTER OO
+104A0..104A9  ; 0 # Nd  [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE
+10800..10805  ; 0 # Lo   [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
+10808         ; 0 # Lo       CYPRIOT SYLLABLE JO
+1080A..10835  ; 0 # Lo  [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
+10837..10838  ; 0 # Lo   [2] CYPRIOT SYLLABLE XA..CYPRIOT SYLLABLE XE
+1083C         ; 0 # Lo       CYPRIOT SYLLABLE ZA
+1083F         ; 0 # Lo       CYPRIOT SYLLABLE ZO
+10900..10915  ; 0 # Lo  [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
+10916..10919  ; 0 # No   [4] PHOENICIAN NUMBER ONE..PHOENICIAN NUMBER ONE HUNDRED
+1091F         ; 0 # Po       PHOENICIAN WORD SEPARATOR
+10920..10939  ; 0 # Lo  [26] LYDIAN LETTER A..LYDIAN LETTER C
+1093F         ; 0 # Po       LYDIAN TRIANGULAR MARK
+10A00         ; 0 # Lo       KHAROSHTHI LETTER A
+10A01..10A03  ; 0 # Mn   [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R
+10A05..10A06  ; 0 # Mn   [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O
+10A0C         ; 0 # Mn       KHAROSHTHI VOWEL LENGTH MARK
+10A0E         ; 0 # Mn       KHAROSHTHI SIGN ANUSVARA
+10A10..10A13  ; 0 # Lo   [4] KHAROSHTHI LETTER KA..KHAROSHTHI LETTER GHA
+10A15..10A17  ; 0 # Lo   [3] KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA
+10A19..10A33  ; 0 # Lo  [27] KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER TTTHA
+10A40..10A47  ; 0 # No   [8] KHAROSHTHI DIGIT ONE..KHAROSHTHI NUMBER ONE THOUSAND
+10A50..10A58  ; 0 # Po   [9] KHAROSHTHI PUNCTUATION DOT..KHAROSHTHI PUNCTUATION LINES
+12000..1236E  ; 0 # Lo [879] CUNEIFORM SIGN A..CUNEIFORM SIGN ZUM
+12400..12462  ; 0 # Nl  [99] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE QUARTER
+12470..12473  ; 0 # Po   [4] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL TRICOLON
+1D000..1D0F5  ; 0 # So [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO
+1D100..1D126  ; 0 # So  [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2
+1D129..1D164  ; 0 # So  [60] MUSICAL SYMBOL MULTIPLE MEASURE REST..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D16A..1D16C  ; 0 # So   [3] MUSICAL SYMBOL FINGERED TREMOLO-1..MUSICAL SYMBOL FINGERED TREMOLO-3
+1D173..1D17A  ; 0 # Cf   [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
+1D183..1D184  ; 0 # So   [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL ARPEGGIATO DOWN
+1D18C..1D1A9  ; 0 # So  [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH
+1D1AE..1D1DD  ; 0 # So  [48] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL PES SUBPUNCTIS
+1D200..1D241  ; 0 # So  [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54
+1D245         ; 0 # So       GREEK MUSICAL LEIMMA
+1D300..1D356  ; 0 # So  [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING
+1D360..1D371  ; 0 # No  [18] COUNTING ROD UNIT DIGIT ONE..COUNTING ROD TENS DIGIT NINE
+1D400..1D454  ; 0 # L&  [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G
+1D456..1D49C  ; 0 # L&  [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A
+1D49E..1D49F  ; 0 # L&   [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D
+1D4A2         ; 0 # L&       MATHEMATICAL SCRIPT CAPITAL G
+1D4A5..1D4A6  ; 0 # L&   [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K
+1D4A9..1D4AC  ; 0 # L&   [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q
+1D4AE..1D4B9  ; 0 # L&  [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D
+1D4BB         ; 0 # L&       MATHEMATICAL SCRIPT SMALL F
+1D4BD..1D4C3  ; 0 # L&   [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N
+1D4C5..1D505  ; 0 # L&  [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B
+1D507..1D50A  ; 0 # L&   [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G
+1D50D..1D514  ; 0 # L&   [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q
+1D516..1D51C  ; 0 # L&   [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y
+1D51E..1D539  ; 0 # L&  [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+1D53B..1D53E  ; 0 # L&   [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+1D540..1D544  ; 0 # L&   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+1D546         ; 0 # L&       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+1D54A..1D550  ; 0 # L&   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+1D552..1D6A5  ; 0 # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D6A8..1D6C0  ; 0 # L&  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
+1D6C1         ; 0 # Sm       MATHEMATICAL BOLD NABLA
+1D6C2..1D6DA  ; 0 # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
+1D6DB         ; 0 # Sm       MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
+1D6DC..1D6FA  ; 0 # L&  [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
+1D6FB         ; 0 # Sm       MATHEMATICAL ITALIC NABLA
+1D6FC..1D714  ; 0 # L&  [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA
+1D715         ; 0 # Sm       MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL
+1D716..1D734  ; 0 # L&  [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA
+1D735         ; 0 # Sm       MATHEMATICAL BOLD ITALIC NABLA
+1D736..1D74E  ; 0 # L&  [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA
+1D74F         ; 0 # Sm       MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL
+1D750..1D76E  ; 0 # L&  [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
+1D76F         ; 0 # Sm       MATHEMATICAL SANS-SERIF BOLD NABLA
+1D770..1D788  ; 0 # L&  [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA
+1D789         ; 0 # Sm       MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
+1D78A..1D7A8  ; 0 # L&  [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
+1D7A9         ; 0 # Sm       MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA
+1D7AA..1D7C2  ; 0 # L&  [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA
+1D7C3         ; 0 # Sm       MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
+1D7C4..1D7CB  ; 0 # L&   [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA
+1D7CE..1D7FF  ; 0 # Nd  [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE
+1F000..1F02B  ; 0 # So  [44] MAHJONG TILE EAST WIND..MAHJONG TILE BACK
+1F030..1F093  ; 0 # So [100] DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
+20000..2A6D6  ; 0 # Lo [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6
+2F800..2FA1D  ; 0 # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+E0001         ; 0 # Cf       LANGUAGE TAG
+E0020..E007F  ; 0 # Cf  [96] TAG SPACE..CANCEL TAG
+E0100..E01EF  ; 0 # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
+F0000..FFFFD  ; 0 # Co [65534] <private-use-F0000>..<private-use-FFFFD>
+100000..10FFFD; 0 # Co [65534] <private-use-100000>..<private-use-10FFFD>
+
+# The above property value applies to 875931 code points not listed here.
+# Total code points: 1113611
+
+# ================================================
+
+# Canonical_Combining_Class=Overlay
+
+0334..0338    ; 1 # Mn   [5] COMBINING TILDE OVERLAY..COMBINING LONG SOLIDUS OVERLAY
+20D2..20D3    ; 1 # Mn   [2] COMBINING LONG VERTICAL LINE OVERLAY..COMBINING SHORT VERTICAL LINE OVERLAY
+20D8..20DA    ; 1 # Mn   [3] COMBINING RING OVERLAY..COMBINING ANTICLOCKWISE RING OVERLAY
+20E5..20E6    ; 1 # Mn   [2] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING DOUBLE VERTICAL STROKE OVERLAY
+20EA..20EB    ; 1 # Mn   [2] COMBINING LEFTWARDS ARROW OVERLAY..COMBINING LONG DOUBLE SOLIDUS OVERLAY
+10A39         ; 1 # Mn       KHAROSHTHI SIGN CAUDA
+1D167..1D169  ; 1 # Mn   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
+
+# Total code points: 18
+
+# ================================================
+
+# Canonical_Combining_Class=Nukta
+
+093C          ; 7 # Mn       DEVANAGARI SIGN NUKTA
+09BC          ; 7 # Mn       BENGALI SIGN NUKTA
+0A3C          ; 7 # Mn       GURMUKHI SIGN NUKTA
+0ABC          ; 7 # Mn       GUJARATI SIGN NUKTA
+0B3C          ; 7 # Mn       ORIYA SIGN NUKTA
+0CBC          ; 7 # Mn       KANNADA SIGN NUKTA
+1037          ; 7 # Mn       MYANMAR SIGN DOT BELOW
+1B34          ; 7 # Mn       BALINESE SIGN REREKAN
+1C37          ; 7 # Mn       LEPCHA SIGN NUKTA
+
+# Total code points: 9
+
+# ================================================
+
+# Canonical_Combining_Class=Kana_Voicing
+
+3099..309A    ; 8 # Mn   [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+
+# Total code points: 2
+
+# ================================================
+
+# Canonical_Combining_Class=Virama
+
+094D          ; 9 # Mn       DEVANAGARI SIGN VIRAMA
+09CD          ; 9 # Mn       BENGALI SIGN VIRAMA
+0A4D          ; 9 # Mn       GURMUKHI SIGN VIRAMA
+0ACD          ; 9 # Mn       GUJARATI SIGN VIRAMA
+0B4D          ; 9 # Mn       ORIYA SIGN VIRAMA
+0BCD          ; 9 # Mn       TAMIL SIGN VIRAMA
+0C4D          ; 9 # Mn       TELUGU SIGN VIRAMA
+0CCD          ; 9 # Mn       KANNADA SIGN VIRAMA
+0D4D          ; 9 # Mn       MALAYALAM SIGN VIRAMA
+0DCA          ; 9 # Mn       SINHALA SIGN AL-LAKUNA
+0E3A          ; 9 # Mn       THAI CHARACTER PHINTHU
+0F84          ; 9 # Mn       TIBETAN MARK HALANTA
+1039..103A    ; 9 # Mn   [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+1714          ; 9 # Mn       TAGALOG SIGN VIRAMA
+1734          ; 9 # Mn       HANUNOO SIGN PAMUDPOD
+17D2          ; 9 # Mn       KHMER SIGN COENG
+1B44          ; 9 # Mc       BALINESE ADEG ADEG
+1BAA          ; 9 # Mc       SUNDANESE SIGN PAMAAEH
+A806          ; 9 # Mn       SYLOTI NAGRI SIGN HASANTA
+A8C4          ; 9 # Mn       SAURASHTRA SIGN VIRAMA
+A953          ; 9 # Mc       REJANG VIRAMA
+10A3F         ; 9 # Mn       KHAROSHTHI VIRAMA
+
+# Total code points: 23
+
+# ================================================
+
+# Canonical_Combining_Class=10
+
+05B0          ; 10 # Mn       HEBREW POINT SHEVA
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=11
+
+05B1          ; 11 # Mn       HEBREW POINT HATAF SEGOL
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=12
+
+05B2          ; 12 # Mn       HEBREW POINT HATAF PATAH
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=13
+
+05B3          ; 13 # Mn       HEBREW POINT HATAF QAMATS
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=14
+
+05B4          ; 14 # Mn       HEBREW POINT HIRIQ
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=15
+
+05B5          ; 15 # Mn       HEBREW POINT TSERE
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=16
+
+05B6          ; 16 # Mn       HEBREW POINT SEGOL
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=17
+
+05B7          ; 17 # Mn       HEBREW POINT PATAH
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=18
+
+05B8          ; 18 # Mn       HEBREW POINT QAMATS
+05C7          ; 18 # Mn       HEBREW POINT QAMATS QATAN
+
+# Total code points: 2
+
+# ================================================
+
+# Canonical_Combining_Class=19
+
+05B9..05BA    ; 19 # Mn   [2] HEBREW POINT HOLAM..HEBREW POINT HOLAM HASER FOR VAV
+
+# Total code points: 2
+
+# ================================================
+
+# Canonical_Combining_Class=20
+
+05BB          ; 20 # Mn       HEBREW POINT QUBUTS
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=21
+
+05BC          ; 21 # Mn       HEBREW POINT DAGESH OR MAPIQ
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=22
+
+05BD          ; 22 # Mn       HEBREW POINT METEG
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=23
+
+05BF          ; 23 # Mn       HEBREW POINT RAFE
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=24
+
+05C1          ; 24 # Mn       HEBREW POINT SHIN DOT
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=25
+
+05C2          ; 25 # Mn       HEBREW POINT SIN DOT
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=26
+
+FB1E          ; 26 # Mn       HEBREW POINT JUDEO-SPANISH VARIKA
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=27
+
+064B          ; 27 # Mn       ARABIC FATHATAN
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=28
+
+064C          ; 28 # Mn       ARABIC DAMMATAN
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=29
+
+064D          ; 29 # Mn       ARABIC KASRATAN
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=30
+
+0618          ; 30 # Mn       ARABIC SMALL FATHA
+064E          ; 30 # Mn       ARABIC FATHA
+
+# Total code points: 2
+
+# ================================================
+
+# Canonical_Combining_Class=31
+
+0619          ; 31 # Mn       ARABIC SMALL DAMMA
+064F          ; 31 # Mn       ARABIC DAMMA
+
+# Total code points: 2
+
+# ================================================
+
+# Canonical_Combining_Class=32
+
+061A          ; 32 # Mn       ARABIC SMALL KASRA
+0650          ; 32 # Mn       ARABIC KASRA
+
+# Total code points: 2
+
+# ================================================
+
+# Canonical_Combining_Class=33
+
+0651          ; 33 # Mn       ARABIC SHADDA
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=34
+
+0652          ; 34 # Mn       ARABIC SUKUN
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=35
+
+0670          ; 35 # Mn       ARABIC LETTER SUPERSCRIPT ALEF
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=36
+
+0711          ; 36 # Mn       SYRIAC LETTER SUPERSCRIPT ALAPH
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=84
+
+0C55          ; 84 # Mn       TELUGU LENGTH MARK
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=91
+
+0C56          ; 91 # Mn       TELUGU AI LENGTH MARK
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=103
+
+0E38..0E39    ; 103 # Mn   [2] THAI CHARACTER SARA U..THAI CHARACTER SARA UU
+
+# Total code points: 2
+
+# ================================================
+
+# Canonical_Combining_Class=107
+
+0E48..0E4B    ; 107 # Mn   [4] THAI CHARACTER MAI EK..THAI CHARACTER MAI CHATTAWA
+
+# Total code points: 4
+
+# ================================================
+
+# Canonical_Combining_Class=118
+
+0EB8..0EB9    ; 118 # Mn   [2] LAO VOWEL SIGN U..LAO VOWEL SIGN UU
+
+# Total code points: 2
+
+# ================================================
+
+# Canonical_Combining_Class=122
+
+0EC8..0ECB    ; 122 # Mn   [4] LAO TONE MAI EK..LAO TONE MAI CATAWA
+
+# Total code points: 4
+
+# ================================================
+
+# Canonical_Combining_Class=129
+
+0F71          ; 129 # Mn       TIBETAN VOWEL SIGN AA
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=130
+
+0F72          ; 130 # Mn       TIBETAN VOWEL SIGN I
+0F7A..0F7D    ; 130 # Mn   [4] TIBETAN VOWEL SIGN E..TIBETAN VOWEL SIGN OO
+0F80          ; 130 # Mn       TIBETAN VOWEL SIGN REVERSED I
+
+# Total code points: 6
+
+# ================================================
+
+# Canonical_Combining_Class=132
+
+0F74          ; 132 # Mn       TIBETAN VOWEL SIGN U
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=Attached_Below
+
+0321..0322    ; 202 # Mn   [2] COMBINING PALATALIZED HOOK BELOW..COMBINING RETROFLEX HOOK BELOW
+0327..0328    ; 202 # Mn   [2] COMBINING CEDILLA..COMBINING OGONEK
+1DD0          ; 202 # Mn       COMBINING IS BELOW
+
+# Total code points: 5
+
+# ================================================
+
+# Canonical_Combining_Class=214
+
+1DCE          ; 214 # Mn       COMBINING OGONEK ABOVE
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=Attached_Above_Right
+
+031B          ; 216 # Mn       COMBINING HORN
+0F39          ; 216 # Mn       TIBETAN MARK TSA -PHRU
+1D165..1D166  ; 216 # Mc   [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
+1D16E..1D172  ; 216 # Mc   [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5
+
+# Total code points: 9
+
+# ================================================
+
+# Canonical_Combining_Class=Below_Left
+
+302A          ; 218 # Mn       IDEOGRAPHIC LEVEL TONE MARK
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=Below
+
+0316..0319    ; 220 # Mn   [4] COMBINING GRAVE ACCENT BELOW..COMBINING RIGHT TACK BELOW
+031C..0320    ; 220 # Mn   [5] COMBINING LEFT HALF RING BELOW..COMBINING MINUS SIGN BELOW
+0323..0326    ; 220 # Mn   [4] COMBINING DOT BELOW..COMBINING COMMA BELOW
+0329..0333    ; 220 # Mn  [11] COMBINING VERTICAL LINE BELOW..COMBINING DOUBLE LOW LINE
+0339..033C    ; 220 # Mn   [4] COMBINING RIGHT HALF RING BELOW..COMBINING SEAGULL BELOW
+0347..0349    ; 220 # Mn   [3] COMBINING EQUALS SIGN BELOW..COMBINING LEFT ANGLE BELOW
+034D..034E    ; 220 # Mn   [2] COMBINING LEFT RIGHT ARROW BELOW..COMBINING UPWARDS ARROW BELOW
+0353..0356    ; 220 # Mn   [4] COMBINING X BELOW..COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW
+0359..035A    ; 220 # Mn   [2] COMBINING ASTERISK BELOW..COMBINING DOUBLE RING BELOW
+0591          ; 220 # Mn       HEBREW ACCENT ETNAHTA
+0596          ; 220 # Mn       HEBREW ACCENT TIPEHA
+059B          ; 220 # Mn       HEBREW ACCENT TEVIR
+05A2..05A7    ; 220 # Mn   [6] HEBREW ACCENT ATNAH HAFUKH..HEBREW ACCENT DARGA
+05AA          ; 220 # Mn       HEBREW ACCENT YERAH BEN YOMO
+05C5          ; 220 # Mn       HEBREW MARK LOWER DOT
+0655..0656    ; 220 # Mn   [2] ARABIC HAMZA BELOW..ARABIC SUBSCRIPT ALEF
+065C          ; 220 # Mn       ARABIC VOWEL SIGN DOT BELOW
+06E3          ; 220 # Mn       ARABIC SMALL LOW SEEN
+06EA          ; 220 # Mn       ARABIC EMPTY CENTRE LOW STOP
+06ED          ; 220 # Mn       ARABIC SMALL LOW MEEM
+0731          ; 220 # Mn       SYRIAC PTHAHA BELOW
+0734          ; 220 # Mn       SYRIAC ZQAPHA BELOW
+0737..0739    ; 220 # Mn   [3] SYRIAC RBASA BELOW..SYRIAC DOTTED ZLAMA ANGULAR
+073B..073C    ; 220 # Mn   [2] SYRIAC HBASA BELOW..SYRIAC HBASA-ESASA DOTTED
+073E          ; 220 # Mn       SYRIAC ESASA BELOW
+0742          ; 220 # Mn       SYRIAC RUKKAKHA
+0744          ; 220 # Mn       SYRIAC TWO VERTICAL DOTS BELOW
+0746          ; 220 # Mn       SYRIAC THREE DOTS BELOW
+0748          ; 220 # Mn       SYRIAC OBLIQUE LINE BELOW
+07F2          ; 220 # Mn       NKO COMBINING NASALIZATION MARK
+0952          ; 220 # Mn       DEVANAGARI STRESS SIGN ANUDATTA
+0F18..0F19    ; 220 # Mn   [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
+0F35          ; 220 # Mn       TIBETAN MARK NGAS BZUNG NYI ZLA
+0F37          ; 220 # Mn       TIBETAN MARK NGAS BZUNG SGOR RTAGS
+0FC6          ; 220 # Mn       TIBETAN SYMBOL PADMA GDAN
+108D          ; 220 # Mn       MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
+193B          ; 220 # Mn       LIMBU SIGN SA-I
+1A18          ; 220 # Mn       BUGINESE VOWEL SIGN U
+1B6C          ; 220 # Mn       BALINESE MUSICAL SYMBOL COMBINING ENDEP
+1DC2          ; 220 # Mn       COMBINING SNAKE BELOW
+1DCA          ; 220 # Mn       COMBINING LATIN SMALL LETTER R BELOW
+1DCF          ; 220 # Mn       COMBINING ZIGZAG BELOW
+1DFF          ; 220 # Mn       COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW
+20E8          ; 220 # Mn       COMBINING TRIPLE UNDERDOT
+20EC..20EF    ; 220 # Mn   [4] COMBINING RIGHTWARDS HARPOON WITH BARB DOWNWARDS..COMBINING RIGHT ARROW BELOW
+A92B..A92D    ; 220 # Mn   [3] KAYAH LI TONE PLOPHU..KAYAH LI TONE CALYA PLOPHU
+101FD         ; 220 # Mn       PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE
+10A0D         ; 220 # Mn       KHAROSHTHI SIGN DOUBLE RING BELOW
+10A3A         ; 220 # Mn       KHAROSHTHI SIGN DOT BELOW
+1D17B..1D182  ; 220 # Mn   [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
+1D18A..1D18B  ; 220 # Mn   [2] MUSICAL SYMBOL COMBINING DOUBLE TONGUE..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
+
+# Total code points: 104
+
+# ================================================
+
+# Canonical_Combining_Class=Below_Right
+
+059A          ; 222 # Mn       HEBREW ACCENT YETIV
+05AD          ; 222 # Mn       HEBREW ACCENT DEHI
+1939          ; 222 # Mn       LIMBU SIGN MUKPHRENG
+302D          ; 222 # Mn       IDEOGRAPHIC ENTERING TONE MARK
+
+# Total code points: 4
+
+# ================================================
+
+# Canonical_Combining_Class=Left
+
+302E..302F    ; 224 # Mn   [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK
+
+# Total code points: 2
+
+# ================================================
+
+# Canonical_Combining_Class=Right
+
+1D16D         ; 226 # Mc       MUSICAL SYMBOL COMBINING AUGMENTATION DOT
+
+# Total code points: 1
+
+# ================================================
+
+# Canonical_Combining_Class=Above_Left
+
+05AE          ; 228 # Mn       HEBREW ACCENT ZINOR
+18A9          ; 228 # Mn       MONGOLIAN LETTER ALI GALI DAGALGA
+302B          ; 228 # Mn       IDEOGRAPHIC RISING TONE MARK
+
+# Total code points: 3
+
+# ================================================
+
+# Canonical_Combining_Class=Above
+
+0300..0314    ; 230 # Mn  [21] COMBINING GRAVE ACCENT..COMBINING REVERSED COMMA ABOVE
+033D..0344    ; 230 # Mn   [8] COMBINING X ABOVE..COMBINING GREEK DIALYTIKA TONOS
+0346          ; 230 # Mn       COMBINING BRIDGE ABOVE
+034A..034C    ; 230 # Mn   [3] COMBINING NOT TILDE ABOVE..COMBINING ALMOST EQUAL TO ABOVE
+0350..0352    ; 230 # Mn   [3] COMBINING RIGHT ARROWHEAD ABOVE..COMBINING FERMATA
+0357          ; 230 # Mn       COMBINING RIGHT HALF RING ABOVE
+035B          ; 230 # Mn       COMBINING ZIGZAG ABOVE
+0363..036F    ; 230 # Mn  [13] COMBINING LATIN SMALL LETTER A..COMBINING LATIN SMALL LETTER X
+0483..0487    ; 230 # Mn   [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE
+0592..0595    ; 230 # Mn   [4] HEBREW ACCENT SEGOL..HEBREW ACCENT ZAQEF GADOL
+0597..0599    ; 230 # Mn   [3] HEBREW ACCENT REVIA..HEBREW ACCENT PASHTA
+059C..05A1    ; 230 # Mn   [6] HEBREW ACCENT GERESH..HEBREW ACCENT PAZER
+05A8..05A9    ; 230 # Mn   [2] HEBREW ACCENT QADMA..HEBREW ACCENT TELISHA QETANA
+05AB..05AC    ; 230 # Mn   [2] HEBREW ACCENT OLE..HEBREW ACCENT ILUY
+05AF          ; 230 # Mn       HEBREW MARK MASORA CIRCLE
+05C4          ; 230 # Mn       HEBREW MARK UPPER DOT
+0610..0617    ; 230 # Mn   [8] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL HIGH ZAIN
+0653..0654    ; 230 # Mn   [2] ARABIC MADDAH ABOVE..ARABIC HAMZA ABOVE
+0657..065B    ; 230 # Mn   [5] ARABIC INVERTED DAMMA..ARABIC VOWEL SIGN INVERTED SMALL V ABOVE
+065D..065E    ; 230 # Mn   [2] ARABIC REVERSED DAMMA..ARABIC FATHA WITH TWO DOTS
+06D6..06DC    ; 230 # Mn   [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN
+06DF..06E2    ; 230 # Mn   [4] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MEEM ISOLATED FORM
+06E4          ; 230 # Mn       ARABIC SMALL HIGH MADDA
+06E7..06E8    ; 230 # Mn   [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON
+06EB..06EC    ; 230 # Mn   [2] ARABIC EMPTY CENTRE HIGH STOP..ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE
+0730          ; 230 # Mn       SYRIAC PTHAHA ABOVE
+0732..0733    ; 230 # Mn   [2] SYRIAC PTHAHA DOTTED..SYRIAC ZQAPHA ABOVE
+0735..0736    ; 230 # Mn   [2] SYRIAC ZQAPHA DOTTED..SYRIAC RBASA ABOVE
+073A          ; 230 # Mn       SYRIAC HBASA ABOVE
+073D          ; 230 # Mn       SYRIAC ESASA ABOVE
+073F..0741    ; 230 # Mn   [3] SYRIAC RWAHA..SYRIAC QUSHSHAYA
+0743          ; 230 # Mn       SYRIAC TWO VERTICAL DOTS ABOVE
+0745          ; 230 # Mn       SYRIAC THREE DOTS ABOVE
+0747          ; 230 # Mn       SYRIAC OBLIQUE LINE ABOVE
+0749..074A    ; 230 # Mn   [2] SYRIAC MUSIC..SYRIAC BARREKH
+07EB..07F1    ; 230 # Mn   [7] NKO COMBINING SHORT HIGH TONE..NKO COMBINING LONG RISING TONE
+07F3          ; 230 # Mn       NKO COMBINING DOUBLE DOT ABOVE
+0951          ; 230 # Mn       DEVANAGARI STRESS SIGN UDATTA
+0953..0954    ; 230 # Mn   [2] DEVANAGARI GRAVE ACCENT..DEVANAGARI ACUTE ACCENT
+0F82..0F83    ; 230 # Mn   [2] TIBETAN SIGN NYI ZLA NAA DA..TIBETAN SIGN SNA LDAN
+0F86..0F87    ; 230 # Mn   [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS
+135F          ; 230 # Mn       ETHIOPIC COMBINING GEMINATION MARK
+17DD          ; 230 # Mn       KHMER SIGN ATTHACAN
+193A          ; 230 # Mn       LIMBU SIGN KEMPHRENG
+1A17          ; 230 # Mn       BUGINESE VOWEL SIGN I
+1B6B          ; 230 # Mn       BALINESE MUSICAL SYMBOL COMBINING TEGEH
+1B6D..1B73    ; 230 # Mn   [7] BALINESE MUSICAL SYMBOL COMBINING KEMPUL..BALINESE MUSICAL SYMBOL COMBINING GONG
+1DC0..1DC1    ; 230 # Mn   [2] COMBINING DOTTED GRAVE ACCENT..COMBINING DOTTED ACUTE ACCENT
+1DC3..1DC9    ; 230 # Mn   [7] COMBINING SUSPENSION MARK..COMBINING ACUTE-GRAVE-ACUTE
+1DCB..1DCC    ; 230 # Mn   [2] COMBINING BREVE-MACRON..COMBINING MACRON-BREVE
+1DD1..1DE6    ; 230 # Mn  [22] COMBINING UR ABOVE..COMBINING LATIN SMALL LETTER Z
+1DFE          ; 230 # Mn       COMBINING LEFT ARROWHEAD ABOVE
+20D0..20D1    ; 230 # Mn   [2] COMBINING LEFT HARPOON ABOVE..COMBINING RIGHT HARPOON ABOVE
+20D4..20D7    ; 230 # Mn   [4] COMBINING ANTICLOCKWISE ARROW ABOVE..COMBINING RIGHT ARROW ABOVE
+20DB..20DC    ; 230 # Mn   [2] COMBINING THREE DOTS ABOVE..COMBINING FOUR DOTS ABOVE
+20E1          ; 230 # Mn       COMBINING LEFT RIGHT ARROW ABOVE
+20E7          ; 230 # Mn       COMBINING ANNUITY SYMBOL
+20E9          ; 230 # Mn       COMBINING WIDE BRIDGE ABOVE
+20F0          ; 230 # Mn       COMBINING ASTERISK ABOVE
+2DE0..2DFF    ; 230 # Mn  [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS
+A66F          ; 230 # Mn       COMBINING CYRILLIC VZMET
+A67C..A67D    ; 230 # Mn   [2] COMBINING CYRILLIC KAVYKA..COMBINING CYRILLIC PAYEROK
+FE20..FE26    ; 230 # Mn   [7] COMBINING LIGATURE LEFT HALF..COMBINING CONJOINING MACRON
+10A0F         ; 230 # Mn       KHAROSHTHI SIGN VISARGA
+10A38         ; 230 # Mn       KHAROSHTHI SIGN BAR ABOVE
+1D185..1D189  ; 230 # Mn   [5] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING BEND
+1D1AA..1D1AD  ; 230 # Mn   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
+1D242..1D244  ; 230 # Mn   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+
+# Total code points: 252
+
+# ================================================
+
+# Canonical_Combining_Class=Above_Right
+
+0315          ; 232 # Mn       COMBINING COMMA ABOVE RIGHT
+031A          ; 232 # Mn       COMBINING LEFT ANGLE ABOVE
+0358          ; 232 # Mn       COMBINING DOT ABOVE RIGHT
+302C          ; 232 # Mn       IDEOGRAPHIC DEPARTING TONE MARK
+
+# Total code points: 4
+
+# ================================================
+
+# Canonical_Combining_Class=Double_Below
+
+035C          ; 233 # Mn       COMBINING DOUBLE BREVE BELOW
+035F          ; 233 # Mn       COMBINING DOUBLE MACRON BELOW
+0362          ; 233 # Mn       COMBINING DOUBLE RIGHTWARDS ARROW BELOW
+
+# Total code points: 3
+
+# ================================================
+
+# Canonical_Combining_Class=Double_Above
+
+035D..035E    ; 234 # Mn   [2] COMBINING DOUBLE BREVE..COMBINING DOUBLE MACRON
+0360..0361    ; 234 # Mn   [2] COMBINING DOUBLE TILDE..COMBINING DOUBLE INVERTED BREVE
+1DCD          ; 234 # Mn       COMBINING DOUBLE CIRCUMFLEX ABOVE
+
+# Total code points: 5
+
+# ================================================
+
+# Canonical_Combining_Class=Iota_Subscript
+
+0345          ; 240 # Mn       COMBINING GREEK YPOGEGRAMMENI
+
+# Total code points: 1
+
+# EOF
diff --git a/third_party/harfbuzz/contrib/tables/DerivedGeneralCategory.txt b/third_party/harfbuzz/contrib/tables/DerivedGeneralCategory.txt
new file mode 100644
index 0000000..8423c70
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/DerivedGeneralCategory.txt
@@ -0,0 +1,3072 @@
+# DerivedGeneralCategory-5.1.0.txt
+# Date: 2008-03-20, 17:54:57 GMT [MD]
+#
+# Unicode Character Database
+# Copyright (c) 1991-2008 Unicode, Inc.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For documentation, see UCD.html
+
+# ================================================
+
+# Property:	General_Category
+
+# ================================================
+
+# General_Category=Unassigned
+
+0378..0379    ; Cn #   [2] <reserved-0378>..<reserved-0379>
+037F..0383    ; Cn #   [5] <reserved-037F>..<reserved-0383>
+038B          ; Cn #       <reserved-038B>
+038D          ; Cn #       <reserved-038D>
+03A2          ; Cn #       <reserved-03A2>
+0524..0530    ; Cn #  [13] <reserved-0524>..<reserved-0530>
+0557..0558    ; Cn #   [2] <reserved-0557>..<reserved-0558>
+0560          ; Cn #       <reserved-0560>
+0588          ; Cn #       <reserved-0588>
+058B..0590    ; Cn #   [6] <reserved-058B>..<reserved-0590>
+05C8..05CF    ; Cn #   [8] <reserved-05C8>..<reserved-05CF>
+05EB..05EF    ; Cn #   [5] <reserved-05EB>..<reserved-05EF>
+05F5..05FF    ; Cn #  [11] <reserved-05F5>..<reserved-05FF>
+0604..0605    ; Cn #   [2] <reserved-0604>..<reserved-0605>
+061C..061D    ; Cn #   [2] <reserved-061C>..<reserved-061D>
+0620          ; Cn #       <reserved-0620>
+065F          ; Cn #       <reserved-065F>
+070E          ; Cn #       <reserved-070E>
+074B..074C    ; Cn #   [2] <reserved-074B>..<reserved-074C>
+07B2..07BF    ; Cn #  [14] <reserved-07B2>..<reserved-07BF>
+07FB..0900    ; Cn # [262] <reserved-07FB>..<reserved-0900>
+093A..093B    ; Cn #   [2] <reserved-093A>..<reserved-093B>
+094E..094F    ; Cn #   [2] <reserved-094E>..<reserved-094F>
+0955..0957    ; Cn #   [3] <reserved-0955>..<reserved-0957>
+0973..097A    ; Cn #   [8] <reserved-0973>..<reserved-097A>
+0980          ; Cn #       <reserved-0980>
+0984          ; Cn #       <reserved-0984>
+098D..098E    ; Cn #   [2] <reserved-098D>..<reserved-098E>
+0991..0992    ; Cn #   [2] <reserved-0991>..<reserved-0992>
+09A9          ; Cn #       <reserved-09A9>
+09B1          ; Cn #       <reserved-09B1>
+09B3..09B5    ; Cn #   [3] <reserved-09B3>..<reserved-09B5>
+09BA..09BB    ; Cn #   [2] <reserved-09BA>..<reserved-09BB>
+09C5..09C6    ; Cn #   [2] <reserved-09C5>..<reserved-09C6>
+09C9..09CA    ; Cn #   [2] <reserved-09C9>..<reserved-09CA>
+09CF..09D6    ; Cn #   [8] <reserved-09CF>..<reserved-09D6>
+09D8..09DB    ; Cn #   [4] <reserved-09D8>..<reserved-09DB>
+09DE          ; Cn #       <reserved-09DE>
+09E4..09E5    ; Cn #   [2] <reserved-09E4>..<reserved-09E5>
+09FB..0A00    ; Cn #   [6] <reserved-09FB>..<reserved-0A00>
+0A04          ; Cn #       <reserved-0A04>
+0A0B..0A0E    ; Cn #   [4] <reserved-0A0B>..<reserved-0A0E>
+0A11..0A12    ; Cn #   [2] <reserved-0A11>..<reserved-0A12>
+0A29          ; Cn #       <reserved-0A29>
+0A31          ; Cn #       <reserved-0A31>
+0A34          ; Cn #       <reserved-0A34>
+0A37          ; Cn #       <reserved-0A37>
+0A3A..0A3B    ; Cn #   [2] <reserved-0A3A>..<reserved-0A3B>
+0A3D          ; Cn #       <reserved-0A3D>
+0A43..0A46    ; Cn #   [4] <reserved-0A43>..<reserved-0A46>
+0A49..0A4A    ; Cn #   [2] <reserved-0A49>..<reserved-0A4A>
+0A4E..0A50    ; Cn #   [3] <reserved-0A4E>..<reserved-0A50>
+0A52..0A58    ; Cn #   [7] <reserved-0A52>..<reserved-0A58>
+0A5D          ; Cn #       <reserved-0A5D>
+0A5F..0A65    ; Cn #   [7] <reserved-0A5F>..<reserved-0A65>
+0A76..0A80    ; Cn #  [11] <reserved-0A76>..<reserved-0A80>
+0A84          ; Cn #       <reserved-0A84>
+0A8E          ; Cn #       <reserved-0A8E>
+0A92          ; Cn #       <reserved-0A92>
+0AA9          ; Cn #       <reserved-0AA9>
+0AB1          ; Cn #       <reserved-0AB1>
+0AB4          ; Cn #       <reserved-0AB4>
+0ABA..0ABB    ; Cn #   [2] <reserved-0ABA>..<reserved-0ABB>
+0AC6          ; Cn #       <reserved-0AC6>
+0ACA          ; Cn #       <reserved-0ACA>
+0ACE..0ACF    ; Cn #   [2] <reserved-0ACE>..<reserved-0ACF>
+0AD1..0ADF    ; Cn #  [15] <reserved-0AD1>..<reserved-0ADF>
+0AE4..0AE5    ; Cn #   [2] <reserved-0AE4>..<reserved-0AE5>
+0AF0          ; Cn #       <reserved-0AF0>
+0AF2..0B00    ; Cn #  [15] <reserved-0AF2>..<reserved-0B00>
+0B04          ; Cn #       <reserved-0B04>
+0B0D..0B0E    ; Cn #   [2] <reserved-0B0D>..<reserved-0B0E>
+0B11..0B12    ; Cn #   [2] <reserved-0B11>..<reserved-0B12>
+0B29          ; Cn #       <reserved-0B29>
+0B31          ; Cn #       <reserved-0B31>
+0B34          ; Cn #       <reserved-0B34>
+0B3A..0B3B    ; Cn #   [2] <reserved-0B3A>..<reserved-0B3B>
+0B45..0B46    ; Cn #   [2] <reserved-0B45>..<reserved-0B46>
+0B49..0B4A    ; Cn #   [2] <reserved-0B49>..<reserved-0B4A>
+0B4E..0B55    ; Cn #   [8] <reserved-0B4E>..<reserved-0B55>
+0B58..0B5B    ; Cn #   [4] <reserved-0B58>..<reserved-0B5B>
+0B5E          ; Cn #       <reserved-0B5E>
+0B64..0B65    ; Cn #   [2] <reserved-0B64>..<reserved-0B65>
+0B72..0B81    ; Cn #  [16] <reserved-0B72>..<reserved-0B81>
+0B84          ; Cn #       <reserved-0B84>
+0B8B..0B8D    ; Cn #   [3] <reserved-0B8B>..<reserved-0B8D>
+0B91          ; Cn #       <reserved-0B91>
+0B96..0B98    ; Cn #   [3] <reserved-0B96>..<reserved-0B98>
+0B9B          ; Cn #       <reserved-0B9B>
+0B9D          ; Cn #       <reserved-0B9D>
+0BA0..0BA2    ; Cn #   [3] <reserved-0BA0>..<reserved-0BA2>
+0BA5..0BA7    ; Cn #   [3] <reserved-0BA5>..<reserved-0BA7>
+0BAB..0BAD    ; Cn #   [3] <reserved-0BAB>..<reserved-0BAD>
+0BBA..0BBD    ; Cn #   [4] <reserved-0BBA>..<reserved-0BBD>
+0BC3..0BC5    ; Cn #   [3] <reserved-0BC3>..<reserved-0BC5>
+0BC9          ; Cn #       <reserved-0BC9>
+0BCE..0BCF    ; Cn #   [2] <reserved-0BCE>..<reserved-0BCF>
+0BD1..0BD6    ; Cn #   [6] <reserved-0BD1>..<reserved-0BD6>
+0BD8..0BE5    ; Cn #  [14] <reserved-0BD8>..<reserved-0BE5>
+0BFB..0C00    ; Cn #   [6] <reserved-0BFB>..<reserved-0C00>
+0C04          ; Cn #       <reserved-0C04>
+0C0D          ; Cn #       <reserved-0C0D>
+0C11          ; Cn #       <reserved-0C11>
+0C29          ; Cn #       <reserved-0C29>
+0C34          ; Cn #       <reserved-0C34>
+0C3A..0C3C    ; Cn #   [3] <reserved-0C3A>..<reserved-0C3C>
+0C45          ; Cn #       <reserved-0C45>
+0C49          ; Cn #       <reserved-0C49>
+0C4E..0C54    ; Cn #   [7] <reserved-0C4E>..<reserved-0C54>
+0C57          ; Cn #       <reserved-0C57>
+0C5A..0C5F    ; Cn #   [6] <reserved-0C5A>..<reserved-0C5F>
+0C64..0C65    ; Cn #   [2] <reserved-0C64>..<reserved-0C65>
+0C70..0C77    ; Cn #   [8] <reserved-0C70>..<reserved-0C77>
+0C80..0C81    ; Cn #   [2] <reserved-0C80>..<reserved-0C81>
+0C84          ; Cn #       <reserved-0C84>
+0C8D          ; Cn #       <reserved-0C8D>
+0C91          ; Cn #       <reserved-0C91>
+0CA9          ; Cn #       <reserved-0CA9>
+0CB4          ; Cn #       <reserved-0CB4>
+0CBA..0CBB    ; Cn #   [2] <reserved-0CBA>..<reserved-0CBB>
+0CC5          ; Cn #       <reserved-0CC5>
+0CC9          ; Cn #       <reserved-0CC9>
+0CCE..0CD4    ; Cn #   [7] <reserved-0CCE>..<reserved-0CD4>
+0CD7..0CDD    ; Cn #   [7] <reserved-0CD7>..<reserved-0CDD>
+0CDF          ; Cn #       <reserved-0CDF>
+0CE4..0CE5    ; Cn #   [2] <reserved-0CE4>..<reserved-0CE5>
+0CF0          ; Cn #       <reserved-0CF0>
+0CF3..0D01    ; Cn #  [15] <reserved-0CF3>..<reserved-0D01>
+0D04          ; Cn #       <reserved-0D04>
+0D0D          ; Cn #       <reserved-0D0D>
+0D11          ; Cn #       <reserved-0D11>
+0D29          ; Cn #       <reserved-0D29>
+0D3A..0D3C    ; Cn #   [3] <reserved-0D3A>..<reserved-0D3C>
+0D45          ; Cn #       <reserved-0D45>
+0D49          ; Cn #       <reserved-0D49>
+0D4E..0D56    ; Cn #   [9] <reserved-0D4E>..<reserved-0D56>
+0D58..0D5F    ; Cn #   [8] <reserved-0D58>..<reserved-0D5F>
+0D64..0D65    ; Cn #   [2] <reserved-0D64>..<reserved-0D65>
+0D76..0D78    ; Cn #   [3] <reserved-0D76>..<reserved-0D78>
+0D80..0D81    ; Cn #   [2] <reserved-0D80>..<reserved-0D81>
+0D84          ; Cn #       <reserved-0D84>
+0D97..0D99    ; Cn #   [3] <reserved-0D97>..<reserved-0D99>
+0DB2          ; Cn #       <reserved-0DB2>
+0DBC          ; Cn #       <reserved-0DBC>
+0DBE..0DBF    ; Cn #   [2] <reserved-0DBE>..<reserved-0DBF>
+0DC7..0DC9    ; Cn #   [3] <reserved-0DC7>..<reserved-0DC9>
+0DCB..0DCE    ; Cn #   [4] <reserved-0DCB>..<reserved-0DCE>
+0DD5          ; Cn #       <reserved-0DD5>
+0DD7          ; Cn #       <reserved-0DD7>
+0DE0..0DF1    ; Cn #  [18] <reserved-0DE0>..<reserved-0DF1>
+0DF5..0E00    ; Cn #  [12] <reserved-0DF5>..<reserved-0E00>
+0E3B..0E3E    ; Cn #   [4] <reserved-0E3B>..<reserved-0E3E>
+0E5C..0E80    ; Cn #  [37] <reserved-0E5C>..<reserved-0E80>
+0E83          ; Cn #       <reserved-0E83>
+0E85..0E86    ; Cn #   [2] <reserved-0E85>..<reserved-0E86>
+0E89          ; Cn #       <reserved-0E89>
+0E8B..0E8C    ; Cn #   [2] <reserved-0E8B>..<reserved-0E8C>
+0E8E..0E93    ; Cn #   [6] <reserved-0E8E>..<reserved-0E93>
+0E98          ; Cn #       <reserved-0E98>
+0EA0          ; Cn #       <reserved-0EA0>
+0EA4          ; Cn #       <reserved-0EA4>
+0EA6          ; Cn #       <reserved-0EA6>
+0EA8..0EA9    ; Cn #   [2] <reserved-0EA8>..<reserved-0EA9>
+0EAC          ; Cn #       <reserved-0EAC>
+0EBA          ; Cn #       <reserved-0EBA>
+0EBE..0EBF    ; Cn #   [2] <reserved-0EBE>..<reserved-0EBF>
+0EC5          ; Cn #       <reserved-0EC5>
+0EC7          ; Cn #       <reserved-0EC7>
+0ECE..0ECF    ; Cn #   [2] <reserved-0ECE>..<reserved-0ECF>
+0EDA..0EDB    ; Cn #   [2] <reserved-0EDA>..<reserved-0EDB>
+0EDE..0EFF    ; Cn #  [34] <reserved-0EDE>..<reserved-0EFF>
+0F48          ; Cn #       <reserved-0F48>
+0F6D..0F70    ; Cn #   [4] <reserved-0F6D>..<reserved-0F70>
+0F8C..0F8F    ; Cn #   [4] <reserved-0F8C>..<reserved-0F8F>
+0F98          ; Cn #       <reserved-0F98>
+0FBD          ; Cn #       <reserved-0FBD>
+0FCD          ; Cn #       <reserved-0FCD>
+0FD5..0FFF    ; Cn #  [43] <reserved-0FD5>..<reserved-0FFF>
+109A..109D    ; Cn #   [4] <reserved-109A>..<reserved-109D>
+10C6..10CF    ; Cn #  [10] <reserved-10C6>..<reserved-10CF>
+10FD..10FF    ; Cn #   [3] <reserved-10FD>..<reserved-10FF>
+115A..115E    ; Cn #   [5] <reserved-115A>..<reserved-115E>
+11A3..11A7    ; Cn #   [5] <reserved-11A3>..<reserved-11A7>
+11FA..11FF    ; Cn #   [6] <reserved-11FA>..<reserved-11FF>
+1249          ; Cn #       <reserved-1249>
+124E..124F    ; Cn #   [2] <reserved-124E>..<reserved-124F>
+1257          ; Cn #       <reserved-1257>
+1259          ; Cn #       <reserved-1259>
+125E..125F    ; Cn #   [2] <reserved-125E>..<reserved-125F>
+1289          ; Cn #       <reserved-1289>
+128E..128F    ; Cn #   [2] <reserved-128E>..<reserved-128F>
+12B1          ; Cn #       <reserved-12B1>
+12B6..12B7    ; Cn #   [2] <reserved-12B6>..<reserved-12B7>
+12BF          ; Cn #       <reserved-12BF>
+12C1          ; Cn #       <reserved-12C1>
+12C6..12C7    ; Cn #   [2] <reserved-12C6>..<reserved-12C7>
+12D7          ; Cn #       <reserved-12D7>
+1311          ; Cn #       <reserved-1311>
+1316..1317    ; Cn #   [2] <reserved-1316>..<reserved-1317>
+135B..135E    ; Cn #   [4] <reserved-135B>..<reserved-135E>
+137D..137F    ; Cn #   [3] <reserved-137D>..<reserved-137F>
+139A..139F    ; Cn #   [6] <reserved-139A>..<reserved-139F>
+13F5..1400    ; Cn #  [12] <reserved-13F5>..<reserved-1400>
+1677..167F    ; Cn #   [9] <reserved-1677>..<reserved-167F>
+169D..169F    ; Cn #   [3] <reserved-169D>..<reserved-169F>
+16F1..16FF    ; Cn #  [15] <reserved-16F1>..<reserved-16FF>
+170D          ; Cn #       <reserved-170D>
+1715..171F    ; Cn #  [11] <reserved-1715>..<reserved-171F>
+1737..173F    ; Cn #   [9] <reserved-1737>..<reserved-173F>
+1754..175F    ; Cn #  [12] <reserved-1754>..<reserved-175F>
+176D          ; Cn #       <reserved-176D>
+1771          ; Cn #       <reserved-1771>
+1774..177F    ; Cn #  [12] <reserved-1774>..<reserved-177F>
+17DE..17DF    ; Cn #   [2] <reserved-17DE>..<reserved-17DF>
+17EA..17EF    ; Cn #   [6] <reserved-17EA>..<reserved-17EF>
+17FA..17FF    ; Cn #   [6] <reserved-17FA>..<reserved-17FF>
+180F          ; Cn #       <reserved-180F>
+181A..181F    ; Cn #   [6] <reserved-181A>..<reserved-181F>
+1878..187F    ; Cn #   [8] <reserved-1878>..<reserved-187F>
+18AB..18FF    ; Cn #  [85] <reserved-18AB>..<reserved-18FF>
+191D..191F    ; Cn #   [3] <reserved-191D>..<reserved-191F>
+192C..192F    ; Cn #   [4] <reserved-192C>..<reserved-192F>
+193C..193F    ; Cn #   [4] <reserved-193C>..<reserved-193F>
+1941..1943    ; Cn #   [3] <reserved-1941>..<reserved-1943>
+196E..196F    ; Cn #   [2] <reserved-196E>..<reserved-196F>
+1975..197F    ; Cn #  [11] <reserved-1975>..<reserved-197F>
+19AA..19AF    ; Cn #   [6] <reserved-19AA>..<reserved-19AF>
+19CA..19CF    ; Cn #   [6] <reserved-19CA>..<reserved-19CF>
+19DA..19DD    ; Cn #   [4] <reserved-19DA>..<reserved-19DD>
+1A1C..1A1D    ; Cn #   [2] <reserved-1A1C>..<reserved-1A1D>
+1A20..1AFF    ; Cn # [224] <reserved-1A20>..<reserved-1AFF>
+1B4C..1B4F    ; Cn #   [4] <reserved-1B4C>..<reserved-1B4F>
+1B7D..1B7F    ; Cn #   [3] <reserved-1B7D>..<reserved-1B7F>
+1BAB..1BAD    ; Cn #   [3] <reserved-1BAB>..<reserved-1BAD>
+1BBA..1BFF    ; Cn #  [70] <reserved-1BBA>..<reserved-1BFF>
+1C38..1C3A    ; Cn #   [3] <reserved-1C38>..<reserved-1C3A>
+1C4A..1C4C    ; Cn #   [3] <reserved-1C4A>..<reserved-1C4C>
+1C80..1CFF    ; Cn # [128] <reserved-1C80>..<reserved-1CFF>
+1DE7..1DFD    ; Cn #  [23] <reserved-1DE7>..<reserved-1DFD>
+1F16..1F17    ; Cn #   [2] <reserved-1F16>..<reserved-1F17>
+1F1E..1F1F    ; Cn #   [2] <reserved-1F1E>..<reserved-1F1F>
+1F46..1F47    ; Cn #   [2] <reserved-1F46>..<reserved-1F47>
+1F4E..1F4F    ; Cn #   [2] <reserved-1F4E>..<reserved-1F4F>
+1F58          ; Cn #       <reserved-1F58>
+1F5A          ; Cn #       <reserved-1F5A>
+1F5C          ; Cn #       <reserved-1F5C>
+1F5E          ; Cn #       <reserved-1F5E>
+1F7E..1F7F    ; Cn #   [2] <reserved-1F7E>..<reserved-1F7F>
+1FB5          ; Cn #       <reserved-1FB5>
+1FC5          ; Cn #       <reserved-1FC5>
+1FD4..1FD5    ; Cn #   [2] <reserved-1FD4>..<reserved-1FD5>
+1FDC          ; Cn #       <reserved-1FDC>
+1FF0..1FF1    ; Cn #   [2] <reserved-1FF0>..<reserved-1FF1>
+1FF5          ; Cn #       <reserved-1FF5>
+1FFF          ; Cn #       <reserved-1FFF>
+2065..2069    ; Cn #   [5] <reserved-2065>..<reserved-2069>
+2072..2073    ; Cn #   [2] <reserved-2072>..<reserved-2073>
+208F          ; Cn #       <reserved-208F>
+2095..209F    ; Cn #  [11] <reserved-2095>..<reserved-209F>
+20B6..20CF    ; Cn #  [26] <reserved-20B6>..<reserved-20CF>
+20F1..20FF    ; Cn #  [15] <reserved-20F1>..<reserved-20FF>
+2150..2152    ; Cn #   [3] <reserved-2150>..<reserved-2152>
+2189..218F    ; Cn #   [7] <reserved-2189>..<reserved-218F>
+23E8..23FF    ; Cn #  [24] <reserved-23E8>..<reserved-23FF>
+2427..243F    ; Cn #  [25] <reserved-2427>..<reserved-243F>
+244B..245F    ; Cn #  [21] <reserved-244B>..<reserved-245F>
+269E..269F    ; Cn #   [2] <reserved-269E>..<reserved-269F>
+26BD..26BF    ; Cn #   [3] <reserved-26BD>..<reserved-26BF>
+26C4..2700    ; Cn #  [61] <reserved-26C4>..<reserved-2700>
+2705          ; Cn #       <reserved-2705>
+270A..270B    ; Cn #   [2] <reserved-270A>..<reserved-270B>
+2728          ; Cn #       <reserved-2728>
+274C          ; Cn #       <reserved-274C>
+274E          ; Cn #       <reserved-274E>
+2753..2755    ; Cn #   [3] <reserved-2753>..<reserved-2755>
+2757          ; Cn #       <reserved-2757>
+275F..2760    ; Cn #   [2] <reserved-275F>..<reserved-2760>
+2795..2797    ; Cn #   [3] <reserved-2795>..<reserved-2797>
+27B0          ; Cn #       <reserved-27B0>
+27BF          ; Cn #       <reserved-27BF>
+27CB          ; Cn #       <reserved-27CB>
+27CD..27CF    ; Cn #   [3] <reserved-27CD>..<reserved-27CF>
+2B4D..2B4F    ; Cn #   [3] <reserved-2B4D>..<reserved-2B4F>
+2B55..2BFF    ; Cn # [171] <reserved-2B55>..<reserved-2BFF>
+2C2F          ; Cn #       <reserved-2C2F>
+2C5F          ; Cn #       <reserved-2C5F>
+2C70          ; Cn #       <reserved-2C70>
+2C7E..2C7F    ; Cn #   [2] <reserved-2C7E>..<reserved-2C7F>
+2CEB..2CF8    ; Cn #  [14] <reserved-2CEB>..<reserved-2CF8>
+2D26..2D2F    ; Cn #  [10] <reserved-2D26>..<reserved-2D2F>
+2D66..2D6E    ; Cn #   [9] <reserved-2D66>..<reserved-2D6E>
+2D70..2D7F    ; Cn #  [16] <reserved-2D70>..<reserved-2D7F>
+2D97..2D9F    ; Cn #   [9] <reserved-2D97>..<reserved-2D9F>
+2DA7          ; Cn #       <reserved-2DA7>
+2DAF          ; Cn #       <reserved-2DAF>
+2DB7          ; Cn #       <reserved-2DB7>
+2DBF          ; Cn #       <reserved-2DBF>
+2DC7          ; Cn #       <reserved-2DC7>
+2DCF          ; Cn #       <reserved-2DCF>
+2DD7          ; Cn #       <reserved-2DD7>
+2DDF          ; Cn #       <reserved-2DDF>
+2E31..2E7F    ; Cn #  [79] <reserved-2E31>..<reserved-2E7F>
+2E9A          ; Cn #       <reserved-2E9A>
+2EF4..2EFF    ; Cn #  [12] <reserved-2EF4>..<reserved-2EFF>
+2FD6..2FEF    ; Cn #  [26] <reserved-2FD6>..<reserved-2FEF>
+2FFC..2FFF    ; Cn #   [4] <reserved-2FFC>..<reserved-2FFF>
+3040          ; Cn #       <reserved-3040>
+3097..3098    ; Cn #   [2] <reserved-3097>..<reserved-3098>
+3100..3104    ; Cn #   [5] <reserved-3100>..<reserved-3104>
+312E..3130    ; Cn #   [3] <reserved-312E>..<reserved-3130>
+318F          ; Cn #       <reserved-318F>
+31B8..31BF    ; Cn #   [8] <reserved-31B8>..<reserved-31BF>
+31E4..31EF    ; Cn #  [12] <reserved-31E4>..<reserved-31EF>
+321F          ; Cn #       <reserved-321F>
+3244..324F    ; Cn #  [12] <reserved-3244>..<reserved-324F>
+32FF          ; Cn #       <reserved-32FF>
+4DB6..4DBF    ; Cn #  [10] <reserved-4DB6>..<reserved-4DBF>
+9FC4..9FFF    ; Cn #  [60] <reserved-9FC4>..<reserved-9FFF>
+A48D..A48F    ; Cn #   [3] <reserved-A48D>..<reserved-A48F>
+A4C7..A4FF    ; Cn #  [57] <reserved-A4C7>..<reserved-A4FF>
+A62C..A63F    ; Cn #  [20] <reserved-A62C>..<reserved-A63F>
+A660..A661    ; Cn #   [2] <reserved-A660>..<reserved-A661>
+A674..A67B    ; Cn #   [8] <reserved-A674>..<reserved-A67B>
+A698..A6FF    ; Cn # [104] <reserved-A698>..<reserved-A6FF>
+A78D..A7FA    ; Cn # [110] <reserved-A78D>..<reserved-A7FA>
+A82C..A83F    ; Cn #  [20] <reserved-A82C>..<reserved-A83F>
+A878..A87F    ; Cn #   [8] <reserved-A878>..<reserved-A87F>
+A8C5..A8CD    ; Cn #   [9] <reserved-A8C5>..<reserved-A8CD>
+A8DA..A8FF    ; Cn #  [38] <reserved-A8DA>..<reserved-A8FF>
+A954..A95E    ; Cn #  [11] <reserved-A954>..<reserved-A95E>
+A960..A9FF    ; Cn # [160] <reserved-A960>..<reserved-A9FF>
+AA37..AA3F    ; Cn #   [9] <reserved-AA37>..<reserved-AA3F>
+AA4E..AA4F    ; Cn #   [2] <reserved-AA4E>..<reserved-AA4F>
+AA5A..AA5B    ; Cn #   [2] <reserved-AA5A>..<reserved-AA5B>
+AA60..ABFF    ; Cn # [416] <reserved-AA60>..<reserved-ABFF>
+D7A4..D7FF    ; Cn #  [92] <reserved-D7A4>..<reserved-D7FF>
+FA2E..FA2F    ; Cn #   [2] <reserved-FA2E>..<reserved-FA2F>
+FA6B..FA6F    ; Cn #   [5] <reserved-FA6B>..<reserved-FA6F>
+FADA..FAFF    ; Cn #  [38] <reserved-FADA>..<reserved-FAFF>
+FB07..FB12    ; Cn #  [12] <reserved-FB07>..<reserved-FB12>
+FB18..FB1C    ; Cn #   [5] <reserved-FB18>..<reserved-FB1C>
+FB37          ; Cn #       <reserved-FB37>
+FB3D          ; Cn #       <reserved-FB3D>
+FB3F          ; Cn #       <reserved-FB3F>
+FB42          ; Cn #       <reserved-FB42>
+FB45          ; Cn #       <reserved-FB45>
+FBB2..FBD2    ; Cn #  [33] <reserved-FBB2>..<reserved-FBD2>
+FD40..FD4F    ; Cn #  [16] <reserved-FD40>..<reserved-FD4F>
+FD90..FD91    ; Cn #   [2] <reserved-FD90>..<reserved-FD91>
+FDC8..FDEF    ; Cn #  [40] <reserved-FDC8>..<noncharacter-FDEF>
+FDFE..FDFF    ; Cn #   [2] <reserved-FDFE>..<reserved-FDFF>
+FE1A..FE1F    ; Cn #   [6] <reserved-FE1A>..<reserved-FE1F>
+FE27..FE2F    ; Cn #   [9] <reserved-FE27>..<reserved-FE2F>
+FE53          ; Cn #       <reserved-FE53>
+FE67          ; Cn #       <reserved-FE67>
+FE6C..FE6F    ; Cn #   [4] <reserved-FE6C>..<reserved-FE6F>
+FE75          ; Cn #       <reserved-FE75>
+FEFD..FEFE    ; Cn #   [2] <reserved-FEFD>..<reserved-FEFE>
+FF00          ; Cn #       <reserved-FF00>
+FFBF..FFC1    ; Cn #   [3] <reserved-FFBF>..<reserved-FFC1>
+FFC8..FFC9    ; Cn #   [2] <reserved-FFC8>..<reserved-FFC9>
+FFD0..FFD1    ; Cn #   [2] <reserved-FFD0>..<reserved-FFD1>
+FFD8..FFD9    ; Cn #   [2] <reserved-FFD8>..<reserved-FFD9>
+FFDD..FFDF    ; Cn #   [3] <reserved-FFDD>..<reserved-FFDF>
+FFE7          ; Cn #       <reserved-FFE7>
+FFEF..FFF8    ; Cn #  [10] <reserved-FFEF>..<reserved-FFF8>
+FFFE..FFFF    ; Cn #   [2] <noncharacter-FFFE>..<noncharacter-FFFF>
+1000C         ; Cn #       <reserved-1000C>
+10027         ; Cn #       <reserved-10027>
+1003B         ; Cn #       <reserved-1003B>
+1003E         ; Cn #       <reserved-1003E>
+1004E..1004F  ; Cn #   [2] <reserved-1004E>..<reserved-1004F>
+1005E..1007F  ; Cn #  [34] <reserved-1005E>..<reserved-1007F>
+100FB..100FF  ; Cn #   [5] <reserved-100FB>..<reserved-100FF>
+10103..10106  ; Cn #   [4] <reserved-10103>..<reserved-10106>
+10134..10136  ; Cn #   [3] <reserved-10134>..<reserved-10136>
+1018B..1018F  ; Cn #   [5] <reserved-1018B>..<reserved-1018F>
+1019C..101CF  ; Cn #  [52] <reserved-1019C>..<reserved-101CF>
+101FE..1027F  ; Cn # [130] <reserved-101FE>..<reserved-1027F>
+1029D..1029F  ; Cn #   [3] <reserved-1029D>..<reserved-1029F>
+102D1..102FF  ; Cn #  [47] <reserved-102D1>..<reserved-102FF>
+1031F         ; Cn #       <reserved-1031F>
+10324..1032F  ; Cn #  [12] <reserved-10324>..<reserved-1032F>
+1034B..1037F  ; Cn #  [53] <reserved-1034B>..<reserved-1037F>
+1039E         ; Cn #       <reserved-1039E>
+103C4..103C7  ; Cn #   [4] <reserved-103C4>..<reserved-103C7>
+103D6..103FF  ; Cn #  [42] <reserved-103D6>..<reserved-103FF>
+1049E..1049F  ; Cn #   [2] <reserved-1049E>..<reserved-1049F>
+104AA..107FF  ; Cn # [854] <reserved-104AA>..<reserved-107FF>
+10806..10807  ; Cn #   [2] <reserved-10806>..<reserved-10807>
+10809         ; Cn #       <reserved-10809>
+10836         ; Cn #       <reserved-10836>
+10839..1083B  ; Cn #   [3] <reserved-10839>..<reserved-1083B>
+1083D..1083E  ; Cn #   [2] <reserved-1083D>..<reserved-1083E>
+10840..108FF  ; Cn # [192] <reserved-10840>..<reserved-108FF>
+1091A..1091E  ; Cn #   [5] <reserved-1091A>..<reserved-1091E>
+1093A..1093E  ; Cn #   [5] <reserved-1093A>..<reserved-1093E>
+10940..109FF  ; Cn # [192] <reserved-10940>..<reserved-109FF>
+10A04         ; Cn #       <reserved-10A04>
+10A07..10A0B  ; Cn #   [5] <reserved-10A07>..<reserved-10A0B>
+10A14         ; Cn #       <reserved-10A14>
+10A18         ; Cn #       <reserved-10A18>
+10A34..10A37  ; Cn #   [4] <reserved-10A34>..<reserved-10A37>
+10A3B..10A3E  ; Cn #   [4] <reserved-10A3B>..<reserved-10A3E>
+10A48..10A4F  ; Cn #   [8] <reserved-10A48>..<reserved-10A4F>
+10A59..11FFF  ; Cn # [5543] <reserved-10A59>..<reserved-11FFF>
+1236F..123FF  ; Cn # [145] <reserved-1236F>..<reserved-123FF>
+12463..1246F  ; Cn #  [13] <reserved-12463>..<reserved-1246F>
+12474..1CFFF  ; Cn # [43916] <reserved-12474>..<reserved-1CFFF>
+1D0F6..1D0FF  ; Cn #  [10] <reserved-1D0F6>..<reserved-1D0FF>
+1D127..1D128  ; Cn #   [2] <reserved-1D127>..<reserved-1D128>
+1D1DE..1D1FF  ; Cn #  [34] <reserved-1D1DE>..<reserved-1D1FF>
+1D246..1D2FF  ; Cn # [186] <reserved-1D246>..<reserved-1D2FF>
+1D357..1D35F  ; Cn #   [9] <reserved-1D357>..<reserved-1D35F>
+1D372..1D3FF  ; Cn # [142] <reserved-1D372>..<reserved-1D3FF>
+1D455         ; Cn #       <reserved-1D455>
+1D49D         ; Cn #       <reserved-1D49D>
+1D4A0..1D4A1  ; Cn #   [2] <reserved-1D4A0>..<reserved-1D4A1>
+1D4A3..1D4A4  ; Cn #   [2] <reserved-1D4A3>..<reserved-1D4A4>
+1D4A7..1D4A8  ; Cn #   [2] <reserved-1D4A7>..<reserved-1D4A8>
+1D4AD         ; Cn #       <reserved-1D4AD>
+1D4BA         ; Cn #       <reserved-1D4BA>
+1D4BC         ; Cn #       <reserved-1D4BC>
+1D4C4         ; Cn #       <reserved-1D4C4>
+1D506         ; Cn #       <reserved-1D506>
+1D50B..1D50C  ; Cn #   [2] <reserved-1D50B>..<reserved-1D50C>
+1D515         ; Cn #       <reserved-1D515>
+1D51D         ; Cn #       <reserved-1D51D>
+1D53A         ; Cn #       <reserved-1D53A>
+1D53F         ; Cn #       <reserved-1D53F>
+1D545         ; Cn #       <reserved-1D545>
+1D547..1D549  ; Cn #   [3] <reserved-1D547>..<reserved-1D549>
+1D551         ; Cn #       <reserved-1D551>
+1D6A6..1D6A7  ; Cn #   [2] <reserved-1D6A6>..<reserved-1D6A7>
+1D7CC..1D7CD  ; Cn #   [2] <reserved-1D7CC>..<reserved-1D7CD>
+1D800..1EFFF  ; Cn # [6144] <reserved-1D800>..<reserved-1EFFF>
+1F02C..1F02F  ; Cn #   [4] <reserved-1F02C>..<reserved-1F02F>
+1F094..1FFFF  ; Cn # [3948] <reserved-1F094>..<noncharacter-1FFFF>
+2A6D7..2F7FF  ; Cn # [20777] <reserved-2A6D7>..<reserved-2F7FF>
+2FA1E..E0000  ; Cn # [722403] <reserved-2FA1E>..<reserved-E0000>
+E0002..E001F  ; Cn #  [30] <reserved-E0002>..<reserved-E001F>
+E0080..E00FF  ; Cn # [128] <reserved-E0080>..<reserved-E00FF>
+E01F0..EFFFF  ; Cn # [65040] <reserved-E01F0>..<noncharacter-EFFFF>
+FFFFE..FFFFF  ; Cn #   [2] <noncharacter-FFFFE>..<noncharacter-FFFFF>
+10FFFE..10FFFF; Cn #   [2] <noncharacter-10FFFE>..<noncharacter-10FFFF>
+
+# Total code points: 873883
+
+# ================================================
+
+# General_Category=Uppercase_Letter
+
+0041..005A    ; Lu #  [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
+00C0..00D6    ; Lu #  [23] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER O WITH DIAERESIS
+00D8..00DE    ; Lu #   [7] LATIN CAPITAL LETTER O WITH STROKE..LATIN CAPITAL LETTER THORN
+0100          ; Lu #       LATIN CAPITAL LETTER A WITH MACRON
+0102          ; Lu #       LATIN CAPITAL LETTER A WITH BREVE
+0104          ; Lu #       LATIN CAPITAL LETTER A WITH OGONEK
+0106          ; Lu #       LATIN CAPITAL LETTER C WITH ACUTE
+0108          ; Lu #       LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+010A          ; Lu #       LATIN CAPITAL LETTER C WITH DOT ABOVE
+010C          ; Lu #       LATIN CAPITAL LETTER C WITH CARON
+010E          ; Lu #       LATIN CAPITAL LETTER D WITH CARON
+0110          ; Lu #       LATIN CAPITAL LETTER D WITH STROKE
+0112          ; Lu #       LATIN CAPITAL LETTER E WITH MACRON
+0114          ; Lu #       LATIN CAPITAL LETTER E WITH BREVE
+0116          ; Lu #       LATIN CAPITAL LETTER E WITH DOT ABOVE
+0118          ; Lu #       LATIN CAPITAL LETTER E WITH OGONEK
+011A          ; Lu #       LATIN CAPITAL LETTER E WITH CARON
+011C          ; Lu #       LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+011E          ; Lu #       LATIN CAPITAL LETTER G WITH BREVE
+0120          ; Lu #       LATIN CAPITAL LETTER G WITH DOT ABOVE
+0122          ; Lu #       LATIN CAPITAL LETTER G WITH CEDILLA
+0124          ; Lu #       LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+0126          ; Lu #       LATIN CAPITAL LETTER H WITH STROKE
+0128          ; Lu #       LATIN CAPITAL LETTER I WITH TILDE
+012A          ; Lu #       LATIN CAPITAL LETTER I WITH MACRON
+012C          ; Lu #       LATIN CAPITAL LETTER I WITH BREVE
+012E          ; Lu #       LATIN CAPITAL LETTER I WITH OGONEK
+0130          ; Lu #       LATIN CAPITAL LETTER I WITH DOT ABOVE
+0132          ; Lu #       LATIN CAPITAL LIGATURE IJ
+0134          ; Lu #       LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+0136          ; Lu #       LATIN CAPITAL LETTER K WITH CEDILLA
+0139          ; Lu #       LATIN CAPITAL LETTER L WITH ACUTE
+013B          ; Lu #       LATIN CAPITAL LETTER L WITH CEDILLA
+013D          ; Lu #       LATIN CAPITAL LETTER L WITH CARON
+013F          ; Lu #       LATIN CAPITAL LETTER L WITH MIDDLE DOT
+0141          ; Lu #       LATIN CAPITAL LETTER L WITH STROKE
+0143          ; Lu #       LATIN CAPITAL LETTER N WITH ACUTE
+0145          ; Lu #       LATIN CAPITAL LETTER N WITH CEDILLA
+0147          ; Lu #       LATIN CAPITAL LETTER N WITH CARON
+014A          ; Lu #       LATIN CAPITAL LETTER ENG
+014C          ; Lu #       LATIN CAPITAL LETTER O WITH MACRON
+014E          ; Lu #       LATIN CAPITAL LETTER O WITH BREVE
+0150          ; Lu #       LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0152          ; Lu #       LATIN CAPITAL LIGATURE OE
+0154          ; Lu #       LATIN CAPITAL LETTER R WITH ACUTE
+0156          ; Lu #       LATIN CAPITAL LETTER R WITH CEDILLA
+0158          ; Lu #       LATIN CAPITAL LETTER R WITH CARON
+015A          ; Lu #       LATIN CAPITAL LETTER S WITH ACUTE
+015C          ; Lu #       LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+015E          ; Lu #       LATIN CAPITAL LETTER S WITH CEDILLA
+0160          ; Lu #       LATIN CAPITAL LETTER S WITH CARON
+0162          ; Lu #       LATIN CAPITAL LETTER T WITH CEDILLA
+0164          ; Lu #       LATIN CAPITAL LETTER T WITH CARON
+0166          ; Lu #       LATIN CAPITAL LETTER T WITH STROKE
+0168          ; Lu #       LATIN CAPITAL LETTER U WITH TILDE
+016A          ; Lu #       LATIN CAPITAL LETTER U WITH MACRON
+016C          ; Lu #       LATIN CAPITAL LETTER U WITH BREVE
+016E          ; Lu #       LATIN CAPITAL LETTER U WITH RING ABOVE
+0170          ; Lu #       LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0172          ; Lu #       LATIN CAPITAL LETTER U WITH OGONEK
+0174          ; Lu #       LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+0176          ; Lu #       LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0178..0179    ; Lu #   [2] LATIN CAPITAL LETTER Y WITH DIAERESIS..LATIN CAPITAL LETTER Z WITH ACUTE
+017B          ; Lu #       LATIN CAPITAL LETTER Z WITH DOT ABOVE
+017D          ; Lu #       LATIN CAPITAL LETTER Z WITH CARON
+0181..0182    ; Lu #   [2] LATIN CAPITAL LETTER B WITH HOOK..LATIN CAPITAL LETTER B WITH TOPBAR
+0184          ; Lu #       LATIN CAPITAL LETTER TONE SIX
+0186..0187    ; Lu #   [2] LATIN CAPITAL LETTER OPEN O..LATIN CAPITAL LETTER C WITH HOOK
+0189..018B    ; Lu #   [3] LATIN CAPITAL LETTER AFRICAN D..LATIN CAPITAL LETTER D WITH TOPBAR
+018E..0191    ; Lu #   [4] LATIN CAPITAL LETTER REVERSED E..LATIN CAPITAL LETTER F WITH HOOK
+0193..0194    ; Lu #   [2] LATIN CAPITAL LETTER G WITH HOOK..LATIN CAPITAL LETTER GAMMA
+0196..0198    ; Lu #   [3] LATIN CAPITAL LETTER IOTA..LATIN CAPITAL LETTER K WITH HOOK
+019C..019D    ; Lu #   [2] LATIN CAPITAL LETTER TURNED M..LATIN CAPITAL LETTER N WITH LEFT HOOK
+019F..01A0    ; Lu #   [2] LATIN CAPITAL LETTER O WITH MIDDLE TILDE..LATIN CAPITAL LETTER O WITH HORN
+01A2          ; Lu #       LATIN CAPITAL LETTER OI
+01A4          ; Lu #       LATIN CAPITAL LETTER P WITH HOOK
+01A6..01A7    ; Lu #   [2] LATIN LETTER YR..LATIN CAPITAL LETTER TONE TWO
+01A9          ; Lu #       LATIN CAPITAL LETTER ESH
+01AC          ; Lu #       LATIN CAPITAL LETTER T WITH HOOK
+01AE..01AF    ; Lu #   [2] LATIN CAPITAL LETTER T WITH RETROFLEX HOOK..LATIN CAPITAL LETTER U WITH HORN
+01B1..01B3    ; Lu #   [3] LATIN CAPITAL LETTER UPSILON..LATIN CAPITAL LETTER Y WITH HOOK
+01B5          ; Lu #       LATIN CAPITAL LETTER Z WITH STROKE
+01B7..01B8    ; Lu #   [2] LATIN CAPITAL LETTER EZH..LATIN CAPITAL LETTER EZH REVERSED
+01BC          ; Lu #       LATIN CAPITAL LETTER TONE FIVE
+01C4          ; Lu #       LATIN CAPITAL LETTER DZ WITH CARON
+01C7          ; Lu #       LATIN CAPITAL LETTER LJ
+01CA          ; Lu #       LATIN CAPITAL LETTER NJ
+01CD          ; Lu #       LATIN CAPITAL LETTER A WITH CARON
+01CF          ; Lu #       LATIN CAPITAL LETTER I WITH CARON
+01D1          ; Lu #       LATIN CAPITAL LETTER O WITH CARON
+01D3          ; Lu #       LATIN CAPITAL LETTER U WITH CARON
+01D5          ; Lu #       LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON
+01D7          ; Lu #       LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE
+01D9          ; Lu #       LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON
+01DB          ; Lu #       LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE
+01DE          ; Lu #       LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON
+01E0          ; Lu #       LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON
+01E2          ; Lu #       LATIN CAPITAL LETTER AE WITH MACRON
+01E4          ; Lu #       LATIN CAPITAL LETTER G WITH STROKE
+01E6          ; Lu #       LATIN CAPITAL LETTER G WITH CARON
+01E8          ; Lu #       LATIN CAPITAL LETTER K WITH CARON
+01EA          ; Lu #       LATIN CAPITAL LETTER O WITH OGONEK
+01EC          ; Lu #       LATIN CAPITAL LETTER O WITH OGONEK AND MACRON
+01EE          ; Lu #       LATIN CAPITAL LETTER EZH WITH CARON
+01F1          ; Lu #       LATIN CAPITAL LETTER DZ
+01F4          ; Lu #       LATIN CAPITAL LETTER G WITH ACUTE
+01F6..01F8    ; Lu #   [3] LATIN CAPITAL LETTER HWAIR..LATIN CAPITAL LETTER N WITH GRAVE
+01FA          ; Lu #       LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+01FC          ; Lu #       LATIN CAPITAL LETTER AE WITH ACUTE
+01FE          ; Lu #       LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+0200          ; Lu #       LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
+0202          ; Lu #       LATIN CAPITAL LETTER A WITH INVERTED BREVE
+0204          ; Lu #       LATIN CAPITAL LETTER E WITH DOUBLE GRAVE
+0206          ; Lu #       LATIN CAPITAL LETTER E WITH INVERTED BREVE
+0208          ; Lu #       LATIN CAPITAL LETTER I WITH DOUBLE GRAVE
+020A          ; Lu #       LATIN CAPITAL LETTER I WITH INVERTED BREVE
+020C          ; Lu #       LATIN CAPITAL LETTER O WITH DOUBLE GRAVE
+020E          ; Lu #       LATIN CAPITAL LETTER O WITH INVERTED BREVE
+0210          ; Lu #       LATIN CAPITAL LETTER R WITH DOUBLE GRAVE
+0212          ; Lu #       LATIN CAPITAL LETTER R WITH INVERTED BREVE
+0214          ; Lu #       LATIN CAPITAL LETTER U WITH DOUBLE GRAVE
+0216          ; Lu #       LATIN CAPITAL LETTER U WITH INVERTED BREVE
+0218          ; Lu #       LATIN CAPITAL LETTER S WITH COMMA BELOW
+021A          ; Lu #       LATIN CAPITAL LETTER T WITH COMMA BELOW
+021C          ; Lu #       LATIN CAPITAL LETTER YOGH
+021E          ; Lu #       LATIN CAPITAL LETTER H WITH CARON
+0220          ; Lu #       LATIN CAPITAL LETTER N WITH LONG RIGHT LEG
+0222          ; Lu #       LATIN CAPITAL LETTER OU
+0224          ; Lu #       LATIN CAPITAL LETTER Z WITH HOOK
+0226          ; Lu #       LATIN CAPITAL LETTER A WITH DOT ABOVE
+0228          ; Lu #       LATIN CAPITAL LETTER E WITH CEDILLA
+022A          ; Lu #       LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON
+022C          ; Lu #       LATIN CAPITAL LETTER O WITH TILDE AND MACRON
+022E          ; Lu #       LATIN CAPITAL LETTER O WITH DOT ABOVE
+0230          ; Lu #       LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON
+0232          ; Lu #       LATIN CAPITAL LETTER Y WITH MACRON
+023A..023B    ; Lu #   [2] LATIN CAPITAL LETTER A WITH STROKE..LATIN CAPITAL LETTER C WITH STROKE
+023D..023E    ; Lu #   [2] LATIN CAPITAL LETTER L WITH BAR..LATIN CAPITAL LETTER T WITH DIAGONAL STROKE
+0241          ; Lu #       LATIN CAPITAL LETTER GLOTTAL STOP
+0243..0246    ; Lu #   [4] LATIN CAPITAL LETTER B WITH STROKE..LATIN CAPITAL LETTER E WITH STROKE
+0248          ; Lu #       LATIN CAPITAL LETTER J WITH STROKE
+024A          ; Lu #       LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL
+024C          ; Lu #       LATIN CAPITAL LETTER R WITH STROKE
+024E          ; Lu #       LATIN CAPITAL LETTER Y WITH STROKE
+0370          ; Lu #       GREEK CAPITAL LETTER HETA
+0372          ; Lu #       GREEK CAPITAL LETTER ARCHAIC SAMPI
+0376          ; Lu #       GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA
+0386          ; Lu #       GREEK CAPITAL LETTER ALPHA WITH TONOS
+0388..038A    ; Lu #   [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS
+038C          ; Lu #       GREEK CAPITAL LETTER OMICRON WITH TONOS
+038E..038F    ; Lu #   [2] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK CAPITAL LETTER OMEGA WITH TONOS
+0391..03A1    ; Lu #  [17] GREEK CAPITAL LETTER ALPHA..GREEK CAPITAL LETTER RHO
+03A3..03AB    ; Lu #   [9] GREEK CAPITAL LETTER SIGMA..GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+03CF          ; Lu #       GREEK CAPITAL KAI SYMBOL
+03D2..03D4    ; Lu #   [3] GREEK UPSILON WITH HOOK SYMBOL..GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL
+03D8          ; Lu #       GREEK LETTER ARCHAIC KOPPA
+03DA          ; Lu #       GREEK LETTER STIGMA
+03DC          ; Lu #       GREEK LETTER DIGAMMA
+03DE          ; Lu #       GREEK LETTER KOPPA
+03E0          ; Lu #       GREEK LETTER SAMPI
+03E2          ; Lu #       COPTIC CAPITAL LETTER SHEI
+03E4          ; Lu #       COPTIC CAPITAL LETTER FEI
+03E6          ; Lu #       COPTIC CAPITAL LETTER KHEI
+03E8          ; Lu #       COPTIC CAPITAL LETTER HORI
+03EA          ; Lu #       COPTIC CAPITAL LETTER GANGIA
+03EC          ; Lu #       COPTIC CAPITAL LETTER SHIMA
+03EE          ; Lu #       COPTIC CAPITAL LETTER DEI
+03F4          ; Lu #       GREEK CAPITAL THETA SYMBOL
+03F7          ; Lu #       GREEK CAPITAL LETTER SHO
+03F9..03FA    ; Lu #   [2] GREEK CAPITAL LUNATE SIGMA SYMBOL..GREEK CAPITAL LETTER SAN
+03FD..042F    ; Lu #  [51] GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL..CYRILLIC CAPITAL LETTER YA
+0460          ; Lu #       CYRILLIC CAPITAL LETTER OMEGA
+0462          ; Lu #       CYRILLIC CAPITAL LETTER YAT
+0464          ; Lu #       CYRILLIC CAPITAL LETTER IOTIFIED E
+0466          ; Lu #       CYRILLIC CAPITAL LETTER LITTLE YUS
+0468          ; Lu #       CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS
+046A          ; Lu #       CYRILLIC CAPITAL LETTER BIG YUS
+046C          ; Lu #       CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS
+046E          ; Lu #       CYRILLIC CAPITAL LETTER KSI
+0470          ; Lu #       CYRILLIC CAPITAL LETTER PSI
+0472          ; Lu #       CYRILLIC CAPITAL LETTER FITA
+0474          ; Lu #       CYRILLIC CAPITAL LETTER IZHITSA
+0476          ; Lu #       CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+0478          ; Lu #       CYRILLIC CAPITAL LETTER UK
+047A          ; Lu #       CYRILLIC CAPITAL LETTER ROUND OMEGA
+047C          ; Lu #       CYRILLIC CAPITAL LETTER OMEGA WITH TITLO
+047E          ; Lu #       CYRILLIC CAPITAL LETTER OT
+0480          ; Lu #       CYRILLIC CAPITAL LETTER KOPPA
+048A          ; Lu #       CYRILLIC CAPITAL LETTER SHORT I WITH TAIL
+048C          ; Lu #       CYRILLIC CAPITAL LETTER SEMISOFT SIGN
+048E          ; Lu #       CYRILLIC CAPITAL LETTER ER WITH TICK
+0490          ; Lu #       CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0492          ; Lu #       CYRILLIC CAPITAL LETTER GHE WITH STROKE
+0494          ; Lu #       CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK
+0496          ; Lu #       CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER
+0498          ; Lu #       CYRILLIC CAPITAL LETTER ZE WITH DESCENDER
+049A          ; Lu #       CYRILLIC CAPITAL LETTER KA WITH DESCENDER
+049C          ; Lu #       CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE
+049E          ; Lu #       CYRILLIC CAPITAL LETTER KA WITH STROKE
+04A0          ; Lu #       CYRILLIC CAPITAL LETTER BASHKIR KA
+04A2          ; Lu #       CYRILLIC CAPITAL LETTER EN WITH DESCENDER
+04A4          ; Lu #       CYRILLIC CAPITAL LIGATURE EN GHE
+04A6          ; Lu #       CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK
+04A8          ; Lu #       CYRILLIC CAPITAL LETTER ABKHASIAN HA
+04AA          ; Lu #       CYRILLIC CAPITAL LETTER ES WITH DESCENDER
+04AC          ; Lu #       CYRILLIC CAPITAL LETTER TE WITH DESCENDER
+04AE          ; Lu #       CYRILLIC CAPITAL LETTER STRAIGHT U
+04B0          ; Lu #       CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE
+04B2          ; Lu #       CYRILLIC CAPITAL LETTER HA WITH DESCENDER
+04B4          ; Lu #       CYRILLIC CAPITAL LIGATURE TE TSE
+04B6          ; Lu #       CYRILLIC CAPITAL LETTER CHE WITH DESCENDER
+04B8          ; Lu #       CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE
+04BA          ; Lu #       CYRILLIC CAPITAL LETTER SHHA
+04BC          ; Lu #       CYRILLIC CAPITAL LETTER ABKHASIAN CHE
+04BE          ; Lu #       CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER
+04C0..04C1    ; Lu #   [2] CYRILLIC LETTER PALOCHKA..CYRILLIC CAPITAL LETTER ZHE WITH BREVE
+04C3          ; Lu #       CYRILLIC CAPITAL LETTER KA WITH HOOK
+04C5          ; Lu #       CYRILLIC CAPITAL LETTER EL WITH TAIL
+04C7          ; Lu #       CYRILLIC CAPITAL LETTER EN WITH HOOK
+04C9          ; Lu #       CYRILLIC CAPITAL LETTER EN WITH TAIL
+04CB          ; Lu #       CYRILLIC CAPITAL LETTER KHAKASSIAN CHE
+04CD          ; Lu #       CYRILLIC CAPITAL LETTER EM WITH TAIL
+04D0          ; Lu #       CYRILLIC CAPITAL LETTER A WITH BREVE
+04D2          ; Lu #       CYRILLIC CAPITAL LETTER A WITH DIAERESIS
+04D4          ; Lu #       CYRILLIC CAPITAL LIGATURE A IE
+04D6          ; Lu #       CYRILLIC CAPITAL LETTER IE WITH BREVE
+04D8          ; Lu #       CYRILLIC CAPITAL LETTER SCHWA
+04DA          ; Lu #       CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS
+04DC          ; Lu #       CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS
+04DE          ; Lu #       CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS
+04E0          ; Lu #       CYRILLIC CAPITAL LETTER ABKHASIAN DZE
+04E2          ; Lu #       CYRILLIC CAPITAL LETTER I WITH MACRON
+04E4          ; Lu #       CYRILLIC CAPITAL LETTER I WITH DIAERESIS
+04E6          ; Lu #       CYRILLIC CAPITAL LETTER O WITH DIAERESIS
+04E8          ; Lu #       CYRILLIC CAPITAL LETTER BARRED O
+04EA          ; Lu #       CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS
+04EC          ; Lu #       CYRILLIC CAPITAL LETTER E WITH DIAERESIS
+04EE          ; Lu #       CYRILLIC CAPITAL LETTER U WITH MACRON
+04F0          ; Lu #       CYRILLIC CAPITAL LETTER U WITH DIAERESIS
+04F2          ; Lu #       CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE
+04F4          ; Lu #       CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS
+04F6          ; Lu #       CYRILLIC CAPITAL LETTER GHE WITH DESCENDER
+04F8          ; Lu #       CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS
+04FA          ; Lu #       CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK
+04FC          ; Lu #       CYRILLIC CAPITAL LETTER HA WITH HOOK
+04FE          ; Lu #       CYRILLIC CAPITAL LETTER HA WITH STROKE
+0500          ; Lu #       CYRILLIC CAPITAL LETTER KOMI DE
+0502          ; Lu #       CYRILLIC CAPITAL LETTER KOMI DJE
+0504          ; Lu #       CYRILLIC CAPITAL LETTER KOMI ZJE
+0506          ; Lu #       CYRILLIC CAPITAL LETTER KOMI DZJE
+0508          ; Lu #       CYRILLIC CAPITAL LETTER KOMI LJE
+050A          ; Lu #       CYRILLIC CAPITAL LETTER KOMI NJE
+050C          ; Lu #       CYRILLIC CAPITAL LETTER KOMI SJE
+050E          ; Lu #       CYRILLIC CAPITAL LETTER KOMI TJE
+0510          ; Lu #       CYRILLIC CAPITAL LETTER REVERSED ZE
+0512          ; Lu #       CYRILLIC CAPITAL LETTER EL WITH HOOK
+0514          ; Lu #       CYRILLIC CAPITAL LETTER LHA
+0516          ; Lu #       CYRILLIC CAPITAL LETTER RHA
+0518          ; Lu #       CYRILLIC CAPITAL LETTER YAE
+051A          ; Lu #       CYRILLIC CAPITAL LETTER QA
+051C          ; Lu #       CYRILLIC CAPITAL LETTER WE
+051E          ; Lu #       CYRILLIC CAPITAL LETTER ALEUT KA
+0520          ; Lu #       CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK
+0522          ; Lu #       CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK
+0531..0556    ; Lu #  [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
+10A0..10C5    ; Lu #  [38] GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE
+1E00          ; Lu #       LATIN CAPITAL LETTER A WITH RING BELOW
+1E02          ; Lu #       LATIN CAPITAL LETTER B WITH DOT ABOVE
+1E04          ; Lu #       LATIN CAPITAL LETTER B WITH DOT BELOW
+1E06          ; Lu #       LATIN CAPITAL LETTER B WITH LINE BELOW
+1E08          ; Lu #       LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE
+1E0A          ; Lu #       LATIN CAPITAL LETTER D WITH DOT ABOVE
+1E0C          ; Lu #       LATIN CAPITAL LETTER D WITH DOT BELOW
+1E0E          ; Lu #       LATIN CAPITAL LETTER D WITH LINE BELOW
+1E10          ; Lu #       LATIN CAPITAL LETTER D WITH CEDILLA
+1E12          ; Lu #       LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW
+1E14          ; Lu #       LATIN CAPITAL LETTER E WITH MACRON AND GRAVE
+1E16          ; Lu #       LATIN CAPITAL LETTER E WITH MACRON AND ACUTE
+1E18          ; Lu #       LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW
+1E1A          ; Lu #       LATIN CAPITAL LETTER E WITH TILDE BELOW
+1E1C          ; Lu #       LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE
+1E1E          ; Lu #       LATIN CAPITAL LETTER F WITH DOT ABOVE
+1E20          ; Lu #       LATIN CAPITAL LETTER G WITH MACRON
+1E22          ; Lu #       LATIN CAPITAL LETTER H WITH DOT ABOVE
+1E24          ; Lu #       LATIN CAPITAL LETTER H WITH DOT BELOW
+1E26          ; Lu #       LATIN CAPITAL LETTER H WITH DIAERESIS
+1E28          ; Lu #       LATIN CAPITAL LETTER H WITH CEDILLA
+1E2A          ; Lu #       LATIN CAPITAL LETTER H WITH BREVE BELOW
+1E2C          ; Lu #       LATIN CAPITAL LETTER I WITH TILDE BELOW
+1E2E          ; Lu #       LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE
+1E30          ; Lu #       LATIN CAPITAL LETTER K WITH ACUTE
+1E32          ; Lu #       LATIN CAPITAL LETTER K WITH DOT BELOW
+1E34          ; Lu #       LATIN CAPITAL LETTER K WITH LINE BELOW
+1E36          ; Lu #       LATIN CAPITAL LETTER L WITH DOT BELOW
+1E38          ; Lu #       LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON
+1E3A          ; Lu #       LATIN CAPITAL LETTER L WITH LINE BELOW
+1E3C          ; Lu #       LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW
+1E3E          ; Lu #       LATIN CAPITAL LETTER M WITH ACUTE
+1E40          ; Lu #       LATIN CAPITAL LETTER M WITH DOT ABOVE
+1E42          ; Lu #       LATIN CAPITAL LETTER M WITH DOT BELOW
+1E44          ; Lu #       LATIN CAPITAL LETTER N WITH DOT ABOVE
+1E46          ; Lu #       LATIN CAPITAL LETTER N WITH DOT BELOW
+1E48          ; Lu #       LATIN CAPITAL LETTER N WITH LINE BELOW
+1E4A          ; Lu #       LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW
+1E4C          ; Lu #       LATIN CAPITAL LETTER O WITH TILDE AND ACUTE
+1E4E          ; Lu #       LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS
+1E50          ; Lu #       LATIN CAPITAL LETTER O WITH MACRON AND GRAVE
+1E52          ; Lu #       LATIN CAPITAL LETTER O WITH MACRON AND ACUTE
+1E54          ; Lu #       LATIN CAPITAL LETTER P WITH ACUTE
+1E56          ; Lu #       LATIN CAPITAL LETTER P WITH DOT ABOVE
+1E58          ; Lu #       LATIN CAPITAL LETTER R WITH DOT ABOVE
+1E5A          ; Lu #       LATIN CAPITAL LETTER R WITH DOT BELOW
+1E5C          ; Lu #       LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON
+1E5E          ; Lu #       LATIN CAPITAL LETTER R WITH LINE BELOW
+1E60          ; Lu #       LATIN CAPITAL LETTER S WITH DOT ABOVE
+1E62          ; Lu #       LATIN CAPITAL LETTER S WITH DOT BELOW
+1E64          ; Lu #       LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE
+1E66          ; Lu #       LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE
+1E68          ; Lu #       LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE
+1E6A          ; Lu #       LATIN CAPITAL LETTER T WITH DOT ABOVE
+1E6C          ; Lu #       LATIN CAPITAL LETTER T WITH DOT BELOW
+1E6E          ; Lu #       LATIN CAPITAL LETTER T WITH LINE BELOW
+1E70          ; Lu #       LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW
+1E72          ; Lu #       LATIN CAPITAL LETTER U WITH DIAERESIS BELOW
+1E74          ; Lu #       LATIN CAPITAL LETTER U WITH TILDE BELOW
+1E76          ; Lu #       LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW
+1E78          ; Lu #       LATIN CAPITAL LETTER U WITH TILDE AND ACUTE
+1E7A          ; Lu #       LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS
+1E7C          ; Lu #       LATIN CAPITAL LETTER V WITH TILDE
+1E7E          ; Lu #       LATIN CAPITAL LETTER V WITH DOT BELOW
+1E80          ; Lu #       LATIN CAPITAL LETTER W WITH GRAVE
+1E82          ; Lu #       LATIN CAPITAL LETTER W WITH ACUTE
+1E84          ; Lu #       LATIN CAPITAL LETTER W WITH DIAERESIS
+1E86          ; Lu #       LATIN CAPITAL LETTER W WITH DOT ABOVE
+1E88          ; Lu #       LATIN CAPITAL LETTER W WITH DOT BELOW
+1E8A          ; Lu #       LATIN CAPITAL LETTER X WITH DOT ABOVE
+1E8C          ; Lu #       LATIN CAPITAL LETTER X WITH DIAERESIS
+1E8E          ; Lu #       LATIN CAPITAL LETTER Y WITH DOT ABOVE
+1E90          ; Lu #       LATIN CAPITAL LETTER Z WITH CIRCUMFLEX
+1E92          ; Lu #       LATIN CAPITAL LETTER Z WITH DOT BELOW
+1E94          ; Lu #       LATIN CAPITAL LETTER Z WITH LINE BELOW
+1E9E          ; Lu #       LATIN CAPITAL LETTER SHARP S
+1EA0          ; Lu #       LATIN CAPITAL LETTER A WITH DOT BELOW
+1EA2          ; Lu #       LATIN CAPITAL LETTER A WITH HOOK ABOVE
+1EA4          ; Lu #       LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE
+1EA6          ; Lu #       LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE
+1EA8          ; Lu #       LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+1EAA          ; Lu #       LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE
+1EAC          ; Lu #       LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+1EAE          ; Lu #       LATIN CAPITAL LETTER A WITH BREVE AND ACUTE
+1EB0          ; Lu #       LATIN CAPITAL LETTER A WITH BREVE AND GRAVE
+1EB2          ; Lu #       LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE
+1EB4          ; Lu #       LATIN CAPITAL LETTER A WITH BREVE AND TILDE
+1EB6          ; Lu #       LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW
+1EB8          ; Lu #       LATIN CAPITAL LETTER E WITH DOT BELOW
+1EBA          ; Lu #       LATIN CAPITAL LETTER E WITH HOOK ABOVE
+1EBC          ; Lu #       LATIN CAPITAL LETTER E WITH TILDE
+1EBE          ; Lu #       LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE
+1EC0          ; Lu #       LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE
+1EC2          ; Lu #       LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+1EC4          ; Lu #       LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE
+1EC6          ; Lu #       LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+1EC8          ; Lu #       LATIN CAPITAL LETTER I WITH HOOK ABOVE
+1ECA          ; Lu #       LATIN CAPITAL LETTER I WITH DOT BELOW
+1ECC          ; Lu #       LATIN CAPITAL LETTER O WITH DOT BELOW
+1ECE          ; Lu #       LATIN CAPITAL LETTER O WITH HOOK ABOVE
+1ED0          ; Lu #       LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE
+1ED2          ; Lu #       LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE
+1ED4          ; Lu #       LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+1ED6          ; Lu #       LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE
+1ED8          ; Lu #       LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+1EDA          ; Lu #       LATIN CAPITAL LETTER O WITH HORN AND ACUTE
+1EDC          ; Lu #       LATIN CAPITAL LETTER O WITH HORN AND GRAVE
+1EDE          ; Lu #       LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE
+1EE0          ; Lu #       LATIN CAPITAL LETTER O WITH HORN AND TILDE
+1EE2          ; Lu #       LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW
+1EE4          ; Lu #       LATIN CAPITAL LETTER U WITH DOT BELOW
+1EE6          ; Lu #       LATIN CAPITAL LETTER U WITH HOOK ABOVE
+1EE8          ; Lu #       LATIN CAPITAL LETTER U WITH HORN AND ACUTE
+1EEA          ; Lu #       LATIN CAPITAL LETTER U WITH HORN AND GRAVE
+1EEC          ; Lu #       LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE
+1EEE          ; Lu #       LATIN CAPITAL LETTER U WITH HORN AND TILDE
+1EF0          ; Lu #       LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW
+1EF2          ; Lu #       LATIN CAPITAL LETTER Y WITH GRAVE
+1EF4          ; Lu #       LATIN CAPITAL LETTER Y WITH DOT BELOW
+1EF6          ; Lu #       LATIN CAPITAL LETTER Y WITH HOOK ABOVE
+1EF8          ; Lu #       LATIN CAPITAL LETTER Y WITH TILDE
+1EFA          ; Lu #       LATIN CAPITAL LETTER MIDDLE-WELSH LL
+1EFC          ; Lu #       LATIN CAPITAL LETTER MIDDLE-WELSH V
+1EFE          ; Lu #       LATIN CAPITAL LETTER Y WITH LOOP
+1F08..1F0F    ; Lu #   [8] GREEK CAPITAL LETTER ALPHA WITH PSILI..GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI
+1F18..1F1D    ; Lu #   [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+1F28..1F2F    ; Lu #   [8] GREEK CAPITAL LETTER ETA WITH PSILI..GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI
+1F38..1F3F    ; Lu #   [8] GREEK CAPITAL LETTER IOTA WITH PSILI..GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI
+1F48..1F4D    ; Lu #   [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+1F59          ; Lu #       GREEK CAPITAL LETTER UPSILON WITH DASIA
+1F5B          ; Lu #       GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+1F5D          ; Lu #       GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+1F5F          ; Lu #       GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F68..1F6F    ; Lu #   [8] GREEK CAPITAL LETTER OMEGA WITH PSILI..GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI
+1FB8..1FBB    ; Lu #   [4] GREEK CAPITAL LETTER ALPHA WITH VRACHY..GREEK CAPITAL LETTER ALPHA WITH OXIA
+1FC8..1FCB    ; Lu #   [4] GREEK CAPITAL LETTER EPSILON WITH VARIA..GREEK CAPITAL LETTER ETA WITH OXIA
+1FD8..1FDB    ; Lu #   [4] GREEK CAPITAL LETTER IOTA WITH VRACHY..GREEK CAPITAL LETTER IOTA WITH OXIA
+1FE8..1FEC    ; Lu #   [5] GREEK CAPITAL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA
+1FF8..1FFB    ; Lu #   [4] GREEK CAPITAL LETTER OMICRON WITH VARIA..GREEK CAPITAL LETTER OMEGA WITH OXIA
+2102          ; Lu #       DOUBLE-STRUCK CAPITAL C
+2107          ; Lu #       EULER CONSTANT
+210B..210D    ; Lu #   [3] SCRIPT CAPITAL H..DOUBLE-STRUCK CAPITAL H
+2110..2112    ; Lu #   [3] SCRIPT CAPITAL I..SCRIPT CAPITAL L
+2115          ; Lu #       DOUBLE-STRUCK CAPITAL N
+2119..211D    ; Lu #   [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R
+2124          ; Lu #       DOUBLE-STRUCK CAPITAL Z
+2126          ; Lu #       OHM SIGN
+2128          ; Lu #       BLACK-LETTER CAPITAL Z
+212A..212D    ; Lu #   [4] KELVIN SIGN..BLACK-LETTER CAPITAL C
+2130..2133    ; Lu #   [4] SCRIPT CAPITAL E..SCRIPT CAPITAL M
+213E..213F    ; Lu #   [2] DOUBLE-STRUCK CAPITAL GAMMA..DOUBLE-STRUCK CAPITAL PI
+2145          ; Lu #       DOUBLE-STRUCK ITALIC CAPITAL D
+2183          ; Lu #       ROMAN NUMERAL REVERSED ONE HUNDRED
+2C00..2C2E    ; Lu #  [47] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE
+2C60          ; Lu #       LATIN CAPITAL LETTER L WITH DOUBLE BAR
+2C62..2C64    ; Lu #   [3] LATIN CAPITAL LETTER L WITH MIDDLE TILDE..LATIN CAPITAL LETTER R WITH TAIL
+2C67          ; Lu #       LATIN CAPITAL LETTER H WITH DESCENDER
+2C69          ; Lu #       LATIN CAPITAL LETTER K WITH DESCENDER
+2C6B          ; Lu #       LATIN CAPITAL LETTER Z WITH DESCENDER
+2C6D..2C6F    ; Lu #   [3] LATIN CAPITAL LETTER ALPHA..LATIN CAPITAL LETTER TURNED A
+2C72          ; Lu #       LATIN CAPITAL LETTER W WITH HOOK
+2C75          ; Lu #       LATIN CAPITAL LETTER HALF H
+2C80          ; Lu #       COPTIC CAPITAL LETTER ALFA
+2C82          ; Lu #       COPTIC CAPITAL LETTER VIDA
+2C84          ; Lu #       COPTIC CAPITAL LETTER GAMMA
+2C86          ; Lu #       COPTIC CAPITAL LETTER DALDA
+2C88          ; Lu #       COPTIC CAPITAL LETTER EIE
+2C8A          ; Lu #       COPTIC CAPITAL LETTER SOU
+2C8C          ; Lu #       COPTIC CAPITAL LETTER ZATA
+2C8E          ; Lu #       COPTIC CAPITAL LETTER HATE
+2C90          ; Lu #       COPTIC CAPITAL LETTER THETHE
+2C92          ; Lu #       COPTIC CAPITAL LETTER IAUDA
+2C94          ; Lu #       COPTIC CAPITAL LETTER KAPA
+2C96          ; Lu #       COPTIC CAPITAL LETTER LAULA
+2C98          ; Lu #       COPTIC CAPITAL LETTER MI
+2C9A          ; Lu #       COPTIC CAPITAL LETTER NI
+2C9C          ; Lu #       COPTIC CAPITAL LETTER KSI
+2C9E          ; Lu #       COPTIC CAPITAL LETTER O
+2CA0          ; Lu #       COPTIC CAPITAL LETTER PI
+2CA2          ; Lu #       COPTIC CAPITAL LETTER RO
+2CA4          ; Lu #       COPTIC CAPITAL LETTER SIMA
+2CA6          ; Lu #       COPTIC CAPITAL LETTER TAU
+2CA8          ; Lu #       COPTIC CAPITAL LETTER UA
+2CAA          ; Lu #       COPTIC CAPITAL LETTER FI
+2CAC          ; Lu #       COPTIC CAPITAL LETTER KHI
+2CAE          ; Lu #       COPTIC CAPITAL LETTER PSI
+2CB0          ; Lu #       COPTIC CAPITAL LETTER OOU
+2CB2          ; Lu #       COPTIC CAPITAL LETTER DIALECT-P ALEF
+2CB4          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC AIN
+2CB6          ; Lu #       COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE
+2CB8          ; Lu #       COPTIC CAPITAL LETTER DIALECT-P KAPA
+2CBA          ; Lu #       COPTIC CAPITAL LETTER DIALECT-P NI
+2CBC          ; Lu #       COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI
+2CBE          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC OOU
+2CC0          ; Lu #       COPTIC CAPITAL LETTER SAMPI
+2CC2          ; Lu #       COPTIC CAPITAL LETTER CROSSED SHEI
+2CC4          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC SHEI
+2CC6          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC ESH
+2CC8          ; Lu #       COPTIC CAPITAL LETTER AKHMIMIC KHEI
+2CCA          ; Lu #       COPTIC CAPITAL LETTER DIALECT-P HORI
+2CCC          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC HORI
+2CCE          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC HA
+2CD0          ; Lu #       COPTIC CAPITAL LETTER L-SHAPED HA
+2CD2          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC HEI
+2CD4          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC HAT
+2CD6          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC GANGIA
+2CD8          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC DJA
+2CDA          ; Lu #       COPTIC CAPITAL LETTER OLD COPTIC SHIMA
+2CDC          ; Lu #       COPTIC CAPITAL LETTER OLD NUBIAN SHIMA
+2CDE          ; Lu #       COPTIC CAPITAL LETTER OLD NUBIAN NGI
+2CE0          ; Lu #       COPTIC CAPITAL LETTER OLD NUBIAN NYI
+2CE2          ; Lu #       COPTIC CAPITAL LETTER OLD NUBIAN WAU
+A640          ; Lu #       CYRILLIC CAPITAL LETTER ZEMLYA
+A642          ; Lu #       CYRILLIC CAPITAL LETTER DZELO
+A644          ; Lu #       CYRILLIC CAPITAL LETTER REVERSED DZE
+A646          ; Lu #       CYRILLIC CAPITAL LETTER IOTA
+A648          ; Lu #       CYRILLIC CAPITAL LETTER DJERV
+A64A          ; Lu #       CYRILLIC CAPITAL LETTER MONOGRAPH UK
+A64C          ; Lu #       CYRILLIC CAPITAL LETTER BROAD OMEGA
+A64E          ; Lu #       CYRILLIC CAPITAL LETTER NEUTRAL YER
+A650          ; Lu #       CYRILLIC CAPITAL LETTER YERU WITH BACK YER
+A652          ; Lu #       CYRILLIC CAPITAL LETTER IOTIFIED YAT
+A654          ; Lu #       CYRILLIC CAPITAL LETTER REVERSED YU
+A656          ; Lu #       CYRILLIC CAPITAL LETTER IOTIFIED A
+A658          ; Lu #       CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS
+A65A          ; Lu #       CYRILLIC CAPITAL LETTER BLENDED YUS
+A65C          ; Lu #       CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS
+A65E          ; Lu #       CYRILLIC CAPITAL LETTER YN
+A662          ; Lu #       CYRILLIC CAPITAL LETTER SOFT DE
+A664          ; Lu #       CYRILLIC CAPITAL LETTER SOFT EL
+A666          ; Lu #       CYRILLIC CAPITAL LETTER SOFT EM
+A668          ; Lu #       CYRILLIC CAPITAL LETTER MONOCULAR O
+A66A          ; Lu #       CYRILLIC CAPITAL LETTER BINOCULAR O
+A66C          ; Lu #       CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O
+A680          ; Lu #       CYRILLIC CAPITAL LETTER DWE
+A682          ; Lu #       CYRILLIC CAPITAL LETTER DZWE
+A684          ; Lu #       CYRILLIC CAPITAL LETTER ZHWE
+A686          ; Lu #       CYRILLIC CAPITAL LETTER CCHE
+A688          ; Lu #       CYRILLIC CAPITAL LETTER DZZE
+A68A          ; Lu #       CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK
+A68C          ; Lu #       CYRILLIC CAPITAL LETTER TWE
+A68E          ; Lu #       CYRILLIC CAPITAL LETTER TSWE
+A690          ; Lu #       CYRILLIC CAPITAL LETTER TSSE
+A692          ; Lu #       CYRILLIC CAPITAL LETTER TCHE
+A694          ; Lu #       CYRILLIC CAPITAL LETTER HWE
+A696          ; Lu #       CYRILLIC CAPITAL LETTER SHWE
+A722          ; Lu #       LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF
+A724          ; Lu #       LATIN CAPITAL LETTER EGYPTOLOGICAL AIN
+A726          ; Lu #       LATIN CAPITAL LETTER HENG
+A728          ; Lu #       LATIN CAPITAL LETTER TZ
+A72A          ; Lu #       LATIN CAPITAL LETTER TRESILLO
+A72C          ; Lu #       LATIN CAPITAL LETTER CUATRILLO
+A72E          ; Lu #       LATIN CAPITAL LETTER CUATRILLO WITH COMMA
+A732          ; Lu #       LATIN CAPITAL LETTER AA
+A734          ; Lu #       LATIN CAPITAL LETTER AO
+A736          ; Lu #       LATIN CAPITAL LETTER AU
+A738          ; Lu #       LATIN CAPITAL LETTER AV
+A73A          ; Lu #       LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR
+A73C          ; Lu #       LATIN CAPITAL LETTER AY
+A73E          ; Lu #       LATIN CAPITAL LETTER REVERSED C WITH DOT
+A740          ; Lu #       LATIN CAPITAL LETTER K WITH STROKE
+A742          ; Lu #       LATIN CAPITAL LETTER K WITH DIAGONAL STROKE
+A744          ; Lu #       LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE
+A746          ; Lu #       LATIN CAPITAL LETTER BROKEN L
+A748          ; Lu #       LATIN CAPITAL LETTER L WITH HIGH STROKE
+A74A          ; Lu #       LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY
+A74C          ; Lu #       LATIN CAPITAL LETTER O WITH LOOP
+A74E          ; Lu #       LATIN CAPITAL LETTER OO
+A750          ; Lu #       LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER
+A752          ; Lu #       LATIN CAPITAL LETTER P WITH FLOURISH
+A754          ; Lu #       LATIN CAPITAL LETTER P WITH SQUIRREL TAIL
+A756          ; Lu #       LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER
+A758          ; Lu #       LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE
+A75A          ; Lu #       LATIN CAPITAL LETTER R ROTUNDA
+A75C          ; Lu #       LATIN CAPITAL LETTER RUM ROTUNDA
+A75E          ; Lu #       LATIN CAPITAL LETTER V WITH DIAGONAL STROKE
+A760          ; Lu #       LATIN CAPITAL LETTER VY
+A762          ; Lu #       LATIN CAPITAL LETTER VISIGOTHIC Z
+A764          ; Lu #       LATIN CAPITAL LETTER THORN WITH STROKE
+A766          ; Lu #       LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER
+A768          ; Lu #       LATIN CAPITAL LETTER VEND
+A76A          ; Lu #       LATIN CAPITAL LETTER ET
+A76C          ; Lu #       LATIN CAPITAL LETTER IS
+A76E          ; Lu #       LATIN CAPITAL LETTER CON
+A779          ; Lu #       LATIN CAPITAL LETTER INSULAR D
+A77B          ; Lu #       LATIN CAPITAL LETTER INSULAR F
+A77D..A77E    ; Lu #   [2] LATIN CAPITAL LETTER INSULAR G..LATIN CAPITAL LETTER TURNED INSULAR G
+A780          ; Lu #       LATIN CAPITAL LETTER TURNED L
+A782          ; Lu #       LATIN CAPITAL LETTER INSULAR R
+A784          ; Lu #       LATIN CAPITAL LETTER INSULAR S
+A786          ; Lu #       LATIN CAPITAL LETTER INSULAR T
+A78B          ; Lu #       LATIN CAPITAL LETTER SALTILLO
+FF21..FF3A    ; Lu #  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z
+10400..10427  ; Lu #  [40] DESERET CAPITAL LETTER LONG I..DESERET CAPITAL LETTER EW
+1D400..1D419  ; Lu #  [26] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL BOLD CAPITAL Z
+1D434..1D44D  ; Lu #  [26] MATHEMATICAL ITALIC CAPITAL A..MATHEMATICAL ITALIC CAPITAL Z
+1D468..1D481  ; Lu #  [26] MATHEMATICAL BOLD ITALIC CAPITAL A..MATHEMATICAL BOLD ITALIC CAPITAL Z
+1D49C         ; Lu #       MATHEMATICAL SCRIPT CAPITAL A
+1D49E..1D49F  ; Lu #   [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D
+1D4A2         ; Lu #       MATHEMATICAL SCRIPT CAPITAL G
+1D4A5..1D4A6  ; Lu #   [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K
+1D4A9..1D4AC  ; Lu #   [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q
+1D4AE..1D4B5  ; Lu #   [8] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT CAPITAL Z
+1D4D0..1D4E9  ; Lu #  [26] MATHEMATICAL BOLD SCRIPT CAPITAL A..MATHEMATICAL BOLD SCRIPT CAPITAL Z
+1D504..1D505  ; Lu #   [2] MATHEMATICAL FRAKTUR CAPITAL A..MATHEMATICAL FRAKTUR CAPITAL B
+1D507..1D50A  ; Lu #   [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G
+1D50D..1D514  ; Lu #   [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q
+1D516..1D51C  ; Lu #   [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y
+1D538..1D539  ; Lu #   [2] MATHEMATICAL DOUBLE-STRUCK CAPITAL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+1D53B..1D53E  ; Lu #   [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+1D540..1D544  ; Lu #   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+1D546         ; Lu #       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+1D54A..1D550  ; Lu #   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+1D56C..1D585  ; Lu #  [26] MATHEMATICAL BOLD FRAKTUR CAPITAL A..MATHEMATICAL BOLD FRAKTUR CAPITAL Z
+1D5A0..1D5B9  ; Lu #  [26] MATHEMATICAL SANS-SERIF CAPITAL A..MATHEMATICAL SANS-SERIF CAPITAL Z
+1D5D4..1D5ED  ; Lu #  [26] MATHEMATICAL SANS-SERIF BOLD CAPITAL A..MATHEMATICAL SANS-SERIF BOLD CAPITAL Z
+1D608..1D621  ; Lu #  [26] MATHEMATICAL SANS-SERIF ITALIC CAPITAL A..MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z
+1D63C..1D655  ; Lu #  [26] MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z
+1D670..1D689  ; Lu #  [26] MATHEMATICAL MONOSPACE CAPITAL A..MATHEMATICAL MONOSPACE CAPITAL Z
+1D6A8..1D6C0  ; Lu #  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
+1D6E2..1D6FA  ; Lu #  [25] MATHEMATICAL ITALIC CAPITAL ALPHA..MATHEMATICAL ITALIC CAPITAL OMEGA
+1D71C..1D734  ; Lu #  [25] MATHEMATICAL BOLD ITALIC CAPITAL ALPHA..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA
+1D756..1D76E  ; Lu #  [25] MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
+1D790..1D7A8  ; Lu #  [25] MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
+1D7CA         ; Lu #       MATHEMATICAL BOLD CAPITAL DIGAMMA
+
+# Total code points: 1421
+
+# ================================================
+
+# General_Category=Lowercase_Letter
+
+0061..007A    ; Ll #  [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z
+00AA          ; Ll #       FEMININE ORDINAL INDICATOR
+00B5          ; Ll #       MICRO SIGN
+00BA          ; Ll #       MASCULINE ORDINAL INDICATOR
+00DF..00F6    ; Ll #  [24] LATIN SMALL LETTER SHARP S..LATIN SMALL LETTER O WITH DIAERESIS
+00F8..00FF    ; Ll #   [8] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER Y WITH DIAERESIS
+0101          ; Ll #       LATIN SMALL LETTER A WITH MACRON
+0103          ; Ll #       LATIN SMALL LETTER A WITH BREVE
+0105          ; Ll #       LATIN SMALL LETTER A WITH OGONEK
+0107          ; Ll #       LATIN SMALL LETTER C WITH ACUTE
+0109          ; Ll #       LATIN SMALL LETTER C WITH CIRCUMFLEX
+010B          ; Ll #       LATIN SMALL LETTER C WITH DOT ABOVE
+010D          ; Ll #       LATIN SMALL LETTER C WITH CARON
+010F          ; Ll #       LATIN SMALL LETTER D WITH CARON
+0111          ; Ll #       LATIN SMALL LETTER D WITH STROKE
+0113          ; Ll #       LATIN SMALL LETTER E WITH MACRON
+0115          ; Ll #       LATIN SMALL LETTER E WITH BREVE
+0117          ; Ll #       LATIN SMALL LETTER E WITH DOT ABOVE
+0119          ; Ll #       LATIN SMALL LETTER E WITH OGONEK
+011B          ; Ll #       LATIN SMALL LETTER E WITH CARON
+011D          ; Ll #       LATIN SMALL LETTER G WITH CIRCUMFLEX
+011F          ; Ll #       LATIN SMALL LETTER G WITH BREVE
+0121          ; Ll #       LATIN SMALL LETTER G WITH DOT ABOVE
+0123          ; Ll #       LATIN SMALL LETTER G WITH CEDILLA
+0125          ; Ll #       LATIN SMALL LETTER H WITH CIRCUMFLEX
+0127          ; Ll #       LATIN SMALL LETTER H WITH STROKE
+0129          ; Ll #       LATIN SMALL LETTER I WITH TILDE
+012B          ; Ll #       LATIN SMALL LETTER I WITH MACRON
+012D          ; Ll #       LATIN SMALL LETTER I WITH BREVE
+012F          ; Ll #       LATIN SMALL LETTER I WITH OGONEK
+0131          ; Ll #       LATIN SMALL LETTER DOTLESS I
+0133          ; Ll #       LATIN SMALL LIGATURE IJ
+0135          ; Ll #       LATIN SMALL LETTER J WITH CIRCUMFLEX
+0137..0138    ; Ll #   [2] LATIN SMALL LETTER K WITH CEDILLA..LATIN SMALL LETTER KRA
+013A          ; Ll #       LATIN SMALL LETTER L WITH ACUTE
+013C          ; Ll #       LATIN SMALL LETTER L WITH CEDILLA
+013E          ; Ll #       LATIN SMALL LETTER L WITH CARON
+0140          ; Ll #       LATIN SMALL LETTER L WITH MIDDLE DOT
+0142          ; Ll #       LATIN SMALL LETTER L WITH STROKE
+0144          ; Ll #       LATIN SMALL LETTER N WITH ACUTE
+0146          ; Ll #       LATIN SMALL LETTER N WITH CEDILLA
+0148..0149    ; Ll #   [2] LATIN SMALL LETTER N WITH CARON..LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+014B          ; Ll #       LATIN SMALL LETTER ENG
+014D          ; Ll #       LATIN SMALL LETTER O WITH MACRON
+014F          ; Ll #       LATIN SMALL LETTER O WITH BREVE
+0151          ; Ll #       LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0153          ; Ll #       LATIN SMALL LIGATURE OE
+0155          ; Ll #       LATIN SMALL LETTER R WITH ACUTE
+0157          ; Ll #       LATIN SMALL LETTER R WITH CEDILLA
+0159          ; Ll #       LATIN SMALL LETTER R WITH CARON
+015B          ; Ll #       LATIN SMALL LETTER S WITH ACUTE
+015D          ; Ll #       LATIN SMALL LETTER S WITH CIRCUMFLEX
+015F          ; Ll #       LATIN SMALL LETTER S WITH CEDILLA
+0161          ; Ll #       LATIN SMALL LETTER S WITH CARON
+0163          ; Ll #       LATIN SMALL LETTER T WITH CEDILLA
+0165          ; Ll #       LATIN SMALL LETTER T WITH CARON
+0167          ; Ll #       LATIN SMALL LETTER T WITH STROKE
+0169          ; Ll #       LATIN SMALL LETTER U WITH TILDE
+016B          ; Ll #       LATIN SMALL LETTER U WITH MACRON
+016D          ; Ll #       LATIN SMALL LETTER U WITH BREVE
+016F          ; Ll #       LATIN SMALL LETTER U WITH RING ABOVE
+0171          ; Ll #       LATIN SMALL LETTER U WITH DOUBLE ACUTE
+0173          ; Ll #       LATIN SMALL LETTER U WITH OGONEK
+0175          ; Ll #       LATIN SMALL LETTER W WITH CIRCUMFLEX
+0177          ; Ll #       LATIN SMALL LETTER Y WITH CIRCUMFLEX
+017A          ; Ll #       LATIN SMALL LETTER Z WITH ACUTE
+017C          ; Ll #       LATIN SMALL LETTER Z WITH DOT ABOVE
+017E..0180    ; Ll #   [3] LATIN SMALL LETTER Z WITH CARON..LATIN SMALL LETTER B WITH STROKE
+0183          ; Ll #       LATIN SMALL LETTER B WITH TOPBAR
+0185          ; Ll #       LATIN SMALL LETTER TONE SIX
+0188          ; Ll #       LATIN SMALL LETTER C WITH HOOK
+018C..018D    ; Ll #   [2] LATIN SMALL LETTER D WITH TOPBAR..LATIN SMALL LETTER TURNED DELTA
+0192          ; Ll #       LATIN SMALL LETTER F WITH HOOK
+0195          ; Ll #       LATIN SMALL LETTER HV
+0199..019B    ; Ll #   [3] LATIN SMALL LETTER K WITH HOOK..LATIN SMALL LETTER LAMBDA WITH STROKE
+019E          ; Ll #       LATIN SMALL LETTER N WITH LONG RIGHT LEG
+01A1          ; Ll #       LATIN SMALL LETTER O WITH HORN
+01A3          ; Ll #       LATIN SMALL LETTER OI
+01A5          ; Ll #       LATIN SMALL LETTER P WITH HOOK
+01A8          ; Ll #       LATIN SMALL LETTER TONE TWO
+01AA..01AB    ; Ll #   [2] LATIN LETTER REVERSED ESH LOOP..LATIN SMALL LETTER T WITH PALATAL HOOK
+01AD          ; Ll #       LATIN SMALL LETTER T WITH HOOK
+01B0          ; Ll #       LATIN SMALL LETTER U WITH HORN
+01B4          ; Ll #       LATIN SMALL LETTER Y WITH HOOK
+01B6          ; Ll #       LATIN SMALL LETTER Z WITH STROKE
+01B9..01BA    ; Ll #   [2] LATIN SMALL LETTER EZH REVERSED..LATIN SMALL LETTER EZH WITH TAIL
+01BD..01BF    ; Ll #   [3] LATIN SMALL LETTER TONE FIVE..LATIN LETTER WYNN
+01C6          ; Ll #       LATIN SMALL LETTER DZ WITH CARON
+01C9          ; Ll #       LATIN SMALL LETTER LJ
+01CC          ; Ll #       LATIN SMALL LETTER NJ
+01CE          ; Ll #       LATIN SMALL LETTER A WITH CARON
+01D0          ; Ll #       LATIN SMALL LETTER I WITH CARON
+01D2          ; Ll #       LATIN SMALL LETTER O WITH CARON
+01D4          ; Ll #       LATIN SMALL LETTER U WITH CARON
+01D6          ; Ll #       LATIN SMALL LETTER U WITH DIAERESIS AND MACRON
+01D8          ; Ll #       LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE
+01DA          ; Ll #       LATIN SMALL LETTER U WITH DIAERESIS AND CARON
+01DC..01DD    ; Ll #   [2] LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE..LATIN SMALL LETTER TURNED E
+01DF          ; Ll #       LATIN SMALL LETTER A WITH DIAERESIS AND MACRON
+01E1          ; Ll #       LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON
+01E3          ; Ll #       LATIN SMALL LETTER AE WITH MACRON
+01E5          ; Ll #       LATIN SMALL LETTER G WITH STROKE
+01E7          ; Ll #       LATIN SMALL LETTER G WITH CARON
+01E9          ; Ll #       LATIN SMALL LETTER K WITH CARON
+01EB          ; Ll #       LATIN SMALL LETTER O WITH OGONEK
+01ED          ; Ll #       LATIN SMALL LETTER O WITH OGONEK AND MACRON
+01EF..01F0    ; Ll #   [2] LATIN SMALL LETTER EZH WITH CARON..LATIN SMALL LETTER J WITH CARON
+01F3          ; Ll #       LATIN SMALL LETTER DZ
+01F5          ; Ll #       LATIN SMALL LETTER G WITH ACUTE
+01F9          ; Ll #       LATIN SMALL LETTER N WITH GRAVE
+01FB          ; Ll #       LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
+01FD          ; Ll #       LATIN SMALL LETTER AE WITH ACUTE
+01FF          ; Ll #       LATIN SMALL LETTER O WITH STROKE AND ACUTE
+0201          ; Ll #       LATIN SMALL LETTER A WITH DOUBLE GRAVE
+0203          ; Ll #       LATIN SMALL LETTER A WITH INVERTED BREVE
+0205          ; Ll #       LATIN SMALL LETTER E WITH DOUBLE GRAVE
+0207          ; Ll #       LATIN SMALL LETTER E WITH INVERTED BREVE
+0209          ; Ll #       LATIN SMALL LETTER I WITH DOUBLE GRAVE
+020B          ; Ll #       LATIN SMALL LETTER I WITH INVERTED BREVE
+020D          ; Ll #       LATIN SMALL LETTER O WITH DOUBLE GRAVE
+020F          ; Ll #       LATIN SMALL LETTER O WITH INVERTED BREVE
+0211          ; Ll #       LATIN SMALL LETTER R WITH DOUBLE GRAVE
+0213          ; Ll #       LATIN SMALL LETTER R WITH INVERTED BREVE
+0215          ; Ll #       LATIN SMALL LETTER U WITH DOUBLE GRAVE
+0217          ; Ll #       LATIN SMALL LETTER U WITH INVERTED BREVE
+0219          ; Ll #       LATIN SMALL LETTER S WITH COMMA BELOW
+021B          ; Ll #       LATIN SMALL LETTER T WITH COMMA BELOW
+021D          ; Ll #       LATIN SMALL LETTER YOGH
+021F          ; Ll #       LATIN SMALL LETTER H WITH CARON
+0221          ; Ll #       LATIN SMALL LETTER D WITH CURL
+0223          ; Ll #       LATIN SMALL LETTER OU
+0225          ; Ll #       LATIN SMALL LETTER Z WITH HOOK
+0227          ; Ll #       LATIN SMALL LETTER A WITH DOT ABOVE
+0229          ; Ll #       LATIN SMALL LETTER E WITH CEDILLA
+022B          ; Ll #       LATIN SMALL LETTER O WITH DIAERESIS AND MACRON
+022D          ; Ll #       LATIN SMALL LETTER O WITH TILDE AND MACRON
+022F          ; Ll #       LATIN SMALL LETTER O WITH DOT ABOVE
+0231          ; Ll #       LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON
+0233..0239    ; Ll #   [7] LATIN SMALL LETTER Y WITH MACRON..LATIN SMALL LETTER QP DIGRAPH
+023C          ; Ll #       LATIN SMALL LETTER C WITH STROKE
+023F..0240    ; Ll #   [2] LATIN SMALL LETTER S WITH SWASH TAIL..LATIN SMALL LETTER Z WITH SWASH TAIL
+0242          ; Ll #       LATIN SMALL LETTER GLOTTAL STOP
+0247          ; Ll #       LATIN SMALL LETTER E WITH STROKE
+0249          ; Ll #       LATIN SMALL LETTER J WITH STROKE
+024B          ; Ll #       LATIN SMALL LETTER Q WITH HOOK TAIL
+024D          ; Ll #       LATIN SMALL LETTER R WITH STROKE
+024F..0293    ; Ll #  [69] LATIN SMALL LETTER Y WITH STROKE..LATIN SMALL LETTER EZH WITH CURL
+0295..02AF    ; Ll #  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+0371          ; Ll #       GREEK SMALL LETTER HETA
+0373          ; Ll #       GREEK SMALL LETTER ARCHAIC SAMPI
+0377          ; Ll #       GREEK SMALL LETTER PAMPHYLIAN DIGAMMA
+037B..037D    ; Ll #   [3] GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL
+0390          ; Ll #       GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+03AC..03CE    ; Ll #  [35] GREEK SMALL LETTER ALPHA WITH TONOS..GREEK SMALL LETTER OMEGA WITH TONOS
+03D0..03D1    ; Ll #   [2] GREEK BETA SYMBOL..GREEK THETA SYMBOL
+03D5..03D7    ; Ll #   [3] GREEK PHI SYMBOL..GREEK KAI SYMBOL
+03D9          ; Ll #       GREEK SMALL LETTER ARCHAIC KOPPA
+03DB          ; Ll #       GREEK SMALL LETTER STIGMA
+03DD          ; Ll #       GREEK SMALL LETTER DIGAMMA
+03DF          ; Ll #       GREEK SMALL LETTER KOPPA
+03E1          ; Ll #       GREEK SMALL LETTER SAMPI
+03E3          ; Ll #       COPTIC SMALL LETTER SHEI
+03E5          ; Ll #       COPTIC SMALL LETTER FEI
+03E7          ; Ll #       COPTIC SMALL LETTER KHEI
+03E9          ; Ll #       COPTIC SMALL LETTER HORI
+03EB          ; Ll #       COPTIC SMALL LETTER GANGIA
+03ED          ; Ll #       COPTIC SMALL LETTER SHIMA
+03EF..03F3    ; Ll #   [5] COPTIC SMALL LETTER DEI..GREEK LETTER YOT
+03F5          ; Ll #       GREEK LUNATE EPSILON SYMBOL
+03F8          ; Ll #       GREEK SMALL LETTER SHO
+03FB..03FC    ; Ll #   [2] GREEK SMALL LETTER SAN..GREEK RHO WITH STROKE SYMBOL
+0430..045F    ; Ll #  [48] CYRILLIC SMALL LETTER A..CYRILLIC SMALL LETTER DZHE
+0461          ; Ll #       CYRILLIC SMALL LETTER OMEGA
+0463          ; Ll #       CYRILLIC SMALL LETTER YAT
+0465          ; Ll #       CYRILLIC SMALL LETTER IOTIFIED E
+0467          ; Ll #       CYRILLIC SMALL LETTER LITTLE YUS
+0469          ; Ll #       CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS
+046B          ; Ll #       CYRILLIC SMALL LETTER BIG YUS
+046D          ; Ll #       CYRILLIC SMALL LETTER IOTIFIED BIG YUS
+046F          ; Ll #       CYRILLIC SMALL LETTER KSI
+0471          ; Ll #       CYRILLIC SMALL LETTER PSI
+0473          ; Ll #       CYRILLIC SMALL LETTER FITA
+0475          ; Ll #       CYRILLIC SMALL LETTER IZHITSA
+0477          ; Ll #       CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+0479          ; Ll #       CYRILLIC SMALL LETTER UK
+047B          ; Ll #       CYRILLIC SMALL LETTER ROUND OMEGA
+047D          ; Ll #       CYRILLIC SMALL LETTER OMEGA WITH TITLO
+047F          ; Ll #       CYRILLIC SMALL LETTER OT
+0481          ; Ll #       CYRILLIC SMALL LETTER KOPPA
+048B          ; Ll #       CYRILLIC SMALL LETTER SHORT I WITH TAIL
+048D          ; Ll #       CYRILLIC SMALL LETTER SEMISOFT SIGN
+048F          ; Ll #       CYRILLIC SMALL LETTER ER WITH TICK
+0491          ; Ll #       CYRILLIC SMALL LETTER GHE WITH UPTURN
+0493          ; Ll #       CYRILLIC SMALL LETTER GHE WITH STROKE
+0495          ; Ll #       CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK
+0497          ; Ll #       CYRILLIC SMALL LETTER ZHE WITH DESCENDER
+0499          ; Ll #       CYRILLIC SMALL LETTER ZE WITH DESCENDER
+049B          ; Ll #       CYRILLIC SMALL LETTER KA WITH DESCENDER
+049D          ; Ll #       CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE
+049F          ; Ll #       CYRILLIC SMALL LETTER KA WITH STROKE
+04A1          ; Ll #       CYRILLIC SMALL LETTER BASHKIR KA
+04A3          ; Ll #       CYRILLIC SMALL LETTER EN WITH DESCENDER
+04A5          ; Ll #       CYRILLIC SMALL LIGATURE EN GHE
+04A7          ; Ll #       CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK
+04A9          ; Ll #       CYRILLIC SMALL LETTER ABKHASIAN HA
+04AB          ; Ll #       CYRILLIC SMALL LETTER ES WITH DESCENDER
+04AD          ; Ll #       CYRILLIC SMALL LETTER TE WITH DESCENDER
+04AF          ; Ll #       CYRILLIC SMALL LETTER STRAIGHT U
+04B1          ; Ll #       CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE
+04B3          ; Ll #       CYRILLIC SMALL LETTER HA WITH DESCENDER
+04B5          ; Ll #       CYRILLIC SMALL LIGATURE TE TSE
+04B7          ; Ll #       CYRILLIC SMALL LETTER CHE WITH DESCENDER
+04B9          ; Ll #       CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE
+04BB          ; Ll #       CYRILLIC SMALL LETTER SHHA
+04BD          ; Ll #       CYRILLIC SMALL LETTER ABKHASIAN CHE
+04BF          ; Ll #       CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER
+04C2          ; Ll #       CYRILLIC SMALL LETTER ZHE WITH BREVE
+04C4          ; Ll #       CYRILLIC SMALL LETTER KA WITH HOOK
+04C6          ; Ll #       CYRILLIC SMALL LETTER EL WITH TAIL
+04C8          ; Ll #       CYRILLIC SMALL LETTER EN WITH HOOK
+04CA          ; Ll #       CYRILLIC SMALL LETTER EN WITH TAIL
+04CC          ; Ll #       CYRILLIC SMALL LETTER KHAKASSIAN CHE
+04CE..04CF    ; Ll #   [2] CYRILLIC SMALL LETTER EM WITH TAIL..CYRILLIC SMALL LETTER PALOCHKA
+04D1          ; Ll #       CYRILLIC SMALL LETTER A WITH BREVE
+04D3          ; Ll #       CYRILLIC SMALL LETTER A WITH DIAERESIS
+04D5          ; Ll #       CYRILLIC SMALL LIGATURE A IE
+04D7          ; Ll #       CYRILLIC SMALL LETTER IE WITH BREVE
+04D9          ; Ll #       CYRILLIC SMALL LETTER SCHWA
+04DB          ; Ll #       CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS
+04DD          ; Ll #       CYRILLIC SMALL LETTER ZHE WITH DIAERESIS
+04DF          ; Ll #       CYRILLIC SMALL LETTER ZE WITH DIAERESIS
+04E1          ; Ll #       CYRILLIC SMALL LETTER ABKHASIAN DZE
+04E3          ; Ll #       CYRILLIC SMALL LETTER I WITH MACRON
+04E5          ; Ll #       CYRILLIC SMALL LETTER I WITH DIAERESIS
+04E7          ; Ll #       CYRILLIC SMALL LETTER O WITH DIAERESIS
+04E9          ; Ll #       CYRILLIC SMALL LETTER BARRED O
+04EB          ; Ll #       CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS
+04ED          ; Ll #       CYRILLIC SMALL LETTER E WITH DIAERESIS
+04EF          ; Ll #       CYRILLIC SMALL LETTER U WITH MACRON
+04F1          ; Ll #       CYRILLIC SMALL LETTER U WITH DIAERESIS
+04F3          ; Ll #       CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE
+04F5          ; Ll #       CYRILLIC SMALL LETTER CHE WITH DIAERESIS
+04F7          ; Ll #       CYRILLIC SMALL LETTER GHE WITH DESCENDER
+04F9          ; Ll #       CYRILLIC SMALL LETTER YERU WITH DIAERESIS
+04FB          ; Ll #       CYRILLIC SMALL LETTER GHE WITH STROKE AND HOOK
+04FD          ; Ll #       CYRILLIC SMALL LETTER HA WITH HOOK
+04FF          ; Ll #       CYRILLIC SMALL LETTER HA WITH STROKE
+0501          ; Ll #       CYRILLIC SMALL LETTER KOMI DE
+0503          ; Ll #       CYRILLIC SMALL LETTER KOMI DJE
+0505          ; Ll #       CYRILLIC SMALL LETTER KOMI ZJE
+0507          ; Ll #       CYRILLIC SMALL LETTER KOMI DZJE
+0509          ; Ll #       CYRILLIC SMALL LETTER KOMI LJE
+050B          ; Ll #       CYRILLIC SMALL LETTER KOMI NJE
+050D          ; Ll #       CYRILLIC SMALL LETTER KOMI SJE
+050F          ; Ll #       CYRILLIC SMALL LETTER KOMI TJE
+0511          ; Ll #       CYRILLIC SMALL LETTER REVERSED ZE
+0513          ; Ll #       CYRILLIC SMALL LETTER EL WITH HOOK
+0515          ; Ll #       CYRILLIC SMALL LETTER LHA
+0517          ; Ll #       CYRILLIC SMALL LETTER RHA
+0519          ; Ll #       CYRILLIC SMALL LETTER YAE
+051B          ; Ll #       CYRILLIC SMALL LETTER QA
+051D          ; Ll #       CYRILLIC SMALL LETTER WE
+051F          ; Ll #       CYRILLIC SMALL LETTER ALEUT KA
+0521          ; Ll #       CYRILLIC SMALL LETTER EL WITH MIDDLE HOOK
+0523          ; Ll #       CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK
+0561..0587    ; Ll #  [39] ARMENIAN SMALL LETTER AYB..ARMENIAN SMALL LIGATURE ECH YIWN
+1D00..1D2B    ; Ll #  [44] LATIN LETTER SMALL CAPITAL A..CYRILLIC LETTER SMALL CAPITAL EL
+1D62..1D77    ; Ll #  [22] LATIN SUBSCRIPT SMALL LETTER I..LATIN SMALL LETTER TURNED G
+1D79..1D9A    ; Ll #  [34] LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK
+1E01          ; Ll #       LATIN SMALL LETTER A WITH RING BELOW
+1E03          ; Ll #       LATIN SMALL LETTER B WITH DOT ABOVE
+1E05          ; Ll #       LATIN SMALL LETTER B WITH DOT BELOW
+1E07          ; Ll #       LATIN SMALL LETTER B WITH LINE BELOW
+1E09          ; Ll #       LATIN SMALL LETTER C WITH CEDILLA AND ACUTE
+1E0B          ; Ll #       LATIN SMALL LETTER D WITH DOT ABOVE
+1E0D          ; Ll #       LATIN SMALL LETTER D WITH DOT BELOW
+1E0F          ; Ll #       LATIN SMALL LETTER D WITH LINE BELOW
+1E11          ; Ll #       LATIN SMALL LETTER D WITH CEDILLA
+1E13          ; Ll #       LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW
+1E15          ; Ll #       LATIN SMALL LETTER E WITH MACRON AND GRAVE
+1E17          ; Ll #       LATIN SMALL LETTER E WITH MACRON AND ACUTE
+1E19          ; Ll #       LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW
+1E1B          ; Ll #       LATIN SMALL LETTER E WITH TILDE BELOW
+1E1D          ; Ll #       LATIN SMALL LETTER E WITH CEDILLA AND BREVE
+1E1F          ; Ll #       LATIN SMALL LETTER F WITH DOT ABOVE
+1E21          ; Ll #       LATIN SMALL LETTER G WITH MACRON
+1E23          ; Ll #       LATIN SMALL LETTER H WITH DOT ABOVE
+1E25          ; Ll #       LATIN SMALL LETTER H WITH DOT BELOW
+1E27          ; Ll #       LATIN SMALL LETTER H WITH DIAERESIS
+1E29          ; Ll #       LATIN SMALL LETTER H WITH CEDILLA
+1E2B          ; Ll #       LATIN SMALL LETTER H WITH BREVE BELOW
+1E2D          ; Ll #       LATIN SMALL LETTER I WITH TILDE BELOW
+1E2F          ; Ll #       LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE
+1E31          ; Ll #       LATIN SMALL LETTER K WITH ACUTE
+1E33          ; Ll #       LATIN SMALL LETTER K WITH DOT BELOW
+1E35          ; Ll #       LATIN SMALL LETTER K WITH LINE BELOW
+1E37          ; Ll #       LATIN SMALL LETTER L WITH DOT BELOW
+1E39          ; Ll #       LATIN SMALL LETTER L WITH DOT BELOW AND MACRON
+1E3B          ; Ll #       LATIN SMALL LETTER L WITH LINE BELOW
+1E3D          ; Ll #       LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW
+1E3F          ; Ll #       LATIN SMALL LETTER M WITH ACUTE
+1E41          ; Ll #       LATIN SMALL LETTER M WITH DOT ABOVE
+1E43          ; Ll #       LATIN SMALL LETTER M WITH DOT BELOW
+1E45          ; Ll #       LATIN SMALL LETTER N WITH DOT ABOVE
+1E47          ; Ll #       LATIN SMALL LETTER N WITH DOT BELOW
+1E49          ; Ll #       LATIN SMALL LETTER N WITH LINE BELOW
+1E4B          ; Ll #       LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW
+1E4D          ; Ll #       LATIN SMALL LETTER O WITH TILDE AND ACUTE
+1E4F          ; Ll #       LATIN SMALL LETTER O WITH TILDE AND DIAERESIS
+1E51          ; Ll #       LATIN SMALL LETTER O WITH MACRON AND GRAVE
+1E53          ; Ll #       LATIN SMALL LETTER O WITH MACRON AND ACUTE
+1E55          ; Ll #       LATIN SMALL LETTER P WITH ACUTE
+1E57          ; Ll #       LATIN SMALL LETTER P WITH DOT ABOVE
+1E59          ; Ll #       LATIN SMALL LETTER R WITH DOT ABOVE
+1E5B          ; Ll #       LATIN SMALL LETTER R WITH DOT BELOW
+1E5D          ; Ll #       LATIN SMALL LETTER R WITH DOT BELOW AND MACRON
+1E5F          ; Ll #       LATIN SMALL LETTER R WITH LINE BELOW
+1E61          ; Ll #       LATIN SMALL LETTER S WITH DOT ABOVE
+1E63          ; Ll #       LATIN SMALL LETTER S WITH DOT BELOW
+1E65          ; Ll #       LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE
+1E67          ; Ll #       LATIN SMALL LETTER S WITH CARON AND DOT ABOVE
+1E69          ; Ll #       LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE
+1E6B          ; Ll #       LATIN SMALL LETTER T WITH DOT ABOVE
+1E6D          ; Ll #       LATIN SMALL LETTER T WITH DOT BELOW
+1E6F          ; Ll #       LATIN SMALL LETTER T WITH LINE BELOW
+1E71          ; Ll #       LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW
+1E73          ; Ll #       LATIN SMALL LETTER U WITH DIAERESIS BELOW
+1E75          ; Ll #       LATIN SMALL LETTER U WITH TILDE BELOW
+1E77          ; Ll #       LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW
+1E79          ; Ll #       LATIN SMALL LETTER U WITH TILDE AND ACUTE
+1E7B          ; Ll #       LATIN SMALL LETTER U WITH MACRON AND DIAERESIS
+1E7D          ; Ll #       LATIN SMALL LETTER V WITH TILDE
+1E7F          ; Ll #       LATIN SMALL LETTER V WITH DOT BELOW
+1E81          ; Ll #       LATIN SMALL LETTER W WITH GRAVE
+1E83          ; Ll #       LATIN SMALL LETTER W WITH ACUTE
+1E85          ; Ll #       LATIN SMALL LETTER W WITH DIAERESIS
+1E87          ; Ll #       LATIN SMALL LETTER W WITH DOT ABOVE
+1E89          ; Ll #       LATIN SMALL LETTER W WITH DOT BELOW
+1E8B          ; Ll #       LATIN SMALL LETTER X WITH DOT ABOVE
+1E8D          ; Ll #       LATIN SMALL LETTER X WITH DIAERESIS
+1E8F          ; Ll #       LATIN SMALL LETTER Y WITH DOT ABOVE
+1E91          ; Ll #       LATIN SMALL LETTER Z WITH CIRCUMFLEX
+1E93          ; Ll #       LATIN SMALL LETTER Z WITH DOT BELOW
+1E95..1E9D    ; Ll #   [9] LATIN SMALL LETTER Z WITH LINE BELOW..LATIN SMALL LETTER LONG S WITH HIGH STROKE
+1E9F          ; Ll #       LATIN SMALL LETTER DELTA
+1EA1          ; Ll #       LATIN SMALL LETTER A WITH DOT BELOW
+1EA3          ; Ll #       LATIN SMALL LETTER A WITH HOOK ABOVE
+1EA5          ; Ll #       LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE
+1EA7          ; Ll #       LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE
+1EA9          ; Ll #       LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+1EAB          ; Ll #       LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE
+1EAD          ; Ll #       LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+1EAF          ; Ll #       LATIN SMALL LETTER A WITH BREVE AND ACUTE
+1EB1          ; Ll #       LATIN SMALL LETTER A WITH BREVE AND GRAVE
+1EB3          ; Ll #       LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE
+1EB5          ; Ll #       LATIN SMALL LETTER A WITH BREVE AND TILDE
+1EB7          ; Ll #       LATIN SMALL LETTER A WITH BREVE AND DOT BELOW
+1EB9          ; Ll #       LATIN SMALL LETTER E WITH DOT BELOW
+1EBB          ; Ll #       LATIN SMALL LETTER E WITH HOOK ABOVE
+1EBD          ; Ll #       LATIN SMALL LETTER E WITH TILDE
+1EBF          ; Ll #       LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE
+1EC1          ; Ll #       LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE
+1EC3          ; Ll #       LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+1EC5          ; Ll #       LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE
+1EC7          ; Ll #       LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+1EC9          ; Ll #       LATIN SMALL LETTER I WITH HOOK ABOVE
+1ECB          ; Ll #       LATIN SMALL LETTER I WITH DOT BELOW
+1ECD          ; Ll #       LATIN SMALL LETTER O WITH DOT BELOW
+1ECF          ; Ll #       LATIN SMALL LETTER O WITH HOOK ABOVE
+1ED1          ; Ll #       LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE
+1ED3          ; Ll #       LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE
+1ED5          ; Ll #       LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+1ED7          ; Ll #       LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE
+1ED9          ; Ll #       LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+1EDB          ; Ll #       LATIN SMALL LETTER O WITH HORN AND ACUTE
+1EDD          ; Ll #       LATIN SMALL LETTER O WITH HORN AND GRAVE
+1EDF          ; Ll #       LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE
+1EE1          ; Ll #       LATIN SMALL LETTER O WITH HORN AND TILDE
+1EE3          ; Ll #       LATIN SMALL LETTER O WITH HORN AND DOT BELOW
+1EE5          ; Ll #       LATIN SMALL LETTER U WITH DOT BELOW
+1EE7          ; Ll #       LATIN SMALL LETTER U WITH HOOK ABOVE
+1EE9          ; Ll #       LATIN SMALL LETTER U WITH HORN AND ACUTE
+1EEB          ; Ll #       LATIN SMALL LETTER U WITH HORN AND GRAVE
+1EED          ; Ll #       LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE
+1EEF          ; Ll #       LATIN SMALL LETTER U WITH HORN AND TILDE
+1EF1          ; Ll #       LATIN SMALL LETTER U WITH HORN AND DOT BELOW
+1EF3          ; Ll #       LATIN SMALL LETTER Y WITH GRAVE
+1EF5          ; Ll #       LATIN SMALL LETTER Y WITH DOT BELOW
+1EF7          ; Ll #       LATIN SMALL LETTER Y WITH HOOK ABOVE
+1EF9          ; Ll #       LATIN SMALL LETTER Y WITH TILDE
+1EFB          ; Ll #       LATIN SMALL LETTER MIDDLE-WELSH LL
+1EFD          ; Ll #       LATIN SMALL LETTER MIDDLE-WELSH V
+1EFF..1F07    ; Ll #   [9] LATIN SMALL LETTER Y WITH LOOP..GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI
+1F10..1F15    ; Ll #   [6] GREEK SMALL LETTER EPSILON WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+1F20..1F27    ; Ll #   [8] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI
+1F30..1F37    ; Ll #   [8] GREEK SMALL LETTER IOTA WITH PSILI..GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI
+1F40..1F45    ; Ll #   [6] GREEK SMALL LETTER OMICRON WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+1F50..1F57    ; Ll #   [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F60..1F67    ; Ll #   [8] GREEK SMALL LETTER OMEGA WITH PSILI..GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI
+1F70..1F7D    ; Ll #  [14] GREEK SMALL LETTER ALPHA WITH VARIA..GREEK SMALL LETTER OMEGA WITH OXIA
+1F80..1F87    ; Ll #   [8] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1F90..1F97    ; Ll #   [8] GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1FA0..1FA7    ; Ll #   [8] GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1FB0..1FB4    ; Ll #   [5] GREEK SMALL LETTER ALPHA WITH VRACHY..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+1FB6..1FB7    ; Ll #   [2] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FBE          ; Ll #       GREEK PROSGEGRAMMENI
+1FC2..1FC4    ; Ll #   [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+1FC6..1FC7    ; Ll #   [2] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FD0..1FD3    ; Ll #   [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FD6..1FD7    ; Ll #   [2] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI
+1FE0..1FE7    ; Ll #   [8] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI
+1FF2..1FF4    ; Ll #   [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+1FF6..1FF7    ; Ll #   [2] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI
+2071          ; Ll #       SUPERSCRIPT LATIN SMALL LETTER I
+207F          ; Ll #       SUPERSCRIPT LATIN SMALL LETTER N
+210A          ; Ll #       SCRIPT SMALL G
+210E..210F    ; Ll #   [2] PLANCK CONSTANT..PLANCK CONSTANT OVER TWO PI
+2113          ; Ll #       SCRIPT SMALL L
+212F          ; Ll #       SCRIPT SMALL E
+2134          ; Ll #       SCRIPT SMALL O
+2139          ; Ll #       INFORMATION SOURCE
+213C..213D    ; Ll #   [2] DOUBLE-STRUCK SMALL PI..DOUBLE-STRUCK SMALL GAMMA
+2146..2149    ; Ll #   [4] DOUBLE-STRUCK ITALIC SMALL D..DOUBLE-STRUCK ITALIC SMALL J
+214E          ; Ll #       TURNED SMALL F
+2184          ; Ll #       LATIN SMALL LETTER REVERSED C
+2C30..2C5E    ; Ll #  [47] GLAGOLITIC SMALL LETTER AZU..GLAGOLITIC SMALL LETTER LATINATE MYSLITE
+2C61          ; Ll #       LATIN SMALL LETTER L WITH DOUBLE BAR
+2C65..2C66    ; Ll #   [2] LATIN SMALL LETTER A WITH STROKE..LATIN SMALL LETTER T WITH DIAGONAL STROKE
+2C68          ; Ll #       LATIN SMALL LETTER H WITH DESCENDER
+2C6A          ; Ll #       LATIN SMALL LETTER K WITH DESCENDER
+2C6C          ; Ll #       LATIN SMALL LETTER Z WITH DESCENDER
+2C71          ; Ll #       LATIN SMALL LETTER V WITH RIGHT HOOK
+2C73..2C74    ; Ll #   [2] LATIN SMALL LETTER W WITH HOOK..LATIN SMALL LETTER V WITH CURL
+2C76..2C7C    ; Ll #   [7] LATIN SMALL LETTER HALF H..LATIN SUBSCRIPT SMALL LETTER J
+2C81          ; Ll #       COPTIC SMALL LETTER ALFA
+2C83          ; Ll #       COPTIC SMALL LETTER VIDA
+2C85          ; Ll #       COPTIC SMALL LETTER GAMMA
+2C87          ; Ll #       COPTIC SMALL LETTER DALDA
+2C89          ; Ll #       COPTIC SMALL LETTER EIE
+2C8B          ; Ll #       COPTIC SMALL LETTER SOU
+2C8D          ; Ll #       COPTIC SMALL LETTER ZATA
+2C8F          ; Ll #       COPTIC SMALL LETTER HATE
+2C91          ; Ll #       COPTIC SMALL LETTER THETHE
+2C93          ; Ll #       COPTIC SMALL LETTER IAUDA
+2C95          ; Ll #       COPTIC SMALL LETTER KAPA
+2C97          ; Ll #       COPTIC SMALL LETTER LAULA
+2C99          ; Ll #       COPTIC SMALL LETTER MI
+2C9B          ; Ll #       COPTIC SMALL LETTER NI
+2C9D          ; Ll #       COPTIC SMALL LETTER KSI
+2C9F          ; Ll #       COPTIC SMALL LETTER O
+2CA1          ; Ll #       COPTIC SMALL LETTER PI
+2CA3          ; Ll #       COPTIC SMALL LETTER RO
+2CA5          ; Ll #       COPTIC SMALL LETTER SIMA
+2CA7          ; Ll #       COPTIC SMALL LETTER TAU
+2CA9          ; Ll #       COPTIC SMALL LETTER UA
+2CAB          ; Ll #       COPTIC SMALL LETTER FI
+2CAD          ; Ll #       COPTIC SMALL LETTER KHI
+2CAF          ; Ll #       COPTIC SMALL LETTER PSI
+2CB1          ; Ll #       COPTIC SMALL LETTER OOU
+2CB3          ; Ll #       COPTIC SMALL LETTER DIALECT-P ALEF
+2CB5          ; Ll #       COPTIC SMALL LETTER OLD COPTIC AIN
+2CB7          ; Ll #       COPTIC SMALL LETTER CRYPTOGRAMMIC EIE
+2CB9          ; Ll #       COPTIC SMALL LETTER DIALECT-P KAPA
+2CBB          ; Ll #       COPTIC SMALL LETTER DIALECT-P NI
+2CBD          ; Ll #       COPTIC SMALL LETTER CRYPTOGRAMMIC NI
+2CBF          ; Ll #       COPTIC SMALL LETTER OLD COPTIC OOU
+2CC1          ; Ll #       COPTIC SMALL LETTER SAMPI
+2CC3          ; Ll #       COPTIC SMALL LETTER CROSSED SHEI
+2CC5          ; Ll #       COPTIC SMALL LETTER OLD COPTIC SHEI
+2CC7          ; Ll #       COPTIC SMALL LETTER OLD COPTIC ESH
+2CC9          ; Ll #       COPTIC SMALL LETTER AKHMIMIC KHEI
+2CCB          ; Ll #       COPTIC SMALL LETTER DIALECT-P HORI
+2CCD          ; Ll #       COPTIC SMALL LETTER OLD COPTIC HORI
+2CCF          ; Ll #       COPTIC SMALL LETTER OLD COPTIC HA
+2CD1          ; Ll #       COPTIC SMALL LETTER L-SHAPED HA
+2CD3          ; Ll #       COPTIC SMALL LETTER OLD COPTIC HEI
+2CD5          ; Ll #       COPTIC SMALL LETTER OLD COPTIC HAT
+2CD7          ; Ll #       COPTIC SMALL LETTER OLD COPTIC GANGIA
+2CD9          ; Ll #       COPTIC SMALL LETTER OLD COPTIC DJA
+2CDB          ; Ll #       COPTIC SMALL LETTER OLD COPTIC SHIMA
+2CDD          ; Ll #       COPTIC SMALL LETTER OLD NUBIAN SHIMA
+2CDF          ; Ll #       COPTIC SMALL LETTER OLD NUBIAN NGI
+2CE1          ; Ll #       COPTIC SMALL LETTER OLD NUBIAN NYI
+2CE3..2CE4    ; Ll #   [2] COPTIC SMALL LETTER OLD NUBIAN WAU..COPTIC SYMBOL KAI
+2D00..2D25    ; Ll #  [38] GEORGIAN SMALL LETTER AN..GEORGIAN SMALL LETTER HOE
+A641          ; Ll #       CYRILLIC SMALL LETTER ZEMLYA
+A643          ; Ll #       CYRILLIC SMALL LETTER DZELO
+A645          ; Ll #       CYRILLIC SMALL LETTER REVERSED DZE
+A647          ; Ll #       CYRILLIC SMALL LETTER IOTA
+A649          ; Ll #       CYRILLIC SMALL LETTER DJERV
+A64B          ; Ll #       CYRILLIC SMALL LETTER MONOGRAPH UK
+A64D          ; Ll #       CYRILLIC SMALL LETTER BROAD OMEGA
+A64F          ; Ll #       CYRILLIC SMALL LETTER NEUTRAL YER
+A651          ; Ll #       CYRILLIC SMALL LETTER YERU WITH BACK YER
+A653          ; Ll #       CYRILLIC SMALL LETTER IOTIFIED YAT
+A655          ; Ll #       CYRILLIC SMALL LETTER REVERSED YU
+A657          ; Ll #       CYRILLIC SMALL LETTER IOTIFIED A
+A659          ; Ll #       CYRILLIC SMALL LETTER CLOSED LITTLE YUS
+A65B          ; Ll #       CYRILLIC SMALL LETTER BLENDED YUS
+A65D          ; Ll #       CYRILLIC SMALL LETTER IOTIFIED CLOSED LITTLE YUS
+A65F          ; Ll #       CYRILLIC SMALL LETTER YN
+A663          ; Ll #       CYRILLIC SMALL LETTER SOFT DE
+A665          ; Ll #       CYRILLIC SMALL LETTER SOFT EL
+A667          ; Ll #       CYRILLIC SMALL LETTER SOFT EM
+A669          ; Ll #       CYRILLIC SMALL LETTER MONOCULAR O
+A66B          ; Ll #       CYRILLIC SMALL LETTER BINOCULAR O
+A66D          ; Ll #       CYRILLIC SMALL LETTER DOUBLE MONOCULAR O
+A681          ; Ll #       CYRILLIC SMALL LETTER DWE
+A683          ; Ll #       CYRILLIC SMALL LETTER DZWE
+A685          ; Ll #       CYRILLIC SMALL LETTER ZHWE
+A687          ; Ll #       CYRILLIC SMALL LETTER CCHE
+A689          ; Ll #       CYRILLIC SMALL LETTER DZZE
+A68B          ; Ll #       CYRILLIC SMALL LETTER TE WITH MIDDLE HOOK
+A68D          ; Ll #       CYRILLIC SMALL LETTER TWE
+A68F          ; Ll #       CYRILLIC SMALL LETTER TSWE
+A691          ; Ll #       CYRILLIC SMALL LETTER TSSE
+A693          ; Ll #       CYRILLIC SMALL LETTER TCHE
+A695          ; Ll #       CYRILLIC SMALL LETTER HWE
+A697          ; Ll #       CYRILLIC SMALL LETTER SHWE
+A723          ; Ll #       LATIN SMALL LETTER EGYPTOLOGICAL ALEF
+A725          ; Ll #       LATIN SMALL LETTER EGYPTOLOGICAL AIN
+A727          ; Ll #       LATIN SMALL LETTER HENG
+A729          ; Ll #       LATIN SMALL LETTER TZ
+A72B          ; Ll #       LATIN SMALL LETTER TRESILLO
+A72D          ; Ll #       LATIN SMALL LETTER CUATRILLO
+A72F..A731    ; Ll #   [3] LATIN SMALL LETTER CUATRILLO WITH COMMA..LATIN LETTER SMALL CAPITAL S
+A733          ; Ll #       LATIN SMALL LETTER AA
+A735          ; Ll #       LATIN SMALL LETTER AO
+A737          ; Ll #       LATIN SMALL LETTER AU
+A739          ; Ll #       LATIN SMALL LETTER AV
+A73B          ; Ll #       LATIN SMALL LETTER AV WITH HORIZONTAL BAR
+A73D          ; Ll #       LATIN SMALL LETTER AY
+A73F          ; Ll #       LATIN SMALL LETTER REVERSED C WITH DOT
+A741          ; Ll #       LATIN SMALL LETTER K WITH STROKE
+A743          ; Ll #       LATIN SMALL LETTER K WITH DIAGONAL STROKE
+A745          ; Ll #       LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE
+A747          ; Ll #       LATIN SMALL LETTER BROKEN L
+A749          ; Ll #       LATIN SMALL LETTER L WITH HIGH STROKE
+A74B          ; Ll #       LATIN SMALL LETTER O WITH LONG STROKE OVERLAY
+A74D          ; Ll #       LATIN SMALL LETTER O WITH LOOP
+A74F          ; Ll #       LATIN SMALL LETTER OO
+A751          ; Ll #       LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER
+A753          ; Ll #       LATIN SMALL LETTER P WITH FLOURISH
+A755          ; Ll #       LATIN SMALL LETTER P WITH SQUIRREL TAIL
+A757          ; Ll #       LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER
+A759          ; Ll #       LATIN SMALL LETTER Q WITH DIAGONAL STROKE
+A75B          ; Ll #       LATIN SMALL LETTER R ROTUNDA
+A75D          ; Ll #       LATIN SMALL LETTER RUM ROTUNDA
+A75F          ; Ll #       LATIN SMALL LETTER V WITH DIAGONAL STROKE
+A761          ; Ll #       LATIN SMALL LETTER VY
+A763          ; Ll #       LATIN SMALL LETTER VISIGOTHIC Z
+A765          ; Ll #       LATIN SMALL LETTER THORN WITH STROKE
+A767          ; Ll #       LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER
+A769          ; Ll #       LATIN SMALL LETTER VEND
+A76B          ; Ll #       LATIN SMALL LETTER ET
+A76D          ; Ll #       LATIN SMALL LETTER IS
+A76F          ; Ll #       LATIN SMALL LETTER CON
+A771..A778    ; Ll #   [8] LATIN SMALL LETTER DUM..LATIN SMALL LETTER UM
+A77A          ; Ll #       LATIN SMALL LETTER INSULAR D
+A77C          ; Ll #       LATIN SMALL LETTER INSULAR F
+A77F          ; Ll #       LATIN SMALL LETTER TURNED INSULAR G
+A781          ; Ll #       LATIN SMALL LETTER TURNED L
+A783          ; Ll #       LATIN SMALL LETTER INSULAR R
+A785          ; Ll #       LATIN SMALL LETTER INSULAR S
+A787          ; Ll #       LATIN SMALL LETTER INSULAR T
+A78C          ; Ll #       LATIN SMALL LETTER SALTILLO
+FB00..FB06    ; Ll #   [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
+FB13..FB17    ; Ll #   [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
+FF41..FF5A    ; Ll #  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z
+10428..1044F  ; Ll #  [40] DESERET SMALL LETTER LONG I..DESERET SMALL LETTER EW
+1D41A..1D433  ; Ll #  [26] MATHEMATICAL BOLD SMALL A..MATHEMATICAL BOLD SMALL Z
+1D44E..1D454  ; Ll #   [7] MATHEMATICAL ITALIC SMALL A..MATHEMATICAL ITALIC SMALL G
+1D456..1D467  ; Ll #  [18] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL ITALIC SMALL Z
+1D482..1D49B  ; Ll #  [26] MATHEMATICAL BOLD ITALIC SMALL A..MATHEMATICAL BOLD ITALIC SMALL Z
+1D4B6..1D4B9  ; Ll #   [4] MATHEMATICAL SCRIPT SMALL A..MATHEMATICAL SCRIPT SMALL D
+1D4BB         ; Ll #       MATHEMATICAL SCRIPT SMALL F
+1D4BD..1D4C3  ; Ll #   [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N
+1D4C5..1D4CF  ; Ll #  [11] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL SCRIPT SMALL Z
+1D4EA..1D503  ; Ll #  [26] MATHEMATICAL BOLD SCRIPT SMALL A..MATHEMATICAL BOLD SCRIPT SMALL Z
+1D51E..1D537  ; Ll #  [26] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL FRAKTUR SMALL Z
+1D552..1D56B  ; Ll #  [26] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL DOUBLE-STRUCK SMALL Z
+1D586..1D59F  ; Ll #  [26] MATHEMATICAL BOLD FRAKTUR SMALL A..MATHEMATICAL BOLD FRAKTUR SMALL Z
+1D5BA..1D5D3  ; Ll #  [26] MATHEMATICAL SANS-SERIF SMALL A..MATHEMATICAL SANS-SERIF SMALL Z
+1D5EE..1D607  ; Ll #  [26] MATHEMATICAL SANS-SERIF BOLD SMALL A..MATHEMATICAL SANS-SERIF BOLD SMALL Z
+1D622..1D63B  ; Ll #  [26] MATHEMATICAL SANS-SERIF ITALIC SMALL A..MATHEMATICAL SANS-SERIF ITALIC SMALL Z
+1D656..1D66F  ; Ll #  [26] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z
+1D68A..1D6A5  ; Ll #  [28] MATHEMATICAL MONOSPACE SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D6C2..1D6DA  ; Ll #  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
+1D6DC..1D6E1  ; Ll #   [6] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL BOLD PI SYMBOL
+1D6FC..1D714  ; Ll #  [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA
+1D716..1D71B  ; Ll #   [6] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL ITALIC PI SYMBOL
+1D736..1D74E  ; Ll #  [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA
+1D750..1D755  ; Ll #   [6] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC PI SYMBOL
+1D770..1D788  ; Ll #  [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA
+1D78A..1D78F  ; Ll #   [6] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD PI SYMBOL
+1D7AA..1D7C2  ; Ll #  [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA
+1D7C4..1D7C9  ; Ll #   [6] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL
+1D7CB         ; Ll #       MATHEMATICAL BOLD SMALL DIGAMMA
+
+# Total code points: 1748
+
+# ================================================
+
+# General_Category=Titlecase_Letter
+
+01C5          ; Lt #       LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
+01C8          ; Lt #       LATIN CAPITAL LETTER L WITH SMALL LETTER J
+01CB          ; Lt #       LATIN CAPITAL LETTER N WITH SMALL LETTER J
+01F2          ; Lt #       LATIN CAPITAL LETTER D WITH SMALL LETTER Z
+1F88..1F8F    ; Lt #   [8] GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI..GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1F98..1F9F    ; Lt #   [8] GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI..GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1FA8..1FAF    ; Lt #   [8] GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI..GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1FBC          ; Lt #       GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FCC          ; Lt #       GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FFC          ; Lt #       GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+
+# Total code points: 31
+
+# ================================================
+
+# General_Category=Modifier_Letter
+
+02B0..02C1    ; Lm #  [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP
+02C6..02D1    ; Lm #  [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON
+02E0..02E4    ; Lm #   [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
+02EC          ; Lm #       MODIFIER LETTER VOICING
+02EE          ; Lm #       MODIFIER LETTER DOUBLE APOSTROPHE
+0374          ; Lm #       GREEK NUMERAL SIGN
+037A          ; Lm #       GREEK YPOGEGRAMMENI
+0559          ; Lm #       ARMENIAN MODIFIER LETTER LEFT HALF RING
+0640          ; Lm #       ARABIC TATWEEL
+06E5..06E6    ; Lm #   [2] ARABIC SMALL WAW..ARABIC SMALL YEH
+07F4..07F5    ; Lm #   [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE
+07FA          ; Lm #       NKO LAJANYALAN
+0971          ; Lm #       DEVANAGARI SIGN HIGH SPACING DOT
+0E46          ; Lm #       THAI CHARACTER MAIYAMOK
+0EC6          ; Lm #       LAO KO LA
+10FC          ; Lm #       MODIFIER LETTER GEORGIAN NAR
+17D7          ; Lm #       KHMER SIGN LEK TOO
+1843          ; Lm #       MONGOLIAN LETTER TODO LONG VOWEL SIGN
+1C78..1C7D    ; Lm #   [6] OL CHIKI MU TTUDDAG..OL CHIKI AHAD
+1D2C..1D61    ; Lm #  [54] MODIFIER LETTER CAPITAL A..MODIFIER LETTER SMALL CHI
+1D78          ; Lm #       MODIFIER LETTER CYRILLIC EN
+1D9B..1DBF    ; Lm #  [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA
+2090..2094    ; Lm #   [5] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER SCHWA
+2C7D          ; Lm #       MODIFIER LETTER CAPITAL V
+2D6F          ; Lm #       TIFINAGH MODIFIER LETTER LABIALIZATION MARK
+2E2F          ; Lm #       VERTICAL TILDE
+3005          ; Lm #       IDEOGRAPHIC ITERATION MARK
+3031..3035    ; Lm #   [5] VERTICAL KANA REPEAT MARK..VERTICAL KANA REPEAT MARK LOWER HALF
+303B          ; Lm #       VERTICAL IDEOGRAPHIC ITERATION MARK
+309D..309E    ; Lm #   [2] HIRAGANA ITERATION MARK..HIRAGANA VOICED ITERATION MARK
+30FC..30FE    ; Lm #   [3] KATAKANA-HIRAGANA PROLONGED SOUND MARK..KATAKANA VOICED ITERATION MARK
+A015          ; Lm #       YI SYLLABLE WU
+A60C          ; Lm #       VAI SYLLABLE LENGTHENER
+A67F          ; Lm #       CYRILLIC PAYEROK
+A717..A71F    ; Lm #   [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK
+A770          ; Lm #       MODIFIER LETTER US
+A788          ; Lm #       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
+FF70          ; Lm #       HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
+FF9E..FF9F    ; Lm #   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+
+# Total code points: 187
+
+# ================================================
+
+# General_Category=Other_Letter
+
+01BB          ; Lo #       LATIN LETTER TWO WITH STROKE
+01C0..01C3    ; Lo #   [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
+0294          ; Lo #       LATIN LETTER GLOTTAL STOP
+05D0..05EA    ; Lo #  [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
+05F0..05F2    ; Lo #   [3] HEBREW LIGATURE YIDDISH DOUBLE VAV..HEBREW LIGATURE YIDDISH DOUBLE YOD
+0621..063F    ; Lo #  [31] ARABIC LETTER HAMZA..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE
+0641..064A    ; Lo #  [10] ARABIC LETTER FEH..ARABIC LETTER YEH
+066E..066F    ; Lo #   [2] ARABIC LETTER DOTLESS BEH..ARABIC LETTER DOTLESS QAF
+0671..06D3    ; Lo #  [99] ARABIC LETTER ALEF WASLA..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+06D5          ; Lo #       ARABIC LETTER AE
+06EE..06EF    ; Lo #   [2] ARABIC LETTER DAL WITH INVERTED V..ARABIC LETTER REH WITH INVERTED V
+06FA..06FC    ; Lo #   [3] ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC LETTER GHAIN WITH DOT BELOW
+06FF          ; Lo #       ARABIC LETTER HEH WITH INVERTED V
+0710          ; Lo #       SYRIAC LETTER ALAPH
+0712..072F    ; Lo #  [30] SYRIAC LETTER BETH..SYRIAC LETTER PERSIAN DHALATH
+074D..07A5    ; Lo #  [89] SYRIAC LETTER SOGDIAN ZHAIN..THAANA LETTER WAAVU
+07B1          ; Lo #       THAANA LETTER NAA
+07CA..07EA    ; Lo #  [33] NKO LETTER A..NKO LETTER JONA RA
+0904..0939    ; Lo #  [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA
+093D          ; Lo #       DEVANAGARI SIGN AVAGRAHA
+0950          ; Lo #       DEVANAGARI OM
+0958..0961    ; Lo #  [10] DEVANAGARI LETTER QA..DEVANAGARI LETTER VOCALIC LL
+0972          ; Lo #       DEVANAGARI LETTER CANDRA A
+097B..097F    ; Lo #   [5] DEVANAGARI LETTER GGA..DEVANAGARI LETTER BBA
+0985..098C    ; Lo #   [8] BENGALI LETTER A..BENGALI LETTER VOCALIC L
+098F..0990    ; Lo #   [2] BENGALI LETTER E..BENGALI LETTER AI
+0993..09A8    ; Lo #  [22] BENGALI LETTER O..BENGALI LETTER NA
+09AA..09B0    ; Lo #   [7] BENGALI LETTER PA..BENGALI LETTER RA
+09B2          ; Lo #       BENGALI LETTER LA
+09B6..09B9    ; Lo #   [4] BENGALI LETTER SHA..BENGALI LETTER HA
+09BD          ; Lo #       BENGALI SIGN AVAGRAHA
+09CE          ; Lo #       BENGALI LETTER KHANDA TA
+09DC..09DD    ; Lo #   [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF..09E1    ; Lo #   [3] BENGALI LETTER YYA..BENGALI LETTER VOCALIC LL
+09F0..09F1    ; Lo #   [2] BENGALI LETTER RA WITH MIDDLE DIAGONAL..BENGALI LETTER RA WITH LOWER DIAGONAL
+0A05..0A0A    ; Lo #   [6] GURMUKHI LETTER A..GURMUKHI LETTER UU
+0A0F..0A10    ; Lo #   [2] GURMUKHI LETTER EE..GURMUKHI LETTER AI
+0A13..0A28    ; Lo #  [22] GURMUKHI LETTER OO..GURMUKHI LETTER NA
+0A2A..0A30    ; Lo #   [7] GURMUKHI LETTER PA..GURMUKHI LETTER RA
+0A32..0A33    ; Lo #   [2] GURMUKHI LETTER LA..GURMUKHI LETTER LLA
+0A35..0A36    ; Lo #   [2] GURMUKHI LETTER VA..GURMUKHI LETTER SHA
+0A38..0A39    ; Lo #   [2] GURMUKHI LETTER SA..GURMUKHI LETTER HA
+0A59..0A5C    ; Lo #   [4] GURMUKHI LETTER KHHA..GURMUKHI LETTER RRA
+0A5E          ; Lo #       GURMUKHI LETTER FA
+0A72..0A74    ; Lo #   [3] GURMUKHI IRI..GURMUKHI EK ONKAR
+0A85..0A8D    ; Lo #   [9] GUJARATI LETTER A..GUJARATI VOWEL CANDRA E
+0A8F..0A91    ; Lo #   [3] GUJARATI LETTER E..GUJARATI VOWEL CANDRA O
+0A93..0AA8    ; Lo #  [22] GUJARATI LETTER O..GUJARATI LETTER NA
+0AAA..0AB0    ; Lo #   [7] GUJARATI LETTER PA..GUJARATI LETTER RA
+0AB2..0AB3    ; Lo #   [2] GUJARATI LETTER LA..GUJARATI LETTER LLA
+0AB5..0AB9    ; Lo #   [5] GUJARATI LETTER VA..GUJARATI LETTER HA
+0ABD          ; Lo #       GUJARATI SIGN AVAGRAHA
+0AD0          ; Lo #       GUJARATI OM
+0AE0..0AE1    ; Lo #   [2] GUJARATI LETTER VOCALIC RR..GUJARATI LETTER VOCALIC LL
+0B05..0B0C    ; Lo #   [8] ORIYA LETTER A..ORIYA LETTER VOCALIC L
+0B0F..0B10    ; Lo #   [2] ORIYA LETTER E..ORIYA LETTER AI
+0B13..0B28    ; Lo #  [22] ORIYA LETTER O..ORIYA LETTER NA
+0B2A..0B30    ; Lo #   [7] ORIYA LETTER PA..ORIYA LETTER RA
+0B32..0B33    ; Lo #   [2] ORIYA LETTER LA..ORIYA LETTER LLA
+0B35..0B39    ; Lo #   [5] ORIYA LETTER VA..ORIYA LETTER HA
+0B3D          ; Lo #       ORIYA SIGN AVAGRAHA
+0B5C..0B5D    ; Lo #   [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0B5F..0B61    ; Lo #   [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL
+0B71          ; Lo #       ORIYA LETTER WA
+0B83          ; Lo #       TAMIL SIGN VISARGA
+0B85..0B8A    ; Lo #   [6] TAMIL LETTER A..TAMIL LETTER UU
+0B8E..0B90    ; Lo #   [3] TAMIL LETTER E..TAMIL LETTER AI
+0B92..0B95    ; Lo #   [4] TAMIL LETTER O..TAMIL LETTER KA
+0B99..0B9A    ; Lo #   [2] TAMIL LETTER NGA..TAMIL LETTER CA
+0B9C          ; Lo #       TAMIL LETTER JA
+0B9E..0B9F    ; Lo #   [2] TAMIL LETTER NYA..TAMIL LETTER TTA
+0BA3..0BA4    ; Lo #   [2] TAMIL LETTER NNA..TAMIL LETTER TA
+0BA8..0BAA    ; Lo #   [3] TAMIL LETTER NA..TAMIL LETTER PA
+0BAE..0BB9    ; Lo #  [12] TAMIL LETTER MA..TAMIL LETTER HA
+0BD0          ; Lo #       TAMIL OM
+0C05..0C0C    ; Lo #   [8] TELUGU LETTER A..TELUGU LETTER VOCALIC L
+0C0E..0C10    ; Lo #   [3] TELUGU LETTER E..TELUGU LETTER AI
+0C12..0C28    ; Lo #  [23] TELUGU LETTER O..TELUGU LETTER NA
+0C2A..0C33    ; Lo #  [10] TELUGU LETTER PA..TELUGU LETTER LLA
+0C35..0C39    ; Lo #   [5] TELUGU LETTER VA..TELUGU LETTER HA
+0C3D          ; Lo #       TELUGU SIGN AVAGRAHA
+0C58..0C59    ; Lo #   [2] TELUGU LETTER TSA..TELUGU LETTER DZA
+0C60..0C61    ; Lo #   [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
+0C85..0C8C    ; Lo #   [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L
+0C8E..0C90    ; Lo #   [3] KANNADA LETTER E..KANNADA LETTER AI
+0C92..0CA8    ; Lo #  [23] KANNADA LETTER O..KANNADA LETTER NA
+0CAA..0CB3    ; Lo #  [10] KANNADA LETTER PA..KANNADA LETTER LLA
+0CB5..0CB9    ; Lo #   [5] KANNADA LETTER VA..KANNADA LETTER HA
+0CBD          ; Lo #       KANNADA SIGN AVAGRAHA
+0CDE          ; Lo #       KANNADA LETTER FA
+0CE0..0CE1    ; Lo #   [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
+0D05..0D0C    ; Lo #   [8] MALAYALAM LETTER A..MALAYALAM LETTER VOCALIC L
+0D0E..0D10    ; Lo #   [3] MALAYALAM LETTER E..MALAYALAM LETTER AI
+0D12..0D28    ; Lo #  [23] MALAYALAM LETTER O..MALAYALAM LETTER NA
+0D2A..0D39    ; Lo #  [16] MALAYALAM LETTER PA..MALAYALAM LETTER HA
+0D3D          ; Lo #       MALAYALAM SIGN AVAGRAHA
+0D60..0D61    ; Lo #   [2] MALAYALAM LETTER VOCALIC RR..MALAYALAM LETTER VOCALIC LL
+0D7A..0D7F    ; Lo #   [6] MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER CHILLU K
+0D85..0D96    ; Lo #  [18] SINHALA LETTER AYANNA..SINHALA LETTER AUYANNA
+0D9A..0DB1    ; Lo #  [24] SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA LETTER DANTAJA NAYANNA
+0DB3..0DBB    ; Lo #   [9] SINHALA LETTER SANYAKA DAYANNA..SINHALA LETTER RAYANNA
+0DBD          ; Lo #       SINHALA LETTER DANTAJA LAYANNA
+0DC0..0DC6    ; Lo #   [7] SINHALA LETTER VAYANNA..SINHALA LETTER FAYANNA
+0E01..0E30    ; Lo #  [48] THAI CHARACTER KO KAI..THAI CHARACTER SARA A
+0E32..0E33    ; Lo #   [2] THAI CHARACTER SARA AA..THAI CHARACTER SARA AM
+0E40..0E45    ; Lo #   [6] THAI CHARACTER SARA E..THAI CHARACTER LAKKHANGYAO
+0E81..0E82    ; Lo #   [2] LAO LETTER KO..LAO LETTER KHO SUNG
+0E84          ; Lo #       LAO LETTER KHO TAM
+0E87..0E88    ; Lo #   [2] LAO LETTER NGO..LAO LETTER CO
+0E8A          ; Lo #       LAO LETTER SO TAM
+0E8D          ; Lo #       LAO LETTER NYO
+0E94..0E97    ; Lo #   [4] LAO LETTER DO..LAO LETTER THO TAM
+0E99..0E9F    ; Lo #   [7] LAO LETTER NO..LAO LETTER FO SUNG
+0EA1..0EA3    ; Lo #   [3] LAO LETTER MO..LAO LETTER LO LING
+0EA5          ; Lo #       LAO LETTER LO LOOT
+0EA7          ; Lo #       LAO LETTER WO
+0EAA..0EAB    ; Lo #   [2] LAO LETTER SO SUNG..LAO LETTER HO SUNG
+0EAD..0EB0    ; Lo #   [4] LAO LETTER O..LAO VOWEL SIGN A
+0EB2..0EB3    ; Lo #   [2] LAO VOWEL SIGN AA..LAO VOWEL SIGN AM
+0EBD          ; Lo #       LAO SEMIVOWEL SIGN NYO
+0EC0..0EC4    ; Lo #   [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI
+0EDC..0EDD    ; Lo #   [2] LAO HO NO..LAO HO MO
+0F00          ; Lo #       TIBETAN SYLLABLE OM
+0F40..0F47    ; Lo #   [8] TIBETAN LETTER KA..TIBETAN LETTER JA
+0F49..0F6C    ; Lo #  [36] TIBETAN LETTER NYA..TIBETAN LETTER RRA
+0F88..0F8B    ; Lo #   [4] TIBETAN SIGN LCE TSA CAN..TIBETAN SIGN GRU MED RGYINGS
+1000..102A    ; Lo #  [43] MYANMAR LETTER KA..MYANMAR LETTER AU
+103F          ; Lo #       MYANMAR LETTER GREAT SA
+1050..1055    ; Lo #   [6] MYANMAR LETTER SHA..MYANMAR LETTER VOCALIC LL
+105A..105D    ; Lo #   [4] MYANMAR LETTER MON NGA..MYANMAR LETTER MON BBE
+1061          ; Lo #       MYANMAR LETTER SGAW KAREN SHA
+1065..1066    ; Lo #   [2] MYANMAR LETTER WESTERN PWO KAREN THA..MYANMAR LETTER WESTERN PWO KAREN PWA
+106E..1070    ; Lo #   [3] MYANMAR LETTER EASTERN PWO KAREN NNA..MYANMAR LETTER EASTERN PWO KAREN GHWA
+1075..1081    ; Lo #  [13] MYANMAR LETTER SHAN KA..MYANMAR LETTER SHAN HA
+108E          ; Lo #       MYANMAR LETTER RUMAI PALAUNG FA
+10D0..10FA    ; Lo #  [43] GEORGIAN LETTER AN..GEORGIAN LETTER AIN
+1100..1159    ; Lo #  [90] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG YEORINHIEUH
+115F..11A2    ; Lo #  [68] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG SSANGARAEA
+11A8..11F9    ; Lo #  [82] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG YEORINHIEUH
+1200..1248    ; Lo #  [73] ETHIOPIC SYLLABLE HA..ETHIOPIC SYLLABLE QWA
+124A..124D    ; Lo #   [4] ETHIOPIC SYLLABLE QWI..ETHIOPIC SYLLABLE QWE
+1250..1256    ; Lo #   [7] ETHIOPIC SYLLABLE QHA..ETHIOPIC SYLLABLE QHO
+1258          ; Lo #       ETHIOPIC SYLLABLE QHWA
+125A..125D    ; Lo #   [4] ETHIOPIC SYLLABLE QHWI..ETHIOPIC SYLLABLE QHWE
+1260..1288    ; Lo #  [41] ETHIOPIC SYLLABLE BA..ETHIOPIC SYLLABLE XWA
+128A..128D    ; Lo #   [4] ETHIOPIC SYLLABLE XWI..ETHIOPIC SYLLABLE XWE
+1290..12B0    ; Lo #  [33] ETHIOPIC SYLLABLE NA..ETHIOPIC SYLLABLE KWA
+12B2..12B5    ; Lo #   [4] ETHIOPIC SYLLABLE KWI..ETHIOPIC SYLLABLE KWE
+12B8..12BE    ; Lo #   [7] ETHIOPIC SYLLABLE KXA..ETHIOPIC SYLLABLE KXO
+12C0          ; Lo #       ETHIOPIC SYLLABLE KXWA
+12C2..12C5    ; Lo #   [4] ETHIOPIC SYLLABLE KXWI..ETHIOPIC SYLLABLE KXWE
+12C8..12D6    ; Lo #  [15] ETHIOPIC SYLLABLE WA..ETHIOPIC SYLLABLE PHARYNGEAL O
+12D8..1310    ; Lo #  [57] ETHIOPIC SYLLABLE ZA..ETHIOPIC SYLLABLE GWA
+1312..1315    ; Lo #   [4] ETHIOPIC SYLLABLE GWI..ETHIOPIC SYLLABLE GWE
+1318..135A    ; Lo #  [67] ETHIOPIC SYLLABLE GGA..ETHIOPIC SYLLABLE FYA
+1380..138F    ; Lo #  [16] ETHIOPIC SYLLABLE SEBATBEIT MWA..ETHIOPIC SYLLABLE PWE
+13A0..13F4    ; Lo #  [85] CHEROKEE LETTER A..CHEROKEE LETTER YV
+1401..166C    ; Lo # [620] CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA
+166F..1676    ; Lo #   [8] CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS NNGAA
+1681..169A    ; Lo #  [26] OGHAM LETTER BEITH..OGHAM LETTER PEITH
+16A0..16EA    ; Lo #  [75] RUNIC LETTER FEHU FEOH FE F..RUNIC LETTER X
+1700..170C    ; Lo #  [13] TAGALOG LETTER A..TAGALOG LETTER YA
+170E..1711    ; Lo #   [4] TAGALOG LETTER LA..TAGALOG LETTER HA
+1720..1731    ; Lo #  [18] HANUNOO LETTER A..HANUNOO LETTER HA
+1740..1751    ; Lo #  [18] BUHID LETTER A..BUHID LETTER HA
+1760..176C    ; Lo #  [13] TAGBANWA LETTER A..TAGBANWA LETTER YA
+176E..1770    ; Lo #   [3] TAGBANWA LETTER LA..TAGBANWA LETTER SA
+1780..17B3    ; Lo #  [52] KHMER LETTER KA..KHMER INDEPENDENT VOWEL QAU
+17DC          ; Lo #       KHMER SIGN AVAKRAHASANYA
+1820..1842    ; Lo #  [35] MONGOLIAN LETTER A..MONGOLIAN LETTER CHI
+1844..1877    ; Lo #  [52] MONGOLIAN LETTER TODO E..MONGOLIAN LETTER MANCHU ZHA
+1880..18A8    ; Lo #  [41] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER MANCHU ALI GALI BHA
+18AA          ; Lo #       MONGOLIAN LETTER MANCHU ALI GALI LHA
+1900..191C    ; Lo #  [29] LIMBU VOWEL-CARRIER LETTER..LIMBU LETTER HA
+1950..196D    ; Lo #  [30] TAI LE LETTER KA..TAI LE LETTER AI
+1970..1974    ; Lo #   [5] TAI LE LETTER TONE-2..TAI LE LETTER TONE-6
+1980..19A9    ; Lo #  [42] NEW TAI LUE LETTER HIGH QA..NEW TAI LUE LETTER LOW XVA
+19C1..19C7    ; Lo #   [7] NEW TAI LUE LETTER FINAL V..NEW TAI LUE LETTER FINAL B
+1A00..1A16    ; Lo #  [23] BUGINESE LETTER KA..BUGINESE LETTER HA
+1B05..1B33    ; Lo #  [47] BALINESE LETTER AKARA..BALINESE LETTER HA
+1B45..1B4B    ; Lo #   [7] BALINESE LETTER KAF SASAK..BALINESE LETTER ASYURA SASAK
+1B83..1BA0    ; Lo #  [30] SUNDANESE LETTER A..SUNDANESE LETTER HA
+1BAE..1BAF    ; Lo #   [2] SUNDANESE LETTER KHA..SUNDANESE LETTER SYA
+1C00..1C23    ; Lo #  [36] LEPCHA LETTER KA..LEPCHA LETTER A
+1C4D..1C4F    ; Lo #   [3] LEPCHA LETTER TTA..LEPCHA LETTER DDA
+1C5A..1C77    ; Lo #  [30] OL CHIKI LETTER LA..OL CHIKI LETTER OH
+2135..2138    ; Lo #   [4] ALEF SYMBOL..DALET SYMBOL
+2D30..2D65    ; Lo #  [54] TIFINAGH LETTER YA..TIFINAGH LETTER YAZZ
+2D80..2D96    ; Lo #  [23] ETHIOPIC SYLLABLE LOA..ETHIOPIC SYLLABLE GGWE
+2DA0..2DA6    ; Lo #   [7] ETHIOPIC SYLLABLE SSA..ETHIOPIC SYLLABLE SSO
+2DA8..2DAE    ; Lo #   [7] ETHIOPIC SYLLABLE CCA..ETHIOPIC SYLLABLE CCO
+2DB0..2DB6    ; Lo #   [7] ETHIOPIC SYLLABLE ZZA..ETHIOPIC SYLLABLE ZZO
+2DB8..2DBE    ; Lo #   [7] ETHIOPIC SYLLABLE CCHA..ETHIOPIC SYLLABLE CCHO
+2DC0..2DC6    ; Lo #   [7] ETHIOPIC SYLLABLE QYA..ETHIOPIC SYLLABLE QYO
+2DC8..2DCE    ; Lo #   [7] ETHIOPIC SYLLABLE KYA..ETHIOPIC SYLLABLE KYO
+2DD0..2DD6    ; Lo #   [7] ETHIOPIC SYLLABLE XYA..ETHIOPIC SYLLABLE XYO
+2DD8..2DDE    ; Lo #   [7] ETHIOPIC SYLLABLE GYA..ETHIOPIC SYLLABLE GYO
+3006          ; Lo #       IDEOGRAPHIC CLOSING MARK
+303C          ; Lo #       MASU MARK
+3041..3096    ; Lo #  [86] HIRAGANA LETTER SMALL A..HIRAGANA LETTER SMALL KE
+309F          ; Lo #       HIRAGANA DIGRAPH YORI
+30A1..30FA    ; Lo #  [90] KATAKANA LETTER SMALL A..KATAKANA LETTER VO
+30FF          ; Lo #       KATAKANA DIGRAPH KOTO
+3105..312D    ; Lo #  [41] BOPOMOFO LETTER B..BOPOMOFO LETTER IH
+3131..318E    ; Lo #  [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE
+31A0..31B7    ; Lo #  [24] BOPOMOFO LETTER BU..BOPOMOFO FINAL LETTER H
+31F0..31FF    ; Lo #  [16] KATAKANA LETTER SMALL KU..KATAKANA LETTER SMALL RO
+3400..4DB5    ; Lo # [6582] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DB5
+4E00..9FC3    ; Lo # [20932] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FC3
+A000..A014    ; Lo #  [21] YI SYLLABLE IT..YI SYLLABLE E
+A016..A48C    ; Lo # [1143] YI SYLLABLE BIT..YI SYLLABLE YYR
+A500..A60B    ; Lo # [268] VAI SYLLABLE EE..VAI SYLLABLE NG
+A610..A61F    ; Lo #  [16] VAI SYLLABLE NDOLE FA..VAI SYMBOL JONG
+A62A..A62B    ; Lo #   [2] VAI SYLLABLE NDOLE MA..VAI SYLLABLE NDOLE DO
+A66E          ; Lo #       CYRILLIC LETTER MULTIOCULAR O
+A7FB..A801    ; Lo #   [7] LATIN EPIGRAPHIC LETTER REVERSED F..SYLOTI NAGRI LETTER I
+A803..A805    ; Lo #   [3] SYLOTI NAGRI LETTER U..SYLOTI NAGRI LETTER O
+A807..A80A    ; Lo #   [4] SYLOTI NAGRI LETTER KO..SYLOTI NAGRI LETTER GHO
+A80C..A822    ; Lo #  [23] SYLOTI NAGRI LETTER CO..SYLOTI NAGRI LETTER HO
+A840..A873    ; Lo #  [52] PHAGS-PA LETTER KA..PHAGS-PA LETTER CANDRABINDU
+A882..A8B3    ; Lo #  [50] SAURASHTRA LETTER A..SAURASHTRA LETTER LLA
+A90A..A925    ; Lo #  [28] KAYAH LI LETTER KA..KAYAH LI LETTER OO
+A930..A946    ; Lo #  [23] REJANG LETTER KA..REJANG LETTER A
+AA00..AA28    ; Lo #  [41] CHAM LETTER A..CHAM LETTER HA
+AA40..AA42    ; Lo #   [3] CHAM LETTER FINAL K..CHAM LETTER FINAL NG
+AA44..AA4B    ; Lo #   [8] CHAM LETTER FINAL CH..CHAM LETTER FINAL SS
+AC00..D7A3    ; Lo # [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH
+F900..FA2D    ; Lo # [302] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA2D
+FA30..FA6A    ; Lo #  [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A
+FA70..FAD9    ; Lo # [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9
+FB1D          ; Lo #       HEBREW LETTER YOD WITH HIRIQ
+FB1F..FB28    ; Lo #  [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV
+FB2A..FB36    ; Lo #  [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C    ; Lo #   [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E          ; Lo #       HEBREW LETTER MEM WITH DAGESH
+FB40..FB41    ; Lo #   [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44    ; Lo #   [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FBB1    ; Lo # [108] HEBREW LETTER TSADI WITH DAGESH..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
+FBD3..FD3D    ; Lo # [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+FD50..FD8F    ; Lo #  [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD92..FDC7    ; Lo #  [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+FDF0..FDFB    ; Lo #  [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
+FE70..FE74    ; Lo #   [5] ARABIC FATHATAN ISOLATED FORM..ARABIC KASRATAN ISOLATED FORM
+FE76..FEFC    ; Lo # [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+FF66..FF6F    ; Lo #  [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU
+FF71..FF9D    ; Lo #  [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N
+FFA0..FFBE    ; Lo #  [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH
+FFC2..FFC7    ; Lo #   [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E
+FFCA..FFCF    ; Lo #   [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE
+FFD2..FFD7    ; Lo #   [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU
+FFDA..FFDC    ; Lo #   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
+10000..1000B  ; Lo #  [12] LINEAR B SYLLABLE B008 A..LINEAR B SYLLABLE B046 JE
+1000D..10026  ; Lo #  [26] LINEAR B SYLLABLE B036 JO..LINEAR B SYLLABLE B032 QO
+10028..1003A  ; Lo #  [19] LINEAR B SYLLABLE B060 RA..LINEAR B SYLLABLE B042 WO
+1003C..1003D  ; Lo #   [2] LINEAR B SYLLABLE B017 ZA..LINEAR B SYLLABLE B074 ZE
+1003F..1004D  ; Lo #  [15] LINEAR B SYLLABLE B020 ZO..LINEAR B SYLLABLE B091 TWO
+10050..1005D  ; Lo #  [14] LINEAR B SYMBOL B018..LINEAR B SYMBOL B089
+10080..100FA  ; Lo # [123] LINEAR B IDEOGRAM B100 MAN..LINEAR B IDEOGRAM VESSEL B305
+10280..1029C  ; Lo #  [29] LYCIAN LETTER A..LYCIAN LETTER X
+102A0..102D0  ; Lo #  [49] CARIAN LETTER A..CARIAN LETTER UUU3
+10300..1031E  ; Lo #  [31] OLD ITALIC LETTER A..OLD ITALIC LETTER UU
+10330..10340  ; Lo #  [17] GOTHIC LETTER AHSA..GOTHIC LETTER PAIRTHRA
+10342..10349  ; Lo #   [8] GOTHIC LETTER RAIDA..GOTHIC LETTER OTHAL
+10380..1039D  ; Lo #  [30] UGARITIC LETTER ALPA..UGARITIC LETTER SSU
+103A0..103C3  ; Lo #  [36] OLD PERSIAN SIGN A..OLD PERSIAN SIGN HA
+103C8..103CF  ; Lo #   [8] OLD PERSIAN SIGN AURAMAZDAA..OLD PERSIAN SIGN BUUMISH
+10450..1049D  ; Lo #  [78] SHAVIAN LETTER PEEP..OSMANYA LETTER OO
+10800..10805  ; Lo #   [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
+10808         ; Lo #       CYPRIOT SYLLABLE JO
+1080A..10835  ; Lo #  [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
+10837..10838  ; Lo #   [2] CYPRIOT SYLLABLE XA..CYPRIOT SYLLABLE XE
+1083C         ; Lo #       CYPRIOT SYLLABLE ZA
+1083F         ; Lo #       CYPRIOT SYLLABLE ZO
+10900..10915  ; Lo #  [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
+10920..10939  ; Lo #  [26] LYDIAN LETTER A..LYDIAN LETTER C
+10A00         ; Lo #       KHAROSHTHI LETTER A
+10A10..10A13  ; Lo #   [4] KHAROSHTHI LETTER KA..KHAROSHTHI LETTER GHA
+10A15..10A17  ; Lo #   [3] KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA
+10A19..10A33  ; Lo #  [27] KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER TTTHA
+12000..1236E  ; Lo # [879] CUNEIFORM SIGN A..CUNEIFORM SIGN ZUM
+20000..2A6D6  ; Lo # [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6
+2F800..2FA1D  ; Lo # [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+
+# Total code points: 90068
+
+# ================================================
+
+# General_Category=Nonspacing_Mark
+
+0300..036F    ; Mn # [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X
+0483..0487    ; Mn #   [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE
+0591..05BD    ; Mn #  [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
+05BF          ; Mn #       HEBREW POINT RAFE
+05C1..05C2    ; Mn #   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
+05C4..05C5    ; Mn #   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
+05C7          ; Mn #       HEBREW POINT QAMATS QATAN
+0610..061A    ; Mn #  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
+064B..065E    ; Mn #  [20] ARABIC FATHATAN..ARABIC FATHA WITH TWO DOTS
+0670          ; Mn #       ARABIC LETTER SUPERSCRIPT ALEF
+06D6..06DC    ; Mn #   [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN
+06DF..06E4    ; Mn #   [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA
+06E7..06E8    ; Mn #   [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON
+06EA..06ED    ; Mn #   [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM
+0711          ; Mn #       SYRIAC LETTER SUPERSCRIPT ALAPH
+0730..074A    ; Mn #  [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH
+07A6..07B0    ; Mn #  [11] THAANA ABAFILI..THAANA SUKUN
+07EB..07F3    ; Mn #   [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE
+0901..0902    ; Mn #   [2] DEVANAGARI SIGN CANDRABINDU..DEVANAGARI SIGN ANUSVARA
+093C          ; Mn #       DEVANAGARI SIGN NUKTA
+0941..0948    ; Mn #   [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI
+094D          ; Mn #       DEVANAGARI SIGN VIRAMA
+0951..0954    ; Mn #   [4] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI ACUTE ACCENT
+0962..0963    ; Mn #   [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL
+0981          ; Mn #       BENGALI SIGN CANDRABINDU
+09BC          ; Mn #       BENGALI SIGN NUKTA
+09C1..09C4    ; Mn #   [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR
+09CD          ; Mn #       BENGALI SIGN VIRAMA
+09E2..09E3    ; Mn #   [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL
+0A01..0A02    ; Mn #   [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI
+0A3C          ; Mn #       GURMUKHI SIGN NUKTA
+0A41..0A42    ; Mn #   [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU
+0A47..0A48    ; Mn #   [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI
+0A4B..0A4D    ; Mn #   [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA
+0A51          ; Mn #       GURMUKHI SIGN UDAAT
+0A70..0A71    ; Mn #   [2] GURMUKHI TIPPI..GURMUKHI ADDAK
+0A75          ; Mn #       GURMUKHI SIGN YAKASH
+0A81..0A82    ; Mn #   [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA
+0ABC          ; Mn #       GUJARATI SIGN NUKTA
+0AC1..0AC5    ; Mn #   [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E
+0AC7..0AC8    ; Mn #   [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI
+0ACD          ; Mn #       GUJARATI SIGN VIRAMA
+0AE2..0AE3    ; Mn #   [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL
+0B01          ; Mn #       ORIYA SIGN CANDRABINDU
+0B3C          ; Mn #       ORIYA SIGN NUKTA
+0B3F          ; Mn #       ORIYA VOWEL SIGN I
+0B41..0B44    ; Mn #   [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
+0B4D          ; Mn #       ORIYA SIGN VIRAMA
+0B56          ; Mn #       ORIYA AI LENGTH MARK
+0B62..0B63    ; Mn #   [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
+0B82          ; Mn #       TAMIL SIGN ANUSVARA
+0BC0          ; Mn #       TAMIL VOWEL SIGN II
+0BCD          ; Mn #       TAMIL SIGN VIRAMA
+0C3E..0C40    ; Mn #   [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II
+0C46..0C48    ; Mn #   [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI
+0C4A..0C4D    ; Mn #   [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
+0C55..0C56    ; Mn #   [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
+0C62..0C63    ; Mn #   [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
+0CBC          ; Mn #       KANNADA SIGN NUKTA
+0CBF          ; Mn #       KANNADA VOWEL SIGN I
+0CC6          ; Mn #       KANNADA VOWEL SIGN E
+0CCC..0CCD    ; Mn #   [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA
+0CE2..0CE3    ; Mn #   [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
+0D41..0D44    ; Mn #   [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR
+0D4D          ; Mn #       MALAYALAM SIGN VIRAMA
+0D62..0D63    ; Mn #   [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL
+0DCA          ; Mn #       SINHALA SIGN AL-LAKUNA
+0DD2..0DD4    ; Mn #   [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA
+0DD6          ; Mn #       SINHALA VOWEL SIGN DIGA PAA-PILLA
+0E31          ; Mn #       THAI CHARACTER MAI HAN-AKAT
+0E34..0E3A    ; Mn #   [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU
+0E47..0E4E    ; Mn #   [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN
+0EB1          ; Mn #       LAO VOWEL SIGN MAI KAN
+0EB4..0EB9    ; Mn #   [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU
+0EBB..0EBC    ; Mn #   [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO
+0EC8..0ECD    ; Mn #   [6] LAO TONE MAI EK..LAO NIGGAHITA
+0F18..0F19    ; Mn #   [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
+0F35          ; Mn #       TIBETAN MARK NGAS BZUNG NYI ZLA
+0F37          ; Mn #       TIBETAN MARK NGAS BZUNG SGOR RTAGS
+0F39          ; Mn #       TIBETAN MARK TSA -PHRU
+0F71..0F7E    ; Mn #  [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO
+0F80..0F84    ; Mn #   [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA
+0F86..0F87    ; Mn #   [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS
+0F90..0F97    ; Mn #   [8] TIBETAN SUBJOINED LETTER KA..TIBETAN SUBJOINED LETTER JA
+0F99..0FBC    ; Mn #  [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA
+0FC6          ; Mn #       TIBETAN SYMBOL PADMA GDAN
+102D..1030    ; Mn #   [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU
+1032..1037    ; Mn #   [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW
+1039..103A    ; Mn #   [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+103D..103E    ; Mn #   [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA
+1058..1059    ; Mn #   [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
+105E..1060    ; Mn #   [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
+1071..1074    ; Mn #   [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE
+1082          ; Mn #       MYANMAR CONSONANT SIGN SHAN MEDIAL WA
+1085..1086    ; Mn #   [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y
+108D          ; Mn #       MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
+135F          ; Mn #       ETHIOPIC COMBINING GEMINATION MARK
+1712..1714    ; Mn #   [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA
+1732..1734    ; Mn #   [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD
+1752..1753    ; Mn #   [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
+1772..1773    ; Mn #   [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U
+17B7..17BD    ; Mn #   [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA
+17C6          ; Mn #       KHMER SIGN NIKAHIT
+17C9..17D3    ; Mn #  [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT
+17DD          ; Mn #       KHMER SIGN ATTHACAN
+180B..180D    ; Mn #   [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
+18A9          ; Mn #       MONGOLIAN LETTER ALI GALI DAGALGA
+1920..1922    ; Mn #   [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U
+1927..1928    ; Mn #   [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O
+1932          ; Mn #       LIMBU SMALL LETTER ANUSVARA
+1939..193B    ; Mn #   [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I
+1A17..1A18    ; Mn #   [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U
+1B00..1B03    ; Mn #   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
+1B34          ; Mn #       BALINESE SIGN REREKAN
+1B36..1B3A    ; Mn #   [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
+1B3C          ; Mn #       BALINESE VOWEL SIGN LA LENGA
+1B42          ; Mn #       BALINESE VOWEL SIGN PEPET
+1B6B..1B73    ; Mn #   [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG
+1B80..1B81    ; Mn #   [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR
+1BA2..1BA5    ; Mn #   [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU
+1BA8..1BA9    ; Mn #   [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG
+1C2C..1C33    ; Mn #   [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T
+1C36..1C37    ; Mn #   [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA
+1DC0..1DE6    ; Mn #  [39] COMBINING DOTTED GRAVE ACCENT..COMBINING LATIN SMALL LETTER Z
+1DFE..1DFF    ; Mn #   [2] COMBINING LEFT ARROWHEAD ABOVE..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW
+20D0..20DC    ; Mn #  [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
+20E1          ; Mn #       COMBINING LEFT RIGHT ARROW ABOVE
+20E5..20F0    ; Mn #  [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE
+2DE0..2DFF    ; Mn #  [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS
+302A..302F    ; Mn #   [6] IDEOGRAPHIC LEVEL TONE MARK..HANGUL DOUBLE DOT TONE MARK
+3099..309A    ; Mn #   [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+A66F          ; Mn #       COMBINING CYRILLIC VZMET
+A67C..A67D    ; Mn #   [2] COMBINING CYRILLIC KAVYKA..COMBINING CYRILLIC PAYEROK
+A802          ; Mn #       SYLOTI NAGRI SIGN DVISVARA
+A806          ; Mn #       SYLOTI NAGRI SIGN HASANTA
+A80B          ; Mn #       SYLOTI NAGRI SIGN ANUSVARA
+A825..A826    ; Mn #   [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E
+A8C4          ; Mn #       SAURASHTRA SIGN VIRAMA
+A926..A92D    ; Mn #   [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU
+A947..A951    ; Mn #  [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
+AA29..AA2E    ; Mn #   [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
+AA31..AA32    ; Mn #   [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
+AA35..AA36    ; Mn #   [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA
+AA43          ; Mn #       CHAM CONSONANT SIGN FINAL NG
+AA4C          ; Mn #       CHAM CONSONANT SIGN FINAL M
+FB1E          ; Mn #       HEBREW POINT JUDEO-SPANISH VARIKA
+FE00..FE0F    ; Mn #  [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
+FE20..FE26    ; Mn #   [7] COMBINING LIGATURE LEFT HALF..COMBINING CONJOINING MACRON
+101FD         ; Mn #       PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE
+10A01..10A03  ; Mn #   [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R
+10A05..10A06  ; Mn #   [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O
+10A0C..10A0F  ; Mn #   [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA
+10A38..10A3A  ; Mn #   [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW
+10A3F         ; Mn #       KHAROSHTHI VIRAMA
+1D167..1D169  ; Mn #   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
+1D17B..1D182  ; Mn #   [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
+1D185..1D18B  ; Mn #   [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
+1D1AA..1D1AD  ; Mn #   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
+1D242..1D244  ; Mn #   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+E0100..E01EF  ; Mn # [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
+
+# Total code points: 1032
+
+# ================================================
+
+# General_Category=Enclosing_Mark
+
+0488..0489    ; Me #   [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN
+06DE          ; Me #       ARABIC START OF RUB EL HIZB
+20DD..20E0    ; Me #   [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH
+20E2..20E4    ; Me #   [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE
+A670..A672    ; Me #   [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN
+
+# Total code points: 13
+
+# ================================================
+
+# General_Category=Spacing_Mark
+
+0903          ; Mc #       DEVANAGARI SIGN VISARGA
+093E..0940    ; Mc #   [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II
+0949..094C    ; Mc #   [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU
+0982..0983    ; Mc #   [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA
+09BE..09C0    ; Mc #   [3] BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN II
+09C7..09C8    ; Mc #   [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI
+09CB..09CC    ; Mc #   [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+09D7          ; Mc #       BENGALI AU LENGTH MARK
+0A03          ; Mc #       GURMUKHI SIGN VISARGA
+0A3E..0A40    ; Mc #   [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II
+0A83          ; Mc #       GUJARATI SIGN VISARGA
+0ABE..0AC0    ; Mc #   [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II
+0AC9          ; Mc #       GUJARATI VOWEL SIGN CANDRA O
+0ACB..0ACC    ; Mc #   [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU
+0B02..0B03    ; Mc #   [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA
+0B3E          ; Mc #       ORIYA VOWEL SIGN AA
+0B40          ; Mc #       ORIYA VOWEL SIGN II
+0B47..0B48    ; Mc #   [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
+0B4B..0B4C    ; Mc #   [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+0B57          ; Mc #       ORIYA AU LENGTH MARK
+0BBE..0BBF    ; Mc #   [2] TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN I
+0BC1..0BC2    ; Mc #   [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU
+0BC6..0BC8    ; Mc #   [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI
+0BCA..0BCC    ; Mc #   [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+0BD7          ; Mc #       TAMIL AU LENGTH MARK
+0C01..0C03    ; Mc #   [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA
+0C41..0C44    ; Mc #   [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR
+0C82..0C83    ; Mc #   [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA
+0CBE          ; Mc #       KANNADA VOWEL SIGN AA
+0CC0..0CC4    ; Mc #   [5] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN VOCALIC RR
+0CC7..0CC8    ; Mc #   [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+0CCA..0CCB    ; Mc #   [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+0CD5..0CD6    ; Mc #   [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+0D02..0D03    ; Mc #   [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA
+0D3E..0D40    ; Mc #   [3] MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN II
+0D46..0D48    ; Mc #   [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI
+0D4A..0D4C    ; Mc #   [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+0D57          ; Mc #       MALAYALAM AU LENGTH MARK
+0D82..0D83    ; Mc #   [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA
+0DCF..0DD1    ; Mc #   [3] SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA
+0DD8..0DDF    ; Mc #   [8] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA
+0DF2..0DF3    ; Mc #   [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA
+0F3E..0F3F    ; Mc #   [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES
+0F7F          ; Mc #       TIBETAN SIGN RNAM BCAD
+102B..102C    ; Mc #   [2] MYANMAR VOWEL SIGN TALL AA..MYANMAR VOWEL SIGN AA
+1031          ; Mc #       MYANMAR VOWEL SIGN E
+1038          ; Mc #       MYANMAR SIGN VISARGA
+103B..103C    ; Mc #   [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA
+1056..1057    ; Mc #   [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR
+1062..1064    ; Mc #   [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO
+1067..106D    ; Mc #   [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5
+1083..1084    ; Mc #   [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E
+1087..108C    ; Mc #   [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
+108F          ; Mc #       MYANMAR SIGN RUMAI PALAUNG TONE-5
+17B6          ; Mc #       KHMER VOWEL SIGN AA
+17BE..17C5    ; Mc #   [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU
+17C7..17C8    ; Mc #   [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU
+1923..1926    ; Mc #   [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU
+1929..192B    ; Mc #   [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA
+1930..1931    ; Mc #   [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA
+1933..1938    ; Mc #   [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA
+19B0..19C0    ; Mc #  [17] NEW TAI LUE VOWEL SIGN VOWEL SHORTENER..NEW TAI LUE VOWEL SIGN IY
+19C8..19C9    ; Mc #   [2] NEW TAI LUE TONE MARK-1..NEW TAI LUE TONE MARK-2
+1A19..1A1B    ; Mc #   [3] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN AE
+1B04          ; Mc #       BALINESE SIGN BISAH
+1B35          ; Mc #       BALINESE VOWEL SIGN TEDUNG
+1B3B          ; Mc #       BALINESE VOWEL SIGN RA REPA TEDUNG
+1B3D..1B41    ; Mc #   [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
+1B43..1B44    ; Mc #   [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
+1B82          ; Mc #       SUNDANESE SIGN PANGWISAD
+1BA1          ; Mc #       SUNDANESE CONSONANT SIGN PAMINGKAL
+1BA6..1BA7    ; Mc #   [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG
+1BAA          ; Mc #       SUNDANESE SIGN PAMAAEH
+1C24..1C2B    ; Mc #   [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
+1C34..1C35    ; Mc #   [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
+A823..A824    ; Mc #   [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
+A827          ; Mc #       SYLOTI NAGRI VOWEL SIGN OO
+A880..A881    ; Mc #   [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA
+A8B4..A8C3    ; Mc #  [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU
+A952..A953    ; Mc #   [2] REJANG CONSONANT SIGN H..REJANG VIRAMA
+AA2F..AA30    ; Mc #   [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
+AA33..AA34    ; Mc #   [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA
+AA4D          ; Mc #       CHAM CONSONANT SIGN FINAL H
+1D165..1D166  ; Mc #   [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
+1D16D..1D172  ; Mc #   [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5
+
+# Total code points: 236
+
+# ================================================
+
+# General_Category=Decimal_Number
+
+0030..0039    ; Nd #  [10] DIGIT ZERO..DIGIT NINE
+0660..0669    ; Nd #  [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE
+06F0..06F9    ; Nd #  [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE
+07C0..07C9    ; Nd #  [10] NKO DIGIT ZERO..NKO DIGIT NINE
+0966..096F    ; Nd #  [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE
+09E6..09EF    ; Nd #  [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE
+0A66..0A6F    ; Nd #  [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE
+0AE6..0AEF    ; Nd #  [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE
+0B66..0B6F    ; Nd #  [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE
+0BE6..0BEF    ; Nd #  [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE
+0C66..0C6F    ; Nd #  [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
+0CE6..0CEF    ; Nd #  [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
+0D66..0D6F    ; Nd #  [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE
+0E50..0E59    ; Nd #  [10] THAI DIGIT ZERO..THAI DIGIT NINE
+0ED0..0ED9    ; Nd #  [10] LAO DIGIT ZERO..LAO DIGIT NINE
+0F20..0F29    ; Nd #  [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE
+1040..1049    ; Nd #  [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE
+1090..1099    ; Nd #  [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE
+17E0..17E9    ; Nd #  [10] KHMER DIGIT ZERO..KHMER DIGIT NINE
+1810..1819    ; Nd #  [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE
+1946..194F    ; Nd #  [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE
+19D0..19D9    ; Nd #  [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE
+1B50..1B59    ; Nd #  [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE
+1BB0..1BB9    ; Nd #  [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE
+1C40..1C49    ; Nd #  [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE
+1C50..1C59    ; Nd #  [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE
+A620..A629    ; Nd #  [10] VAI DIGIT ZERO..VAI DIGIT NINE
+A8D0..A8D9    ; Nd #  [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE
+A900..A909    ; Nd #  [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE
+AA50..AA59    ; Nd #  [10] CHAM DIGIT ZERO..CHAM DIGIT NINE
+FF10..FF19    ; Nd #  [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
+104A0..104A9  ; Nd #  [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE
+1D7CE..1D7FF  ; Nd #  [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE
+
+# Total code points: 370
+
+# ================================================
+
+# General_Category=Letter_Number
+
+16EE..16F0    ; Nl #   [3] RUNIC ARLAUG SYMBOL..RUNIC BELGTHOR SYMBOL
+2160..2182    ; Nl #  [35] ROMAN NUMERAL ONE..ROMAN NUMERAL TEN THOUSAND
+2185..2188    ; Nl #   [4] ROMAN NUMERAL SIX LATE FORM..ROMAN NUMERAL ONE HUNDRED THOUSAND
+3007          ; Nl #       IDEOGRAPHIC NUMBER ZERO
+3021..3029    ; Nl #   [9] HANGZHOU NUMERAL ONE..HANGZHOU NUMERAL NINE
+3038..303A    ; Nl #   [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY
+10140..10174  ; Nl #  [53] GREEK ACROPHONIC ATTIC ONE QUARTER..GREEK ACROPHONIC STRATIAN FIFTY MNAS
+10341         ; Nl #       GOTHIC LETTER NINETY
+1034A         ; Nl #       GOTHIC LETTER NINE HUNDRED
+103D1..103D5  ; Nl #   [5] OLD PERSIAN NUMBER ONE..OLD PERSIAN NUMBER HUNDRED
+12400..12462  ; Nl #  [99] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE QUARTER
+
+# Total code points: 214
+
+# ================================================
+
+# General_Category=Other_Number
+
+00B2..00B3    ; No #   [2] SUPERSCRIPT TWO..SUPERSCRIPT THREE
+00B9          ; No #       SUPERSCRIPT ONE
+00BC..00BE    ; No #   [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS
+09F4..09F9    ; No #   [6] BENGALI CURRENCY NUMERATOR ONE..BENGALI CURRENCY DENOMINATOR SIXTEEN
+0BF0..0BF2    ; No #   [3] TAMIL NUMBER TEN..TAMIL NUMBER ONE THOUSAND
+0C78..0C7E    ; No #   [7] TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR..TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR
+0D70..0D75    ; No #   [6] MALAYALAM NUMBER TEN..MALAYALAM FRACTION THREE QUARTERS
+0F2A..0F33    ; No #  [10] TIBETAN DIGIT HALF ONE..TIBETAN DIGIT HALF ZERO
+1369..137C    ; No #  [20] ETHIOPIC DIGIT ONE..ETHIOPIC NUMBER TEN THOUSAND
+17F0..17F9    ; No #  [10] KHMER SYMBOL LEK ATTAK SON..KHMER SYMBOL LEK ATTAK PRAM-BUON
+2070          ; No #       SUPERSCRIPT ZERO
+2074..2079    ; No #   [6] SUPERSCRIPT FOUR..SUPERSCRIPT NINE
+2080..2089    ; No #  [10] SUBSCRIPT ZERO..SUBSCRIPT NINE
+2153..215F    ; No #  [13] VULGAR FRACTION ONE THIRD..FRACTION NUMERATOR ONE
+2460..249B    ; No #  [60] CIRCLED DIGIT ONE..NUMBER TWENTY FULL STOP
+24EA..24FF    ; No #  [22] CIRCLED DIGIT ZERO..NEGATIVE CIRCLED DIGIT ZERO
+2776..2793    ; No #  [30] DINGBAT NEGATIVE CIRCLED DIGIT ONE..DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN
+2CFD          ; No #       COPTIC FRACTION ONE HALF
+3192..3195    ; No #   [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC ANNOTATION FOUR MARK
+3220..3229    ; No #  [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN
+3251..325F    ; No #  [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE
+3280..3289    ; No #  [10] CIRCLED IDEOGRAPH ONE..CIRCLED IDEOGRAPH TEN
+32B1..32BF    ; No #  [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY
+10107..10133  ; No #  [45] AEGEAN NUMBER ONE..AEGEAN NUMBER NINETY THOUSAND
+10175..10178  ; No #   [4] GREEK ONE HALF SIGN..GREEK THREE QUARTERS SIGN
+1018A         ; No #       GREEK ZERO SIGN
+10320..10323  ; No #   [4] OLD ITALIC NUMERAL ONE..OLD ITALIC NUMERAL FIFTY
+10916..10919  ; No #   [4] PHOENICIAN NUMBER ONE..PHOENICIAN NUMBER ONE HUNDRED
+10A40..10A47  ; No #   [8] KHAROSHTHI DIGIT ONE..KHAROSHTHI NUMBER ONE THOUSAND
+1D360..1D371  ; No #  [18] COUNTING ROD UNIT DIGIT ONE..COUNTING ROD TENS DIGIT NINE
+
+# Total code points: 349
+
+# ================================================
+
+# General_Category=Space_Separator
+
+0020          ; Zs #       SPACE
+00A0          ; Zs #       NO-BREAK SPACE
+1680          ; Zs #       OGHAM SPACE MARK
+180E          ; Zs #       MONGOLIAN VOWEL SEPARATOR
+2000..200A    ; Zs #  [11] EN QUAD..HAIR SPACE
+202F          ; Zs #       NARROW NO-BREAK SPACE
+205F          ; Zs #       MEDIUM MATHEMATICAL SPACE
+3000          ; Zs #       IDEOGRAPHIC SPACE
+
+# Total code points: 18
+
+# ================================================
+
+# General_Category=Line_Separator
+
+2028          ; Zl #       LINE SEPARATOR
+
+# Total code points: 1
+
+# ================================================
+
+# General_Category=Paragraph_Separator
+
+2029          ; Zp #       PARAGRAPH SEPARATOR
+
+# Total code points: 1
+
+# ================================================
+
+# General_Category=Control
+
+0000..001F    ; Cc #  [32] <control-0000>..<control-001F>
+007F..009F    ; Cc #  [33] <control-007F>..<control-009F>
+
+# Total code points: 65
+
+# ================================================
+
+# General_Category=Format
+
+00AD          ; Cf #       SOFT HYPHEN
+0600..0603    ; Cf #   [4] ARABIC NUMBER SIGN..ARABIC SIGN SAFHA
+06DD          ; Cf #       ARABIC END OF AYAH
+070F          ; Cf #       SYRIAC ABBREVIATION MARK
+17B4..17B5    ; Cf #   [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
+200B..200F    ; Cf #   [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK
+202A..202E    ; Cf #   [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
+2060..2064    ; Cf #   [5] WORD JOINER..INVISIBLE PLUS
+206A..206F    ; Cf #   [6] INHIBIT SYMMETRIC SWAPPING..NOMINAL DIGIT SHAPES
+FEFF          ; Cf #       ZERO WIDTH NO-BREAK SPACE
+FFF9..FFFB    ; Cf #   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR
+1D173..1D17A  ; Cf #   [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
+E0001         ; Cf #       LANGUAGE TAG
+E0020..E007F  ; Cf #  [96] TAG SPACE..CANCEL TAG
+
+# Total code points: 139
+
+# ================================================
+
+# General_Category=Private_Use
+
+E000..F8FF    ; Co # [6400] <private-use-E000>..<private-use-F8FF>
+F0000..FFFFD  ; Co # [65534] <private-use-F0000>..<private-use-FFFFD>
+100000..10FFFD; Co # [65534] <private-use-100000>..<private-use-10FFFD>
+
+# Total code points: 137468
+
+# ================================================
+
+# General_Category=Surrogate
+
+D800..DFFF    ; Cs # [2048] <surrogate-D800>..<surrogate-DFFF>
+
+# Total code points: 2048
+
+# ================================================
+
+# General_Category=Dash_Punctuation
+
+002D          ; Pd #       HYPHEN-MINUS
+058A          ; Pd #       ARMENIAN HYPHEN
+05BE          ; Pd #       HEBREW PUNCTUATION MAQAF
+1806          ; Pd #       MONGOLIAN TODO SOFT HYPHEN
+2010..2015    ; Pd #   [6] HYPHEN..HORIZONTAL BAR
+2E17          ; Pd #       DOUBLE OBLIQUE HYPHEN
+2E1A          ; Pd #       HYPHEN WITH DIAERESIS
+301C          ; Pd #       WAVE DASH
+3030          ; Pd #       WAVY DASH
+30A0          ; Pd #       KATAKANA-HIRAGANA DOUBLE HYPHEN
+FE31..FE32    ; Pd #   [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH
+FE58          ; Pd #       SMALL EM DASH
+FE63          ; Pd #       SMALL HYPHEN-MINUS
+FF0D          ; Pd #       FULLWIDTH HYPHEN-MINUS
+
+# Total code points: 20
+
+# ================================================
+
+# General_Category=Open_Punctuation
+
+0028          ; Ps #       LEFT PARENTHESIS
+005B          ; Ps #       LEFT SQUARE BRACKET
+007B          ; Ps #       LEFT CURLY BRACKET
+0F3A          ; Ps #       TIBETAN MARK GUG RTAGS GYON
+0F3C          ; Ps #       TIBETAN MARK ANG KHANG GYON
+169B          ; Ps #       OGHAM FEATHER MARK
+201A          ; Ps #       SINGLE LOW-9 QUOTATION MARK
+201E          ; Ps #       DOUBLE LOW-9 QUOTATION MARK
+2045          ; Ps #       LEFT SQUARE BRACKET WITH QUILL
+207D          ; Ps #       SUPERSCRIPT LEFT PARENTHESIS
+208D          ; Ps #       SUBSCRIPT LEFT PARENTHESIS
+2329          ; Ps #       LEFT-POINTING ANGLE BRACKET
+2768          ; Ps #       MEDIUM LEFT PARENTHESIS ORNAMENT
+276A          ; Ps #       MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT
+276C          ; Ps #       MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT
+276E          ; Ps #       HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT
+2770          ; Ps #       HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT
+2772          ; Ps #       LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
+2774          ; Ps #       MEDIUM LEFT CURLY BRACKET ORNAMENT
+27C5          ; Ps #       LEFT S-SHAPED BAG DELIMITER
+27E6          ; Ps #       MATHEMATICAL LEFT WHITE SQUARE BRACKET
+27E8          ; Ps #       MATHEMATICAL LEFT ANGLE BRACKET
+27EA          ; Ps #       MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
+27EC          ; Ps #       MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
+27EE          ; Ps #       MATHEMATICAL LEFT FLATTENED PARENTHESIS
+2983          ; Ps #       LEFT WHITE CURLY BRACKET
+2985          ; Ps #       LEFT WHITE PARENTHESIS
+2987          ; Ps #       Z NOTATION LEFT IMAGE BRACKET
+2989          ; Ps #       Z NOTATION LEFT BINDING BRACKET
+298B          ; Ps #       LEFT SQUARE BRACKET WITH UNDERBAR
+298D          ; Ps #       LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
+298F          ; Ps #       LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+2991          ; Ps #       LEFT ANGLE BRACKET WITH DOT
+2993          ; Ps #       LEFT ARC LESS-THAN BRACKET
+2995          ; Ps #       DOUBLE LEFT ARC GREATER-THAN BRACKET
+2997          ; Ps #       LEFT BLACK TORTOISE SHELL BRACKET
+29D8          ; Ps #       LEFT WIGGLY FENCE
+29DA          ; Ps #       LEFT DOUBLE WIGGLY FENCE
+29FC          ; Ps #       LEFT-POINTING CURVED ANGLE BRACKET
+2E22          ; Ps #       TOP LEFT HALF BRACKET
+2E24          ; Ps #       BOTTOM LEFT HALF BRACKET
+2E26          ; Ps #       LEFT SIDEWAYS U BRACKET
+2E28          ; Ps #       LEFT DOUBLE PARENTHESIS
+3008          ; Ps #       LEFT ANGLE BRACKET
+300A          ; Ps #       LEFT DOUBLE ANGLE BRACKET
+300C          ; Ps #       LEFT CORNER BRACKET
+300E          ; Ps #       LEFT WHITE CORNER BRACKET
+3010          ; Ps #       LEFT BLACK LENTICULAR BRACKET
+3014          ; Ps #       LEFT TORTOISE SHELL BRACKET
+3016          ; Ps #       LEFT WHITE LENTICULAR BRACKET
+3018          ; Ps #       LEFT WHITE TORTOISE SHELL BRACKET
+301A          ; Ps #       LEFT WHITE SQUARE BRACKET
+301D          ; Ps #       REVERSED DOUBLE PRIME QUOTATION MARK
+FD3E          ; Ps #       ORNATE LEFT PARENTHESIS
+FE17          ; Ps #       PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET
+FE35          ; Ps #       PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS
+FE37          ; Ps #       PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET
+FE39          ; Ps #       PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET
+FE3B          ; Ps #       PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET
+FE3D          ; Ps #       PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET
+FE3F          ; Ps #       PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET
+FE41          ; Ps #       PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET
+FE43          ; Ps #       PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET
+FE47          ; Ps #       PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET
+FE59          ; Ps #       SMALL LEFT PARENTHESIS
+FE5B          ; Ps #       SMALL LEFT CURLY BRACKET
+FE5D          ; Ps #       SMALL LEFT TORTOISE SHELL BRACKET
+FF08          ; Ps #       FULLWIDTH LEFT PARENTHESIS
+FF3B          ; Ps #       FULLWIDTH LEFT SQUARE BRACKET
+FF5B          ; Ps #       FULLWIDTH LEFT CURLY BRACKET
+FF5F          ; Ps #       FULLWIDTH LEFT WHITE PARENTHESIS
+FF62          ; Ps #       HALFWIDTH LEFT CORNER BRACKET
+
+# Total code points: 72
+
+# ================================================
+
+# General_Category=Close_Punctuation
+
+0029          ; Pe #       RIGHT PARENTHESIS
+005D          ; Pe #       RIGHT SQUARE BRACKET
+007D          ; Pe #       RIGHT CURLY BRACKET
+0F3B          ; Pe #       TIBETAN MARK GUG RTAGS GYAS
+0F3D          ; Pe #       TIBETAN MARK ANG KHANG GYAS
+169C          ; Pe #       OGHAM REVERSED FEATHER MARK
+2046          ; Pe #       RIGHT SQUARE BRACKET WITH QUILL
+207E          ; Pe #       SUPERSCRIPT RIGHT PARENTHESIS
+208E          ; Pe #       SUBSCRIPT RIGHT PARENTHESIS
+232A          ; Pe #       RIGHT-POINTING ANGLE BRACKET
+2769          ; Pe #       MEDIUM RIGHT PARENTHESIS ORNAMENT
+276B          ; Pe #       MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT
+276D          ; Pe #       MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT
+276F          ; Pe #       HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT
+2771          ; Pe #       HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT
+2773          ; Pe #       LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
+2775          ; Pe #       MEDIUM RIGHT CURLY BRACKET ORNAMENT
+27C6          ; Pe #       RIGHT S-SHAPED BAG DELIMITER
+27E7          ; Pe #       MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+27E9          ; Pe #       MATHEMATICAL RIGHT ANGLE BRACKET
+27EB          ; Pe #       MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
+27ED          ; Pe #       MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
+27EF          ; Pe #       MATHEMATICAL RIGHT FLATTENED PARENTHESIS
+2984          ; Pe #       RIGHT WHITE CURLY BRACKET
+2986          ; Pe #       RIGHT WHITE PARENTHESIS
+2988          ; Pe #       Z NOTATION RIGHT IMAGE BRACKET
+298A          ; Pe #       Z NOTATION RIGHT BINDING BRACKET
+298C          ; Pe #       RIGHT SQUARE BRACKET WITH UNDERBAR
+298E          ; Pe #       RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+2990          ; Pe #       RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
+2992          ; Pe #       RIGHT ANGLE BRACKET WITH DOT
+2994          ; Pe #       RIGHT ARC GREATER-THAN BRACKET
+2996          ; Pe #       DOUBLE RIGHT ARC LESS-THAN BRACKET
+2998          ; Pe #       RIGHT BLACK TORTOISE SHELL BRACKET
+29D9          ; Pe #       RIGHT WIGGLY FENCE
+29DB          ; Pe #       RIGHT DOUBLE WIGGLY FENCE
+29FD          ; Pe #       RIGHT-POINTING CURVED ANGLE BRACKET
+2E23          ; Pe #       TOP RIGHT HALF BRACKET
+2E25          ; Pe #       BOTTOM RIGHT HALF BRACKET
+2E27          ; Pe #       RIGHT SIDEWAYS U BRACKET
+2E29          ; Pe #       RIGHT DOUBLE PARENTHESIS
+3009          ; Pe #       RIGHT ANGLE BRACKET
+300B          ; Pe #       RIGHT DOUBLE ANGLE BRACKET
+300D          ; Pe #       RIGHT CORNER BRACKET
+300F          ; Pe #       RIGHT WHITE CORNER BRACKET
+3011          ; Pe #       RIGHT BLACK LENTICULAR BRACKET
+3015          ; Pe #       RIGHT TORTOISE SHELL BRACKET
+3017          ; Pe #       RIGHT WHITE LENTICULAR BRACKET
+3019          ; Pe #       RIGHT WHITE TORTOISE SHELL BRACKET
+301B          ; Pe #       RIGHT WHITE SQUARE BRACKET
+301E..301F    ; Pe #   [2] DOUBLE PRIME QUOTATION MARK..LOW DOUBLE PRIME QUOTATION MARK
+FD3F          ; Pe #       ORNATE RIGHT PARENTHESIS
+FE18          ; Pe #       PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET
+FE36          ; Pe #       PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS
+FE38          ; Pe #       PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET
+FE3A          ; Pe #       PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET
+FE3C          ; Pe #       PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET
+FE3E          ; Pe #       PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET
+FE40          ; Pe #       PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET
+FE42          ; Pe #       PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET
+FE44          ; Pe #       PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET
+FE48          ; Pe #       PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET
+FE5A          ; Pe #       SMALL RIGHT PARENTHESIS
+FE5C          ; Pe #       SMALL RIGHT CURLY BRACKET
+FE5E          ; Pe #       SMALL RIGHT TORTOISE SHELL BRACKET
+FF09          ; Pe #       FULLWIDTH RIGHT PARENTHESIS
+FF3D          ; Pe #       FULLWIDTH RIGHT SQUARE BRACKET
+FF5D          ; Pe #       FULLWIDTH RIGHT CURLY BRACKET
+FF60          ; Pe #       FULLWIDTH RIGHT WHITE PARENTHESIS
+FF63          ; Pe #       HALFWIDTH RIGHT CORNER BRACKET
+
+# Total code points: 71
+
+# ================================================
+
+# General_Category=Connector_Punctuation
+
+005F          ; Pc #       LOW LINE
+203F..2040    ; Pc #   [2] UNDERTIE..CHARACTER TIE
+2054          ; Pc #       INVERTED UNDERTIE
+FE33..FE34    ; Pc #   [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
+FE4D..FE4F    ; Pc #   [3] DASHED LOW LINE..WAVY LOW LINE
+FF3F          ; Pc #       FULLWIDTH LOW LINE
+
+# Total code points: 10
+
+# ================================================
+
+# General_Category=Other_Punctuation
+
+0021..0023    ; Po #   [3] EXCLAMATION MARK..NUMBER SIGN
+0025..0027    ; Po #   [3] PERCENT SIGN..APOSTROPHE
+002A          ; Po #       ASTERISK
+002C          ; Po #       COMMA
+002E..002F    ; Po #   [2] FULL STOP..SOLIDUS
+003A..003B    ; Po #   [2] COLON..SEMICOLON
+003F..0040    ; Po #   [2] QUESTION MARK..COMMERCIAL AT
+005C          ; Po #       REVERSE SOLIDUS
+00A1          ; Po #       INVERTED EXCLAMATION MARK
+00B7          ; Po #       MIDDLE DOT
+00BF          ; Po #       INVERTED QUESTION MARK
+037E          ; Po #       GREEK QUESTION MARK
+0387          ; Po #       GREEK ANO TELEIA
+055A..055F    ; Po #   [6] ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK
+0589          ; Po #       ARMENIAN FULL STOP
+05C0          ; Po #       HEBREW PUNCTUATION PASEQ
+05C3          ; Po #       HEBREW PUNCTUATION SOF PASUQ
+05C6          ; Po #       HEBREW PUNCTUATION NUN HAFUKHA
+05F3..05F4    ; Po #   [2] HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATION GERSHAYIM
+0609..060A    ; Po #   [2] ARABIC-INDIC PER MILLE SIGN..ARABIC-INDIC PER TEN THOUSAND SIGN
+060C..060D    ; Po #   [2] ARABIC COMMA..ARABIC DATE SEPARATOR
+061B          ; Po #       ARABIC SEMICOLON
+061E..061F    ; Po #   [2] ARABIC TRIPLE DOT PUNCTUATION MARK..ARABIC QUESTION MARK
+066A..066D    ; Po #   [4] ARABIC PERCENT SIGN..ARABIC FIVE POINTED STAR
+06D4          ; Po #       ARABIC FULL STOP
+0700..070D    ; Po #  [14] SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN ASTERISCUS
+07F7..07F9    ; Po #   [3] NKO SYMBOL GBAKURUNEN..NKO EXCLAMATION MARK
+0964..0965    ; Po #   [2] DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA
+0970          ; Po #       DEVANAGARI ABBREVIATION SIGN
+0DF4          ; Po #       SINHALA PUNCTUATION KUNDDALIYA
+0E4F          ; Po #       THAI CHARACTER FONGMAN
+0E5A..0E5B    ; Po #   [2] THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT
+0F04..0F12    ; Po #  [15] TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK RGYA GRAM SHAD
+0F85          ; Po #       TIBETAN MARK PALUTA
+0FD0..0FD4    ; Po #   [5] TIBETAN MARK BSKA- SHOG GI MGO RGYAN..TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA
+104A..104F    ; Po #   [6] MYANMAR SIGN LITTLE SECTION..MYANMAR SYMBOL GENITIVE
+10FB          ; Po #       GEORGIAN PARAGRAPH SEPARATOR
+1361..1368    ; Po #   [8] ETHIOPIC WORDSPACE..ETHIOPIC PARAGRAPH SEPARATOR
+166D..166E    ; Po #   [2] CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP
+16EB..16ED    ; Po #   [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION
+1735..1736    ; Po #   [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION
+17D4..17D6    ; Po #   [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH
+17D8..17DA    ; Po #   [3] KHMER SIGN BEYYAL..KHMER SIGN KOOMUUT
+1800..1805    ; Po #   [6] MONGOLIAN BIRGA..MONGOLIAN FOUR DOTS
+1807..180A    ; Po #   [4] MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER..MONGOLIAN NIRUGU
+1944..1945    ; Po #   [2] LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK
+19DE..19DF    ; Po #   [2] NEW TAI LUE SIGN LAE..NEW TAI LUE SIGN LAEV
+1A1E..1A1F    ; Po #   [2] BUGINESE PALLAWA..BUGINESE END OF SECTION
+1B5A..1B60    ; Po #   [7] BALINESE PANTI..BALINESE PAMENENG
+1C3B..1C3F    ; Po #   [5] LEPCHA PUNCTUATION TA-ROL..LEPCHA PUNCTUATION TSHOOK
+1C7E..1C7F    ; Po #   [2] OL CHIKI PUNCTUATION MUCAAD..OL CHIKI PUNCTUATION DOUBLE MUCAAD
+2016..2017    ; Po #   [2] DOUBLE VERTICAL LINE..DOUBLE LOW LINE
+2020..2027    ; Po #   [8] DAGGER..HYPHENATION POINT
+2030..2038    ; Po #   [9] PER MILLE SIGN..CARET
+203B..203E    ; Po #   [4] REFERENCE MARK..OVERLINE
+2041..2043    ; Po #   [3] CARET INSERTION POINT..HYPHEN BULLET
+2047..2051    ; Po #  [11] DOUBLE QUESTION MARK..TWO ASTERISKS ALIGNED VERTICALLY
+2053          ; Po #       SWUNG DASH
+2055..205E    ; Po #  [10] FLOWER PUNCTUATION MARK..VERTICAL FOUR DOTS
+2CF9..2CFC    ; Po #   [4] COPTIC OLD NUBIAN FULL STOP..COPTIC OLD NUBIAN VERSE DIVIDER
+2CFE..2CFF    ; Po #   [2] COPTIC FULL STOP..COPTIC MORPHOLOGICAL DIVIDER
+2E00..2E01    ; Po #   [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER
+2E06..2E08    ; Po #   [3] RAISED INTERPOLATION MARKER..DOTTED TRANSPOSITION MARKER
+2E0B          ; Po #       RAISED SQUARE
+2E0E..2E16    ; Po #   [9] EDITORIAL CORONIS..DOTTED RIGHT-POINTING ANGLE
+2E18..2E19    ; Po #   [2] INVERTED INTERROBANG..PALM BRANCH
+2E1B          ; Po #       TILDE WITH RING ABOVE
+2E1E..2E1F    ; Po #   [2] TILDE WITH DOT ABOVE..TILDE WITH DOT BELOW
+2E2A..2E2E    ; Po #   [5] TWO DOTS OVER ONE DOT PUNCTUATION..REVERSED QUESTION MARK
+2E30          ; Po #       RING POINT
+3001..3003    ; Po #   [3] IDEOGRAPHIC COMMA..DITTO MARK
+303D          ; Po #       PART ALTERNATION MARK
+30FB          ; Po #       KATAKANA MIDDLE DOT
+A60D..A60F    ; Po #   [3] VAI COMMA..VAI QUESTION MARK
+A673          ; Po #       SLAVONIC ASTERISK
+A67E          ; Po #       CYRILLIC KAVYKA
+A874..A877    ; Po #   [4] PHAGS-PA SINGLE HEAD MARK..PHAGS-PA MARK DOUBLE SHAD
+A8CE..A8CF    ; Po #   [2] SAURASHTRA DANDA..SAURASHTRA DOUBLE DANDA
+A92E..A92F    ; Po #   [2] KAYAH LI SIGN CWI..KAYAH LI SIGN SHYA
+A95F          ; Po #       REJANG SECTION MARK
+AA5C..AA5F    ; Po #   [4] CHAM PUNCTUATION SPIRAL..CHAM PUNCTUATION TRIPLE DANDA
+FE10..FE16    ; Po #   [7] PRESENTATION FORM FOR VERTICAL COMMA..PRESENTATION FORM FOR VERTICAL QUESTION MARK
+FE19          ; Po #       PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS
+FE30          ; Po #       PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+FE45..FE46    ; Po #   [2] SESAME DOT..WHITE SESAME DOT
+FE49..FE4C    ; Po #   [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE
+FE50..FE52    ; Po #   [3] SMALL COMMA..SMALL FULL STOP
+FE54..FE57    ; Po #   [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK
+FE5F..FE61    ; Po #   [3] SMALL NUMBER SIGN..SMALL ASTERISK
+FE68          ; Po #       SMALL REVERSE SOLIDUS
+FE6A..FE6B    ; Po #   [2] SMALL PERCENT SIGN..SMALL COMMERCIAL AT
+FF01..FF03    ; Po #   [3] FULLWIDTH EXCLAMATION MARK..FULLWIDTH NUMBER SIGN
+FF05..FF07    ; Po #   [3] FULLWIDTH PERCENT SIGN..FULLWIDTH APOSTROPHE
+FF0A          ; Po #       FULLWIDTH ASTERISK
+FF0C          ; Po #       FULLWIDTH COMMA
+FF0E..FF0F    ; Po #   [2] FULLWIDTH FULL STOP..FULLWIDTH SOLIDUS
+FF1A..FF1B    ; Po #   [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON
+FF1F..FF20    ; Po #   [2] FULLWIDTH QUESTION MARK..FULLWIDTH COMMERCIAL AT
+FF3C          ; Po #       FULLWIDTH REVERSE SOLIDUS
+FF61          ; Po #       HALFWIDTH IDEOGRAPHIC FULL STOP
+FF64..FF65    ; Po #   [2] HALFWIDTH IDEOGRAPHIC COMMA..HALFWIDTH KATAKANA MIDDLE DOT
+10100..10101  ; Po #   [2] AEGEAN WORD SEPARATOR LINE..AEGEAN WORD SEPARATOR DOT
+1039F         ; Po #       UGARITIC WORD DIVIDER
+103D0         ; Po #       OLD PERSIAN WORD DIVIDER
+1091F         ; Po #       PHOENICIAN WORD SEPARATOR
+1093F         ; Po #       LYDIAN TRIANGULAR MARK
+10A50..10A58  ; Po #   [9] KHAROSHTHI PUNCTUATION DOT..KHAROSHTHI PUNCTUATION LINES
+12470..12473  ; Po #   [4] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL TRICOLON
+
+# Total code points: 315
+
+# ================================================
+
+# General_Category=Math_Symbol
+
+002B          ; Sm #       PLUS SIGN
+003C..003E    ; Sm #   [3] LESS-THAN SIGN..GREATER-THAN SIGN
+007C          ; Sm #       VERTICAL LINE
+007E          ; Sm #       TILDE
+00AC          ; Sm #       NOT SIGN
+00B1          ; Sm #       PLUS-MINUS SIGN
+00D7          ; Sm #       MULTIPLICATION SIGN
+00F7          ; Sm #       DIVISION SIGN
+03F6          ; Sm #       GREEK REVERSED LUNATE EPSILON SYMBOL
+0606..0608    ; Sm #   [3] ARABIC-INDIC CUBE ROOT..ARABIC RAY
+2044          ; Sm #       FRACTION SLASH
+2052          ; Sm #       COMMERCIAL MINUS SIGN
+207A..207C    ; Sm #   [3] SUPERSCRIPT PLUS SIGN..SUPERSCRIPT EQUALS SIGN
+208A..208C    ; Sm #   [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN
+2140..2144    ; Sm #   [5] DOUBLE-STRUCK N-ARY SUMMATION..TURNED SANS-SERIF CAPITAL Y
+214B          ; Sm #       TURNED AMPERSAND
+2190..2194    ; Sm #   [5] LEFTWARDS ARROW..LEFT RIGHT ARROW
+219A..219B    ; Sm #   [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE
+21A0          ; Sm #       RIGHTWARDS TWO HEADED ARROW
+21A3          ; Sm #       RIGHTWARDS ARROW WITH TAIL
+21A6          ; Sm #       RIGHTWARDS ARROW FROM BAR
+21AE          ; Sm #       LEFT RIGHT ARROW WITH STROKE
+21CE..21CF    ; Sm #   [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE
+21D2          ; Sm #       RIGHTWARDS DOUBLE ARROW
+21D4          ; Sm #       LEFT RIGHT DOUBLE ARROW
+21F4..22FF    ; Sm # [268] RIGHT ARROW WITH SMALL CIRCLE..Z NOTATION BAG MEMBERSHIP
+2308..230B    ; Sm #   [4] LEFT CEILING..RIGHT FLOOR
+2320..2321    ; Sm #   [2] TOP HALF INTEGRAL..BOTTOM HALF INTEGRAL
+237C          ; Sm #       RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW
+239B..23B3    ; Sm #  [25] LEFT PARENTHESIS UPPER HOOK..SUMMATION BOTTOM
+23DC..23E1    ; Sm #   [6] TOP PARENTHESIS..BOTTOM TORTOISE SHELL BRACKET
+25B7          ; Sm #       WHITE RIGHT-POINTING TRIANGLE
+25C1          ; Sm #       WHITE LEFT-POINTING TRIANGLE
+25F8..25FF    ; Sm #   [8] UPPER LEFT TRIANGLE..LOWER RIGHT TRIANGLE
+266F          ; Sm #       MUSIC SHARP SIGN
+27C0..27C4    ; Sm #   [5] THREE DIMENSIONAL ANGLE..OPEN SUPERSET
+27C7..27CA    ; Sm #   [4] OR WITH DOT INSIDE..VERTICAL BAR WITH HORIZONTAL STROKE
+27CC          ; Sm #       LONG DIVISION
+27D0..27E5    ; Sm #  [22] WHITE DIAMOND WITH CENTRED DOT..WHITE SQUARE WITH RIGHTWARDS TICK
+27F0..27FF    ; Sm #  [16] UPWARDS QUADRUPLE ARROW..LONG RIGHTWARDS SQUIGGLE ARROW
+2900..2982    ; Sm # [131] RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE..Z NOTATION TYPE COLON
+2999..29D7    ; Sm #  [63] DOTTED FENCE..BLACK HOURGLASS
+29DC..29FB    ; Sm #  [32] INCOMPLETE INFINITY..TRIPLE PLUS
+29FE..2AFF    ; Sm # [258] TINY..N-ARY WHITE VERTICAL BAR
+2B30..2B44    ; Sm #  [21] LEFT ARROW WITH SMALL CIRCLE..RIGHTWARDS ARROW THROUGH SUPERSET
+2B47..2B4C    ; Sm #   [6] REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR
+FB29          ; Sm #       HEBREW LETTER ALTERNATIVE PLUS SIGN
+FE62          ; Sm #       SMALL PLUS SIGN
+FE64..FE66    ; Sm #   [3] SMALL LESS-THAN SIGN..SMALL EQUALS SIGN
+FF0B          ; Sm #       FULLWIDTH PLUS SIGN
+FF1C..FF1E    ; Sm #   [3] FULLWIDTH LESS-THAN SIGN..FULLWIDTH GREATER-THAN SIGN
+FF5C          ; Sm #       FULLWIDTH VERTICAL LINE
+FF5E          ; Sm #       FULLWIDTH TILDE
+FFE2          ; Sm #       FULLWIDTH NOT SIGN
+FFE9..FFEC    ; Sm #   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW
+1D6C1         ; Sm #       MATHEMATICAL BOLD NABLA
+1D6DB         ; Sm #       MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
+1D6FB         ; Sm #       MATHEMATICAL ITALIC NABLA
+1D715         ; Sm #       MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL
+1D735         ; Sm #       MATHEMATICAL BOLD ITALIC NABLA
+1D74F         ; Sm #       MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL
+1D76F         ; Sm #       MATHEMATICAL SANS-SERIF BOLD NABLA
+1D789         ; Sm #       MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
+1D7A9         ; Sm #       MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA
+1D7C3         ; Sm #       MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
+
+# Total code points: 945
+
+# ================================================
+
+# General_Category=Currency_Symbol
+
+0024          ; Sc #       DOLLAR SIGN
+00A2..00A5    ; Sc #   [4] CENT SIGN..YEN SIGN
+060B          ; Sc #       AFGHANI SIGN
+09F2..09F3    ; Sc #   [2] BENGALI RUPEE MARK..BENGALI RUPEE SIGN
+0AF1          ; Sc #       GUJARATI RUPEE SIGN
+0BF9          ; Sc #       TAMIL RUPEE SIGN
+0E3F          ; Sc #       THAI CURRENCY SYMBOL BAHT
+17DB          ; Sc #       KHMER CURRENCY SYMBOL RIEL
+20A0..20B5    ; Sc #  [22] EURO-CURRENCY SIGN..CEDI SIGN
+FDFC          ; Sc #       RIAL SIGN
+FE69          ; Sc #       SMALL DOLLAR SIGN
+FF04          ; Sc #       FULLWIDTH DOLLAR SIGN
+FFE0..FFE1    ; Sc #   [2] FULLWIDTH CENT SIGN..FULLWIDTH POUND SIGN
+FFE5..FFE6    ; Sc #   [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN
+
+# Total code points: 41
+
+# ================================================
+
+# General_Category=Modifier_Symbol
+
+005E          ; Sk #       CIRCUMFLEX ACCENT
+0060          ; Sk #       GRAVE ACCENT
+00A8          ; Sk #       DIAERESIS
+00AF          ; Sk #       MACRON
+00B4          ; Sk #       ACUTE ACCENT
+00B8          ; Sk #       CEDILLA
+02C2..02C5    ; Sk #   [4] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER DOWN ARROWHEAD
+02D2..02DF    ; Sk #  [14] MODIFIER LETTER CENTRED RIGHT HALF RING..MODIFIER LETTER CROSS ACCENT
+02E5..02EB    ; Sk #   [7] MODIFIER LETTER EXTRA-HIGH TONE BAR..MODIFIER LETTER YANG DEPARTING TONE MARK
+02ED          ; Sk #       MODIFIER LETTER UNASPIRATED
+02EF..02FF    ; Sk #  [17] MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER LETTER LOW LEFT ARROW
+0375          ; Sk #       GREEK LOWER NUMERAL SIGN
+0384..0385    ; Sk #   [2] GREEK TONOS..GREEK DIALYTIKA TONOS
+1FBD          ; Sk #       GREEK KORONIS
+1FBF..1FC1    ; Sk #   [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI
+1FCD..1FCF    ; Sk #   [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI
+1FDD..1FDF    ; Sk #   [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI
+1FED..1FEF    ; Sk #   [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA
+1FFD..1FFE    ; Sk #   [2] GREEK OXIA..GREEK DASIA
+309B..309C    ; Sk #   [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+A700..A716    ; Sk #  [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR
+A720..A721    ; Sk #   [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE
+A789..A78A    ; Sk #   [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
+FF3E          ; Sk #       FULLWIDTH CIRCUMFLEX ACCENT
+FF40          ; Sk #       FULLWIDTH GRAVE ACCENT
+FFE3          ; Sk #       FULLWIDTH MACRON
+
+# Total code points: 99
+
+# ================================================
+
+# General_Category=Other_Symbol
+
+00A6..00A7    ; So #   [2] BROKEN BAR..SECTION SIGN
+00A9          ; So #       COPYRIGHT SIGN
+00AE          ; So #       REGISTERED SIGN
+00B0          ; So #       DEGREE SIGN
+00B6          ; So #       PILCROW SIGN
+0482          ; So #       CYRILLIC THOUSANDS SIGN
+060E..060F    ; So #   [2] ARABIC POETIC VERSE SIGN..ARABIC SIGN MISRA
+06E9          ; So #       ARABIC PLACE OF SAJDAH
+06FD..06FE    ; So #   [2] ARABIC SIGN SINDHI AMPERSAND..ARABIC SIGN SINDHI POSTPOSITION MEN
+07F6          ; So #       NKO SYMBOL OO DENNEN
+09FA          ; So #       BENGALI ISSHAR
+0B70          ; So #       ORIYA ISSHAR
+0BF3..0BF8    ; So #   [6] TAMIL DAY SIGN..TAMIL AS ABOVE SIGN
+0BFA          ; So #       TAMIL NUMBER SIGN
+0C7F          ; So #       TELUGU SIGN TUUMU
+0CF1..0CF2    ; So #   [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
+0D79          ; So #       MALAYALAM DATE MARK
+0F01..0F03    ; So #   [3] TIBETAN MARK GTER YIG MGO TRUNCATED A..TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA
+0F13..0F17    ; So #   [5] TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN..TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS
+0F1A..0F1F    ; So #   [6] TIBETAN SIGN RDEL DKAR GCIG..TIBETAN SIGN RDEL DKAR RDEL NAG
+0F34          ; So #       TIBETAN MARK BSDUS RTAGS
+0F36          ; So #       TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN
+0F38          ; So #       TIBETAN MARK CHE MGO
+0FBE..0FC5    ; So #   [8] TIBETAN KU RU KHA..TIBETAN SYMBOL RDO RJE
+0FC7..0FCC    ; So #   [6] TIBETAN SYMBOL RDO RJE RGYA GRAM..TIBETAN SYMBOL NOR BU BZHI -KHYIL
+0FCE..0FCF    ; So #   [2] TIBETAN SIGN RDEL NAG RDEL DKAR..TIBETAN SIGN RDEL NAG GSUM
+109E..109F    ; So #   [2] MYANMAR SYMBOL SHAN ONE..MYANMAR SYMBOL SHAN EXCLAMATION
+1360          ; So #       ETHIOPIC SECTION MARK
+1390..1399    ; So #  [10] ETHIOPIC TONAL MARK YIZET..ETHIOPIC TONAL MARK KURT
+1940          ; So #       LIMBU SIGN LOO
+19E0..19FF    ; So #  [32] KHMER SYMBOL PATHAMASAT..KHMER SYMBOL DAP-PRAM ROC
+1B61..1B6A    ; So #  [10] BALINESE MUSICAL SYMBOL DONG..BALINESE MUSICAL SYMBOL DANG GEDE
+1B74..1B7C    ; So #   [9] BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG..BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING
+2100..2101    ; So #   [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT
+2103..2106    ; So #   [4] DEGREE CELSIUS..CADA UNA
+2108..2109    ; So #   [2] SCRUPLE..DEGREE FAHRENHEIT
+2114          ; So #       L B BAR SYMBOL
+2116..2118    ; So #   [3] NUMERO SIGN..SCRIPT CAPITAL P
+211E..2123    ; So #   [6] PRESCRIPTION TAKE..VERSICLE
+2125          ; So #       OUNCE SIGN
+2127          ; So #       INVERTED OHM SIGN
+2129          ; So #       TURNED GREEK SMALL LETTER IOTA
+212E          ; So #       ESTIMATED SYMBOL
+213A..213B    ; So #   [2] ROTATED CAPITAL Q..FACSIMILE SIGN
+214A          ; So #       PROPERTY LINE
+214C..214D    ; So #   [2] PER SIGN..AKTIESELSKAB
+214F          ; So #       SYMBOL FOR SAMARITAN SOURCE
+2195..2199    ; So #   [5] UP DOWN ARROW..SOUTH WEST ARROW
+219C..219F    ; So #   [4] LEFTWARDS WAVE ARROW..UPWARDS TWO HEADED ARROW
+21A1..21A2    ; So #   [2] DOWNWARDS TWO HEADED ARROW..LEFTWARDS ARROW WITH TAIL
+21A4..21A5    ; So #   [2] LEFTWARDS ARROW FROM BAR..UPWARDS ARROW FROM BAR
+21A7..21AD    ; So #   [7] DOWNWARDS ARROW FROM BAR..LEFT RIGHT WAVE ARROW
+21AF..21CD    ; So #  [31] DOWNWARDS ZIGZAG ARROW..LEFTWARDS DOUBLE ARROW WITH STROKE
+21D0..21D1    ; So #   [2] LEFTWARDS DOUBLE ARROW..UPWARDS DOUBLE ARROW
+21D3          ; So #       DOWNWARDS DOUBLE ARROW
+21D5..21F3    ; So #  [31] UP DOWN DOUBLE ARROW..UP DOWN WHITE ARROW
+2300..2307    ; So #   [8] DIAMETER SIGN..WAVY LINE
+230C..231F    ; So #  [20] BOTTOM RIGHT CROP..BOTTOM RIGHT CORNER
+2322..2328    ; So #   [7] FROWN..KEYBOARD
+232B..237B    ; So #  [81] ERASE TO THE LEFT..NOT CHECK MARK
+237D..239A    ; So #  [30] SHOULDERED OPEN BOX..CLEAR SCREEN SYMBOL
+23B4..23DB    ; So #  [40] TOP SQUARE BRACKET..FUSE
+23E2..23E7    ; So #   [6] WHITE TRAPEZIUM..ELECTRICAL INTERSECTION
+2400..2426    ; So #  [39] SYMBOL FOR NULL..SYMBOL FOR SUBSTITUTE FORM TWO
+2440..244A    ; So #  [11] OCR HOOK..OCR DOUBLE BACKSLASH
+249C..24E9    ; So #  [78] PARENTHESIZED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z
+2500..25B6    ; So # [183] BOX DRAWINGS LIGHT HORIZONTAL..BLACK RIGHT-POINTING TRIANGLE
+25B8..25C0    ; So #   [9] BLACK RIGHT-POINTING SMALL TRIANGLE..BLACK LEFT-POINTING TRIANGLE
+25C2..25F7    ; So #  [54] BLACK LEFT-POINTING SMALL TRIANGLE..WHITE CIRCLE WITH UPPER RIGHT QUADRANT
+2600..266E    ; So # [111] BLACK SUN WITH RAYS..MUSIC NATURAL SIGN
+2670..269D    ; So #  [46] WEST SYRIAC CROSS..OUTLINED WHITE STAR
+26A0..26BC    ; So #  [29] WARNING SIGN..SESQUIQUADRATE
+26C0..26C3    ; So #   [4] WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
+2701..2704    ; So #   [4] UPPER BLADE SCISSORS..WHITE SCISSORS
+2706..2709    ; So #   [4] TELEPHONE LOCATION SIGN..ENVELOPE
+270C..2727    ; So #  [28] VICTORY HAND..WHITE FOUR POINTED STAR
+2729..274B    ; So #  [35] STRESS OUTLINED WHITE STAR..HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK
+274D          ; So #       SHADOWED WHITE CIRCLE
+274F..2752    ; So #   [4] LOWER RIGHT DROP-SHADOWED WHITE SQUARE..UPPER RIGHT SHADOWED WHITE SQUARE
+2756          ; So #       BLACK DIAMOND MINUS WHITE X
+2758..275E    ; So #   [7] LIGHT VERTICAL BAR..HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT
+2761..2767    ; So #   [7] CURVED STEM PARAGRAPH SIGN ORNAMENT..ROTATED FLORAL HEART BULLET
+2794          ; So #       HEAVY WIDE-HEADED RIGHTWARDS ARROW
+2798..27AF    ; So #  [24] HEAVY SOUTH EAST ARROW..NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27B1..27BE    ; So #  [14] NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW..OPEN-OUTLINED RIGHTWARDS ARROW
+2800..28FF    ; So # [256] BRAILLE PATTERN BLANK..BRAILLE PATTERN DOTS-12345678
+2B00..2B2F    ; So #  [48] NORTH EAST WHITE ARROW..WHITE VERTICAL ELLIPSE
+2B45..2B46    ; So #   [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW
+2B50..2B54    ; So #   [5] WHITE MEDIUM STAR..WHITE RIGHT-POINTING PENTAGON
+2CE5..2CEA    ; So #   [6] COPTIC SYMBOL MI RO..COPTIC SYMBOL SHIMA SIMA
+2E80..2E99    ; So #  [26] CJK RADICAL REPEAT..CJK RADICAL RAP
+2E9B..2EF3    ; So #  [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE
+2F00..2FD5    ; So # [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE
+2FF0..2FFB    ; So #  [12] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID
+3004          ; So #       JAPANESE INDUSTRIAL STANDARD SYMBOL
+3012..3013    ; So #   [2] POSTAL MARK..GETA MARK
+3020          ; So #       POSTAL MARK FACE
+3036..3037    ; So #   [2] CIRCLED POSTAL MARK..IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL
+303E..303F    ; So #   [2] IDEOGRAPHIC VARIATION INDICATOR..IDEOGRAPHIC HALF FILL SPACE
+3190..3191    ; So #   [2] IDEOGRAPHIC ANNOTATION LINKING MARK..IDEOGRAPHIC ANNOTATION REVERSE MARK
+3196..319F    ; So #  [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC ANNOTATION MAN MARK
+31C0..31E3    ; So #  [36] CJK STROKE T..CJK STROKE Q
+3200..321E    ; So #  [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU
+322A..3243    ; So #  [26] PARENTHESIZED IDEOGRAPH MOON..PARENTHESIZED IDEOGRAPH REACH
+3250          ; So #       PARTNERSHIP SIGN
+3260..327F    ; So #  [32] CIRCLED HANGUL KIYEOK..KOREAN STANDARD SYMBOL
+328A..32B0    ; So #  [39] CIRCLED IDEOGRAPH MOON..CIRCLED IDEOGRAPH NIGHT
+32C0..32FE    ; So #  [63] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..CIRCLED KATAKANA WO
+3300..33FF    ; So # [256] SQUARE APAATO..SQUARE GAL
+4DC0..4DFF    ; So #  [64] HEXAGRAM FOR THE CREATIVE HEAVEN..HEXAGRAM FOR BEFORE COMPLETION
+A490..A4C6    ; So #  [55] YI RADICAL QOT..YI RADICAL KE
+A828..A82B    ; So #   [4] SYLOTI NAGRI POETRY MARK-1..SYLOTI NAGRI POETRY MARK-4
+FDFD          ; So #       ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM
+FFE4          ; So #       FULLWIDTH BROKEN BAR
+FFE8          ; So #       HALFWIDTH FORMS LIGHT VERTICAL
+FFED..FFEE    ; So #   [2] HALFWIDTH BLACK SQUARE..HALFWIDTH WHITE CIRCLE
+FFFC..FFFD    ; So #   [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHARACTER
+10102         ; So #       AEGEAN CHECK MARK
+10137..1013F  ; So #   [9] AEGEAN WEIGHT BASE UNIT..AEGEAN MEASURE THIRD SUBUNIT
+10179..10189  ; So #  [17] GREEK YEAR SIGN..GREEK TRYBLION BASE SIGN
+10190..1019B  ; So #  [12] ROMAN SEXTANS SIGN..ROMAN CENTURIAL SIGN
+101D0..101FC  ; So #  [45] PHAISTOS DISC SIGN PEDESTRIAN..PHAISTOS DISC SIGN WAVY BAND
+1D000..1D0F5  ; So # [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO
+1D100..1D126  ; So #  [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2
+1D129..1D164  ; So #  [60] MUSICAL SYMBOL MULTIPLE MEASURE REST..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D16A..1D16C  ; So #   [3] MUSICAL SYMBOL FINGERED TREMOLO-1..MUSICAL SYMBOL FINGERED TREMOLO-3
+1D183..1D184  ; So #   [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL ARPEGGIATO DOWN
+1D18C..1D1A9  ; So #  [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH
+1D1AE..1D1DD  ; So #  [48] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL PES SUBPUNCTIS
+1D200..1D241  ; So #  [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54
+1D245         ; So #       GREEK MUSICAL LEIMMA
+1D300..1D356  ; So #  [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING
+1F000..1F02B  ; So #  [44] MAHJONG TILE EAST WIND..MAHJONG TILE BACK
+1F030..1F093  ; So # [100] DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
+
+# Total code points: 3225
+
+# ================================================
+
+# General_Category=Initial_Punctuation
+
+00AB          ; Pi #       LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+2018          ; Pi #       LEFT SINGLE QUOTATION MARK
+201B..201C    ; Pi #   [2] SINGLE HIGH-REVERSED-9 QUOTATION MARK..LEFT DOUBLE QUOTATION MARK
+201F          ; Pi #       DOUBLE HIGH-REVERSED-9 QUOTATION MARK
+2039          ; Pi #       SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+2E02          ; Pi #       LEFT SUBSTITUTION BRACKET
+2E04          ; Pi #       LEFT DOTTED SUBSTITUTION BRACKET
+2E09          ; Pi #       LEFT TRANSPOSITION BRACKET
+2E0C          ; Pi #       LEFT RAISED OMISSION BRACKET
+2E1C          ; Pi #       LEFT LOW PARAPHRASE BRACKET
+2E20          ; Pi #       LEFT VERTICAL BAR WITH QUILL
+
+# Total code points: 12
+
+# ================================================
+
+# General_Category=Final_Punctuation
+
+00BB          ; Pf #       RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+2019          ; Pf #       RIGHT SINGLE QUOTATION MARK
+201D          ; Pf #       RIGHT DOUBLE QUOTATION MARK
+203A          ; Pf #       SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+2E03          ; Pf #       RIGHT SUBSTITUTION BRACKET
+2E05          ; Pf #       RIGHT DOTTED SUBSTITUTION BRACKET
+2E0A          ; Pf #       RIGHT TRANSPOSITION BRACKET
+2E0D          ; Pf #       RIGHT RAISED OMISSION BRACKET
+2E1D          ; Pf #       RIGHT LOW PARAPHRASE BRACKET
+2E21          ; Pf #       RIGHT VERTICAL BAR WITH QUILL
+
+# Total code points: 10
+
+# EOF
diff --git a/third_party/harfbuzz/contrib/tables/GraphemeBreakProperty.txt b/third_party/harfbuzz/contrib/tables/GraphemeBreakProperty.txt
new file mode 100644
index 0000000..50477a1
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/GraphemeBreakProperty.txt
@@ -0,0 +1,1166 @@
+# GraphemeBreakProperty-5.1.0.txt
+# Date: 2008-03-03, 21:57:47 GMT [MD]
+#
+# Unicode Character Database
+# Copyright (c) 1991-2008 Unicode, Inc.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For documentation, see UCD.html
+
+# ================================================
+
+# Property:	Grapheme_Cluster_Break
+
+#  All code points not explicitly listed for Grapheme_Cluster_Break
+#  have the value Other (XX).
+
+# @missing: 0000..10FFFF; Other
+
+# ================================================
+
+000D          ; CR # Cc       <control-000D>
+
+# Total code points: 1
+
+# ================================================
+
+000A          ; LF # Cc       <control-000A>
+
+# Total code points: 1
+
+# ================================================
+
+0000..0009    ; Control # Cc  [10] <control-0000>..<control-0009>
+000B..000C    ; Control # Cc   [2] <control-000B>..<control-000C>
+000E..001F    ; Control # Cc  [18] <control-000E>..<control-001F>
+007F..009F    ; Control # Cc  [33] <control-007F>..<control-009F>
+00AD          ; Control # Cf       SOFT HYPHEN
+0600..0603    ; Control # Cf   [4] ARABIC NUMBER SIGN..ARABIC SIGN SAFHA
+06DD          ; Control # Cf       ARABIC END OF AYAH
+070F          ; Control # Cf       SYRIAC ABBREVIATION MARK
+17B4..17B5    ; Control # Cf   [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
+200B          ; Control # Cf       ZERO WIDTH SPACE
+200E..200F    ; Control # Cf   [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK
+2028          ; Control # Zl       LINE SEPARATOR
+2029          ; Control # Zp       PARAGRAPH SEPARATOR
+202A..202E    ; Control # Cf   [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
+2060..2064    ; Control # Cf   [5] WORD JOINER..INVISIBLE PLUS
+206A..206F    ; Control # Cf   [6] INHIBIT SYMMETRIC SWAPPING..NOMINAL DIGIT SHAPES
+FEFF          ; Control # Cf       ZERO WIDTH NO-BREAK SPACE
+FFF9..FFFB    ; Control # Cf   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR
+1D173..1D17A  ; Control # Cf   [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
+E0001         ; Control # Cf       LANGUAGE TAG
+E0020..E007F  ; Control # Cf  [96] TAG SPACE..CANCEL TAG
+
+# Total code points: 202
+
+# ================================================
+
+0300..036F    ; Extend # Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X
+0483..0487    ; Extend # Mn   [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE
+0488..0489    ; Extend # Me   [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN
+0591..05BD    ; Extend # Mn  [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
+05BF          ; Extend # Mn       HEBREW POINT RAFE
+05C1..05C2    ; Extend # Mn   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
+05C4..05C5    ; Extend # Mn   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
+05C7          ; Extend # Mn       HEBREW POINT QAMATS QATAN
+0610..061A    ; Extend # Mn  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
+064B..065E    ; Extend # Mn  [20] ARABIC FATHATAN..ARABIC FATHA WITH TWO DOTS
+0670          ; Extend # Mn       ARABIC LETTER SUPERSCRIPT ALEF
+06D6..06DC    ; Extend # Mn   [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN
+06DE          ; Extend # Me       ARABIC START OF RUB EL HIZB
+06DF..06E4    ; Extend # Mn   [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA
+06E7..06E8    ; Extend # Mn   [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON
+06EA..06ED    ; Extend # Mn   [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM
+0711          ; Extend # Mn       SYRIAC LETTER SUPERSCRIPT ALAPH
+0730..074A    ; Extend # Mn  [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH
+07A6..07B0    ; Extend # Mn  [11] THAANA ABAFILI..THAANA SUKUN
+07EB..07F3    ; Extend # Mn   [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE
+0901..0902    ; Extend # Mn   [2] DEVANAGARI SIGN CANDRABINDU..DEVANAGARI SIGN ANUSVARA
+093C          ; Extend # Mn       DEVANAGARI SIGN NUKTA
+0941..0948    ; Extend # Mn   [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI
+094D          ; Extend # Mn       DEVANAGARI SIGN VIRAMA
+0951..0954    ; Extend # Mn   [4] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI ACUTE ACCENT
+0962..0963    ; Extend # Mn   [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL
+0981          ; Extend # Mn       BENGALI SIGN CANDRABINDU
+09BC          ; Extend # Mn       BENGALI SIGN NUKTA
+09BE          ; Extend # Mc       BENGALI VOWEL SIGN AA
+09C1..09C4    ; Extend # Mn   [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR
+09CD          ; Extend # Mn       BENGALI SIGN VIRAMA
+09D7          ; Extend # Mc       BENGALI AU LENGTH MARK
+09E2..09E3    ; Extend # Mn   [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL
+0A01..0A02    ; Extend # Mn   [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI
+0A3C          ; Extend # Mn       GURMUKHI SIGN NUKTA
+0A41..0A42    ; Extend # Mn   [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU
+0A47..0A48    ; Extend # Mn   [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI
+0A4B..0A4D    ; Extend # Mn   [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA
+0A51          ; Extend # Mn       GURMUKHI SIGN UDAAT
+0A70..0A71    ; Extend # Mn   [2] GURMUKHI TIPPI..GURMUKHI ADDAK
+0A75          ; Extend # Mn       GURMUKHI SIGN YAKASH
+0A81..0A82    ; Extend # Mn   [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA
+0ABC          ; Extend # Mn       GUJARATI SIGN NUKTA
+0AC1..0AC5    ; Extend # Mn   [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E
+0AC7..0AC8    ; Extend # Mn   [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI
+0ACD          ; Extend # Mn       GUJARATI SIGN VIRAMA
+0AE2..0AE3    ; Extend # Mn   [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL
+0B01          ; Extend # Mn       ORIYA SIGN CANDRABINDU
+0B3C          ; Extend # Mn       ORIYA SIGN NUKTA
+0B3E          ; Extend # Mc       ORIYA VOWEL SIGN AA
+0B3F          ; Extend # Mn       ORIYA VOWEL SIGN I
+0B41..0B44    ; Extend # Mn   [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
+0B4D          ; Extend # Mn       ORIYA SIGN VIRAMA
+0B56          ; Extend # Mn       ORIYA AI LENGTH MARK
+0B57          ; Extend # Mc       ORIYA AU LENGTH MARK
+0B62..0B63    ; Extend # Mn   [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
+0B82          ; Extend # Mn       TAMIL SIGN ANUSVARA
+0BBE          ; Extend # Mc       TAMIL VOWEL SIGN AA
+0BC0          ; Extend # Mn       TAMIL VOWEL SIGN II
+0BCD          ; Extend # Mn       TAMIL SIGN VIRAMA
+0BD7          ; Extend # Mc       TAMIL AU LENGTH MARK
+0C3E..0C40    ; Extend # Mn   [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II
+0C46..0C48    ; Extend # Mn   [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI
+0C4A..0C4D    ; Extend # Mn   [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
+0C55..0C56    ; Extend # Mn   [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
+0C62..0C63    ; Extend # Mn   [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
+0CBC          ; Extend # Mn       KANNADA SIGN NUKTA
+0CBF          ; Extend # Mn       KANNADA VOWEL SIGN I
+0CC2          ; Extend # Mc       KANNADA VOWEL SIGN UU
+0CC6          ; Extend # Mn       KANNADA VOWEL SIGN E
+0CCC..0CCD    ; Extend # Mn   [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA
+0CD5..0CD6    ; Extend # Mc   [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+0CE2..0CE3    ; Extend # Mn   [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
+0D3E          ; Extend # Mc       MALAYALAM VOWEL SIGN AA
+0D41..0D44    ; Extend # Mn   [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR
+0D4D          ; Extend # Mn       MALAYALAM SIGN VIRAMA
+0D57          ; Extend # Mc       MALAYALAM AU LENGTH MARK
+0D62..0D63    ; Extend # Mn   [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL
+0DCA          ; Extend # Mn       SINHALA SIGN AL-LAKUNA
+0DCF          ; Extend # Mc       SINHALA VOWEL SIGN AELA-PILLA
+0DD2..0DD4    ; Extend # Mn   [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA
+0DD6          ; Extend # Mn       SINHALA VOWEL SIGN DIGA PAA-PILLA
+0DDF          ; Extend # Mc       SINHALA VOWEL SIGN GAYANUKITTA
+0E30          ; Extend # Lo       THAI CHARACTER SARA A
+0E31          ; Extend # Mn       THAI CHARACTER MAI HAN-AKAT
+0E32..0E33    ; Extend # Lo   [2] THAI CHARACTER SARA AA..THAI CHARACTER SARA AM
+0E34..0E3A    ; Extend # Mn   [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU
+0E45          ; Extend # Lo       THAI CHARACTER LAKKHANGYAO
+0E47..0E4E    ; Extend # Mn   [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN
+0EB0          ; Extend # Lo       LAO VOWEL SIGN A
+0EB1          ; Extend # Mn       LAO VOWEL SIGN MAI KAN
+0EB2..0EB3    ; Extend # Lo   [2] LAO VOWEL SIGN AA..LAO VOWEL SIGN AM
+0EB4..0EB9    ; Extend # Mn   [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU
+0EBB..0EBC    ; Extend # Mn   [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO
+0EC8..0ECD    ; Extend # Mn   [6] LAO TONE MAI EK..LAO NIGGAHITA
+0F18..0F19    ; Extend # Mn   [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
+0F35          ; Extend # Mn       TIBETAN MARK NGAS BZUNG NYI ZLA
+0F37          ; Extend # Mn       TIBETAN MARK NGAS BZUNG SGOR RTAGS
+0F39          ; Extend # Mn       TIBETAN MARK TSA -PHRU
+0F71..0F7E    ; Extend # Mn  [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO
+0F80..0F84    ; Extend # Mn   [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA
+0F86..0F87    ; Extend # Mn   [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS
+0F90..0F97    ; Extend # Mn   [8] TIBETAN SUBJOINED LETTER KA..TIBETAN SUBJOINED LETTER JA
+0F99..0FBC    ; Extend # Mn  [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA
+0FC6          ; Extend # Mn       TIBETAN SYMBOL PADMA GDAN
+102D..1030    ; Extend # Mn   [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU
+1032..1037    ; Extend # Mn   [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW
+1039..103A    ; Extend # Mn   [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+103D..103E    ; Extend # Mn   [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA
+1058..1059    ; Extend # Mn   [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
+105E..1060    ; Extend # Mn   [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
+1071..1074    ; Extend # Mn   [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE
+1082          ; Extend # Mn       MYANMAR CONSONANT SIGN SHAN MEDIAL WA
+1085..1086    ; Extend # Mn   [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y
+108D          ; Extend # Mn       MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
+135F          ; Extend # Mn       ETHIOPIC COMBINING GEMINATION MARK
+1712..1714    ; Extend # Mn   [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA
+1732..1734    ; Extend # Mn   [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD
+1752..1753    ; Extend # Mn   [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
+1772..1773    ; Extend # Mn   [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U
+17B7..17BD    ; Extend # Mn   [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA
+17C6          ; Extend # Mn       KHMER SIGN NIKAHIT
+17C9..17D3    ; Extend # Mn  [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT
+17DD          ; Extend # Mn       KHMER SIGN ATTHACAN
+180B..180D    ; Extend # Mn   [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
+18A9          ; Extend # Mn       MONGOLIAN LETTER ALI GALI DAGALGA
+1920..1922    ; Extend # Mn   [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U
+1927..1928    ; Extend # Mn   [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O
+1932          ; Extend # Mn       LIMBU SMALL LETTER ANUSVARA
+1939..193B    ; Extend # Mn   [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I
+1A17..1A18    ; Extend # Mn   [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U
+1B00..1B03    ; Extend # Mn   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
+1B34          ; Extend # Mn       BALINESE SIGN REREKAN
+1B36..1B3A    ; Extend # Mn   [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
+1B3C          ; Extend # Mn       BALINESE VOWEL SIGN LA LENGA
+1B42          ; Extend # Mn       BALINESE VOWEL SIGN PEPET
+1B6B..1B73    ; Extend # Mn   [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG
+1B80..1B81    ; Extend # Mn   [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR
+1BA2..1BA5    ; Extend # Mn   [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU
+1BA8..1BA9    ; Extend # Mn   [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG
+1C2C..1C33    ; Extend # Mn   [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T
+1C36..1C37    ; Extend # Mn   [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA
+1DC0..1DE6    ; Extend # Mn  [39] COMBINING DOTTED GRAVE ACCENT..COMBINING LATIN SMALL LETTER Z
+1DFE..1DFF    ; Extend # Mn   [2] COMBINING LEFT ARROWHEAD ABOVE..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW
+200C..200D    ; Extend # Cf   [2] ZERO WIDTH NON-JOINER..ZERO WIDTH JOINER
+20D0..20DC    ; Extend # Mn  [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
+20DD..20E0    ; Extend # Me   [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH
+20E1          ; Extend # Mn       COMBINING LEFT RIGHT ARROW ABOVE
+20E2..20E4    ; Extend # Me   [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE
+20E5..20F0    ; Extend # Mn  [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE
+2DE0..2DFF    ; Extend # Mn  [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS
+302A..302F    ; Extend # Mn   [6] IDEOGRAPHIC LEVEL TONE MARK..HANGUL DOUBLE DOT TONE MARK
+3099..309A    ; Extend # Mn   [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+A66F          ; Extend # Mn       COMBINING CYRILLIC VZMET
+A670..A672    ; Extend # Me   [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN
+A67C..A67D    ; Extend # Mn   [2] COMBINING CYRILLIC KAVYKA..COMBINING CYRILLIC PAYEROK
+A802          ; Extend # Mn       SYLOTI NAGRI SIGN DVISVARA
+A806          ; Extend # Mn       SYLOTI NAGRI SIGN HASANTA
+A80B          ; Extend # Mn       SYLOTI NAGRI SIGN ANUSVARA
+A825..A826    ; Extend # Mn   [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E
+A8C4          ; Extend # Mn       SAURASHTRA SIGN VIRAMA
+A926..A92D    ; Extend # Mn   [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU
+A947..A951    ; Extend # Mn  [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
+AA29..AA2E    ; Extend # Mn   [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
+AA31..AA32    ; Extend # Mn   [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
+AA35..AA36    ; Extend # Mn   [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA
+AA43          ; Extend # Mn       CHAM CONSONANT SIGN FINAL NG
+AA4C          ; Extend # Mn       CHAM CONSONANT SIGN FINAL M
+FB1E          ; Extend # Mn       HEBREW POINT JUDEO-SPANISH VARIKA
+FE00..FE0F    ; Extend # Mn  [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
+FE20..FE26    ; Extend # Mn   [7] COMBINING LIGATURE LEFT HALF..COMBINING CONJOINING MACRON
+FF9E..FF9F    ; Extend # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+101FD         ; Extend # Mn       PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE
+10A01..10A03  ; Extend # Mn   [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R
+10A05..10A06  ; Extend # Mn   [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O
+10A0C..10A0F  ; Extend # Mn   [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA
+10A38..10A3A  ; Extend # Mn   [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW
+10A3F         ; Extend # Mn       KHAROSHTHI VIRAMA
+1D165         ; Extend # Mc       MUSICAL SYMBOL COMBINING STEM
+1D167..1D169  ; Extend # Mn   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
+1D16E..1D172  ; Extend # Mc   [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5
+1D17B..1D182  ; Extend # Mn   [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
+1D185..1D18B  ; Extend # Mn   [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
+1D1AA..1D1AD  ; Extend # Mn   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
+1D242..1D244  ; Extend # Mn   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+E0100..E01EF  ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
+
+# Total code points: 1075
+
+# ================================================
+
+0E40..0E44    ; Prepend # Lo   [5] THAI CHARACTER SARA E..THAI CHARACTER SARA AI MAIMALAI
+0EC0..0EC4    ; Prepend # Lo   [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI
+
+# Total code points: 10
+
+# ================================================
+
+0903          ; SpacingMark # Mc       DEVANAGARI SIGN VISARGA
+093E..0940    ; SpacingMark # Mc   [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II
+0949..094C    ; SpacingMark # Mc   [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU
+0982..0983    ; SpacingMark # Mc   [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA
+09BF..09C0    ; SpacingMark # Mc   [2] BENGALI VOWEL SIGN I..BENGALI VOWEL SIGN II
+09C7..09C8    ; SpacingMark # Mc   [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI
+09CB..09CC    ; SpacingMark # Mc   [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+0A03          ; SpacingMark # Mc       GURMUKHI SIGN VISARGA
+0A3E..0A40    ; SpacingMark # Mc   [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II
+0A83          ; SpacingMark # Mc       GUJARATI SIGN VISARGA
+0ABE..0AC0    ; SpacingMark # Mc   [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II
+0AC9          ; SpacingMark # Mc       GUJARATI VOWEL SIGN CANDRA O
+0ACB..0ACC    ; SpacingMark # Mc   [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU
+0B02..0B03    ; SpacingMark # Mc   [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA
+0B40          ; SpacingMark # Mc       ORIYA VOWEL SIGN II
+0B47..0B48    ; SpacingMark # Mc   [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
+0B4B..0B4C    ; SpacingMark # Mc   [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+0BBF          ; SpacingMark # Mc       TAMIL VOWEL SIGN I
+0BC1..0BC2    ; SpacingMark # Mc   [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU
+0BC6..0BC8    ; SpacingMark # Mc   [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI
+0BCA..0BCC    ; SpacingMark # Mc   [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+0C01..0C03    ; SpacingMark # Mc   [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA
+0C41..0C44    ; SpacingMark # Mc   [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR
+0C82..0C83    ; SpacingMark # Mc   [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA
+0CBE          ; SpacingMark # Mc       KANNADA VOWEL SIGN AA
+0CC0..0CC1    ; SpacingMark # Mc   [2] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN U
+0CC3..0CC4    ; SpacingMark # Mc   [2] KANNADA VOWEL SIGN VOCALIC R..KANNADA VOWEL SIGN VOCALIC RR
+0CC7..0CC8    ; SpacingMark # Mc   [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+0CCA..0CCB    ; SpacingMark # Mc   [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+0D02..0D03    ; SpacingMark # Mc   [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA
+0D3F..0D40    ; SpacingMark # Mc   [2] MALAYALAM VOWEL SIGN I..MALAYALAM VOWEL SIGN II
+0D46..0D48    ; SpacingMark # Mc   [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI
+0D4A..0D4C    ; SpacingMark # Mc   [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+0D82..0D83    ; SpacingMark # Mc   [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA
+0DD0..0DD1    ; SpacingMark # Mc   [2] SINHALA VOWEL SIGN KETTI AEDA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA
+0DD8..0DDE    ; SpacingMark # Mc   [7] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA
+0DF2..0DF3    ; SpacingMark # Mc   [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA
+0F3E..0F3F    ; SpacingMark # Mc   [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES
+0F7F          ; SpacingMark # Mc       TIBETAN SIGN RNAM BCAD
+102B..102C    ; SpacingMark # Mc   [2] MYANMAR VOWEL SIGN TALL AA..MYANMAR VOWEL SIGN AA
+1031          ; SpacingMark # Mc       MYANMAR VOWEL SIGN E
+1038          ; SpacingMark # Mc       MYANMAR SIGN VISARGA
+103B..103C    ; SpacingMark # Mc   [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA
+1056..1057    ; SpacingMark # Mc   [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR
+1062..1064    ; SpacingMark # Mc   [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO
+1067..106D    ; SpacingMark # Mc   [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5
+1083..1084    ; SpacingMark # Mc   [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E
+1087..108C    ; SpacingMark # Mc   [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
+108F          ; SpacingMark # Mc       MYANMAR SIGN RUMAI PALAUNG TONE-5
+17B6          ; SpacingMark # Mc       KHMER VOWEL SIGN AA
+17BE..17C5    ; SpacingMark # Mc   [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU
+17C7..17C8    ; SpacingMark # Mc   [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU
+1923..1926    ; SpacingMark # Mc   [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU
+1929..192B    ; SpacingMark # Mc   [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA
+1930..1931    ; SpacingMark # Mc   [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA
+1933..1938    ; SpacingMark # Mc   [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA
+19B0..19C0    ; SpacingMark # Mc  [17] NEW TAI LUE VOWEL SIGN VOWEL SHORTENER..NEW TAI LUE VOWEL SIGN IY
+19C8..19C9    ; SpacingMark # Mc   [2] NEW TAI LUE TONE MARK-1..NEW TAI LUE TONE MARK-2
+1A19..1A1B    ; SpacingMark # Mc   [3] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN AE
+1B04          ; SpacingMark # Mc       BALINESE SIGN BISAH
+1B35          ; SpacingMark # Mc       BALINESE VOWEL SIGN TEDUNG
+1B3B          ; SpacingMark # Mc       BALINESE VOWEL SIGN RA REPA TEDUNG
+1B3D..1B41    ; SpacingMark # Mc   [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
+1B43..1B44    ; SpacingMark # Mc   [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
+1B82          ; SpacingMark # Mc       SUNDANESE SIGN PANGWISAD
+1BA1          ; SpacingMark # Mc       SUNDANESE CONSONANT SIGN PAMINGKAL
+1BA6..1BA7    ; SpacingMark # Mc   [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG
+1BAA          ; SpacingMark # Mc       SUNDANESE SIGN PAMAAEH
+1C24..1C2B    ; SpacingMark # Mc   [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
+1C34..1C35    ; SpacingMark # Mc   [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
+A823..A824    ; SpacingMark # Mc   [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
+A827          ; SpacingMark # Mc       SYLOTI NAGRI VOWEL SIGN OO
+A880..A881    ; SpacingMark # Mc   [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA
+A8B4..A8C3    ; SpacingMark # Mc  [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU
+A952..A953    ; SpacingMark # Mc   [2] REJANG CONSONANT SIGN H..REJANG VIRAMA
+AA2F..AA30    ; SpacingMark # Mc   [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
+AA33..AA34    ; SpacingMark # Mc   [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA
+AA4D          ; SpacingMark # Mc       CHAM CONSONANT SIGN FINAL H
+1D166         ; SpacingMark # Mc       MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
+1D16D         ; SpacingMark # Mc       MUSICAL SYMBOL COMBINING AUGMENTATION DOT
+
+# Total code points: 217
+
+# ================================================
+
+1100..1159    ; L # Lo  [90] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG YEORINHIEUH
+115F          ; L # Lo       HANGUL CHOSEONG FILLER
+
+# Total code points: 91
+
+# ================================================
+
+1160..11A2    ; V # Lo  [67] HANGUL JUNGSEONG FILLER..HANGUL JUNGSEONG SSANGARAEA
+
+# Total code points: 67
+
+# ================================================
+
+11A8..11F9    ; T # Lo  [82] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG YEORINHIEUH
+
+# Total code points: 82
+
+# ================================================
+
+AC00          ; LV # Lo       HANGUL SYLLABLE GA
+AC1C          ; LV # Lo       HANGUL SYLLABLE GAE
+AC38          ; LV # Lo       HANGUL SYLLABLE GYA
+AC54          ; LV # Lo       HANGUL SYLLABLE GYAE
+AC70          ; LV # Lo       HANGUL SYLLABLE GEO
+AC8C          ; LV # Lo       HANGUL SYLLABLE GE
+ACA8          ; LV # Lo       HANGUL SYLLABLE GYEO
+ACC4          ; LV # Lo       HANGUL SYLLABLE GYE
+ACE0          ; LV # Lo       HANGUL SYLLABLE GO
+ACFC          ; LV # Lo       HANGUL SYLLABLE GWA
+AD18          ; LV # Lo       HANGUL SYLLABLE GWAE
+AD34          ; LV # Lo       HANGUL SYLLABLE GOE
+AD50          ; LV # Lo       HANGUL SYLLABLE GYO
+AD6C          ; LV # Lo       HANGUL SYLLABLE GU
+AD88          ; LV # Lo       HANGUL SYLLABLE GWEO
+ADA4          ; LV # Lo       HANGUL SYLLABLE GWE
+ADC0          ; LV # Lo       HANGUL SYLLABLE GWI
+ADDC          ; LV # Lo       HANGUL SYLLABLE GYU
+ADF8          ; LV # Lo       HANGUL SYLLABLE GEU
+AE14          ; LV # Lo       HANGUL SYLLABLE GYI
+AE30          ; LV # Lo       HANGUL SYLLABLE GI
+AE4C          ; LV # Lo       HANGUL SYLLABLE GGA
+AE68          ; LV # Lo       HANGUL SYLLABLE GGAE
+AE84          ; LV # Lo       HANGUL SYLLABLE GGYA
+AEA0          ; LV # Lo       HANGUL SYLLABLE GGYAE
+AEBC          ; LV # Lo       HANGUL SYLLABLE GGEO
+AED8          ; LV # Lo       HANGUL SYLLABLE GGE
+AEF4          ; LV # Lo       HANGUL SYLLABLE GGYEO
+AF10          ; LV # Lo       HANGUL SYLLABLE GGYE
+AF2C          ; LV # Lo       HANGUL SYLLABLE GGO
+AF48          ; LV # Lo       HANGUL SYLLABLE GGWA
+AF64          ; LV # Lo       HANGUL SYLLABLE GGWAE
+AF80          ; LV # Lo       HANGUL SYLLABLE GGOE
+AF9C          ; LV # Lo       HANGUL SYLLABLE GGYO
+AFB8          ; LV # Lo       HANGUL SYLLABLE GGU
+AFD4          ; LV # Lo       HANGUL SYLLABLE GGWEO
+AFF0          ; LV # Lo       HANGUL SYLLABLE GGWE
+B00C          ; LV # Lo       HANGUL SYLLABLE GGWI
+B028          ; LV # Lo       HANGUL SYLLABLE GGYU
+B044          ; LV # Lo       HANGUL SYLLABLE GGEU
+B060          ; LV # Lo       HANGUL SYLLABLE GGYI
+B07C          ; LV # Lo       HANGUL SYLLABLE GGI
+B098          ; LV # Lo       HANGUL SYLLABLE NA
+B0B4          ; LV # Lo       HANGUL SYLLABLE NAE
+B0D0          ; LV # Lo       HANGUL SYLLABLE NYA
+B0EC          ; LV # Lo       HANGUL SYLLABLE NYAE
+B108          ; LV # Lo       HANGUL SYLLABLE NEO
+B124          ; LV # Lo       HANGUL SYLLABLE NE
+B140          ; LV # Lo       HANGUL SYLLABLE NYEO
+B15C          ; LV # Lo       HANGUL SYLLABLE NYE
+B178          ; LV # Lo       HANGUL SYLLABLE NO
+B194          ; LV # Lo       HANGUL SYLLABLE NWA
+B1B0          ; LV # Lo       HANGUL SYLLABLE NWAE
+B1CC          ; LV # Lo       HANGUL SYLLABLE NOE
+B1E8          ; LV # Lo       HANGUL SYLLABLE NYO
+B204          ; LV # Lo       HANGUL SYLLABLE NU
+B220          ; LV # Lo       HANGUL SYLLABLE NWEO
+B23C          ; LV # Lo       HANGUL SYLLABLE NWE
+B258          ; LV # Lo       HANGUL SYLLABLE NWI
+B274          ; LV # Lo       HANGUL SYLLABLE NYU
+B290          ; LV # Lo       HANGUL SYLLABLE NEU
+B2AC          ; LV # Lo       HANGUL SYLLABLE NYI
+B2C8          ; LV # Lo       HANGUL SYLLABLE NI
+B2E4          ; LV # Lo       HANGUL SYLLABLE DA
+B300          ; LV # Lo       HANGUL SYLLABLE DAE
+B31C          ; LV # Lo       HANGUL SYLLABLE DYA
+B338          ; LV # Lo       HANGUL SYLLABLE DYAE
+B354          ; LV # Lo       HANGUL SYLLABLE DEO
+B370          ; LV # Lo       HANGUL SYLLABLE DE
+B38C          ; LV # Lo       HANGUL SYLLABLE DYEO
+B3A8          ; LV # Lo       HANGUL SYLLABLE DYE
+B3C4          ; LV # Lo       HANGUL SYLLABLE DO
+B3E0          ; LV # Lo       HANGUL SYLLABLE DWA
+B3FC          ; LV # Lo       HANGUL SYLLABLE DWAE
+B418          ; LV # Lo       HANGUL SYLLABLE DOE
+B434          ; LV # Lo       HANGUL SYLLABLE DYO
+B450          ; LV # Lo       HANGUL SYLLABLE DU
+B46C          ; LV # Lo       HANGUL SYLLABLE DWEO
+B488          ; LV # Lo       HANGUL SYLLABLE DWE
+B4A4          ; LV # Lo       HANGUL SYLLABLE DWI
+B4C0          ; LV # Lo       HANGUL SYLLABLE DYU
+B4DC          ; LV # Lo       HANGUL SYLLABLE DEU
+B4F8          ; LV # Lo       HANGUL SYLLABLE DYI
+B514          ; LV # Lo       HANGUL SYLLABLE DI
+B530          ; LV # Lo       HANGUL SYLLABLE DDA
+B54C          ; LV # Lo       HANGUL SYLLABLE DDAE
+B568          ; LV # Lo       HANGUL SYLLABLE DDYA
+B584          ; LV # Lo       HANGUL SYLLABLE DDYAE
+B5A0          ; LV # Lo       HANGUL SYLLABLE DDEO
+B5BC          ; LV # Lo       HANGUL SYLLABLE DDE
+B5D8          ; LV # Lo       HANGUL SYLLABLE DDYEO
+B5F4          ; LV # Lo       HANGUL SYLLABLE DDYE
+B610          ; LV # Lo       HANGUL SYLLABLE DDO
+B62C          ; LV # Lo       HANGUL SYLLABLE DDWA
+B648          ; LV # Lo       HANGUL SYLLABLE DDWAE
+B664          ; LV # Lo       HANGUL SYLLABLE DDOE
+B680          ; LV # Lo       HANGUL SYLLABLE DDYO
+B69C          ; LV # Lo       HANGUL SYLLABLE DDU
+B6B8          ; LV # Lo       HANGUL SYLLABLE DDWEO
+B6D4          ; LV # Lo       HANGUL SYLLABLE DDWE
+B6F0          ; LV # Lo       HANGUL SYLLABLE DDWI
+B70C          ; LV # Lo       HANGUL SYLLABLE DDYU
+B728          ; LV # Lo       HANGUL SYLLABLE DDEU
+B744          ; LV # Lo       HANGUL SYLLABLE DDYI
+B760          ; LV # Lo       HANGUL SYLLABLE DDI
+B77C          ; LV # Lo       HANGUL SYLLABLE RA
+B798          ; LV # Lo       HANGUL SYLLABLE RAE
+B7B4          ; LV # Lo       HANGUL SYLLABLE RYA
+B7D0          ; LV # Lo       HANGUL SYLLABLE RYAE
+B7EC          ; LV # Lo       HANGUL SYLLABLE REO
+B808          ; LV # Lo       HANGUL SYLLABLE RE
+B824          ; LV # Lo       HANGUL SYLLABLE RYEO
+B840          ; LV # Lo       HANGUL SYLLABLE RYE
+B85C          ; LV # Lo       HANGUL SYLLABLE RO
+B878          ; LV # Lo       HANGUL SYLLABLE RWA
+B894          ; LV # Lo       HANGUL SYLLABLE RWAE
+B8B0          ; LV # Lo       HANGUL SYLLABLE ROE
+B8CC          ; LV # Lo       HANGUL SYLLABLE RYO
+B8E8          ; LV # Lo       HANGUL SYLLABLE RU
+B904          ; LV # Lo       HANGUL SYLLABLE RWEO
+B920          ; LV # Lo       HANGUL SYLLABLE RWE
+B93C          ; LV # Lo       HANGUL SYLLABLE RWI
+B958          ; LV # Lo       HANGUL SYLLABLE RYU
+B974          ; LV # Lo       HANGUL SYLLABLE REU
+B990          ; LV # Lo       HANGUL SYLLABLE RYI
+B9AC          ; LV # Lo       HANGUL SYLLABLE RI
+B9C8          ; LV # Lo       HANGUL SYLLABLE MA
+B9E4          ; LV # Lo       HANGUL SYLLABLE MAE
+BA00          ; LV # Lo       HANGUL SYLLABLE MYA
+BA1C          ; LV # Lo       HANGUL SYLLABLE MYAE
+BA38          ; LV # Lo       HANGUL SYLLABLE MEO
+BA54          ; LV # Lo       HANGUL SYLLABLE ME
+BA70          ; LV # Lo       HANGUL SYLLABLE MYEO
+BA8C          ; LV # Lo       HANGUL SYLLABLE MYE
+BAA8          ; LV # Lo       HANGUL SYLLABLE MO
+BAC4          ; LV # Lo       HANGUL SYLLABLE MWA
+BAE0          ; LV # Lo       HANGUL SYLLABLE MWAE
+BAFC          ; LV # Lo       HANGUL SYLLABLE MOE
+BB18          ; LV # Lo       HANGUL SYLLABLE MYO
+BB34          ; LV # Lo       HANGUL SYLLABLE MU
+BB50          ; LV # Lo       HANGUL SYLLABLE MWEO
+BB6C          ; LV # Lo       HANGUL SYLLABLE MWE
+BB88          ; LV # Lo       HANGUL SYLLABLE MWI
+BBA4          ; LV # Lo       HANGUL SYLLABLE MYU
+BBC0          ; LV # Lo       HANGUL SYLLABLE MEU
+BBDC          ; LV # Lo       HANGUL SYLLABLE MYI
+BBF8          ; LV # Lo       HANGUL SYLLABLE MI
+BC14          ; LV # Lo       HANGUL SYLLABLE BA
+BC30          ; LV # Lo       HANGUL SYLLABLE BAE
+BC4C          ; LV # Lo       HANGUL SYLLABLE BYA
+BC68          ; LV # Lo       HANGUL SYLLABLE BYAE
+BC84          ; LV # Lo       HANGUL SYLLABLE BEO
+BCA0          ; LV # Lo       HANGUL SYLLABLE BE
+BCBC          ; LV # Lo       HANGUL SYLLABLE BYEO
+BCD8          ; LV # Lo       HANGUL SYLLABLE BYE
+BCF4          ; LV # Lo       HANGUL SYLLABLE BO
+BD10          ; LV # Lo       HANGUL SYLLABLE BWA
+BD2C          ; LV # Lo       HANGUL SYLLABLE BWAE
+BD48          ; LV # Lo       HANGUL SYLLABLE BOE
+BD64          ; LV # Lo       HANGUL SYLLABLE BYO
+BD80          ; LV # Lo       HANGUL SYLLABLE BU
+BD9C          ; LV # Lo       HANGUL SYLLABLE BWEO
+BDB8          ; LV # Lo       HANGUL SYLLABLE BWE
+BDD4          ; LV # Lo       HANGUL SYLLABLE BWI
+BDF0          ; LV # Lo       HANGUL SYLLABLE BYU
+BE0C          ; LV # Lo       HANGUL SYLLABLE BEU
+BE28          ; LV # Lo       HANGUL SYLLABLE BYI
+BE44          ; LV # Lo       HANGUL SYLLABLE BI
+BE60          ; LV # Lo       HANGUL SYLLABLE BBA
+BE7C          ; LV # Lo       HANGUL SYLLABLE BBAE
+BE98          ; LV # Lo       HANGUL SYLLABLE BBYA
+BEB4          ; LV # Lo       HANGUL SYLLABLE BBYAE
+BED0          ; LV # Lo       HANGUL SYLLABLE BBEO
+BEEC          ; LV # Lo       HANGUL SYLLABLE BBE
+BF08          ; LV # Lo       HANGUL SYLLABLE BBYEO
+BF24          ; LV # Lo       HANGUL SYLLABLE BBYE
+BF40          ; LV # Lo       HANGUL SYLLABLE BBO
+BF5C          ; LV # Lo       HANGUL SYLLABLE BBWA
+BF78          ; LV # Lo       HANGUL SYLLABLE BBWAE
+BF94          ; LV # Lo       HANGUL SYLLABLE BBOE
+BFB0          ; LV # Lo       HANGUL SYLLABLE BBYO
+BFCC          ; LV # Lo       HANGUL SYLLABLE BBU
+BFE8          ; LV # Lo       HANGUL SYLLABLE BBWEO
+C004          ; LV # Lo       HANGUL SYLLABLE BBWE
+C020          ; LV # Lo       HANGUL SYLLABLE BBWI
+C03C          ; LV # Lo       HANGUL SYLLABLE BBYU
+C058          ; LV # Lo       HANGUL SYLLABLE BBEU
+C074          ; LV # Lo       HANGUL SYLLABLE BBYI
+C090          ; LV # Lo       HANGUL SYLLABLE BBI
+C0AC          ; LV # Lo       HANGUL SYLLABLE SA
+C0C8          ; LV # Lo       HANGUL SYLLABLE SAE
+C0E4          ; LV # Lo       HANGUL SYLLABLE SYA
+C100          ; LV # Lo       HANGUL SYLLABLE SYAE
+C11C          ; LV # Lo       HANGUL SYLLABLE SEO
+C138          ; LV # Lo       HANGUL SYLLABLE SE
+C154          ; LV # Lo       HANGUL SYLLABLE SYEO
+C170          ; LV # Lo       HANGUL SYLLABLE SYE
+C18C          ; LV # Lo       HANGUL SYLLABLE SO
+C1A8          ; LV # Lo       HANGUL SYLLABLE SWA
+C1C4          ; LV # Lo       HANGUL SYLLABLE SWAE
+C1E0          ; LV # Lo       HANGUL SYLLABLE SOE
+C1FC          ; LV # Lo       HANGUL SYLLABLE SYO
+C218          ; LV # Lo       HANGUL SYLLABLE SU
+C234          ; LV # Lo       HANGUL SYLLABLE SWEO
+C250          ; LV # Lo       HANGUL SYLLABLE SWE
+C26C          ; LV # Lo       HANGUL SYLLABLE SWI
+C288          ; LV # Lo       HANGUL SYLLABLE SYU
+C2A4          ; LV # Lo       HANGUL SYLLABLE SEU
+C2C0          ; LV # Lo       HANGUL SYLLABLE SYI
+C2DC          ; LV # Lo       HANGUL SYLLABLE SI
+C2F8          ; LV # Lo       HANGUL SYLLABLE SSA
+C314          ; LV # Lo       HANGUL SYLLABLE SSAE
+C330          ; LV # Lo       HANGUL SYLLABLE SSYA
+C34C          ; LV # Lo       HANGUL SYLLABLE SSYAE
+C368          ; LV # Lo       HANGUL SYLLABLE SSEO
+C384          ; LV # Lo       HANGUL SYLLABLE SSE
+C3A0          ; LV # Lo       HANGUL SYLLABLE SSYEO
+C3BC          ; LV # Lo       HANGUL SYLLABLE SSYE
+C3D8          ; LV # Lo       HANGUL SYLLABLE SSO
+C3F4          ; LV # Lo       HANGUL SYLLABLE SSWA
+C410          ; LV # Lo       HANGUL SYLLABLE SSWAE
+C42C          ; LV # Lo       HANGUL SYLLABLE SSOE
+C448          ; LV # Lo       HANGUL SYLLABLE SSYO
+C464          ; LV # Lo       HANGUL SYLLABLE SSU
+C480          ; LV # Lo       HANGUL SYLLABLE SSWEO
+C49C          ; LV # Lo       HANGUL SYLLABLE SSWE
+C4B8          ; LV # Lo       HANGUL SYLLABLE SSWI
+C4D4          ; LV # Lo       HANGUL SYLLABLE SSYU
+C4F0          ; LV # Lo       HANGUL SYLLABLE SSEU
+C50C          ; LV # Lo       HANGUL SYLLABLE SSYI
+C528          ; LV # Lo       HANGUL SYLLABLE SSI
+C544          ; LV # Lo       HANGUL SYLLABLE A
+C560          ; LV # Lo       HANGUL SYLLABLE AE
+C57C          ; LV # Lo       HANGUL SYLLABLE YA
+C598          ; LV # Lo       HANGUL SYLLABLE YAE
+C5B4          ; LV # Lo       HANGUL SYLLABLE EO
+C5D0          ; LV # Lo       HANGUL SYLLABLE E
+C5EC          ; LV # Lo       HANGUL SYLLABLE YEO
+C608          ; LV # Lo       HANGUL SYLLABLE YE
+C624          ; LV # Lo       HANGUL SYLLABLE O
+C640          ; LV # Lo       HANGUL SYLLABLE WA
+C65C          ; LV # Lo       HANGUL SYLLABLE WAE
+C678          ; LV # Lo       HANGUL SYLLABLE OE
+C694          ; LV # Lo       HANGUL SYLLABLE YO
+C6B0          ; LV # Lo       HANGUL SYLLABLE U
+C6CC          ; LV # Lo       HANGUL SYLLABLE WEO
+C6E8          ; LV # Lo       HANGUL SYLLABLE WE
+C704          ; LV # Lo       HANGUL SYLLABLE WI
+C720          ; LV # Lo       HANGUL SYLLABLE YU
+C73C          ; LV # Lo       HANGUL SYLLABLE EU
+C758          ; LV # Lo       HANGUL SYLLABLE YI
+C774          ; LV # Lo       HANGUL SYLLABLE I
+C790          ; LV # Lo       HANGUL SYLLABLE JA
+C7AC          ; LV # Lo       HANGUL SYLLABLE JAE
+C7C8          ; LV # Lo       HANGUL SYLLABLE JYA
+C7E4          ; LV # Lo       HANGUL SYLLABLE JYAE
+C800          ; LV # Lo       HANGUL SYLLABLE JEO
+C81C          ; LV # Lo       HANGUL SYLLABLE JE
+C838          ; LV # Lo       HANGUL SYLLABLE JYEO
+C854          ; LV # Lo       HANGUL SYLLABLE JYE
+C870          ; LV # Lo       HANGUL SYLLABLE JO
+C88C          ; LV # Lo       HANGUL SYLLABLE JWA
+C8A8          ; LV # Lo       HANGUL SYLLABLE JWAE
+C8C4          ; LV # Lo       HANGUL SYLLABLE JOE
+C8E0          ; LV # Lo       HANGUL SYLLABLE JYO
+C8FC          ; LV # Lo       HANGUL SYLLABLE JU
+C918          ; LV # Lo       HANGUL SYLLABLE JWEO
+C934          ; LV # Lo       HANGUL SYLLABLE JWE
+C950          ; LV # Lo       HANGUL SYLLABLE JWI
+C96C          ; LV # Lo       HANGUL SYLLABLE JYU
+C988          ; LV # Lo       HANGUL SYLLABLE JEU
+C9A4          ; LV # Lo       HANGUL SYLLABLE JYI
+C9C0          ; LV # Lo       HANGUL SYLLABLE JI
+C9DC          ; LV # Lo       HANGUL SYLLABLE JJA
+C9F8          ; LV # Lo       HANGUL SYLLABLE JJAE
+CA14          ; LV # Lo       HANGUL SYLLABLE JJYA
+CA30          ; LV # Lo       HANGUL SYLLABLE JJYAE
+CA4C          ; LV # Lo       HANGUL SYLLABLE JJEO
+CA68          ; LV # Lo       HANGUL SYLLABLE JJE
+CA84          ; LV # Lo       HANGUL SYLLABLE JJYEO
+CAA0          ; LV # Lo       HANGUL SYLLABLE JJYE
+CABC          ; LV # Lo       HANGUL SYLLABLE JJO
+CAD8          ; LV # Lo       HANGUL SYLLABLE JJWA
+CAF4          ; LV # Lo       HANGUL SYLLABLE JJWAE
+CB10          ; LV # Lo       HANGUL SYLLABLE JJOE
+CB2C          ; LV # Lo       HANGUL SYLLABLE JJYO
+CB48          ; LV # Lo       HANGUL SYLLABLE JJU
+CB64          ; LV # Lo       HANGUL SYLLABLE JJWEO
+CB80          ; LV # Lo       HANGUL SYLLABLE JJWE
+CB9C          ; LV # Lo       HANGUL SYLLABLE JJWI
+CBB8          ; LV # Lo       HANGUL SYLLABLE JJYU
+CBD4          ; LV # Lo       HANGUL SYLLABLE JJEU
+CBF0          ; LV # Lo       HANGUL SYLLABLE JJYI
+CC0C          ; LV # Lo       HANGUL SYLLABLE JJI
+CC28          ; LV # Lo       HANGUL SYLLABLE CA
+CC44          ; LV # Lo       HANGUL SYLLABLE CAE
+CC60          ; LV # Lo       HANGUL SYLLABLE CYA
+CC7C          ; LV # Lo       HANGUL SYLLABLE CYAE
+CC98          ; LV # Lo       HANGUL SYLLABLE CEO
+CCB4          ; LV # Lo       HANGUL SYLLABLE CE
+CCD0          ; LV # Lo       HANGUL SYLLABLE CYEO
+CCEC          ; LV # Lo       HANGUL SYLLABLE CYE
+CD08          ; LV # Lo       HANGUL SYLLABLE CO
+CD24          ; LV # Lo       HANGUL SYLLABLE CWA
+CD40          ; LV # Lo       HANGUL SYLLABLE CWAE
+CD5C          ; LV # Lo       HANGUL SYLLABLE COE
+CD78          ; LV # Lo       HANGUL SYLLABLE CYO
+CD94          ; LV # Lo       HANGUL SYLLABLE CU
+CDB0          ; LV # Lo       HANGUL SYLLABLE CWEO
+CDCC          ; LV # Lo       HANGUL SYLLABLE CWE
+CDE8          ; LV # Lo       HANGUL SYLLABLE CWI
+CE04          ; LV # Lo       HANGUL SYLLABLE CYU
+CE20          ; LV # Lo       HANGUL SYLLABLE CEU
+CE3C          ; LV # Lo       HANGUL SYLLABLE CYI
+CE58          ; LV # Lo       HANGUL SYLLABLE CI
+CE74          ; LV # Lo       HANGUL SYLLABLE KA
+CE90          ; LV # Lo       HANGUL SYLLABLE KAE
+CEAC          ; LV # Lo       HANGUL SYLLABLE KYA
+CEC8          ; LV # Lo       HANGUL SYLLABLE KYAE
+CEE4          ; LV # Lo       HANGUL SYLLABLE KEO
+CF00          ; LV # Lo       HANGUL SYLLABLE KE
+CF1C          ; LV # Lo       HANGUL SYLLABLE KYEO
+CF38          ; LV # Lo       HANGUL SYLLABLE KYE
+CF54          ; LV # Lo       HANGUL SYLLABLE KO
+CF70          ; LV # Lo       HANGUL SYLLABLE KWA
+CF8C          ; LV # Lo       HANGUL SYLLABLE KWAE
+CFA8          ; LV # Lo       HANGUL SYLLABLE KOE
+CFC4          ; LV # Lo       HANGUL SYLLABLE KYO
+CFE0          ; LV # Lo       HANGUL SYLLABLE KU
+CFFC          ; LV # Lo       HANGUL SYLLABLE KWEO
+D018          ; LV # Lo       HANGUL SYLLABLE KWE
+D034          ; LV # Lo       HANGUL SYLLABLE KWI
+D050          ; LV # Lo       HANGUL SYLLABLE KYU
+D06C          ; LV # Lo       HANGUL SYLLABLE KEU
+D088          ; LV # Lo       HANGUL SYLLABLE KYI
+D0A4          ; LV # Lo       HANGUL SYLLABLE KI
+D0C0          ; LV # Lo       HANGUL SYLLABLE TA
+D0DC          ; LV # Lo       HANGUL SYLLABLE TAE
+D0F8          ; LV # Lo       HANGUL SYLLABLE TYA
+D114          ; LV # Lo       HANGUL SYLLABLE TYAE
+D130          ; LV # Lo       HANGUL SYLLABLE TEO
+D14C          ; LV # Lo       HANGUL SYLLABLE TE
+D168          ; LV # Lo       HANGUL SYLLABLE TYEO
+D184          ; LV # Lo       HANGUL SYLLABLE TYE
+D1A0          ; LV # Lo       HANGUL SYLLABLE TO
+D1BC          ; LV # Lo       HANGUL SYLLABLE TWA
+D1D8          ; LV # Lo       HANGUL SYLLABLE TWAE
+D1F4          ; LV # Lo       HANGUL SYLLABLE TOE
+D210          ; LV # Lo       HANGUL SYLLABLE TYO
+D22C          ; LV # Lo       HANGUL SYLLABLE TU
+D248          ; LV # Lo       HANGUL SYLLABLE TWEO
+D264          ; LV # Lo       HANGUL SYLLABLE TWE
+D280          ; LV # Lo       HANGUL SYLLABLE TWI
+D29C          ; LV # Lo       HANGUL SYLLABLE TYU
+D2B8          ; LV # Lo       HANGUL SYLLABLE TEU
+D2D4          ; LV # Lo       HANGUL SYLLABLE TYI
+D2F0          ; LV # Lo       HANGUL SYLLABLE TI
+D30C          ; LV # Lo       HANGUL SYLLABLE PA
+D328          ; LV # Lo       HANGUL SYLLABLE PAE
+D344          ; LV # Lo       HANGUL SYLLABLE PYA
+D360          ; LV # Lo       HANGUL SYLLABLE PYAE
+D37C          ; LV # Lo       HANGUL SYLLABLE PEO
+D398          ; LV # Lo       HANGUL SYLLABLE PE
+D3B4          ; LV # Lo       HANGUL SYLLABLE PYEO
+D3D0          ; LV # Lo       HANGUL SYLLABLE PYE
+D3EC          ; LV # Lo       HANGUL SYLLABLE PO
+D408          ; LV # Lo       HANGUL SYLLABLE PWA
+D424          ; LV # Lo       HANGUL SYLLABLE PWAE
+D440          ; LV # Lo       HANGUL SYLLABLE POE
+D45C          ; LV # Lo       HANGUL SYLLABLE PYO
+D478          ; LV # Lo       HANGUL SYLLABLE PU
+D494          ; LV # Lo       HANGUL SYLLABLE PWEO
+D4B0          ; LV # Lo       HANGUL SYLLABLE PWE
+D4CC          ; LV # Lo       HANGUL SYLLABLE PWI
+D4E8          ; LV # Lo       HANGUL SYLLABLE PYU
+D504          ; LV # Lo       HANGUL SYLLABLE PEU
+D520          ; LV # Lo       HANGUL SYLLABLE PYI
+D53C          ; LV # Lo       HANGUL SYLLABLE PI
+D558          ; LV # Lo       HANGUL SYLLABLE HA
+D574          ; LV # Lo       HANGUL SYLLABLE HAE
+D590          ; LV # Lo       HANGUL SYLLABLE HYA
+D5AC          ; LV # Lo       HANGUL SYLLABLE HYAE
+D5C8          ; LV # Lo       HANGUL SYLLABLE HEO
+D5E4          ; LV # Lo       HANGUL SYLLABLE HE
+D600          ; LV # Lo       HANGUL SYLLABLE HYEO
+D61C          ; LV # Lo       HANGUL SYLLABLE HYE
+D638          ; LV # Lo       HANGUL SYLLABLE HO
+D654          ; LV # Lo       HANGUL SYLLABLE HWA
+D670          ; LV # Lo       HANGUL SYLLABLE HWAE
+D68C          ; LV # Lo       HANGUL SYLLABLE HOE
+D6A8          ; LV # Lo       HANGUL SYLLABLE HYO
+D6C4          ; LV # Lo       HANGUL SYLLABLE HU
+D6E0          ; LV # Lo       HANGUL SYLLABLE HWEO
+D6FC          ; LV # Lo       HANGUL SYLLABLE HWE
+D718          ; LV # Lo       HANGUL SYLLABLE HWI
+D734          ; LV # Lo       HANGUL SYLLABLE HYU
+D750          ; LV # Lo       HANGUL SYLLABLE HEU
+D76C          ; LV # Lo       HANGUL SYLLABLE HYI
+D788          ; LV # Lo       HANGUL SYLLABLE HI
+
+# Total code points: 399
+
+# ================================================
+
+AC01..AC1B    ; LVT # Lo  [27] HANGUL SYLLABLE GAG..HANGUL SYLLABLE GAH
+AC1D..AC37    ; LVT # Lo  [27] HANGUL SYLLABLE GAEG..HANGUL SYLLABLE GAEH
+AC39..AC53    ; LVT # Lo  [27] HANGUL SYLLABLE GYAG..HANGUL SYLLABLE GYAH
+AC55..AC6F    ; LVT # Lo  [27] HANGUL SYLLABLE GYAEG..HANGUL SYLLABLE GYAEH
+AC71..AC8B    ; LVT # Lo  [27] HANGUL SYLLABLE GEOG..HANGUL SYLLABLE GEOH
+AC8D..ACA7    ; LVT # Lo  [27] HANGUL SYLLABLE GEG..HANGUL SYLLABLE GEH
+ACA9..ACC3    ; LVT # Lo  [27] HANGUL SYLLABLE GYEOG..HANGUL SYLLABLE GYEOH
+ACC5..ACDF    ; LVT # Lo  [27] HANGUL SYLLABLE GYEG..HANGUL SYLLABLE GYEH
+ACE1..ACFB    ; LVT # Lo  [27] HANGUL SYLLABLE GOG..HANGUL SYLLABLE GOH
+ACFD..AD17    ; LVT # Lo  [27] HANGUL SYLLABLE GWAG..HANGUL SYLLABLE GWAH
+AD19..AD33    ; LVT # Lo  [27] HANGUL SYLLABLE GWAEG..HANGUL SYLLABLE GWAEH
+AD35..AD4F    ; LVT # Lo  [27] HANGUL SYLLABLE GOEG..HANGUL SYLLABLE GOEH
+AD51..AD6B    ; LVT # Lo  [27] HANGUL SYLLABLE GYOG..HANGUL SYLLABLE GYOH
+AD6D..AD87    ; LVT # Lo  [27] HANGUL SYLLABLE GUG..HANGUL SYLLABLE GUH
+AD89..ADA3    ; LVT # Lo  [27] HANGUL SYLLABLE GWEOG..HANGUL SYLLABLE GWEOH
+ADA5..ADBF    ; LVT # Lo  [27] HANGUL SYLLABLE GWEG..HANGUL SYLLABLE GWEH
+ADC1..ADDB    ; LVT # Lo  [27] HANGUL SYLLABLE GWIG..HANGUL SYLLABLE GWIH
+ADDD..ADF7    ; LVT # Lo  [27] HANGUL SYLLABLE GYUG..HANGUL SYLLABLE GYUH
+ADF9..AE13    ; LVT # Lo  [27] HANGUL SYLLABLE GEUG..HANGUL SYLLABLE GEUH
+AE15..AE2F    ; LVT # Lo  [27] HANGUL SYLLABLE GYIG..HANGUL SYLLABLE GYIH
+AE31..AE4B    ; LVT # Lo  [27] HANGUL SYLLABLE GIG..HANGUL SYLLABLE GIH
+AE4D..AE67    ; LVT # Lo  [27] HANGUL SYLLABLE GGAG..HANGUL SYLLABLE GGAH
+AE69..AE83    ; LVT # Lo  [27] HANGUL SYLLABLE GGAEG..HANGUL SYLLABLE GGAEH
+AE85..AE9F    ; LVT # Lo  [27] HANGUL SYLLABLE GGYAG..HANGUL SYLLABLE GGYAH
+AEA1..AEBB    ; LVT # Lo  [27] HANGUL SYLLABLE GGYAEG..HANGUL SYLLABLE GGYAEH
+AEBD..AED7    ; LVT # Lo  [27] HANGUL SYLLABLE GGEOG..HANGUL SYLLABLE GGEOH
+AED9..AEF3    ; LVT # Lo  [27] HANGUL SYLLABLE GGEG..HANGUL SYLLABLE GGEH
+AEF5..AF0F    ; LVT # Lo  [27] HANGUL SYLLABLE GGYEOG..HANGUL SYLLABLE GGYEOH
+AF11..AF2B    ; LVT # Lo  [27] HANGUL SYLLABLE GGYEG..HANGUL SYLLABLE GGYEH
+AF2D..AF47    ; LVT # Lo  [27] HANGUL SYLLABLE GGOG..HANGUL SYLLABLE GGOH
+AF49..AF63    ; LVT # Lo  [27] HANGUL SYLLABLE GGWAG..HANGUL SYLLABLE GGWAH
+AF65..AF7F    ; LVT # Lo  [27] HANGUL SYLLABLE GGWAEG..HANGUL SYLLABLE GGWAEH
+AF81..AF9B    ; LVT # Lo  [27] HANGUL SYLLABLE GGOEG..HANGUL SYLLABLE GGOEH
+AF9D..AFB7    ; LVT # Lo  [27] HANGUL SYLLABLE GGYOG..HANGUL SYLLABLE GGYOH
+AFB9..AFD3    ; LVT # Lo  [27] HANGUL SYLLABLE GGUG..HANGUL SYLLABLE GGUH
+AFD5..AFEF    ; LVT # Lo  [27] HANGUL SYLLABLE GGWEOG..HANGUL SYLLABLE GGWEOH
+AFF1..B00B    ; LVT # Lo  [27] HANGUL SYLLABLE GGWEG..HANGUL SYLLABLE GGWEH
+B00D..B027    ; LVT # Lo  [27] HANGUL SYLLABLE GGWIG..HANGUL SYLLABLE GGWIH
+B029..B043    ; LVT # Lo  [27] HANGUL SYLLABLE GGYUG..HANGUL SYLLABLE GGYUH
+B045..B05F    ; LVT # Lo  [27] HANGUL SYLLABLE GGEUG..HANGUL SYLLABLE GGEUH
+B061..B07B    ; LVT # Lo  [27] HANGUL SYLLABLE GGYIG..HANGUL SYLLABLE GGYIH
+B07D..B097    ; LVT # Lo  [27] HANGUL SYLLABLE GGIG..HANGUL SYLLABLE GGIH
+B099..B0B3    ; LVT # Lo  [27] HANGUL SYLLABLE NAG..HANGUL SYLLABLE NAH
+B0B5..B0CF    ; LVT # Lo  [27] HANGUL SYLLABLE NAEG..HANGUL SYLLABLE NAEH
+B0D1..B0EB    ; LVT # Lo  [27] HANGUL SYLLABLE NYAG..HANGUL SYLLABLE NYAH
+B0ED..B107    ; LVT # Lo  [27] HANGUL SYLLABLE NYAEG..HANGUL SYLLABLE NYAEH
+B109..B123    ; LVT # Lo  [27] HANGUL SYLLABLE NEOG..HANGUL SYLLABLE NEOH
+B125..B13F    ; LVT # Lo  [27] HANGUL SYLLABLE NEG..HANGUL SYLLABLE NEH
+B141..B15B    ; LVT # Lo  [27] HANGUL SYLLABLE NYEOG..HANGUL SYLLABLE NYEOH
+B15D..B177    ; LVT # Lo  [27] HANGUL SYLLABLE NYEG..HANGUL SYLLABLE NYEH
+B179..B193    ; LVT # Lo  [27] HANGUL SYLLABLE NOG..HANGUL SYLLABLE NOH
+B195..B1AF    ; LVT # Lo  [27] HANGUL SYLLABLE NWAG..HANGUL SYLLABLE NWAH
+B1B1..B1CB    ; LVT # Lo  [27] HANGUL SYLLABLE NWAEG..HANGUL SYLLABLE NWAEH
+B1CD..B1E7    ; LVT # Lo  [27] HANGUL SYLLABLE NOEG..HANGUL SYLLABLE NOEH
+B1E9..B203    ; LVT # Lo  [27] HANGUL SYLLABLE NYOG..HANGUL SYLLABLE NYOH
+B205..B21F    ; LVT # Lo  [27] HANGUL SYLLABLE NUG..HANGUL SYLLABLE NUH
+B221..B23B    ; LVT # Lo  [27] HANGUL SYLLABLE NWEOG..HANGUL SYLLABLE NWEOH
+B23D..B257    ; LVT # Lo  [27] HANGUL SYLLABLE NWEG..HANGUL SYLLABLE NWEH
+B259..B273    ; LVT # Lo  [27] HANGUL SYLLABLE NWIG..HANGUL SYLLABLE NWIH
+B275..B28F    ; LVT # Lo  [27] HANGUL SYLLABLE NYUG..HANGUL SYLLABLE NYUH
+B291..B2AB    ; LVT # Lo  [27] HANGUL SYLLABLE NEUG..HANGUL SYLLABLE NEUH
+B2AD..B2C7    ; LVT # Lo  [27] HANGUL SYLLABLE NYIG..HANGUL SYLLABLE NYIH
+B2C9..B2E3    ; LVT # Lo  [27] HANGUL SYLLABLE NIG..HANGUL SYLLABLE NIH
+B2E5..B2FF    ; LVT # Lo  [27] HANGUL SYLLABLE DAG..HANGUL SYLLABLE DAH
+B301..B31B    ; LVT # Lo  [27] HANGUL SYLLABLE DAEG..HANGUL SYLLABLE DAEH
+B31D..B337    ; LVT # Lo  [27] HANGUL SYLLABLE DYAG..HANGUL SYLLABLE DYAH
+B339..B353    ; LVT # Lo  [27] HANGUL SYLLABLE DYAEG..HANGUL SYLLABLE DYAEH
+B355..B36F    ; LVT # Lo  [27] HANGUL SYLLABLE DEOG..HANGUL SYLLABLE DEOH
+B371..B38B    ; LVT # Lo  [27] HANGUL SYLLABLE DEG..HANGUL SYLLABLE DEH
+B38D..B3A7    ; LVT # Lo  [27] HANGUL SYLLABLE DYEOG..HANGUL SYLLABLE DYEOH
+B3A9..B3C3    ; LVT # Lo  [27] HANGUL SYLLABLE DYEG..HANGUL SYLLABLE DYEH
+B3C5..B3DF    ; LVT # Lo  [27] HANGUL SYLLABLE DOG..HANGUL SYLLABLE DOH
+B3E1..B3FB    ; LVT # Lo  [27] HANGUL SYLLABLE DWAG..HANGUL SYLLABLE DWAH
+B3FD..B417    ; LVT # Lo  [27] HANGUL SYLLABLE DWAEG..HANGUL SYLLABLE DWAEH
+B419..B433    ; LVT # Lo  [27] HANGUL SYLLABLE DOEG..HANGUL SYLLABLE DOEH
+B435..B44F    ; LVT # Lo  [27] HANGUL SYLLABLE DYOG..HANGUL SYLLABLE DYOH
+B451..B46B    ; LVT # Lo  [27] HANGUL SYLLABLE DUG..HANGUL SYLLABLE DUH
+B46D..B487    ; LVT # Lo  [27] HANGUL SYLLABLE DWEOG..HANGUL SYLLABLE DWEOH
+B489..B4A3    ; LVT # Lo  [27] HANGUL SYLLABLE DWEG..HANGUL SYLLABLE DWEH
+B4A5..B4BF    ; LVT # Lo  [27] HANGUL SYLLABLE DWIG..HANGUL SYLLABLE DWIH
+B4C1..B4DB    ; LVT # Lo  [27] HANGUL SYLLABLE DYUG..HANGUL SYLLABLE DYUH
+B4DD..B4F7    ; LVT # Lo  [27] HANGUL SYLLABLE DEUG..HANGUL SYLLABLE DEUH
+B4F9..B513    ; LVT # Lo  [27] HANGUL SYLLABLE DYIG..HANGUL SYLLABLE DYIH
+B515..B52F    ; LVT # Lo  [27] HANGUL SYLLABLE DIG..HANGUL SYLLABLE DIH
+B531..B54B    ; LVT # Lo  [27] HANGUL SYLLABLE DDAG..HANGUL SYLLABLE DDAH
+B54D..B567    ; LVT # Lo  [27] HANGUL SYLLABLE DDAEG..HANGUL SYLLABLE DDAEH
+B569..B583    ; LVT # Lo  [27] HANGUL SYLLABLE DDYAG..HANGUL SYLLABLE DDYAH
+B585..B59F    ; LVT # Lo  [27] HANGUL SYLLABLE DDYAEG..HANGUL SYLLABLE DDYAEH
+B5A1..B5BB    ; LVT # Lo  [27] HANGUL SYLLABLE DDEOG..HANGUL SYLLABLE DDEOH
+B5BD..B5D7    ; LVT # Lo  [27] HANGUL SYLLABLE DDEG..HANGUL SYLLABLE DDEH
+B5D9..B5F3    ; LVT # Lo  [27] HANGUL SYLLABLE DDYEOG..HANGUL SYLLABLE DDYEOH
+B5F5..B60F    ; LVT # Lo  [27] HANGUL SYLLABLE DDYEG..HANGUL SYLLABLE DDYEH
+B611..B62B    ; LVT # Lo  [27] HANGUL SYLLABLE DDOG..HANGUL SYLLABLE DDOH
+B62D..B647    ; LVT # Lo  [27] HANGUL SYLLABLE DDWAG..HANGUL SYLLABLE DDWAH
+B649..B663    ; LVT # Lo  [27] HANGUL SYLLABLE DDWAEG..HANGUL SYLLABLE DDWAEH
+B665..B67F    ; LVT # Lo  [27] HANGUL SYLLABLE DDOEG..HANGUL SYLLABLE DDOEH
+B681..B69B    ; LVT # Lo  [27] HANGUL SYLLABLE DDYOG..HANGUL SYLLABLE DDYOH
+B69D..B6B7    ; LVT # Lo  [27] HANGUL SYLLABLE DDUG..HANGUL SYLLABLE DDUH
+B6B9..B6D3    ; LVT # Lo  [27] HANGUL SYLLABLE DDWEOG..HANGUL SYLLABLE DDWEOH
+B6D5..B6EF    ; LVT # Lo  [27] HANGUL SYLLABLE DDWEG..HANGUL SYLLABLE DDWEH
+B6F1..B70B    ; LVT # Lo  [27] HANGUL SYLLABLE DDWIG..HANGUL SYLLABLE DDWIH
+B70D..B727    ; LVT # Lo  [27] HANGUL SYLLABLE DDYUG..HANGUL SYLLABLE DDYUH
+B729..B743    ; LVT # Lo  [27] HANGUL SYLLABLE DDEUG..HANGUL SYLLABLE DDEUH
+B745..B75F    ; LVT # Lo  [27] HANGUL SYLLABLE DDYIG..HANGUL SYLLABLE DDYIH
+B761..B77B    ; LVT # Lo  [27] HANGUL SYLLABLE DDIG..HANGUL SYLLABLE DDIH
+B77D..B797    ; LVT # Lo  [27] HANGUL SYLLABLE RAG..HANGUL SYLLABLE RAH
+B799..B7B3    ; LVT # Lo  [27] HANGUL SYLLABLE RAEG..HANGUL SYLLABLE RAEH
+B7B5..B7CF    ; LVT # Lo  [27] HANGUL SYLLABLE RYAG..HANGUL SYLLABLE RYAH
+B7D1..B7EB    ; LVT # Lo  [27] HANGUL SYLLABLE RYAEG..HANGUL SYLLABLE RYAEH
+B7ED..B807    ; LVT # Lo  [27] HANGUL SYLLABLE REOG..HANGUL SYLLABLE REOH
+B809..B823    ; LVT # Lo  [27] HANGUL SYLLABLE REG..HANGUL SYLLABLE REH
+B825..B83F    ; LVT # Lo  [27] HANGUL SYLLABLE RYEOG..HANGUL SYLLABLE RYEOH
+B841..B85B    ; LVT # Lo  [27] HANGUL SYLLABLE RYEG..HANGUL SYLLABLE RYEH
+B85D..B877    ; LVT # Lo  [27] HANGUL SYLLABLE ROG..HANGUL SYLLABLE ROH
+B879..B893    ; LVT # Lo  [27] HANGUL SYLLABLE RWAG..HANGUL SYLLABLE RWAH
+B895..B8AF    ; LVT # Lo  [27] HANGUL SYLLABLE RWAEG..HANGUL SYLLABLE RWAEH
+B8B1..B8CB    ; LVT # Lo  [27] HANGUL SYLLABLE ROEG..HANGUL SYLLABLE ROEH
+B8CD..B8E7    ; LVT # Lo  [27] HANGUL SYLLABLE RYOG..HANGUL SYLLABLE RYOH
+B8E9..B903    ; LVT # Lo  [27] HANGUL SYLLABLE RUG..HANGUL SYLLABLE RUH
+B905..B91F    ; LVT # Lo  [27] HANGUL SYLLABLE RWEOG..HANGUL SYLLABLE RWEOH
+B921..B93B    ; LVT # Lo  [27] HANGUL SYLLABLE RWEG..HANGUL SYLLABLE RWEH
+B93D..B957    ; LVT # Lo  [27] HANGUL SYLLABLE RWIG..HANGUL SYLLABLE RWIH
+B959..B973    ; LVT # Lo  [27] HANGUL SYLLABLE RYUG..HANGUL SYLLABLE RYUH
+B975..B98F    ; LVT # Lo  [27] HANGUL SYLLABLE REUG..HANGUL SYLLABLE REUH
+B991..B9AB    ; LVT # Lo  [27] HANGUL SYLLABLE RYIG..HANGUL SYLLABLE RYIH
+B9AD..B9C7    ; LVT # Lo  [27] HANGUL SYLLABLE RIG..HANGUL SYLLABLE RIH
+B9C9..B9E3    ; LVT # Lo  [27] HANGUL SYLLABLE MAG..HANGUL SYLLABLE MAH
+B9E5..B9FF    ; LVT # Lo  [27] HANGUL SYLLABLE MAEG..HANGUL SYLLABLE MAEH
+BA01..BA1B    ; LVT # Lo  [27] HANGUL SYLLABLE MYAG..HANGUL SYLLABLE MYAH
+BA1D..BA37    ; LVT # Lo  [27] HANGUL SYLLABLE MYAEG..HANGUL SYLLABLE MYAEH
+BA39..BA53    ; LVT # Lo  [27] HANGUL SYLLABLE MEOG..HANGUL SYLLABLE MEOH
+BA55..BA6F    ; LVT # Lo  [27] HANGUL SYLLABLE MEG..HANGUL SYLLABLE MEH
+BA71..BA8B    ; LVT # Lo  [27] HANGUL SYLLABLE MYEOG..HANGUL SYLLABLE MYEOH
+BA8D..BAA7    ; LVT # Lo  [27] HANGUL SYLLABLE MYEG..HANGUL SYLLABLE MYEH
+BAA9..BAC3    ; LVT # Lo  [27] HANGUL SYLLABLE MOG..HANGUL SYLLABLE MOH
+BAC5..BADF    ; LVT # Lo  [27] HANGUL SYLLABLE MWAG..HANGUL SYLLABLE MWAH
+BAE1..BAFB    ; LVT # Lo  [27] HANGUL SYLLABLE MWAEG..HANGUL SYLLABLE MWAEH
+BAFD..BB17    ; LVT # Lo  [27] HANGUL SYLLABLE MOEG..HANGUL SYLLABLE MOEH
+BB19..BB33    ; LVT # Lo  [27] HANGUL SYLLABLE MYOG..HANGUL SYLLABLE MYOH
+BB35..BB4F    ; LVT # Lo  [27] HANGUL SYLLABLE MUG..HANGUL SYLLABLE MUH
+BB51..BB6B    ; LVT # Lo  [27] HANGUL SYLLABLE MWEOG..HANGUL SYLLABLE MWEOH
+BB6D..BB87    ; LVT # Lo  [27] HANGUL SYLLABLE MWEG..HANGUL SYLLABLE MWEH
+BB89..BBA3    ; LVT # Lo  [27] HANGUL SYLLABLE MWIG..HANGUL SYLLABLE MWIH
+BBA5..BBBF    ; LVT # Lo  [27] HANGUL SYLLABLE MYUG..HANGUL SYLLABLE MYUH
+BBC1..BBDB    ; LVT # Lo  [27] HANGUL SYLLABLE MEUG..HANGUL SYLLABLE MEUH
+BBDD..BBF7    ; LVT # Lo  [27] HANGUL SYLLABLE MYIG..HANGUL SYLLABLE MYIH
+BBF9..BC13    ; LVT # Lo  [27] HANGUL SYLLABLE MIG..HANGUL SYLLABLE MIH
+BC15..BC2F    ; LVT # Lo  [27] HANGUL SYLLABLE BAG..HANGUL SYLLABLE BAH
+BC31..BC4B    ; LVT # Lo  [27] HANGUL SYLLABLE BAEG..HANGUL SYLLABLE BAEH
+BC4D..BC67    ; LVT # Lo  [27] HANGUL SYLLABLE BYAG..HANGUL SYLLABLE BYAH
+BC69..BC83    ; LVT # Lo  [27] HANGUL SYLLABLE BYAEG..HANGUL SYLLABLE BYAEH
+BC85..BC9F    ; LVT # Lo  [27] HANGUL SYLLABLE BEOG..HANGUL SYLLABLE BEOH
+BCA1..BCBB    ; LVT # Lo  [27] HANGUL SYLLABLE BEG..HANGUL SYLLABLE BEH
+BCBD..BCD7    ; LVT # Lo  [27] HANGUL SYLLABLE BYEOG..HANGUL SYLLABLE BYEOH
+BCD9..BCF3    ; LVT # Lo  [27] HANGUL SYLLABLE BYEG..HANGUL SYLLABLE BYEH
+BCF5..BD0F    ; LVT # Lo  [27] HANGUL SYLLABLE BOG..HANGUL SYLLABLE BOH
+BD11..BD2B    ; LVT # Lo  [27] HANGUL SYLLABLE BWAG..HANGUL SYLLABLE BWAH
+BD2D..BD47    ; LVT # Lo  [27] HANGUL SYLLABLE BWAEG..HANGUL SYLLABLE BWAEH
+BD49..BD63    ; LVT # Lo  [27] HANGUL SYLLABLE BOEG..HANGUL SYLLABLE BOEH
+BD65..BD7F    ; LVT # Lo  [27] HANGUL SYLLABLE BYOG..HANGUL SYLLABLE BYOH
+BD81..BD9B    ; LVT # Lo  [27] HANGUL SYLLABLE BUG..HANGUL SYLLABLE BUH
+BD9D..BDB7    ; LVT # Lo  [27] HANGUL SYLLABLE BWEOG..HANGUL SYLLABLE BWEOH
+BDB9..BDD3    ; LVT # Lo  [27] HANGUL SYLLABLE BWEG..HANGUL SYLLABLE BWEH
+BDD5..BDEF    ; LVT # Lo  [27] HANGUL SYLLABLE BWIG..HANGUL SYLLABLE BWIH
+BDF1..BE0B    ; LVT # Lo  [27] HANGUL SYLLABLE BYUG..HANGUL SYLLABLE BYUH
+BE0D..BE27    ; LVT # Lo  [27] HANGUL SYLLABLE BEUG..HANGUL SYLLABLE BEUH
+BE29..BE43    ; LVT # Lo  [27] HANGUL SYLLABLE BYIG..HANGUL SYLLABLE BYIH
+BE45..BE5F    ; LVT # Lo  [27] HANGUL SYLLABLE BIG..HANGUL SYLLABLE BIH
+BE61..BE7B    ; LVT # Lo  [27] HANGUL SYLLABLE BBAG..HANGUL SYLLABLE BBAH
+BE7D..BE97    ; LVT # Lo  [27] HANGUL SYLLABLE BBAEG..HANGUL SYLLABLE BBAEH
+BE99..BEB3    ; LVT # Lo  [27] HANGUL SYLLABLE BBYAG..HANGUL SYLLABLE BBYAH
+BEB5..BECF    ; LVT # Lo  [27] HANGUL SYLLABLE BBYAEG..HANGUL SYLLABLE BBYAEH
+BED1..BEEB    ; LVT # Lo  [27] HANGUL SYLLABLE BBEOG..HANGUL SYLLABLE BBEOH
+BEED..BF07    ; LVT # Lo  [27] HANGUL SYLLABLE BBEG..HANGUL SYLLABLE BBEH
+BF09..BF23    ; LVT # Lo  [27] HANGUL SYLLABLE BBYEOG..HANGUL SYLLABLE BBYEOH
+BF25..BF3F    ; LVT # Lo  [27] HANGUL SYLLABLE BBYEG..HANGUL SYLLABLE BBYEH
+BF41..BF5B    ; LVT # Lo  [27] HANGUL SYLLABLE BBOG..HANGUL SYLLABLE BBOH
+BF5D..BF77    ; LVT # Lo  [27] HANGUL SYLLABLE BBWAG..HANGUL SYLLABLE BBWAH
+BF79..BF93    ; LVT # Lo  [27] HANGUL SYLLABLE BBWAEG..HANGUL SYLLABLE BBWAEH
+BF95..BFAF    ; LVT # Lo  [27] HANGUL SYLLABLE BBOEG..HANGUL SYLLABLE BBOEH
+BFB1..BFCB    ; LVT # Lo  [27] HANGUL SYLLABLE BBYOG..HANGUL SYLLABLE BBYOH
+BFCD..BFE7    ; LVT # Lo  [27] HANGUL SYLLABLE BBUG..HANGUL SYLLABLE BBUH
+BFE9..C003    ; LVT # Lo  [27] HANGUL SYLLABLE BBWEOG..HANGUL SYLLABLE BBWEOH
+C005..C01F    ; LVT # Lo  [27] HANGUL SYLLABLE BBWEG..HANGUL SYLLABLE BBWEH
+C021..C03B    ; LVT # Lo  [27] HANGUL SYLLABLE BBWIG..HANGUL SYLLABLE BBWIH
+C03D..C057    ; LVT # Lo  [27] HANGUL SYLLABLE BBYUG..HANGUL SYLLABLE BBYUH
+C059..C073    ; LVT # Lo  [27] HANGUL SYLLABLE BBEUG..HANGUL SYLLABLE BBEUH
+C075..C08F    ; LVT # Lo  [27] HANGUL SYLLABLE BBYIG..HANGUL SYLLABLE BBYIH
+C091..C0AB    ; LVT # Lo  [27] HANGUL SYLLABLE BBIG..HANGUL SYLLABLE BBIH
+C0AD..C0C7    ; LVT # Lo  [27] HANGUL SYLLABLE SAG..HANGUL SYLLABLE SAH
+C0C9..C0E3    ; LVT # Lo  [27] HANGUL SYLLABLE SAEG..HANGUL SYLLABLE SAEH
+C0E5..C0FF    ; LVT # Lo  [27] HANGUL SYLLABLE SYAG..HANGUL SYLLABLE SYAH
+C101..C11B    ; LVT # Lo  [27] HANGUL SYLLABLE SYAEG..HANGUL SYLLABLE SYAEH
+C11D..C137    ; LVT # Lo  [27] HANGUL SYLLABLE SEOG..HANGUL SYLLABLE SEOH
+C139..C153    ; LVT # Lo  [27] HANGUL SYLLABLE SEG..HANGUL SYLLABLE SEH
+C155..C16F    ; LVT # Lo  [27] HANGUL SYLLABLE SYEOG..HANGUL SYLLABLE SYEOH
+C171..C18B    ; LVT # Lo  [27] HANGUL SYLLABLE SYEG..HANGUL SYLLABLE SYEH
+C18D..C1A7    ; LVT # Lo  [27] HANGUL SYLLABLE SOG..HANGUL SYLLABLE SOH
+C1A9..C1C3    ; LVT # Lo  [27] HANGUL SYLLABLE SWAG..HANGUL SYLLABLE SWAH
+C1C5..C1DF    ; LVT # Lo  [27] HANGUL SYLLABLE SWAEG..HANGUL SYLLABLE SWAEH
+C1E1..C1FB    ; LVT # Lo  [27] HANGUL SYLLABLE SOEG..HANGUL SYLLABLE SOEH
+C1FD..C217    ; LVT # Lo  [27] HANGUL SYLLABLE SYOG..HANGUL SYLLABLE SYOH
+C219..C233    ; LVT # Lo  [27] HANGUL SYLLABLE SUG..HANGUL SYLLABLE SUH
+C235..C24F    ; LVT # Lo  [27] HANGUL SYLLABLE SWEOG..HANGUL SYLLABLE SWEOH
+C251..C26B    ; LVT # Lo  [27] HANGUL SYLLABLE SWEG..HANGUL SYLLABLE SWEH
+C26D..C287    ; LVT # Lo  [27] HANGUL SYLLABLE SWIG..HANGUL SYLLABLE SWIH
+C289..C2A3    ; LVT # Lo  [27] HANGUL SYLLABLE SYUG..HANGUL SYLLABLE SYUH
+C2A5..C2BF    ; LVT # Lo  [27] HANGUL SYLLABLE SEUG..HANGUL SYLLABLE SEUH
+C2C1..C2DB    ; LVT # Lo  [27] HANGUL SYLLABLE SYIG..HANGUL SYLLABLE SYIH
+C2DD..C2F7    ; LVT # Lo  [27] HANGUL SYLLABLE SIG..HANGUL SYLLABLE SIH
+C2F9..C313    ; LVT # Lo  [27] HANGUL SYLLABLE SSAG..HANGUL SYLLABLE SSAH
+C315..C32F    ; LVT # Lo  [27] HANGUL SYLLABLE SSAEG..HANGUL SYLLABLE SSAEH
+C331..C34B    ; LVT # Lo  [27] HANGUL SYLLABLE SSYAG..HANGUL SYLLABLE SSYAH
+C34D..C367    ; LVT # Lo  [27] HANGUL SYLLABLE SSYAEG..HANGUL SYLLABLE SSYAEH
+C369..C383    ; LVT # Lo  [27] HANGUL SYLLABLE SSEOG..HANGUL SYLLABLE SSEOH
+C385..C39F    ; LVT # Lo  [27] HANGUL SYLLABLE SSEG..HANGUL SYLLABLE SSEH
+C3A1..C3BB    ; LVT # Lo  [27] HANGUL SYLLABLE SSYEOG..HANGUL SYLLABLE SSYEOH
+C3BD..C3D7    ; LVT # Lo  [27] HANGUL SYLLABLE SSYEG..HANGUL SYLLABLE SSYEH
+C3D9..C3F3    ; LVT # Lo  [27] HANGUL SYLLABLE SSOG..HANGUL SYLLABLE SSOH
+C3F5..C40F    ; LVT # Lo  [27] HANGUL SYLLABLE SSWAG..HANGUL SYLLABLE SSWAH
+C411..C42B    ; LVT # Lo  [27] HANGUL SYLLABLE SSWAEG..HANGUL SYLLABLE SSWAEH
+C42D..C447    ; LVT # Lo  [27] HANGUL SYLLABLE SSOEG..HANGUL SYLLABLE SSOEH
+C449..C463    ; LVT # Lo  [27] HANGUL SYLLABLE SSYOG..HANGUL SYLLABLE SSYOH
+C465..C47F    ; LVT # Lo  [27] HANGUL SYLLABLE SSUG..HANGUL SYLLABLE SSUH
+C481..C49B    ; LVT # Lo  [27] HANGUL SYLLABLE SSWEOG..HANGUL SYLLABLE SSWEOH
+C49D..C4B7    ; LVT # Lo  [27] HANGUL SYLLABLE SSWEG..HANGUL SYLLABLE SSWEH
+C4B9..C4D3    ; LVT # Lo  [27] HANGUL SYLLABLE SSWIG..HANGUL SYLLABLE SSWIH
+C4D5..C4EF    ; LVT # Lo  [27] HANGUL SYLLABLE SSYUG..HANGUL SYLLABLE SSYUH
+C4F1..C50B    ; LVT # Lo  [27] HANGUL SYLLABLE SSEUG..HANGUL SYLLABLE SSEUH
+C50D..C527    ; LVT # Lo  [27] HANGUL SYLLABLE SSYIG..HANGUL SYLLABLE SSYIH
+C529..C543    ; LVT # Lo  [27] HANGUL SYLLABLE SSIG..HANGUL SYLLABLE SSIH
+C545..C55F    ; LVT # Lo  [27] HANGUL SYLLABLE AG..HANGUL SYLLABLE AH
+C561..C57B    ; LVT # Lo  [27] HANGUL SYLLABLE AEG..HANGUL SYLLABLE AEH
+C57D..C597    ; LVT # Lo  [27] HANGUL SYLLABLE YAG..HANGUL SYLLABLE YAH
+C599..C5B3    ; LVT # Lo  [27] HANGUL SYLLABLE YAEG..HANGUL SYLLABLE YAEH
+C5B5..C5CF    ; LVT # Lo  [27] HANGUL SYLLABLE EOG..HANGUL SYLLABLE EOH
+C5D1..C5EB    ; LVT # Lo  [27] HANGUL SYLLABLE EG..HANGUL SYLLABLE EH
+C5ED..C607    ; LVT # Lo  [27] HANGUL SYLLABLE YEOG..HANGUL SYLLABLE YEOH
+C609..C623    ; LVT # Lo  [27] HANGUL SYLLABLE YEG..HANGUL SYLLABLE YEH
+C625..C63F    ; LVT # Lo  [27] HANGUL SYLLABLE OG..HANGUL SYLLABLE OH
+C641..C65B    ; LVT # Lo  [27] HANGUL SYLLABLE WAG..HANGUL SYLLABLE WAH
+C65D..C677    ; LVT # Lo  [27] HANGUL SYLLABLE WAEG..HANGUL SYLLABLE WAEH
+C679..C693    ; LVT # Lo  [27] HANGUL SYLLABLE OEG..HANGUL SYLLABLE OEH
+C695..C6AF    ; LVT # Lo  [27] HANGUL SYLLABLE YOG..HANGUL SYLLABLE YOH
+C6B1..C6CB    ; LVT # Lo  [27] HANGUL SYLLABLE UG..HANGUL SYLLABLE UH
+C6CD..C6E7    ; LVT # Lo  [27] HANGUL SYLLABLE WEOG..HANGUL SYLLABLE WEOH
+C6E9..C703    ; LVT # Lo  [27] HANGUL SYLLABLE WEG..HANGUL SYLLABLE WEH
+C705..C71F    ; LVT # Lo  [27] HANGUL SYLLABLE WIG..HANGUL SYLLABLE WIH
+C721..C73B    ; LVT # Lo  [27] HANGUL SYLLABLE YUG..HANGUL SYLLABLE YUH
+C73D..C757    ; LVT # Lo  [27] HANGUL SYLLABLE EUG..HANGUL SYLLABLE EUH
+C759..C773    ; LVT # Lo  [27] HANGUL SYLLABLE YIG..HANGUL SYLLABLE YIH
+C775..C78F    ; LVT # Lo  [27] HANGUL SYLLABLE IG..HANGUL SYLLABLE IH
+C791..C7AB    ; LVT # Lo  [27] HANGUL SYLLABLE JAG..HANGUL SYLLABLE JAH
+C7AD..C7C7    ; LVT # Lo  [27] HANGUL SYLLABLE JAEG..HANGUL SYLLABLE JAEH
+C7C9..C7E3    ; LVT # Lo  [27] HANGUL SYLLABLE JYAG..HANGUL SYLLABLE JYAH
+C7E5..C7FF    ; LVT # Lo  [27] HANGUL SYLLABLE JYAEG..HANGUL SYLLABLE JYAEH
+C801..C81B    ; LVT # Lo  [27] HANGUL SYLLABLE JEOG..HANGUL SYLLABLE JEOH
+C81D..C837    ; LVT # Lo  [27] HANGUL SYLLABLE JEG..HANGUL SYLLABLE JEH
+C839..C853    ; LVT # Lo  [27] HANGUL SYLLABLE JYEOG..HANGUL SYLLABLE JYEOH
+C855..C86F    ; LVT # Lo  [27] HANGUL SYLLABLE JYEG..HANGUL SYLLABLE JYEH
+C871..C88B    ; LVT # Lo  [27] HANGUL SYLLABLE JOG..HANGUL SYLLABLE JOH
+C88D..C8A7    ; LVT # Lo  [27] HANGUL SYLLABLE JWAG..HANGUL SYLLABLE JWAH
+C8A9..C8C3    ; LVT # Lo  [27] HANGUL SYLLABLE JWAEG..HANGUL SYLLABLE JWAEH
+C8C5..C8DF    ; LVT # Lo  [27] HANGUL SYLLABLE JOEG..HANGUL SYLLABLE JOEH
+C8E1..C8FB    ; LVT # Lo  [27] HANGUL SYLLABLE JYOG..HANGUL SYLLABLE JYOH
+C8FD..C917    ; LVT # Lo  [27] HANGUL SYLLABLE JUG..HANGUL SYLLABLE JUH
+C919..C933    ; LVT # Lo  [27] HANGUL SYLLABLE JWEOG..HANGUL SYLLABLE JWEOH
+C935..C94F    ; LVT # Lo  [27] HANGUL SYLLABLE JWEG..HANGUL SYLLABLE JWEH
+C951..C96B    ; LVT # Lo  [27] HANGUL SYLLABLE JWIG..HANGUL SYLLABLE JWIH
+C96D..C987    ; LVT # Lo  [27] HANGUL SYLLABLE JYUG..HANGUL SYLLABLE JYUH
+C989..C9A3    ; LVT # Lo  [27] HANGUL SYLLABLE JEUG..HANGUL SYLLABLE JEUH
+C9A5..C9BF    ; LVT # Lo  [27] HANGUL SYLLABLE JYIG..HANGUL SYLLABLE JYIH
+C9C1..C9DB    ; LVT # Lo  [27] HANGUL SYLLABLE JIG..HANGUL SYLLABLE JIH
+C9DD..C9F7    ; LVT # Lo  [27] HANGUL SYLLABLE JJAG..HANGUL SYLLABLE JJAH
+C9F9..CA13    ; LVT # Lo  [27] HANGUL SYLLABLE JJAEG..HANGUL SYLLABLE JJAEH
+CA15..CA2F    ; LVT # Lo  [27] HANGUL SYLLABLE JJYAG..HANGUL SYLLABLE JJYAH
+CA31..CA4B    ; LVT # Lo  [27] HANGUL SYLLABLE JJYAEG..HANGUL SYLLABLE JJYAEH
+CA4D..CA67    ; LVT # Lo  [27] HANGUL SYLLABLE JJEOG..HANGUL SYLLABLE JJEOH
+CA69..CA83    ; LVT # Lo  [27] HANGUL SYLLABLE JJEG..HANGUL SYLLABLE JJEH
+CA85..CA9F    ; LVT # Lo  [27] HANGUL SYLLABLE JJYEOG..HANGUL SYLLABLE JJYEOH
+CAA1..CABB    ; LVT # Lo  [27] HANGUL SYLLABLE JJYEG..HANGUL SYLLABLE JJYEH
+CABD..CAD7    ; LVT # Lo  [27] HANGUL SYLLABLE JJOG..HANGUL SYLLABLE JJOH
+CAD9..CAF3    ; LVT # Lo  [27] HANGUL SYLLABLE JJWAG..HANGUL SYLLABLE JJWAH
+CAF5..CB0F    ; LVT # Lo  [27] HANGUL SYLLABLE JJWAEG..HANGUL SYLLABLE JJWAEH
+CB11..CB2B    ; LVT # Lo  [27] HANGUL SYLLABLE JJOEG..HANGUL SYLLABLE JJOEH
+CB2D..CB47    ; LVT # Lo  [27] HANGUL SYLLABLE JJYOG..HANGUL SYLLABLE JJYOH
+CB49..CB63    ; LVT # Lo  [27] HANGUL SYLLABLE JJUG..HANGUL SYLLABLE JJUH
+CB65..CB7F    ; LVT # Lo  [27] HANGUL SYLLABLE JJWEOG..HANGUL SYLLABLE JJWEOH
+CB81..CB9B    ; LVT # Lo  [27] HANGUL SYLLABLE JJWEG..HANGUL SYLLABLE JJWEH
+CB9D..CBB7    ; LVT # Lo  [27] HANGUL SYLLABLE JJWIG..HANGUL SYLLABLE JJWIH
+CBB9..CBD3    ; LVT # Lo  [27] HANGUL SYLLABLE JJYUG..HANGUL SYLLABLE JJYUH
+CBD5..CBEF    ; LVT # Lo  [27] HANGUL SYLLABLE JJEUG..HANGUL SYLLABLE JJEUH
+CBF1..CC0B    ; LVT # Lo  [27] HANGUL SYLLABLE JJYIG..HANGUL SYLLABLE JJYIH
+CC0D..CC27    ; LVT # Lo  [27] HANGUL SYLLABLE JJIG..HANGUL SYLLABLE JJIH
+CC29..CC43    ; LVT # Lo  [27] HANGUL SYLLABLE CAG..HANGUL SYLLABLE CAH
+CC45..CC5F    ; LVT # Lo  [27] HANGUL SYLLABLE CAEG..HANGUL SYLLABLE CAEH
+CC61..CC7B    ; LVT # Lo  [27] HANGUL SYLLABLE CYAG..HANGUL SYLLABLE CYAH
+CC7D..CC97    ; LVT # Lo  [27] HANGUL SYLLABLE CYAEG..HANGUL SYLLABLE CYAEH
+CC99..CCB3    ; LVT # Lo  [27] HANGUL SYLLABLE CEOG..HANGUL SYLLABLE CEOH
+CCB5..CCCF    ; LVT # Lo  [27] HANGUL SYLLABLE CEG..HANGUL SYLLABLE CEH
+CCD1..CCEB    ; LVT # Lo  [27] HANGUL SYLLABLE CYEOG..HANGUL SYLLABLE CYEOH
+CCED..CD07    ; LVT # Lo  [27] HANGUL SYLLABLE CYEG..HANGUL SYLLABLE CYEH
+CD09..CD23    ; LVT # Lo  [27] HANGUL SYLLABLE COG..HANGUL SYLLABLE COH
+CD25..CD3F    ; LVT # Lo  [27] HANGUL SYLLABLE CWAG..HANGUL SYLLABLE CWAH
+CD41..CD5B    ; LVT # Lo  [27] HANGUL SYLLABLE CWAEG..HANGUL SYLLABLE CWAEH
+CD5D..CD77    ; LVT # Lo  [27] HANGUL SYLLABLE COEG..HANGUL SYLLABLE COEH
+CD79..CD93    ; LVT # Lo  [27] HANGUL SYLLABLE CYOG..HANGUL SYLLABLE CYOH
+CD95..CDAF    ; LVT # Lo  [27] HANGUL SYLLABLE CUG..HANGUL SYLLABLE CUH
+CDB1..CDCB    ; LVT # Lo  [27] HANGUL SYLLABLE CWEOG..HANGUL SYLLABLE CWEOH
+CDCD..CDE7    ; LVT # Lo  [27] HANGUL SYLLABLE CWEG..HANGUL SYLLABLE CWEH
+CDE9..CE03    ; LVT # Lo  [27] HANGUL SYLLABLE CWIG..HANGUL SYLLABLE CWIH
+CE05..CE1F    ; LVT # Lo  [27] HANGUL SYLLABLE CYUG..HANGUL SYLLABLE CYUH
+CE21..CE3B    ; LVT # Lo  [27] HANGUL SYLLABLE CEUG..HANGUL SYLLABLE CEUH
+CE3D..CE57    ; LVT # Lo  [27] HANGUL SYLLABLE CYIG..HANGUL SYLLABLE CYIH
+CE59..CE73    ; LVT # Lo  [27] HANGUL SYLLABLE CIG..HANGUL SYLLABLE CIH
+CE75..CE8F    ; LVT # Lo  [27] HANGUL SYLLABLE KAG..HANGUL SYLLABLE KAH
+CE91..CEAB    ; LVT # Lo  [27] HANGUL SYLLABLE KAEG..HANGUL SYLLABLE KAEH
+CEAD..CEC7    ; LVT # Lo  [27] HANGUL SYLLABLE KYAG..HANGUL SYLLABLE KYAH
+CEC9..CEE3    ; LVT # Lo  [27] HANGUL SYLLABLE KYAEG..HANGUL SYLLABLE KYAEH
+CEE5..CEFF    ; LVT # Lo  [27] HANGUL SYLLABLE KEOG..HANGUL SYLLABLE KEOH
+CF01..CF1B    ; LVT # Lo  [27] HANGUL SYLLABLE KEG..HANGUL SYLLABLE KEH
+CF1D..CF37    ; LVT # Lo  [27] HANGUL SYLLABLE KYEOG..HANGUL SYLLABLE KYEOH
+CF39..CF53    ; LVT # Lo  [27] HANGUL SYLLABLE KYEG..HANGUL SYLLABLE KYEH
+CF55..CF6F    ; LVT # Lo  [27] HANGUL SYLLABLE KOG..HANGUL SYLLABLE KOH
+CF71..CF8B    ; LVT # Lo  [27] HANGUL SYLLABLE KWAG..HANGUL SYLLABLE KWAH
+CF8D..CFA7    ; LVT # Lo  [27] HANGUL SYLLABLE KWAEG..HANGUL SYLLABLE KWAEH
+CFA9..CFC3    ; LVT # Lo  [27] HANGUL SYLLABLE KOEG..HANGUL SYLLABLE KOEH
+CFC5..CFDF    ; LVT # Lo  [27] HANGUL SYLLABLE KYOG..HANGUL SYLLABLE KYOH
+CFE1..CFFB    ; LVT # Lo  [27] HANGUL SYLLABLE KUG..HANGUL SYLLABLE KUH
+CFFD..D017    ; LVT # Lo  [27] HANGUL SYLLABLE KWEOG..HANGUL SYLLABLE KWEOH
+D019..D033    ; LVT # Lo  [27] HANGUL SYLLABLE KWEG..HANGUL SYLLABLE KWEH
+D035..D04F    ; LVT # Lo  [27] HANGUL SYLLABLE KWIG..HANGUL SYLLABLE KWIH
+D051..D06B    ; LVT # Lo  [27] HANGUL SYLLABLE KYUG..HANGUL SYLLABLE KYUH
+D06D..D087    ; LVT # Lo  [27] HANGUL SYLLABLE KEUG..HANGUL SYLLABLE KEUH
+D089..D0A3    ; LVT # Lo  [27] HANGUL SYLLABLE KYIG..HANGUL SYLLABLE KYIH
+D0A5..D0BF    ; LVT # Lo  [27] HANGUL SYLLABLE KIG..HANGUL SYLLABLE KIH
+D0C1..D0DB    ; LVT # Lo  [27] HANGUL SYLLABLE TAG..HANGUL SYLLABLE TAH
+D0DD..D0F7    ; LVT # Lo  [27] HANGUL SYLLABLE TAEG..HANGUL SYLLABLE TAEH
+D0F9..D113    ; LVT # Lo  [27] HANGUL SYLLABLE TYAG..HANGUL SYLLABLE TYAH
+D115..D12F    ; LVT # Lo  [27] HANGUL SYLLABLE TYAEG..HANGUL SYLLABLE TYAEH
+D131..D14B    ; LVT # Lo  [27] HANGUL SYLLABLE TEOG..HANGUL SYLLABLE TEOH
+D14D..D167    ; LVT # Lo  [27] HANGUL SYLLABLE TEG..HANGUL SYLLABLE TEH
+D169..D183    ; LVT # Lo  [27] HANGUL SYLLABLE TYEOG..HANGUL SYLLABLE TYEOH
+D185..D19F    ; LVT # Lo  [27] HANGUL SYLLABLE TYEG..HANGUL SYLLABLE TYEH
+D1A1..D1BB    ; LVT # Lo  [27] HANGUL SYLLABLE TOG..HANGUL SYLLABLE TOH
+D1BD..D1D7    ; LVT # Lo  [27] HANGUL SYLLABLE TWAG..HANGUL SYLLABLE TWAH
+D1D9..D1F3    ; LVT # Lo  [27] HANGUL SYLLABLE TWAEG..HANGUL SYLLABLE TWAEH
+D1F5..D20F    ; LVT # Lo  [27] HANGUL SYLLABLE TOEG..HANGUL SYLLABLE TOEH
+D211..D22B    ; LVT # Lo  [27] HANGUL SYLLABLE TYOG..HANGUL SYLLABLE TYOH
+D22D..D247    ; LVT # Lo  [27] HANGUL SYLLABLE TUG..HANGUL SYLLABLE TUH
+D249..D263    ; LVT # Lo  [27] HANGUL SYLLABLE TWEOG..HANGUL SYLLABLE TWEOH
+D265..D27F    ; LVT # Lo  [27] HANGUL SYLLABLE TWEG..HANGUL SYLLABLE TWEH
+D281..D29B    ; LVT # Lo  [27] HANGUL SYLLABLE TWIG..HANGUL SYLLABLE TWIH
+D29D..D2B7    ; LVT # Lo  [27] HANGUL SYLLABLE TYUG..HANGUL SYLLABLE TYUH
+D2B9..D2D3    ; LVT # Lo  [27] HANGUL SYLLABLE TEUG..HANGUL SYLLABLE TEUH
+D2D5..D2EF    ; LVT # Lo  [27] HANGUL SYLLABLE TYIG..HANGUL SYLLABLE TYIH
+D2F1..D30B    ; LVT # Lo  [27] HANGUL SYLLABLE TIG..HANGUL SYLLABLE TIH
+D30D..D327    ; LVT # Lo  [27] HANGUL SYLLABLE PAG..HANGUL SYLLABLE PAH
+D329..D343    ; LVT # Lo  [27] HANGUL SYLLABLE PAEG..HANGUL SYLLABLE PAEH
+D345..D35F    ; LVT # Lo  [27] HANGUL SYLLABLE PYAG..HANGUL SYLLABLE PYAH
+D361..D37B    ; LVT # Lo  [27] HANGUL SYLLABLE PYAEG..HANGUL SYLLABLE PYAEH
+D37D..D397    ; LVT # Lo  [27] HANGUL SYLLABLE PEOG..HANGUL SYLLABLE PEOH
+D399..D3B3    ; LVT # Lo  [27] HANGUL SYLLABLE PEG..HANGUL SYLLABLE PEH
+D3B5..D3CF    ; LVT # Lo  [27] HANGUL SYLLABLE PYEOG..HANGUL SYLLABLE PYEOH
+D3D1..D3EB    ; LVT # Lo  [27] HANGUL SYLLABLE PYEG..HANGUL SYLLABLE PYEH
+D3ED..D407    ; LVT # Lo  [27] HANGUL SYLLABLE POG..HANGUL SYLLABLE POH
+D409..D423    ; LVT # Lo  [27] HANGUL SYLLABLE PWAG..HANGUL SYLLABLE PWAH
+D425..D43F    ; LVT # Lo  [27] HANGUL SYLLABLE PWAEG..HANGUL SYLLABLE PWAEH
+D441..D45B    ; LVT # Lo  [27] HANGUL SYLLABLE POEG..HANGUL SYLLABLE POEH
+D45D..D477    ; LVT # Lo  [27] HANGUL SYLLABLE PYOG..HANGUL SYLLABLE PYOH
+D479..D493    ; LVT # Lo  [27] HANGUL SYLLABLE PUG..HANGUL SYLLABLE PUH
+D495..D4AF    ; LVT # Lo  [27] HANGUL SYLLABLE PWEOG..HANGUL SYLLABLE PWEOH
+D4B1..D4CB    ; LVT # Lo  [27] HANGUL SYLLABLE PWEG..HANGUL SYLLABLE PWEH
+D4CD..D4E7    ; LVT # Lo  [27] HANGUL SYLLABLE PWIG..HANGUL SYLLABLE PWIH
+D4E9..D503    ; LVT # Lo  [27] HANGUL SYLLABLE PYUG..HANGUL SYLLABLE PYUH
+D505..D51F    ; LVT # Lo  [27] HANGUL SYLLABLE PEUG..HANGUL SYLLABLE PEUH
+D521..D53B    ; LVT # Lo  [27] HANGUL SYLLABLE PYIG..HANGUL SYLLABLE PYIH
+D53D..D557    ; LVT # Lo  [27] HANGUL SYLLABLE PIG..HANGUL SYLLABLE PIH
+D559..D573    ; LVT # Lo  [27] HANGUL SYLLABLE HAG..HANGUL SYLLABLE HAH
+D575..D58F    ; LVT # Lo  [27] HANGUL SYLLABLE HAEG..HANGUL SYLLABLE HAEH
+D591..D5AB    ; LVT # Lo  [27] HANGUL SYLLABLE HYAG..HANGUL SYLLABLE HYAH
+D5AD..D5C7    ; LVT # Lo  [27] HANGUL SYLLABLE HYAEG..HANGUL SYLLABLE HYAEH
+D5C9..D5E3    ; LVT # Lo  [27] HANGUL SYLLABLE HEOG..HANGUL SYLLABLE HEOH
+D5E5..D5FF    ; LVT # Lo  [27] HANGUL SYLLABLE HEG..HANGUL SYLLABLE HEH
+D601..D61B    ; LVT # Lo  [27] HANGUL SYLLABLE HYEOG..HANGUL SYLLABLE HYEOH
+D61D..D637    ; LVT # Lo  [27] HANGUL SYLLABLE HYEG..HANGUL SYLLABLE HYEH
+D639..D653    ; LVT # Lo  [27] HANGUL SYLLABLE HOG..HANGUL SYLLABLE HOH
+D655..D66F    ; LVT # Lo  [27] HANGUL SYLLABLE HWAG..HANGUL SYLLABLE HWAH
+D671..D68B    ; LVT # Lo  [27] HANGUL SYLLABLE HWAEG..HANGUL SYLLABLE HWAEH
+D68D..D6A7    ; LVT # Lo  [27] HANGUL SYLLABLE HOEG..HANGUL SYLLABLE HOEH
+D6A9..D6C3    ; LVT # Lo  [27] HANGUL SYLLABLE HYOG..HANGUL SYLLABLE HYOH
+D6C5..D6DF    ; LVT # Lo  [27] HANGUL SYLLABLE HUG..HANGUL SYLLABLE HUH
+D6E1..D6FB    ; LVT # Lo  [27] HANGUL SYLLABLE HWEOG..HANGUL SYLLABLE HWEOH
+D6FD..D717    ; LVT # Lo  [27] HANGUL SYLLABLE HWEG..HANGUL SYLLABLE HWEH
+D719..D733    ; LVT # Lo  [27] HANGUL SYLLABLE HWIG..HANGUL SYLLABLE HWIH
+D735..D74F    ; LVT # Lo  [27] HANGUL SYLLABLE HYUG..HANGUL SYLLABLE HYUH
+D751..D76B    ; LVT # Lo  [27] HANGUL SYLLABLE HEUG..HANGUL SYLLABLE HEUH
+D76D..D787    ; LVT # Lo  [27] HANGUL SYLLABLE HYIG..HANGUL SYLLABLE HYIH
+D789..D7A3    ; LVT # Lo  [27] HANGUL SYLLABLE HIG..HANGUL SYLLABLE HIH
+
+# Total code points: 10773
+
+# EOF
diff --git a/third_party/harfbuzz/contrib/tables/README b/third_party/harfbuzz/contrib/tables/README
new file mode 100644
index 0000000..605d1c0
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/README
@@ -0,0 +1,17 @@
+This directory contains Python script to parse several of the Unicode tables
+that are downloadable from the web and generate C header files from them.
+
+These are the locations of the files which are parsed. You should download these
+files and put them in this directory.
+
+http://www.unicode.org/Public/5.1.0/ucd/extracted/DerivedGeneralCategory.txt
+http://www.unicode.org/Public/5.1.0/ucd/extracted/DerivedCombiningClass.txt
+http://www.unicode.org/Public/UNIDATA/auxiliary/GraphemeBreakProperty.txt
+http://www.unicode.org/Public/5.1.0/ucd/Scripts.txt
+
+Then you can run the following python scripts to generate the header files:
+
+python category-parse.py DerivedGeneralCategory.txt category-properties.h
+python combining-class-parse.py DerivedCombiningClass.txt combining-properties.h
+python grapheme-break-parse.py GraphemeBreakProperty.txt grapheme-break-properties.h
+python scripts-parse.py Scripts.txt script-properties.h
diff --git a/third_party/harfbuzz/contrib/tables/Scripts.txt b/third_party/harfbuzz/contrib/tables/Scripts.txt
new file mode 100644
index 0000000..7065486
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/Scripts.txt
@@ -0,0 +1,1747 @@
+# Scripts-5.1.0.txt
+# Date: 2008-03-20, 17:55:33 GMT [MD]
+#
+# Unicode Character Database
+# Copyright (c) 1991-2008 Unicode, Inc.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For documentation, see UCD.html
+
+# ================================================
+
+# Property:	Script
+
+#  All code points not explicitly listed for Script
+#  have the value Unknown (Zzzz).
+
+# @missing: 0000..10FFFF; Unknown
+
+# ================================================
+
+0000..001F    ; Common # Cc  [32] <control-0000>..<control-001F>
+0020          ; Common # Zs       SPACE
+0021..0023    ; Common # Po   [3] EXCLAMATION MARK..NUMBER SIGN
+0024          ; Common # Sc       DOLLAR SIGN
+0025..0027    ; Common # Po   [3] PERCENT SIGN..APOSTROPHE
+0028          ; Common # Ps       LEFT PARENTHESIS
+0029          ; Common # Pe       RIGHT PARENTHESIS
+002A          ; Common # Po       ASTERISK
+002B          ; Common # Sm       PLUS SIGN
+002C          ; Common # Po       COMMA
+002D          ; Common # Pd       HYPHEN-MINUS
+002E..002F    ; Common # Po   [2] FULL STOP..SOLIDUS
+0030..0039    ; Common # Nd  [10] DIGIT ZERO..DIGIT NINE
+003A..003B    ; Common # Po   [2] COLON..SEMICOLON
+003C..003E    ; Common # Sm   [3] LESS-THAN SIGN..GREATER-THAN SIGN
+003F..0040    ; Common # Po   [2] QUESTION MARK..COMMERCIAL AT
+005B          ; Common # Ps       LEFT SQUARE BRACKET
+005C          ; Common # Po       REVERSE SOLIDUS
+005D          ; Common # Pe       RIGHT SQUARE BRACKET
+005E          ; Common # Sk       CIRCUMFLEX ACCENT
+005F          ; Common # Pc       LOW LINE
+0060          ; Common # Sk       GRAVE ACCENT
+007B          ; Common # Ps       LEFT CURLY BRACKET
+007C          ; Common # Sm       VERTICAL LINE
+007D          ; Common # Pe       RIGHT CURLY BRACKET
+007E          ; Common # Sm       TILDE
+007F..009F    ; Common # Cc  [33] <control-007F>..<control-009F>
+00A0          ; Common # Zs       NO-BREAK SPACE
+00A1          ; Common # Po       INVERTED EXCLAMATION MARK
+00A2..00A5    ; Common # Sc   [4] CENT SIGN..YEN SIGN
+00A6..00A7    ; Common # So   [2] BROKEN BAR..SECTION SIGN
+00A8          ; Common # Sk       DIAERESIS
+00A9          ; Common # So       COPYRIGHT SIGN
+00AB          ; Common # Pi       LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+00AC          ; Common # Sm       NOT SIGN
+00AD          ; Common # Cf       SOFT HYPHEN
+00AE          ; Common # So       REGISTERED SIGN
+00AF          ; Common # Sk       MACRON
+00B0          ; Common # So       DEGREE SIGN
+00B1          ; Common # Sm       PLUS-MINUS SIGN
+00B2..00B3    ; Common # No   [2] SUPERSCRIPT TWO..SUPERSCRIPT THREE
+00B4          ; Common # Sk       ACUTE ACCENT
+00B5          ; Common # L&       MICRO SIGN
+00B6          ; Common # So       PILCROW SIGN
+00B7          ; Common # Po       MIDDLE DOT
+00B8          ; Common # Sk       CEDILLA
+00B9          ; Common # No       SUPERSCRIPT ONE
+00BB          ; Common # Pf       RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+00BC..00BE    ; Common # No   [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS
+00BF          ; Common # Po       INVERTED QUESTION MARK
+00D7          ; Common # Sm       MULTIPLICATION SIGN
+00F7          ; Common # Sm       DIVISION SIGN
+02B9..02C1    ; Common # Lm   [9] MODIFIER LETTER PRIME..MODIFIER LETTER REVERSED GLOTTAL STOP
+02C2..02C5    ; Common # Sk   [4] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER DOWN ARROWHEAD
+02C6..02D1    ; Common # Lm  [12] MODIFIER LETTER CIRCUMFLEX ACCENT..MODIFIER LETTER HALF TRIANGULAR COLON
+02D2..02DF    ; Common # Sk  [14] MODIFIER LETTER CENTRED RIGHT HALF RING..MODIFIER LETTER CROSS ACCENT
+02E5..02EB    ; Common # Sk   [7] MODIFIER LETTER EXTRA-HIGH TONE BAR..MODIFIER LETTER YANG DEPARTING TONE MARK
+02EC          ; Common # Lm       MODIFIER LETTER VOICING
+02ED          ; Common # Sk       MODIFIER LETTER UNASPIRATED
+02EE          ; Common # Lm       MODIFIER LETTER DOUBLE APOSTROPHE
+02EF..02FF    ; Common # Sk  [17] MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER LETTER LOW LEFT ARROW
+0374          ; Common # Lm       GREEK NUMERAL SIGN
+037E          ; Common # Po       GREEK QUESTION MARK
+0385          ; Common # Sk       GREEK DIALYTIKA TONOS
+0387          ; Common # Po       GREEK ANO TELEIA
+0589          ; Common # Po       ARMENIAN FULL STOP
+0600..0603    ; Common # Cf   [4] ARABIC NUMBER SIGN..ARABIC SIGN SAFHA
+060C          ; Common # Po       ARABIC COMMA
+061B          ; Common # Po       ARABIC SEMICOLON
+061F          ; Common # Po       ARABIC QUESTION MARK
+0640          ; Common # Lm       ARABIC TATWEEL
+0660..0669    ; Common # Nd  [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE
+06DD          ; Common # Cf       ARABIC END OF AYAH
+0964..0965    ; Common # Po   [2] DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA
+0970          ; Common # Po       DEVANAGARI ABBREVIATION SIGN
+0CF1..0CF2    ; Common # So   [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
+0E3F          ; Common # Sc       THAI CURRENCY SYMBOL BAHT
+10FB          ; Common # Po       GEORGIAN PARAGRAPH SEPARATOR
+16EB..16ED    ; Common # Po   [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION
+1735..1736    ; Common # Po   [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION
+1802..1803    ; Common # Po   [2] MONGOLIAN COMMA..MONGOLIAN FULL STOP
+1805          ; Common # Po       MONGOLIAN FOUR DOTS
+2000..200A    ; Common # Zs  [11] EN QUAD..HAIR SPACE
+200B          ; Common # Cf       ZERO WIDTH SPACE
+200E..200F    ; Common # Cf   [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK
+2010..2015    ; Common # Pd   [6] HYPHEN..HORIZONTAL BAR
+2016..2017    ; Common # Po   [2] DOUBLE VERTICAL LINE..DOUBLE LOW LINE
+2018          ; Common # Pi       LEFT SINGLE QUOTATION MARK
+2019          ; Common # Pf       RIGHT SINGLE QUOTATION MARK
+201A          ; Common # Ps       SINGLE LOW-9 QUOTATION MARK
+201B..201C    ; Common # Pi   [2] SINGLE HIGH-REVERSED-9 QUOTATION MARK..LEFT DOUBLE QUOTATION MARK
+201D          ; Common # Pf       RIGHT DOUBLE QUOTATION MARK
+201E          ; Common # Ps       DOUBLE LOW-9 QUOTATION MARK
+201F          ; Common # Pi       DOUBLE HIGH-REVERSED-9 QUOTATION MARK
+2020..2027    ; Common # Po   [8] DAGGER..HYPHENATION POINT
+2028          ; Common # Zl       LINE SEPARATOR
+2029          ; Common # Zp       PARAGRAPH SEPARATOR
+202A..202E    ; Common # Cf   [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
+202F          ; Common # Zs       NARROW NO-BREAK SPACE
+2030..2038    ; Common # Po   [9] PER MILLE SIGN..CARET
+2039          ; Common # Pi       SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+203A          ; Common # Pf       SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+203B..203E    ; Common # Po   [4] REFERENCE MARK..OVERLINE
+203F..2040    ; Common # Pc   [2] UNDERTIE..CHARACTER TIE
+2041..2043    ; Common # Po   [3] CARET INSERTION POINT..HYPHEN BULLET
+2044          ; Common # Sm       FRACTION SLASH
+2045          ; Common # Ps       LEFT SQUARE BRACKET WITH QUILL
+2046          ; Common # Pe       RIGHT SQUARE BRACKET WITH QUILL
+2047..2051    ; Common # Po  [11] DOUBLE QUESTION MARK..TWO ASTERISKS ALIGNED VERTICALLY
+2052          ; Common # Sm       COMMERCIAL MINUS SIGN
+2053          ; Common # Po       SWUNG DASH
+2054          ; Common # Pc       INVERTED UNDERTIE
+2055..205E    ; Common # Po  [10] FLOWER PUNCTUATION MARK..VERTICAL FOUR DOTS
+205F          ; Common # Zs       MEDIUM MATHEMATICAL SPACE
+2060..2064    ; Common # Cf   [5] WORD JOINER..INVISIBLE PLUS
+206A..206F    ; Common # Cf   [6] INHIBIT SYMMETRIC SWAPPING..NOMINAL DIGIT SHAPES
+2070          ; Common # No       SUPERSCRIPT ZERO
+2074..2079    ; Common # No   [6] SUPERSCRIPT FOUR..SUPERSCRIPT NINE
+207A..207C    ; Common # Sm   [3] SUPERSCRIPT PLUS SIGN..SUPERSCRIPT EQUALS SIGN
+207D          ; Common # Ps       SUPERSCRIPT LEFT PARENTHESIS
+207E          ; Common # Pe       SUPERSCRIPT RIGHT PARENTHESIS
+2080..2089    ; Common # No  [10] SUBSCRIPT ZERO..SUBSCRIPT NINE
+208A..208C    ; Common # Sm   [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN
+208D          ; Common # Ps       SUBSCRIPT LEFT PARENTHESIS
+208E          ; Common # Pe       SUBSCRIPT RIGHT PARENTHESIS
+20A0..20B5    ; Common # Sc  [22] EURO-CURRENCY SIGN..CEDI SIGN
+2100..2101    ; Common # So   [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT
+2102          ; Common # L&       DOUBLE-STRUCK CAPITAL C
+2103..2106    ; Common # So   [4] DEGREE CELSIUS..CADA UNA
+2107          ; Common # L&       EULER CONSTANT
+2108..2109    ; Common # So   [2] SCRUPLE..DEGREE FAHRENHEIT
+210A..2113    ; Common # L&  [10] SCRIPT SMALL G..SCRIPT SMALL L
+2114          ; Common # So       L B BAR SYMBOL
+2115          ; Common # L&       DOUBLE-STRUCK CAPITAL N
+2116..2118    ; Common # So   [3] NUMERO SIGN..SCRIPT CAPITAL P
+2119..211D    ; Common # L&   [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R
+211E..2123    ; Common # So   [6] PRESCRIPTION TAKE..VERSICLE
+2124          ; Common # L&       DOUBLE-STRUCK CAPITAL Z
+2125          ; Common # So       OUNCE SIGN
+2127          ; Common # So       INVERTED OHM SIGN
+2128          ; Common # L&       BLACK-LETTER CAPITAL Z
+2129          ; Common # So       TURNED GREEK SMALL LETTER IOTA
+212C..212D    ; Common # L&   [2] SCRIPT CAPITAL B..BLACK-LETTER CAPITAL C
+212E          ; Common # So       ESTIMATED SYMBOL
+212F..2131    ; Common # L&   [3] SCRIPT SMALL E..SCRIPT CAPITAL F
+2133..2134    ; Common # L&   [2] SCRIPT CAPITAL M..SCRIPT SMALL O
+2135..2138    ; Common # Lo   [4] ALEF SYMBOL..DALET SYMBOL
+2139          ; Common # L&       INFORMATION SOURCE
+213A..213B    ; Common # So   [2] ROTATED CAPITAL Q..FACSIMILE SIGN
+213C..213F    ; Common # L&   [4] DOUBLE-STRUCK SMALL PI..DOUBLE-STRUCK CAPITAL PI
+2140..2144    ; Common # Sm   [5] DOUBLE-STRUCK N-ARY SUMMATION..TURNED SANS-SERIF CAPITAL Y
+2145..2149    ; Common # L&   [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J
+214A          ; Common # So       PROPERTY LINE
+214B          ; Common # Sm       TURNED AMPERSAND
+214C..214D    ; Common # So   [2] PER SIGN..AKTIESELSKAB
+214F          ; Common # So       SYMBOL FOR SAMARITAN SOURCE
+2153..215F    ; Common # No  [13] VULGAR FRACTION ONE THIRD..FRACTION NUMERATOR ONE
+2190..2194    ; Common # Sm   [5] LEFTWARDS ARROW..LEFT RIGHT ARROW
+2195..2199    ; Common # So   [5] UP DOWN ARROW..SOUTH WEST ARROW
+219A..219B    ; Common # Sm   [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE
+219C..219F    ; Common # So   [4] LEFTWARDS WAVE ARROW..UPWARDS TWO HEADED ARROW
+21A0          ; Common # Sm       RIGHTWARDS TWO HEADED ARROW
+21A1..21A2    ; Common # So   [2] DOWNWARDS TWO HEADED ARROW..LEFTWARDS ARROW WITH TAIL
+21A3          ; Common # Sm       RIGHTWARDS ARROW WITH TAIL
+21A4..21A5    ; Common # So   [2] LEFTWARDS ARROW FROM BAR..UPWARDS ARROW FROM BAR
+21A6          ; Common # Sm       RIGHTWARDS ARROW FROM BAR
+21A7..21AD    ; Common # So   [7] DOWNWARDS ARROW FROM BAR..LEFT RIGHT WAVE ARROW
+21AE          ; Common # Sm       LEFT RIGHT ARROW WITH STROKE
+21AF..21CD    ; Common # So  [31] DOWNWARDS ZIGZAG ARROW..LEFTWARDS DOUBLE ARROW WITH STROKE
+21CE..21CF    ; Common # Sm   [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE
+21D0..21D1    ; Common # So   [2] LEFTWARDS DOUBLE ARROW..UPWARDS DOUBLE ARROW
+21D2          ; Common # Sm       RIGHTWARDS DOUBLE ARROW
+21D3          ; Common # So       DOWNWARDS DOUBLE ARROW
+21D4          ; Common # Sm       LEFT RIGHT DOUBLE ARROW
+21D5..21F3    ; Common # So  [31] UP DOWN DOUBLE ARROW..UP DOWN WHITE ARROW
+21F4..22FF    ; Common # Sm [268] RIGHT ARROW WITH SMALL CIRCLE..Z NOTATION BAG MEMBERSHIP
+2300..2307    ; Common # So   [8] DIAMETER SIGN..WAVY LINE
+2308..230B    ; Common # Sm   [4] LEFT CEILING..RIGHT FLOOR
+230C..231F    ; Common # So  [20] BOTTOM RIGHT CROP..BOTTOM RIGHT CORNER
+2320..2321    ; Common # Sm   [2] TOP HALF INTEGRAL..BOTTOM HALF INTEGRAL
+2322..2328    ; Common # So   [7] FROWN..KEYBOARD
+2329          ; Common # Ps       LEFT-POINTING ANGLE BRACKET
+232A          ; Common # Pe       RIGHT-POINTING ANGLE BRACKET
+232B..237B    ; Common # So  [81] ERASE TO THE LEFT..NOT CHECK MARK
+237C          ; Common # Sm       RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW
+237D..239A    ; Common # So  [30] SHOULDERED OPEN BOX..CLEAR SCREEN SYMBOL
+239B..23B3    ; Common # Sm  [25] LEFT PARENTHESIS UPPER HOOK..SUMMATION BOTTOM
+23B4..23DB    ; Common # So  [40] TOP SQUARE BRACKET..FUSE
+23DC..23E1    ; Common # Sm   [6] TOP PARENTHESIS..BOTTOM TORTOISE SHELL BRACKET
+23E2..23E7    ; Common # So   [6] WHITE TRAPEZIUM..ELECTRICAL INTERSECTION
+2400..2426    ; Common # So  [39] SYMBOL FOR NULL..SYMBOL FOR SUBSTITUTE FORM TWO
+2440..244A    ; Common # So  [11] OCR HOOK..OCR DOUBLE BACKSLASH
+2460..249B    ; Common # No  [60] CIRCLED DIGIT ONE..NUMBER TWENTY FULL STOP
+249C..24E9    ; Common # So  [78] PARENTHESIZED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z
+24EA..24FF    ; Common # No  [22] CIRCLED DIGIT ZERO..NEGATIVE CIRCLED DIGIT ZERO
+2500..25B6    ; Common # So [183] BOX DRAWINGS LIGHT HORIZONTAL..BLACK RIGHT-POINTING TRIANGLE
+25B7          ; Common # Sm       WHITE RIGHT-POINTING TRIANGLE
+25B8..25C0    ; Common # So   [9] BLACK RIGHT-POINTING SMALL TRIANGLE..BLACK LEFT-POINTING TRIANGLE
+25C1          ; Common # Sm       WHITE LEFT-POINTING TRIANGLE
+25C2..25F7    ; Common # So  [54] BLACK LEFT-POINTING SMALL TRIANGLE..WHITE CIRCLE WITH UPPER RIGHT QUADRANT
+25F8..25FF    ; Common # Sm   [8] UPPER LEFT TRIANGLE..LOWER RIGHT TRIANGLE
+2600..266E    ; Common # So [111] BLACK SUN WITH RAYS..MUSIC NATURAL SIGN
+266F          ; Common # Sm       MUSIC SHARP SIGN
+2670..269D    ; Common # So  [46] WEST SYRIAC CROSS..OUTLINED WHITE STAR
+26A0..26BC    ; Common # So  [29] WARNING SIGN..SESQUIQUADRATE
+26C0..26C3    ; Common # So   [4] WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
+2701..2704    ; Common # So   [4] UPPER BLADE SCISSORS..WHITE SCISSORS
+2706..2709    ; Common # So   [4] TELEPHONE LOCATION SIGN..ENVELOPE
+270C..2727    ; Common # So  [28] VICTORY HAND..WHITE FOUR POINTED STAR
+2729..274B    ; Common # So  [35] STRESS OUTLINED WHITE STAR..HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK
+274D          ; Common # So       SHADOWED WHITE CIRCLE
+274F..2752    ; Common # So   [4] LOWER RIGHT DROP-SHADOWED WHITE SQUARE..UPPER RIGHT SHADOWED WHITE SQUARE
+2756          ; Common # So       BLACK DIAMOND MINUS WHITE X
+2758..275E    ; Common # So   [7] LIGHT VERTICAL BAR..HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT
+2761..2767    ; Common # So   [7] CURVED STEM PARAGRAPH SIGN ORNAMENT..ROTATED FLORAL HEART BULLET
+2768          ; Common # Ps       MEDIUM LEFT PARENTHESIS ORNAMENT
+2769          ; Common # Pe       MEDIUM RIGHT PARENTHESIS ORNAMENT
+276A          ; Common # Ps       MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT
+276B          ; Common # Pe       MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT
+276C          ; Common # Ps       MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT
+276D          ; Common # Pe       MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT
+276E          ; Common # Ps       HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT
+276F          ; Common # Pe       HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT
+2770          ; Common # Ps       HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT
+2771          ; Common # Pe       HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT
+2772          ; Common # Ps       LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
+2773          ; Common # Pe       LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
+2774          ; Common # Ps       MEDIUM LEFT CURLY BRACKET ORNAMENT
+2775          ; Common # Pe       MEDIUM RIGHT CURLY BRACKET ORNAMENT
+2776..2793    ; Common # No  [30] DINGBAT NEGATIVE CIRCLED DIGIT ONE..DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN
+2794          ; Common # So       HEAVY WIDE-HEADED RIGHTWARDS ARROW
+2798..27AF    ; Common # So  [24] HEAVY SOUTH EAST ARROW..NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW
+27B1..27BE    ; Common # So  [14] NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW..OPEN-OUTLINED RIGHTWARDS ARROW
+27C0..27C4    ; Common # Sm   [5] THREE DIMENSIONAL ANGLE..OPEN SUPERSET
+27C5          ; Common # Ps       LEFT S-SHAPED BAG DELIMITER
+27C6          ; Common # Pe       RIGHT S-SHAPED BAG DELIMITER
+27C7..27CA    ; Common # Sm   [4] OR WITH DOT INSIDE..VERTICAL BAR WITH HORIZONTAL STROKE
+27CC          ; Common # Sm       LONG DIVISION
+27D0..27E5    ; Common # Sm  [22] WHITE DIAMOND WITH CENTRED DOT..WHITE SQUARE WITH RIGHTWARDS TICK
+27E6          ; Common # Ps       MATHEMATICAL LEFT WHITE SQUARE BRACKET
+27E7          ; Common # Pe       MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+27E8          ; Common # Ps       MATHEMATICAL LEFT ANGLE BRACKET
+27E9          ; Common # Pe       MATHEMATICAL RIGHT ANGLE BRACKET
+27EA          ; Common # Ps       MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
+27EB          ; Common # Pe       MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
+27EC          ; Common # Ps       MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
+27ED          ; Common # Pe       MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
+27EE          ; Common # Ps       MATHEMATICAL LEFT FLATTENED PARENTHESIS
+27EF          ; Common # Pe       MATHEMATICAL RIGHT FLATTENED PARENTHESIS
+27F0..27FF    ; Common # Sm  [16] UPWARDS QUADRUPLE ARROW..LONG RIGHTWARDS SQUIGGLE ARROW
+2900..2982    ; Common # Sm [131] RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE..Z NOTATION TYPE COLON
+2983          ; Common # Ps       LEFT WHITE CURLY BRACKET
+2984          ; Common # Pe       RIGHT WHITE CURLY BRACKET
+2985          ; Common # Ps       LEFT WHITE PARENTHESIS
+2986          ; Common # Pe       RIGHT WHITE PARENTHESIS
+2987          ; Common # Ps       Z NOTATION LEFT IMAGE BRACKET
+2988          ; Common # Pe       Z NOTATION RIGHT IMAGE BRACKET
+2989          ; Common # Ps       Z NOTATION LEFT BINDING BRACKET
+298A          ; Common # Pe       Z NOTATION RIGHT BINDING BRACKET
+298B          ; Common # Ps       LEFT SQUARE BRACKET WITH UNDERBAR
+298C          ; Common # Pe       RIGHT SQUARE BRACKET WITH UNDERBAR
+298D          ; Common # Ps       LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
+298E          ; Common # Pe       RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+298F          ; Common # Ps       LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+2990          ; Common # Pe       RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
+2991          ; Common # Ps       LEFT ANGLE BRACKET WITH DOT
+2992          ; Common # Pe       RIGHT ANGLE BRACKET WITH DOT
+2993          ; Common # Ps       LEFT ARC LESS-THAN BRACKET
+2994          ; Common # Pe       RIGHT ARC GREATER-THAN BRACKET
+2995          ; Common # Ps       DOUBLE LEFT ARC GREATER-THAN BRACKET
+2996          ; Common # Pe       DOUBLE RIGHT ARC LESS-THAN BRACKET
+2997          ; Common # Ps       LEFT BLACK TORTOISE SHELL BRACKET
+2998          ; Common # Pe       RIGHT BLACK TORTOISE SHELL BRACKET
+2999..29D7    ; Common # Sm  [63] DOTTED FENCE..BLACK HOURGLASS
+29D8          ; Common # Ps       LEFT WIGGLY FENCE
+29D9          ; Common # Pe       RIGHT WIGGLY FENCE
+29DA          ; Common # Ps       LEFT DOUBLE WIGGLY FENCE
+29DB          ; Common # Pe       RIGHT DOUBLE WIGGLY FENCE
+29DC..29FB    ; Common # Sm  [32] INCOMPLETE INFINITY..TRIPLE PLUS
+29FC          ; Common # Ps       LEFT-POINTING CURVED ANGLE BRACKET
+29FD          ; Common # Pe       RIGHT-POINTING CURVED ANGLE BRACKET
+29FE..2AFF    ; Common # Sm [258] TINY..N-ARY WHITE VERTICAL BAR
+2B00..2B2F    ; Common # So  [48] NORTH EAST WHITE ARROW..WHITE VERTICAL ELLIPSE
+2B30..2B44    ; Common # Sm  [21] LEFT ARROW WITH SMALL CIRCLE..RIGHTWARDS ARROW THROUGH SUPERSET
+2B45..2B46    ; Common # So   [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW
+2B47..2B4C    ; Common # Sm   [6] REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR
+2B50..2B54    ; Common # So   [5] WHITE MEDIUM STAR..WHITE RIGHT-POINTING PENTAGON
+2E00..2E01    ; Common # Po   [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER
+2E02          ; Common # Pi       LEFT SUBSTITUTION BRACKET
+2E03          ; Common # Pf       RIGHT SUBSTITUTION BRACKET
+2E04          ; Common # Pi       LEFT DOTTED SUBSTITUTION BRACKET
+2E05          ; Common # Pf       RIGHT DOTTED SUBSTITUTION BRACKET
+2E06..2E08    ; Common # Po   [3] RAISED INTERPOLATION MARKER..DOTTED TRANSPOSITION MARKER
+2E09          ; Common # Pi       LEFT TRANSPOSITION BRACKET
+2E0A          ; Common # Pf       RIGHT TRANSPOSITION BRACKET
+2E0B          ; Common # Po       RAISED SQUARE
+2E0C          ; Common # Pi       LEFT RAISED OMISSION BRACKET
+2E0D          ; Common # Pf       RIGHT RAISED OMISSION BRACKET
+2E0E..2E16    ; Common # Po   [9] EDITORIAL CORONIS..DOTTED RIGHT-POINTING ANGLE
+2E17          ; Common # Pd       DOUBLE OBLIQUE HYPHEN
+2E18..2E19    ; Common # Po   [2] INVERTED INTERROBANG..PALM BRANCH
+2E1A          ; Common # Pd       HYPHEN WITH DIAERESIS
+2E1B          ; Common # Po       TILDE WITH RING ABOVE
+2E1C          ; Common # Pi       LEFT LOW PARAPHRASE BRACKET
+2E1D          ; Common # Pf       RIGHT LOW PARAPHRASE BRACKET
+2E1E..2E1F    ; Common # Po   [2] TILDE WITH DOT ABOVE..TILDE WITH DOT BELOW
+2E20          ; Common # Pi       LEFT VERTICAL BAR WITH QUILL
+2E21          ; Common # Pf       RIGHT VERTICAL BAR WITH QUILL
+2E22          ; Common # Ps       TOP LEFT HALF BRACKET
+2E23          ; Common # Pe       TOP RIGHT HALF BRACKET
+2E24          ; Common # Ps       BOTTOM LEFT HALF BRACKET
+2E25          ; Common # Pe       BOTTOM RIGHT HALF BRACKET
+2E26          ; Common # Ps       LEFT SIDEWAYS U BRACKET
+2E27          ; Common # Pe       RIGHT SIDEWAYS U BRACKET
+2E28          ; Common # Ps       LEFT DOUBLE PARENTHESIS
+2E29          ; Common # Pe       RIGHT DOUBLE PARENTHESIS
+2E2A..2E2E    ; Common # Po   [5] TWO DOTS OVER ONE DOT PUNCTUATION..REVERSED QUESTION MARK
+2E2F          ; Common # Lm       VERTICAL TILDE
+2E30          ; Common # Po       RING POINT
+2FF0..2FFB    ; Common # So  [12] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID
+3000          ; Common # Zs       IDEOGRAPHIC SPACE
+3001..3003    ; Common # Po   [3] IDEOGRAPHIC COMMA..DITTO MARK
+3004          ; Common # So       JAPANESE INDUSTRIAL STANDARD SYMBOL
+3006          ; Common # Lo       IDEOGRAPHIC CLOSING MARK
+3008          ; Common # Ps       LEFT ANGLE BRACKET
+3009          ; Common # Pe       RIGHT ANGLE BRACKET
+300A          ; Common # Ps       LEFT DOUBLE ANGLE BRACKET
+300B          ; Common # Pe       RIGHT DOUBLE ANGLE BRACKET
+300C          ; Common # Ps       LEFT CORNER BRACKET
+300D          ; Common # Pe       RIGHT CORNER BRACKET
+300E          ; Common # Ps       LEFT WHITE CORNER BRACKET
+300F          ; Common # Pe       RIGHT WHITE CORNER BRACKET
+3010          ; Common # Ps       LEFT BLACK LENTICULAR BRACKET
+3011          ; Common # Pe       RIGHT BLACK LENTICULAR BRACKET
+3012..3013    ; Common # So   [2] POSTAL MARK..GETA MARK
+3014          ; Common # Ps       LEFT TORTOISE SHELL BRACKET
+3015          ; Common # Pe       RIGHT TORTOISE SHELL BRACKET
+3016          ; Common # Ps       LEFT WHITE LENTICULAR BRACKET
+3017          ; Common # Pe       RIGHT WHITE LENTICULAR BRACKET
+3018          ; Common # Ps       LEFT WHITE TORTOISE SHELL BRACKET
+3019          ; Common # Pe       RIGHT WHITE TORTOISE SHELL BRACKET
+301A          ; Common # Ps       LEFT WHITE SQUARE BRACKET
+301B          ; Common # Pe       RIGHT WHITE SQUARE BRACKET
+301C          ; Common # Pd       WAVE DASH
+301D          ; Common # Ps       REVERSED DOUBLE PRIME QUOTATION MARK
+301E..301F    ; Common # Pe   [2] DOUBLE PRIME QUOTATION MARK..LOW DOUBLE PRIME QUOTATION MARK
+3020          ; Common # So       POSTAL MARK FACE
+3030          ; Common # Pd       WAVY DASH
+3031..3035    ; Common # Lm   [5] VERTICAL KANA REPEAT MARK..VERTICAL KANA REPEAT MARK LOWER HALF
+3036..3037    ; Common # So   [2] CIRCLED POSTAL MARK..IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL
+303C          ; Common # Lo       MASU MARK
+303D          ; Common # Po       PART ALTERNATION MARK
+303E..303F    ; Common # So   [2] IDEOGRAPHIC VARIATION INDICATOR..IDEOGRAPHIC HALF FILL SPACE
+309B..309C    ; Common # Sk   [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+30A0          ; Common # Pd       KATAKANA-HIRAGANA DOUBLE HYPHEN
+30FB          ; Common # Po       KATAKANA MIDDLE DOT
+30FC          ; Common # Lm       KATAKANA-HIRAGANA PROLONGED SOUND MARK
+3190..3191    ; Common # So   [2] IDEOGRAPHIC ANNOTATION LINKING MARK..IDEOGRAPHIC ANNOTATION REVERSE MARK
+3192..3195    ; Common # No   [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC ANNOTATION FOUR MARK
+3196..319F    ; Common # So  [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC ANNOTATION MAN MARK
+31C0..31E3    ; Common # So  [36] CJK STROKE T..CJK STROKE Q
+3220..3229    ; Common # No  [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN
+322A..3243    ; Common # So  [26] PARENTHESIZED IDEOGRAPH MOON..PARENTHESIZED IDEOGRAPH REACH
+3250          ; Common # So       PARTNERSHIP SIGN
+3251..325F    ; Common # No  [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE
+327F          ; Common # So       KOREAN STANDARD SYMBOL
+3280..3289    ; Common # No  [10] CIRCLED IDEOGRAPH ONE..CIRCLED IDEOGRAPH TEN
+328A..32B0    ; Common # So  [39] CIRCLED IDEOGRAPH MOON..CIRCLED IDEOGRAPH NIGHT
+32B1..32BF    ; Common # No  [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY
+32C0..32CF    ; Common # So  [16] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..LIMITED LIABILITY SIGN
+3358..33FF    ; Common # So [168] IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO..SQUARE GAL
+4DC0..4DFF    ; Common # So  [64] HEXAGRAM FOR THE CREATIVE HEAVEN..HEXAGRAM FOR BEFORE COMPLETION
+A700..A716    ; Common # Sk  [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR
+A717..A71F    ; Common # Lm   [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK
+A720..A721    ; Common # Sk   [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE
+A788          ; Common # Lm       MODIFIER LETTER LOW CIRCUMFLEX ACCENT
+A789..A78A    ; Common # Sk   [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
+FD3E          ; Common # Ps       ORNATE LEFT PARENTHESIS
+FD3F          ; Common # Pe       ORNATE RIGHT PARENTHESIS
+FDFD          ; Common # So       ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM
+FE10..FE16    ; Common # Po   [7] PRESENTATION FORM FOR VERTICAL COMMA..PRESENTATION FORM FOR VERTICAL QUESTION MARK
+FE17          ; Common # Ps       PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET
+FE18          ; Common # Pe       PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET
+FE19          ; Common # Po       PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS
+FE30          ; Common # Po       PRESENTATION FORM FOR VERTICAL TWO DOT LEADER
+FE31..FE32    ; Common # Pd   [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH
+FE33..FE34    ; Common # Pc   [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE
+FE35          ; Common # Ps       PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS
+FE36          ; Common # Pe       PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS
+FE37          ; Common # Ps       PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET
+FE38          ; Common # Pe       PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET
+FE39          ; Common # Ps       PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET
+FE3A          ; Common # Pe       PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET
+FE3B          ; Common # Ps       PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET
+FE3C          ; Common # Pe       PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET
+FE3D          ; Common # Ps       PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET
+FE3E          ; Common # Pe       PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET
+FE3F          ; Common # Ps       PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET
+FE40          ; Common # Pe       PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET
+FE41          ; Common # Ps       PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET
+FE42          ; Common # Pe       PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET
+FE43          ; Common # Ps       PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET
+FE44          ; Common # Pe       PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET
+FE45..FE46    ; Common # Po   [2] SESAME DOT..WHITE SESAME DOT
+FE47          ; Common # Ps       PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET
+FE48          ; Common # Pe       PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET
+FE49..FE4C    ; Common # Po   [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE
+FE4D..FE4F    ; Common # Pc   [3] DASHED LOW LINE..WAVY LOW LINE
+FE50..FE52    ; Common # Po   [3] SMALL COMMA..SMALL FULL STOP
+FE54..FE57    ; Common # Po   [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK
+FE58          ; Common # Pd       SMALL EM DASH
+FE59          ; Common # Ps       SMALL LEFT PARENTHESIS
+FE5A          ; Common # Pe       SMALL RIGHT PARENTHESIS
+FE5B          ; Common # Ps       SMALL LEFT CURLY BRACKET
+FE5C          ; Common # Pe       SMALL RIGHT CURLY BRACKET
+FE5D          ; Common # Ps       SMALL LEFT TORTOISE SHELL BRACKET
+FE5E          ; Common # Pe       SMALL RIGHT TORTOISE SHELL BRACKET
+FE5F..FE61    ; Common # Po   [3] SMALL NUMBER SIGN..SMALL ASTERISK
+FE62          ; Common # Sm       SMALL PLUS SIGN
+FE63          ; Common # Pd       SMALL HYPHEN-MINUS
+FE64..FE66    ; Common # Sm   [3] SMALL LESS-THAN SIGN..SMALL EQUALS SIGN
+FE68          ; Common # Po       SMALL REVERSE SOLIDUS
+FE69          ; Common # Sc       SMALL DOLLAR SIGN
+FE6A..FE6B    ; Common # Po   [2] SMALL PERCENT SIGN..SMALL COMMERCIAL AT
+FEFF          ; Common # Cf       ZERO WIDTH NO-BREAK SPACE
+FF01..FF03    ; Common # Po   [3] FULLWIDTH EXCLAMATION MARK..FULLWIDTH NUMBER SIGN
+FF04          ; Common # Sc       FULLWIDTH DOLLAR SIGN
+FF05..FF07    ; Common # Po   [3] FULLWIDTH PERCENT SIGN..FULLWIDTH APOSTROPHE
+FF08          ; Common # Ps       FULLWIDTH LEFT PARENTHESIS
+FF09          ; Common # Pe       FULLWIDTH RIGHT PARENTHESIS
+FF0A          ; Common # Po       FULLWIDTH ASTERISK
+FF0B          ; Common # Sm       FULLWIDTH PLUS SIGN
+FF0C          ; Common # Po       FULLWIDTH COMMA
+FF0D          ; Common # Pd       FULLWIDTH HYPHEN-MINUS
+FF0E..FF0F    ; Common # Po   [2] FULLWIDTH FULL STOP..FULLWIDTH SOLIDUS
+FF10..FF19    ; Common # Nd  [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE
+FF1A..FF1B    ; Common # Po   [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON
+FF1C..FF1E    ; Common # Sm   [3] FULLWIDTH LESS-THAN SIGN..FULLWIDTH GREATER-THAN SIGN
+FF1F..FF20    ; Common # Po   [2] FULLWIDTH QUESTION MARK..FULLWIDTH COMMERCIAL AT
+FF3B          ; Common # Ps       FULLWIDTH LEFT SQUARE BRACKET
+FF3C          ; Common # Po       FULLWIDTH REVERSE SOLIDUS
+FF3D          ; Common # Pe       FULLWIDTH RIGHT SQUARE BRACKET
+FF3E          ; Common # Sk       FULLWIDTH CIRCUMFLEX ACCENT
+FF3F          ; Common # Pc       FULLWIDTH LOW LINE
+FF40          ; Common # Sk       FULLWIDTH GRAVE ACCENT
+FF5B          ; Common # Ps       FULLWIDTH LEFT CURLY BRACKET
+FF5C          ; Common # Sm       FULLWIDTH VERTICAL LINE
+FF5D          ; Common # Pe       FULLWIDTH RIGHT CURLY BRACKET
+FF5E          ; Common # Sm       FULLWIDTH TILDE
+FF5F          ; Common # Ps       FULLWIDTH LEFT WHITE PARENTHESIS
+FF60          ; Common # Pe       FULLWIDTH RIGHT WHITE PARENTHESIS
+FF61          ; Common # Po       HALFWIDTH IDEOGRAPHIC FULL STOP
+FF62          ; Common # Ps       HALFWIDTH LEFT CORNER BRACKET
+FF63          ; Common # Pe       HALFWIDTH RIGHT CORNER BRACKET
+FF64..FF65    ; Common # Po   [2] HALFWIDTH IDEOGRAPHIC COMMA..HALFWIDTH KATAKANA MIDDLE DOT
+FF70          ; Common # Lm       HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK
+FF9E..FF9F    ; Common # Lm   [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+FFE0..FFE1    ; Common # Sc   [2] FULLWIDTH CENT SIGN..FULLWIDTH POUND SIGN
+FFE2          ; Common # Sm       FULLWIDTH NOT SIGN
+FFE3          ; Common # Sk       FULLWIDTH MACRON
+FFE4          ; Common # So       FULLWIDTH BROKEN BAR
+FFE5..FFE6    ; Common # Sc   [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN
+FFE8          ; Common # So       HALFWIDTH FORMS LIGHT VERTICAL
+FFE9..FFEC    ; Common # Sm   [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW
+FFED..FFEE    ; Common # So   [2] HALFWIDTH BLACK SQUARE..HALFWIDTH WHITE CIRCLE
+FFF9..FFFB    ; Common # Cf   [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR
+FFFC..FFFD    ; Common # So   [2] OBJECT REPLACEMENT CHARACTER..REPLACEMENT CHARACTER
+10100..10101  ; Common # Po   [2] AEGEAN WORD SEPARATOR LINE..AEGEAN WORD SEPARATOR DOT
+10102         ; Common # So       AEGEAN CHECK MARK
+10107..10133  ; Common # No  [45] AEGEAN NUMBER ONE..AEGEAN NUMBER NINETY THOUSAND
+10137..1013F  ; Common # So   [9] AEGEAN WEIGHT BASE UNIT..AEGEAN MEASURE THIRD SUBUNIT
+10190..1019B  ; Common # So  [12] ROMAN SEXTANS SIGN..ROMAN CENTURIAL SIGN
+101D0..101FC  ; Common # So  [45] PHAISTOS DISC SIGN PEDESTRIAN..PHAISTOS DISC SIGN WAVY BAND
+1D000..1D0F5  ; Common # So [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO
+1D100..1D126  ; Common # So  [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2
+1D129..1D164  ; Common # So  [60] MUSICAL SYMBOL MULTIPLE MEASURE REST..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D165..1D166  ; Common # Mc   [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
+1D16A..1D16C  ; Common # So   [3] MUSICAL SYMBOL FINGERED TREMOLO-1..MUSICAL SYMBOL FINGERED TREMOLO-3
+1D16D..1D172  ; Common # Mc   [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5
+1D173..1D17A  ; Common # Cf   [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
+1D183..1D184  ; Common # So   [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL ARPEGGIATO DOWN
+1D18C..1D1A9  ; Common # So  [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH
+1D1AE..1D1DD  ; Common # So  [48] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL PES SUBPUNCTIS
+1D300..1D356  ; Common # So  [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING
+1D360..1D371  ; Common # No  [18] COUNTING ROD UNIT DIGIT ONE..COUNTING ROD TENS DIGIT NINE
+1D400..1D454  ; Common # L&  [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G
+1D456..1D49C  ; Common # L&  [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A
+1D49E..1D49F  ; Common # L&   [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D
+1D4A2         ; Common # L&       MATHEMATICAL SCRIPT CAPITAL G
+1D4A5..1D4A6  ; Common # L&   [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K
+1D4A9..1D4AC  ; Common # L&   [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q
+1D4AE..1D4B9  ; Common # L&  [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D
+1D4BB         ; Common # L&       MATHEMATICAL SCRIPT SMALL F
+1D4BD..1D4C3  ; Common # L&   [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N
+1D4C5..1D505  ; Common # L&  [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B
+1D507..1D50A  ; Common # L&   [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G
+1D50D..1D514  ; Common # L&   [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q
+1D516..1D51C  ; Common # L&   [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y
+1D51E..1D539  ; Common # L&  [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+1D53B..1D53E  ; Common # L&   [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+1D540..1D544  ; Common # L&   [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+1D546         ; Common # L&       MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+1D54A..1D550  ; Common # L&   [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+1D552..1D6A5  ; Common # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J
+1D6A8..1D6C0  ; Common # L&  [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA
+1D6C1         ; Common # Sm       MATHEMATICAL BOLD NABLA
+1D6C2..1D6DA  ; Common # L&  [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA
+1D6DB         ; Common # Sm       MATHEMATICAL BOLD PARTIAL DIFFERENTIAL
+1D6DC..1D6FA  ; Common # L&  [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA
+1D6FB         ; Common # Sm       MATHEMATICAL ITALIC NABLA
+1D6FC..1D714  ; Common # L&  [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA
+1D715         ; Common # Sm       MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL
+1D716..1D734  ; Common # L&  [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA
+1D735         ; Common # Sm       MATHEMATICAL BOLD ITALIC NABLA
+1D736..1D74E  ; Common # L&  [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA
+1D74F         ; Common # Sm       MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL
+1D750..1D76E  ; Common # L&  [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA
+1D76F         ; Common # Sm       MATHEMATICAL SANS-SERIF BOLD NABLA
+1D770..1D788  ; Common # L&  [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA
+1D789         ; Common # Sm       MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL
+1D78A..1D7A8  ; Common # L&  [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA
+1D7A9         ; Common # Sm       MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA
+1D7AA..1D7C2  ; Common # L&  [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA
+1D7C3         ; Common # Sm       MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL
+1D7C4..1D7CB  ; Common # L&   [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA
+1D7CE..1D7FF  ; Common # Nd  [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE
+1F000..1F02B  ; Common # So  [44] MAHJONG TILE EAST WIND..MAHJONG TILE BACK
+1F030..1F093  ; Common # So [100] DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
+E0001         ; Common # Cf       LANGUAGE TAG
+E0020..E007F  ; Common # Cf  [96] TAG SPACE..CANCEL TAG
+
+# Total code points: 5178
+
+# ================================================
+
+0041..005A    ; Latin # L&  [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z
+0061..007A    ; Latin # L&  [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z
+00AA          ; Latin # L&       FEMININE ORDINAL INDICATOR
+00BA          ; Latin # L&       MASCULINE ORDINAL INDICATOR
+00C0..00D6    ; Latin # L&  [23] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER O WITH DIAERESIS
+00D8..00F6    ; Latin # L&  [31] LATIN CAPITAL LETTER O WITH STROKE..LATIN SMALL LETTER O WITH DIAERESIS
+00F8..01BA    ; Latin # L& [195] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER EZH WITH TAIL
+01BB          ; Latin # Lo       LATIN LETTER TWO WITH STROKE
+01BC..01BF    ; Latin # L&   [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN
+01C0..01C3    ; Latin # Lo   [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK
+01C4..0293    ; Latin # L& [208] LATIN CAPITAL LETTER DZ WITH CARON..LATIN SMALL LETTER EZH WITH CURL
+0294          ; Latin # Lo       LATIN LETTER GLOTTAL STOP
+0295..02AF    ; Latin # L&  [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL
+02B0..02B8    ; Latin # Lm   [9] MODIFIER LETTER SMALL H..MODIFIER LETTER SMALL Y
+02E0..02E4    ; Latin # Lm   [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP
+1D00..1D25    ; Latin # L&  [38] LATIN LETTER SMALL CAPITAL A..LATIN LETTER AIN
+1D2C..1D5C    ; Latin # Lm  [49] MODIFIER LETTER CAPITAL A..MODIFIER LETTER SMALL AIN
+1D62..1D65    ; Latin # L&   [4] LATIN SUBSCRIPT SMALL LETTER I..LATIN SUBSCRIPT SMALL LETTER V
+1D6B..1D77    ; Latin # L&  [13] LATIN SMALL LETTER UE..LATIN SMALL LETTER TURNED G
+1D79..1D9A    ; Latin # L&  [34] LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK
+1D9B..1DBE    ; Latin # Lm  [36] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL EZH
+1E00..1EFF    ; Latin # L& [256] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER Y WITH LOOP
+2071          ; Latin # L&       SUPERSCRIPT LATIN SMALL LETTER I
+207F          ; Latin # L&       SUPERSCRIPT LATIN SMALL LETTER N
+2090..2094    ; Latin # Lm   [5] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER SCHWA
+212A..212B    ; Latin # L&   [2] KELVIN SIGN..ANGSTROM SIGN
+2132          ; Latin # L&       TURNED CAPITAL F
+214E          ; Latin # L&       TURNED SMALL F
+2160..2182    ; Latin # Nl  [35] ROMAN NUMERAL ONE..ROMAN NUMERAL TEN THOUSAND
+2183..2184    ; Latin # L&   [2] ROMAN NUMERAL REVERSED ONE HUNDRED..LATIN SMALL LETTER REVERSED C
+2185..2188    ; Latin # Nl   [4] ROMAN NUMERAL SIX LATE FORM..ROMAN NUMERAL ONE HUNDRED THOUSAND
+2C60..2C6F    ; Latin # L&  [16] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN CAPITAL LETTER TURNED A
+2C71..2C7C    ; Latin # L&  [12] LATIN SMALL LETTER V WITH RIGHT HOOK..LATIN SUBSCRIPT SMALL LETTER J
+2C7D          ; Latin # Lm       MODIFIER LETTER CAPITAL V
+A722..A76F    ; Latin # L&  [78] LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF..LATIN SMALL LETTER CON
+A770          ; Latin # Lm       MODIFIER LETTER US
+A771..A787    ; Latin # L&  [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER INSULAR T
+A78B..A78C    ; Latin # L&   [2] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER SALTILLO
+A7FB..A7FF    ; Latin # Lo   [5] LATIN EPIGRAPHIC LETTER REVERSED F..LATIN EPIGRAPHIC LETTER ARCHAIC M
+FB00..FB06    ; Latin # L&   [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST
+FF21..FF3A    ; Latin # L&  [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z
+FF41..FF5A    ; Latin # L&  [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z
+
+# Total code points: 1241
+
+# ================================================
+
+0370..0373    ; Greek # L&   [4] GREEK CAPITAL LETTER HETA..GREEK SMALL LETTER ARCHAIC SAMPI
+0375          ; Greek # Sk       GREEK LOWER NUMERAL SIGN
+0376..0377    ; Greek # L&   [2] GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA..GREEK SMALL LETTER PAMPHYLIAN DIGAMMA
+037A          ; Greek # Lm       GREEK YPOGEGRAMMENI
+037B..037D    ; Greek # L&   [3] GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL
+0384          ; Greek # Sk       GREEK TONOS
+0386          ; Greek # L&       GREEK CAPITAL LETTER ALPHA WITH TONOS
+0388..038A    ; Greek # L&   [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS
+038C          ; Greek # L&       GREEK CAPITAL LETTER OMICRON WITH TONOS
+038E..03A1    ; Greek # L&  [20] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK CAPITAL LETTER RHO
+03A3..03E1    ; Greek # L&  [63] GREEK CAPITAL LETTER SIGMA..GREEK SMALL LETTER SAMPI
+03F0..03F5    ; Greek # L&   [6] GREEK KAPPA SYMBOL..GREEK LUNATE EPSILON SYMBOL
+03F6          ; Greek # Sm       GREEK REVERSED LUNATE EPSILON SYMBOL
+03F7..03FF    ; Greek # L&   [9] GREEK CAPITAL LETTER SHO..GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL
+1D26..1D2A    ; Greek # L&   [5] GREEK LETTER SMALL CAPITAL GAMMA..GREEK LETTER SMALL CAPITAL PSI
+1D5D..1D61    ; Greek # Lm   [5] MODIFIER LETTER SMALL BETA..MODIFIER LETTER SMALL CHI
+1D66..1D6A    ; Greek # L&   [5] GREEK SUBSCRIPT SMALL LETTER BETA..GREEK SUBSCRIPT SMALL LETTER CHI
+1DBF          ; Greek # Lm       MODIFIER LETTER SMALL THETA
+1F00..1F15    ; Greek # L&  [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA
+1F18..1F1D    ; Greek # L&   [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+1F20..1F45    ; Greek # L&  [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA
+1F48..1F4D    ; Greek # L&   [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+1F50..1F57    ; Greek # L&   [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F59          ; Greek # L&       GREEK CAPITAL LETTER UPSILON WITH DASIA
+1F5B          ; Greek # L&       GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+1F5D          ; Greek # L&       GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+1F5F..1F7D    ; Greek # L&  [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA
+1F80..1FB4    ; Greek # L&  [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+1FB6..1FBC    ; Greek # L&   [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FBD          ; Greek # Sk       GREEK KORONIS
+1FBE          ; Greek # L&       GREEK PROSGEGRAMMENI
+1FBF..1FC1    ; Greek # Sk   [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI
+1FC2..1FC4    ; Greek # L&   [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+1FC6..1FCC    ; Greek # L&   [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FCD..1FCF    ; Greek # Sk   [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI
+1FD0..1FD3    ; Greek # L&   [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FD6..1FDB    ; Greek # L&   [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA
+1FDD..1FDF    ; Greek # Sk   [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI
+1FE0..1FEC    ; Greek # L&  [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA
+1FED..1FEF    ; Greek # Sk   [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA
+1FF2..1FF4    ; Greek # L&   [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+1FF6..1FFC    ; Greek # L&   [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+1FFD..1FFE    ; Greek # Sk   [2] GREEK OXIA..GREEK DASIA
+2126          ; Greek # L&       OHM SIGN
+10140..10174  ; Greek # Nl  [53] GREEK ACROPHONIC ATTIC ONE QUARTER..GREEK ACROPHONIC STRATIAN FIFTY MNAS
+10175..10178  ; Greek # No   [4] GREEK ONE HALF SIGN..GREEK THREE QUARTERS SIGN
+10179..10189  ; Greek # So  [17] GREEK YEAR SIGN..GREEK TRYBLION BASE SIGN
+1018A         ; Greek # No       GREEK ZERO SIGN
+1D200..1D241  ; Greek # So  [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54
+1D242..1D244  ; Greek # Mn   [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+1D245         ; Greek # So       GREEK MUSICAL LEIMMA
+
+# Total code points: 511
+
+# ================================================
+
+0400..0481    ; Cyrillic # L& [130] CYRILLIC CAPITAL LETTER IE WITH GRAVE..CYRILLIC SMALL LETTER KOPPA
+0482          ; Cyrillic # So       CYRILLIC THOUSANDS SIGN
+0483..0487    ; Cyrillic # Mn   [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE
+0488..0489    ; Cyrillic # Me   [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN
+048A..0523    ; Cyrillic # L& [154] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER EN WITH MIDDLE HOOK
+1D2B          ; Cyrillic # L&       CYRILLIC LETTER SMALL CAPITAL EL
+1D78          ; Cyrillic # Lm       MODIFIER LETTER CYRILLIC EN
+2DE0..2DFF    ; Cyrillic # Mn  [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS
+A640..A65F    ; Cyrillic # L&  [32] CYRILLIC CAPITAL LETTER ZEMLYA..CYRILLIC SMALL LETTER YN
+A662..A66D    ; Cyrillic # L&  [12] CYRILLIC CAPITAL LETTER SOFT DE..CYRILLIC SMALL LETTER DOUBLE MONOCULAR O
+A66E          ; Cyrillic # Lo       CYRILLIC LETTER MULTIOCULAR O
+A66F          ; Cyrillic # Mn       COMBINING CYRILLIC VZMET
+A670..A672    ; Cyrillic # Me   [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN
+A673          ; Cyrillic # Po       SLAVONIC ASTERISK
+A67C..A67D    ; Cyrillic # Mn   [2] COMBINING CYRILLIC KAVYKA..COMBINING CYRILLIC PAYEROK
+A67E          ; Cyrillic # Po       CYRILLIC KAVYKA
+A67F          ; Cyrillic # Lm       CYRILLIC PAYEROK
+A680..A697    ; Cyrillic # L&  [24] CYRILLIC CAPITAL LETTER DWE..CYRILLIC SMALL LETTER SHWE
+
+# Total code points: 404
+
+# ================================================
+
+0531..0556    ; Armenian # L&  [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH
+0559          ; Armenian # Lm       ARMENIAN MODIFIER LETTER LEFT HALF RING
+055A..055F    ; Armenian # Po   [6] ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK
+0561..0587    ; Armenian # L&  [39] ARMENIAN SMALL LETTER AYB..ARMENIAN SMALL LIGATURE ECH YIWN
+058A          ; Armenian # Pd       ARMENIAN HYPHEN
+FB13..FB17    ; Armenian # L&   [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH
+
+# Total code points: 90
+
+# ================================================
+
+0591..05BD    ; Hebrew # Mn  [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
+05BE          ; Hebrew # Pd       HEBREW PUNCTUATION MAQAF
+05BF          ; Hebrew # Mn       HEBREW POINT RAFE
+05C0          ; Hebrew # Po       HEBREW PUNCTUATION PASEQ
+05C1..05C2    ; Hebrew # Mn   [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
+05C3          ; Hebrew # Po       HEBREW PUNCTUATION SOF PASUQ
+05C4..05C5    ; Hebrew # Mn   [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
+05C6          ; Hebrew # Po       HEBREW PUNCTUATION NUN HAFUKHA
+05C7          ; Hebrew # Mn       HEBREW POINT QAMATS QATAN
+05D0..05EA    ; Hebrew # Lo  [27] HEBREW LETTER ALEF..HEBREW LETTER TAV
+05F0..05F2    ; Hebrew # Lo   [3] HEBREW LIGATURE YIDDISH DOUBLE VAV..HEBREW LIGATURE YIDDISH DOUBLE YOD
+05F3..05F4    ; Hebrew # Po   [2] HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATION GERSHAYIM
+FB1D          ; Hebrew # Lo       HEBREW LETTER YOD WITH HIRIQ
+FB1E          ; Hebrew # Mn       HEBREW POINT JUDEO-SPANISH VARIKA
+FB1F..FB28    ; Hebrew # Lo  [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV
+FB29          ; Hebrew # Sm       HEBREW LETTER ALTERNATIVE PLUS SIGN
+FB2A..FB36    ; Hebrew # Lo  [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH
+FB38..FB3C    ; Hebrew # Lo   [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH
+FB3E          ; Hebrew # Lo       HEBREW LETTER MEM WITH DAGESH
+FB40..FB41    ; Hebrew # Lo   [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH
+FB43..FB44    ; Hebrew # Lo   [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH
+FB46..FB4F    ; Hebrew # Lo  [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED
+
+# Total code points: 133
+
+# ================================================
+
+0606..0608    ; Arabic # Sm   [3] ARABIC-INDIC CUBE ROOT..ARABIC RAY
+0609..060A    ; Arabic # Po   [2] ARABIC-INDIC PER MILLE SIGN..ARABIC-INDIC PER TEN THOUSAND SIGN
+060B          ; Arabic # Sc       AFGHANI SIGN
+060D          ; Arabic # Po       ARABIC DATE SEPARATOR
+060E..060F    ; Arabic # So   [2] ARABIC POETIC VERSE SIGN..ARABIC SIGN MISRA
+0610..061A    ; Arabic # Mn  [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
+061E          ; Arabic # Po       ARABIC TRIPLE DOT PUNCTUATION MARK
+0621..063F    ; Arabic # Lo  [31] ARABIC LETTER HAMZA..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE
+0641..064A    ; Arabic # Lo  [10] ARABIC LETTER FEH..ARABIC LETTER YEH
+0656..065E    ; Arabic # Mn   [9] ARABIC SUBSCRIPT ALEF..ARABIC FATHA WITH TWO DOTS
+066A..066D    ; Arabic # Po   [4] ARABIC PERCENT SIGN..ARABIC FIVE POINTED STAR
+066E..066F    ; Arabic # Lo   [2] ARABIC LETTER DOTLESS BEH..ARABIC LETTER DOTLESS QAF
+0671..06D3    ; Arabic # Lo  [99] ARABIC LETTER ALEF WASLA..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE
+06D4          ; Arabic # Po       ARABIC FULL STOP
+06D5          ; Arabic # Lo       ARABIC LETTER AE
+06D6..06DC    ; Arabic # Mn   [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN
+06DE          ; Arabic # Me       ARABIC START OF RUB EL HIZB
+06DF..06E4    ; Arabic # Mn   [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA
+06E5..06E6    ; Arabic # Lm   [2] ARABIC SMALL WAW..ARABIC SMALL YEH
+06E7..06E8    ; Arabic # Mn   [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON
+06E9          ; Arabic # So       ARABIC PLACE OF SAJDAH
+06EA..06ED    ; Arabic # Mn   [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM
+06EE..06EF    ; Arabic # Lo   [2] ARABIC LETTER DAL WITH INVERTED V..ARABIC LETTER REH WITH INVERTED V
+06F0..06F9    ; Arabic # Nd  [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE
+06FA..06FC    ; Arabic # Lo   [3] ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC LETTER GHAIN WITH DOT BELOW
+06FD..06FE    ; Arabic # So   [2] ARABIC SIGN SINDHI AMPERSAND..ARABIC SIGN SINDHI POSTPOSITION MEN
+06FF          ; Arabic # Lo       ARABIC LETTER HEH WITH INVERTED V
+0750..077F    ; Arabic # Lo  [48] ARABIC LETTER BEH WITH THREE DOTS HORIZONTALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS ABOVE
+FB50..FBB1    ; Arabic # Lo  [98] ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM
+FBD3..FD3D    ; Arabic # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM
+FD50..FD8F    ; Arabic # Lo  [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM
+FD92..FDC7    ; Arabic # Lo  [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM
+FDF0..FDFB    ; Arabic # Lo  [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU
+FDFC          ; Arabic # Sc       RIAL SIGN
+FE70..FE74    ; Arabic # Lo   [5] ARABIC FATHATAN ISOLATED FORM..ARABIC KASRATAN ISOLATED FORM
+FE76..FEFC    ; Arabic # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+
+# Total code points: 999
+
+# ================================================
+
+0700..070D    ; Syriac # Po  [14] SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN ASTERISCUS
+070F          ; Syriac # Cf       SYRIAC ABBREVIATION MARK
+0710          ; Syriac # Lo       SYRIAC LETTER ALAPH
+0711          ; Syriac # Mn       SYRIAC LETTER SUPERSCRIPT ALAPH
+0712..072F    ; Syriac # Lo  [30] SYRIAC LETTER BETH..SYRIAC LETTER PERSIAN DHALATH
+0730..074A    ; Syriac # Mn  [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH
+074D..074F    ; Syriac # Lo   [3] SYRIAC LETTER SOGDIAN ZHAIN..SYRIAC LETTER SOGDIAN FE
+
+# Total code points: 77
+
+# ================================================
+
+0780..07A5    ; Thaana # Lo  [38] THAANA LETTER HAA..THAANA LETTER WAAVU
+07A6..07B0    ; Thaana # Mn  [11] THAANA ABAFILI..THAANA SUKUN
+07B1          ; Thaana # Lo       THAANA LETTER NAA
+
+# Total code points: 50
+
+# ================================================
+
+0901..0902    ; Devanagari # Mn   [2] DEVANAGARI SIGN CANDRABINDU..DEVANAGARI SIGN ANUSVARA
+0903          ; Devanagari # Mc       DEVANAGARI SIGN VISARGA
+0904..0939    ; Devanagari # Lo  [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA
+093C          ; Devanagari # Mn       DEVANAGARI SIGN NUKTA
+093D          ; Devanagari # Lo       DEVANAGARI SIGN AVAGRAHA
+093E..0940    ; Devanagari # Mc   [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II
+0941..0948    ; Devanagari # Mn   [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI
+0949..094C    ; Devanagari # Mc   [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU
+094D          ; Devanagari # Mn       DEVANAGARI SIGN VIRAMA
+0950          ; Devanagari # Lo       DEVANAGARI OM
+0953..0954    ; Devanagari # Mn   [2] DEVANAGARI GRAVE ACCENT..DEVANAGARI ACUTE ACCENT
+0958..0961    ; Devanagari # Lo  [10] DEVANAGARI LETTER QA..DEVANAGARI LETTER VOCALIC LL
+0962..0963    ; Devanagari # Mn   [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL
+0966..096F    ; Devanagari # Nd  [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE
+0971          ; Devanagari # Lm       DEVANAGARI SIGN HIGH SPACING DOT
+0972          ; Devanagari # Lo       DEVANAGARI LETTER CANDRA A
+097B..097F    ; Devanagari # Lo   [5] DEVANAGARI LETTER GGA..DEVANAGARI LETTER BBA
+
+# Total code points: 107
+
+# ================================================
+
+0981          ; Bengali # Mn       BENGALI SIGN CANDRABINDU
+0982..0983    ; Bengali # Mc   [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA
+0985..098C    ; Bengali # Lo   [8] BENGALI LETTER A..BENGALI LETTER VOCALIC L
+098F..0990    ; Bengali # Lo   [2] BENGALI LETTER E..BENGALI LETTER AI
+0993..09A8    ; Bengali # Lo  [22] BENGALI LETTER O..BENGALI LETTER NA
+09AA..09B0    ; Bengali # Lo   [7] BENGALI LETTER PA..BENGALI LETTER RA
+09B2          ; Bengali # Lo       BENGALI LETTER LA
+09B6..09B9    ; Bengali # Lo   [4] BENGALI LETTER SHA..BENGALI LETTER HA
+09BC          ; Bengali # Mn       BENGALI SIGN NUKTA
+09BD          ; Bengali # Lo       BENGALI SIGN AVAGRAHA
+09BE..09C0    ; Bengali # Mc   [3] BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN II
+09C1..09C4    ; Bengali # Mn   [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR
+09C7..09C8    ; Bengali # Mc   [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI
+09CB..09CC    ; Bengali # Mc   [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+09CD          ; Bengali # Mn       BENGALI SIGN VIRAMA
+09CE          ; Bengali # Lo       BENGALI LETTER KHANDA TA
+09D7          ; Bengali # Mc       BENGALI AU LENGTH MARK
+09DC..09DD    ; Bengali # Lo   [2] BENGALI LETTER RRA..BENGALI LETTER RHA
+09DF..09E1    ; Bengali # Lo   [3] BENGALI LETTER YYA..BENGALI LETTER VOCALIC LL
+09E2..09E3    ; Bengali # Mn   [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL
+09E6..09EF    ; Bengali # Nd  [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE
+09F0..09F1    ; Bengali # Lo   [2] BENGALI LETTER RA WITH MIDDLE DIAGONAL..BENGALI LETTER RA WITH LOWER DIAGONAL
+09F2..09F3    ; Bengali # Sc   [2] BENGALI RUPEE MARK..BENGALI RUPEE SIGN
+09F4..09F9    ; Bengali # No   [6] BENGALI CURRENCY NUMERATOR ONE..BENGALI CURRENCY DENOMINATOR SIXTEEN
+09FA          ; Bengali # So       BENGALI ISSHAR
+
+# Total code points: 91
+
+# ================================================
+
+0A01..0A02    ; Gurmukhi # Mn   [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI
+0A03          ; Gurmukhi # Mc       GURMUKHI SIGN VISARGA
+0A05..0A0A    ; Gurmukhi # Lo   [6] GURMUKHI LETTER A..GURMUKHI LETTER UU
+0A0F..0A10    ; Gurmukhi # Lo   [2] GURMUKHI LETTER EE..GURMUKHI LETTER AI
+0A13..0A28    ; Gurmukhi # Lo  [22] GURMUKHI LETTER OO..GURMUKHI LETTER NA
+0A2A..0A30    ; Gurmukhi # Lo   [7] GURMUKHI LETTER PA..GURMUKHI LETTER RA
+0A32..0A33    ; Gurmukhi # Lo   [2] GURMUKHI LETTER LA..GURMUKHI LETTER LLA
+0A35..0A36    ; Gurmukhi # Lo   [2] GURMUKHI LETTER VA..GURMUKHI LETTER SHA
+0A38..0A39    ; Gurmukhi # Lo   [2] GURMUKHI LETTER SA..GURMUKHI LETTER HA
+0A3C          ; Gurmukhi # Mn       GURMUKHI SIGN NUKTA
+0A3E..0A40    ; Gurmukhi # Mc   [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II
+0A41..0A42    ; Gurmukhi # Mn   [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU
+0A47..0A48    ; Gurmukhi # Mn   [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI
+0A4B..0A4D    ; Gurmukhi # Mn   [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA
+0A51          ; Gurmukhi # Mn       GURMUKHI SIGN UDAAT
+0A59..0A5C    ; Gurmukhi # Lo   [4] GURMUKHI LETTER KHHA..GURMUKHI LETTER RRA
+0A5E          ; Gurmukhi # Lo       GURMUKHI LETTER FA
+0A66..0A6F    ; Gurmukhi # Nd  [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE
+0A70..0A71    ; Gurmukhi # Mn   [2] GURMUKHI TIPPI..GURMUKHI ADDAK
+0A72..0A74    ; Gurmukhi # Lo   [3] GURMUKHI IRI..GURMUKHI EK ONKAR
+0A75          ; Gurmukhi # Mn       GURMUKHI SIGN YAKASH
+
+# Total code points: 79
+
+# ================================================
+
+0A81..0A82    ; Gujarati # Mn   [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA
+0A83          ; Gujarati # Mc       GUJARATI SIGN VISARGA
+0A85..0A8D    ; Gujarati # Lo   [9] GUJARATI LETTER A..GUJARATI VOWEL CANDRA E
+0A8F..0A91    ; Gujarati # Lo   [3] GUJARATI LETTER E..GUJARATI VOWEL CANDRA O
+0A93..0AA8    ; Gujarati # Lo  [22] GUJARATI LETTER O..GUJARATI LETTER NA
+0AAA..0AB0    ; Gujarati # Lo   [7] GUJARATI LETTER PA..GUJARATI LETTER RA
+0AB2..0AB3    ; Gujarati # Lo   [2] GUJARATI LETTER LA..GUJARATI LETTER LLA
+0AB5..0AB9    ; Gujarati # Lo   [5] GUJARATI LETTER VA..GUJARATI LETTER HA
+0ABC          ; Gujarati # Mn       GUJARATI SIGN NUKTA
+0ABD          ; Gujarati # Lo       GUJARATI SIGN AVAGRAHA
+0ABE..0AC0    ; Gujarati # Mc   [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II
+0AC1..0AC5    ; Gujarati # Mn   [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E
+0AC7..0AC8    ; Gujarati # Mn   [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI
+0AC9          ; Gujarati # Mc       GUJARATI VOWEL SIGN CANDRA O
+0ACB..0ACC    ; Gujarati # Mc   [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU
+0ACD          ; Gujarati # Mn       GUJARATI SIGN VIRAMA
+0AD0          ; Gujarati # Lo       GUJARATI OM
+0AE0..0AE1    ; Gujarati # Lo   [2] GUJARATI LETTER VOCALIC RR..GUJARATI LETTER VOCALIC LL
+0AE2..0AE3    ; Gujarati # Mn   [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL
+0AE6..0AEF    ; Gujarati # Nd  [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE
+0AF1          ; Gujarati # Sc       GUJARATI RUPEE SIGN
+
+# Total code points: 83
+
+# ================================================
+
+0B01          ; Oriya # Mn       ORIYA SIGN CANDRABINDU
+0B02..0B03    ; Oriya # Mc   [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA
+0B05..0B0C    ; Oriya # Lo   [8] ORIYA LETTER A..ORIYA LETTER VOCALIC L
+0B0F..0B10    ; Oriya # Lo   [2] ORIYA LETTER E..ORIYA LETTER AI
+0B13..0B28    ; Oriya # Lo  [22] ORIYA LETTER O..ORIYA LETTER NA
+0B2A..0B30    ; Oriya # Lo   [7] ORIYA LETTER PA..ORIYA LETTER RA
+0B32..0B33    ; Oriya # Lo   [2] ORIYA LETTER LA..ORIYA LETTER LLA
+0B35..0B39    ; Oriya # Lo   [5] ORIYA LETTER VA..ORIYA LETTER HA
+0B3C          ; Oriya # Mn       ORIYA SIGN NUKTA
+0B3D          ; Oriya # Lo       ORIYA SIGN AVAGRAHA
+0B3E          ; Oriya # Mc       ORIYA VOWEL SIGN AA
+0B3F          ; Oriya # Mn       ORIYA VOWEL SIGN I
+0B40          ; Oriya # Mc       ORIYA VOWEL SIGN II
+0B41..0B44    ; Oriya # Mn   [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
+0B47..0B48    ; Oriya # Mc   [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
+0B4B..0B4C    ; Oriya # Mc   [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+0B4D          ; Oriya # Mn       ORIYA SIGN VIRAMA
+0B56          ; Oriya # Mn       ORIYA AI LENGTH MARK
+0B57          ; Oriya # Mc       ORIYA AU LENGTH MARK
+0B5C..0B5D    ; Oriya # Lo   [2] ORIYA LETTER RRA..ORIYA LETTER RHA
+0B5F..0B61    ; Oriya # Lo   [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL
+0B62..0B63    ; Oriya # Mn   [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
+0B66..0B6F    ; Oriya # Nd  [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE
+0B70          ; Oriya # So       ORIYA ISSHAR
+0B71          ; Oriya # Lo       ORIYA LETTER WA
+
+# Total code points: 84
+
+# ================================================
+
+0B82          ; Tamil # Mn       TAMIL SIGN ANUSVARA
+0B83          ; Tamil # Lo       TAMIL SIGN VISARGA
+0B85..0B8A    ; Tamil # Lo   [6] TAMIL LETTER A..TAMIL LETTER UU
+0B8E..0B90    ; Tamil # Lo   [3] TAMIL LETTER E..TAMIL LETTER AI
+0B92..0B95    ; Tamil # Lo   [4] TAMIL LETTER O..TAMIL LETTER KA
+0B99..0B9A    ; Tamil # Lo   [2] TAMIL LETTER NGA..TAMIL LETTER CA
+0B9C          ; Tamil # Lo       TAMIL LETTER JA
+0B9E..0B9F    ; Tamil # Lo   [2] TAMIL LETTER NYA..TAMIL LETTER TTA
+0BA3..0BA4    ; Tamil # Lo   [2] TAMIL LETTER NNA..TAMIL LETTER TA
+0BA8..0BAA    ; Tamil # Lo   [3] TAMIL LETTER NA..TAMIL LETTER PA
+0BAE..0BB9    ; Tamil # Lo  [12] TAMIL LETTER MA..TAMIL LETTER HA
+0BBE..0BBF    ; Tamil # Mc   [2] TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN I
+0BC0          ; Tamil # Mn       TAMIL VOWEL SIGN II
+0BC1..0BC2    ; Tamil # Mc   [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU
+0BC6..0BC8    ; Tamil # Mc   [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI
+0BCA..0BCC    ; Tamil # Mc   [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+0BCD          ; Tamil # Mn       TAMIL SIGN VIRAMA
+0BD0          ; Tamil # Lo       TAMIL OM
+0BD7          ; Tamil # Mc       TAMIL AU LENGTH MARK
+0BE6..0BEF    ; Tamil # Nd  [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE
+0BF0..0BF2    ; Tamil # No   [3] TAMIL NUMBER TEN..TAMIL NUMBER ONE THOUSAND
+0BF3..0BF8    ; Tamil # So   [6] TAMIL DAY SIGN..TAMIL AS ABOVE SIGN
+0BF9          ; Tamil # Sc       TAMIL RUPEE SIGN
+0BFA          ; Tamil # So       TAMIL NUMBER SIGN
+
+# Total code points: 72
+
+# ================================================
+
+0C01..0C03    ; Telugu # Mc   [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA
+0C05..0C0C    ; Telugu # Lo   [8] TELUGU LETTER A..TELUGU LETTER VOCALIC L
+0C0E..0C10    ; Telugu # Lo   [3] TELUGU LETTER E..TELUGU LETTER AI
+0C12..0C28    ; Telugu # Lo  [23] TELUGU LETTER O..TELUGU LETTER NA
+0C2A..0C33    ; Telugu # Lo  [10] TELUGU LETTER PA..TELUGU LETTER LLA
+0C35..0C39    ; Telugu # Lo   [5] TELUGU LETTER VA..TELUGU LETTER HA
+0C3D          ; Telugu # Lo       TELUGU SIGN AVAGRAHA
+0C3E..0C40    ; Telugu # Mn   [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II
+0C41..0C44    ; Telugu # Mc   [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR
+0C46..0C48    ; Telugu # Mn   [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI
+0C4A..0C4D    ; Telugu # Mn   [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
+0C55..0C56    ; Telugu # Mn   [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
+0C58..0C59    ; Telugu # Lo   [2] TELUGU LETTER TSA..TELUGU LETTER DZA
+0C60..0C61    ; Telugu # Lo   [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL
+0C62..0C63    ; Telugu # Mn   [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
+0C66..0C6F    ; Telugu # Nd  [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE
+0C78..0C7E    ; Telugu # No   [7] TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR..TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR
+0C7F          ; Telugu # So       TELUGU SIGN TUUMU
+
+# Total code points: 93
+
+# ================================================
+
+0C82..0C83    ; Kannada # Mc   [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA
+0C85..0C8C    ; Kannada # Lo   [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L
+0C8E..0C90    ; Kannada # Lo   [3] KANNADA LETTER E..KANNADA LETTER AI
+0C92..0CA8    ; Kannada # Lo  [23] KANNADA LETTER O..KANNADA LETTER NA
+0CAA..0CB3    ; Kannada # Lo  [10] KANNADA LETTER PA..KANNADA LETTER LLA
+0CB5..0CB9    ; Kannada # Lo   [5] KANNADA LETTER VA..KANNADA LETTER HA
+0CBC          ; Kannada # Mn       KANNADA SIGN NUKTA
+0CBD          ; Kannada # Lo       KANNADA SIGN AVAGRAHA
+0CBE          ; Kannada # Mc       KANNADA VOWEL SIGN AA
+0CBF          ; Kannada # Mn       KANNADA VOWEL SIGN I
+0CC0..0CC4    ; Kannada # Mc   [5] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN VOCALIC RR
+0CC6          ; Kannada # Mn       KANNADA VOWEL SIGN E
+0CC7..0CC8    ; Kannada # Mc   [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+0CCA..0CCB    ; Kannada # Mc   [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+0CCC..0CCD    ; Kannada # Mn   [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA
+0CD5..0CD6    ; Kannada # Mc   [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+0CDE          ; Kannada # Lo       KANNADA LETTER FA
+0CE0..0CE1    ; Kannada # Lo   [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL
+0CE2..0CE3    ; Kannada # Mn   [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
+0CE6..0CEF    ; Kannada # Nd  [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
+
+# Total code points: 84
+
+# ================================================
+
+0D02..0D03    ; Malayalam # Mc   [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA
+0D05..0D0C    ; Malayalam # Lo   [8] MALAYALAM LETTER A..MALAYALAM LETTER VOCALIC L
+0D0E..0D10    ; Malayalam # Lo   [3] MALAYALAM LETTER E..MALAYALAM LETTER AI
+0D12..0D28    ; Malayalam # Lo  [23] MALAYALAM LETTER O..MALAYALAM LETTER NA
+0D2A..0D39    ; Malayalam # Lo  [16] MALAYALAM LETTER PA..MALAYALAM LETTER HA
+0D3D          ; Malayalam # Lo       MALAYALAM SIGN AVAGRAHA
+0D3E..0D40    ; Malayalam # Mc   [3] MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN II
+0D41..0D44    ; Malayalam # Mn   [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR
+0D46..0D48    ; Malayalam # Mc   [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI
+0D4A..0D4C    ; Malayalam # Mc   [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+0D4D          ; Malayalam # Mn       MALAYALAM SIGN VIRAMA
+0D57          ; Malayalam # Mc       MALAYALAM AU LENGTH MARK
+0D60..0D61    ; Malayalam # Lo   [2] MALAYALAM LETTER VOCALIC RR..MALAYALAM LETTER VOCALIC LL
+0D62..0D63    ; Malayalam # Mn   [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL
+0D66..0D6F    ; Malayalam # Nd  [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE
+0D70..0D75    ; Malayalam # No   [6] MALAYALAM NUMBER TEN..MALAYALAM FRACTION THREE QUARTERS
+0D79          ; Malayalam # So       MALAYALAM DATE MARK
+0D7A..0D7F    ; Malayalam # Lo   [6] MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER CHILLU K
+
+# Total code points: 95
+
+# ================================================
+
+0D82..0D83    ; Sinhala # Mc   [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA
+0D85..0D96    ; Sinhala # Lo  [18] SINHALA LETTER AYANNA..SINHALA LETTER AUYANNA
+0D9A..0DB1    ; Sinhala # Lo  [24] SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA LETTER DANTAJA NAYANNA
+0DB3..0DBB    ; Sinhala # Lo   [9] SINHALA LETTER SANYAKA DAYANNA..SINHALA LETTER RAYANNA
+0DBD          ; Sinhala # Lo       SINHALA LETTER DANTAJA LAYANNA
+0DC0..0DC6    ; Sinhala # Lo   [7] SINHALA LETTER VAYANNA..SINHALA LETTER FAYANNA
+0DCA          ; Sinhala # Mn       SINHALA SIGN AL-LAKUNA
+0DCF..0DD1    ; Sinhala # Mc   [3] SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA
+0DD2..0DD4    ; Sinhala # Mn   [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA
+0DD6          ; Sinhala # Mn       SINHALA VOWEL SIGN DIGA PAA-PILLA
+0DD8..0DDF    ; Sinhala # Mc   [8] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA
+0DF2..0DF3    ; Sinhala # Mc   [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA
+0DF4          ; Sinhala # Po       SINHALA PUNCTUATION KUNDDALIYA
+
+# Total code points: 80
+
+# ================================================
+
+0E01..0E30    ; Thai # Lo  [48] THAI CHARACTER KO KAI..THAI CHARACTER SARA A
+0E31          ; Thai # Mn       THAI CHARACTER MAI HAN-AKAT
+0E32..0E33    ; Thai # Lo   [2] THAI CHARACTER SARA AA..THAI CHARACTER SARA AM
+0E34..0E3A    ; Thai # Mn   [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU
+0E40..0E45    ; Thai # Lo   [6] THAI CHARACTER SARA E..THAI CHARACTER LAKKHANGYAO
+0E46          ; Thai # Lm       THAI CHARACTER MAIYAMOK
+0E47..0E4E    ; Thai # Mn   [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN
+0E4F          ; Thai # Po       THAI CHARACTER FONGMAN
+0E50..0E59    ; Thai # Nd  [10] THAI DIGIT ZERO..THAI DIGIT NINE
+0E5A..0E5B    ; Thai # Po   [2] THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT
+
+# Total code points: 86
+
+# ================================================
+
+0E81..0E82    ; Lao # Lo   [2] LAO LETTER KO..LAO LETTER KHO SUNG
+0E84          ; Lao # Lo       LAO LETTER KHO TAM
+0E87..0E88    ; Lao # Lo   [2] LAO LETTER NGO..LAO LETTER CO
+0E8A          ; Lao # Lo       LAO LETTER SO TAM
+0E8D          ; Lao # Lo       LAO LETTER NYO
+0E94..0E97    ; Lao # Lo   [4] LAO LETTER DO..LAO LETTER THO TAM
+0E99..0E9F    ; Lao # Lo   [7] LAO LETTER NO..LAO LETTER FO SUNG
+0EA1..0EA3    ; Lao # Lo   [3] LAO LETTER MO..LAO LETTER LO LING
+0EA5          ; Lao # Lo       LAO LETTER LO LOOT
+0EA7          ; Lao # Lo       LAO LETTER WO
+0EAA..0EAB    ; Lao # Lo   [2] LAO LETTER SO SUNG..LAO LETTER HO SUNG
+0EAD..0EB0    ; Lao # Lo   [4] LAO LETTER O..LAO VOWEL SIGN A
+0EB1          ; Lao # Mn       LAO VOWEL SIGN MAI KAN
+0EB2..0EB3    ; Lao # Lo   [2] LAO VOWEL SIGN AA..LAO VOWEL SIGN AM
+0EB4..0EB9    ; Lao # Mn   [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU
+0EBB..0EBC    ; Lao # Mn   [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO
+0EBD          ; Lao # Lo       LAO SEMIVOWEL SIGN NYO
+0EC0..0EC4    ; Lao # Lo   [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI
+0EC6          ; Lao # Lm       LAO KO LA
+0EC8..0ECD    ; Lao # Mn   [6] LAO TONE MAI EK..LAO NIGGAHITA
+0ED0..0ED9    ; Lao # Nd  [10] LAO DIGIT ZERO..LAO DIGIT NINE
+0EDC..0EDD    ; Lao # Lo   [2] LAO HO NO..LAO HO MO
+
+# Total code points: 65
+
+# ================================================
+
+0F00          ; Tibetan # Lo       TIBETAN SYLLABLE OM
+0F01..0F03    ; Tibetan # So   [3] TIBETAN MARK GTER YIG MGO TRUNCATED A..TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA
+0F04..0F12    ; Tibetan # Po  [15] TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK RGYA GRAM SHAD
+0F13..0F17    ; Tibetan # So   [5] TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN..TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS
+0F18..0F19    ; Tibetan # Mn   [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
+0F1A..0F1F    ; Tibetan # So   [6] TIBETAN SIGN RDEL DKAR GCIG..TIBETAN SIGN RDEL DKAR RDEL NAG
+0F20..0F29    ; Tibetan # Nd  [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE
+0F2A..0F33    ; Tibetan # No  [10] TIBETAN DIGIT HALF ONE..TIBETAN DIGIT HALF ZERO
+0F34          ; Tibetan # So       TIBETAN MARK BSDUS RTAGS
+0F35          ; Tibetan # Mn       TIBETAN MARK NGAS BZUNG NYI ZLA
+0F36          ; Tibetan # So       TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN
+0F37          ; Tibetan # Mn       TIBETAN MARK NGAS BZUNG SGOR RTAGS
+0F38          ; Tibetan # So       TIBETAN MARK CHE MGO
+0F39          ; Tibetan # Mn       TIBETAN MARK TSA -PHRU
+0F3A          ; Tibetan # Ps       TIBETAN MARK GUG RTAGS GYON
+0F3B          ; Tibetan # Pe       TIBETAN MARK GUG RTAGS GYAS
+0F3C          ; Tibetan # Ps       TIBETAN MARK ANG KHANG GYON
+0F3D          ; Tibetan # Pe       TIBETAN MARK ANG KHANG GYAS
+0F3E..0F3F    ; Tibetan # Mc   [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES
+0F40..0F47    ; Tibetan # Lo   [8] TIBETAN LETTER KA..TIBETAN LETTER JA
+0F49..0F6C    ; Tibetan # Lo  [36] TIBETAN LETTER NYA..TIBETAN LETTER RRA
+0F71..0F7E    ; Tibetan # Mn  [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO
+0F7F          ; Tibetan # Mc       TIBETAN SIGN RNAM BCAD
+0F80..0F84    ; Tibetan # Mn   [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA
+0F85          ; Tibetan # Po       TIBETAN MARK PALUTA
+0F86..0F87    ; Tibetan # Mn   [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS
+0F88..0F8B    ; Tibetan # Lo   [4] TIBETAN SIGN LCE TSA CAN..TIBETAN SIGN GRU MED RGYINGS
+0F90..0F97    ; Tibetan # Mn   [8] TIBETAN SUBJOINED LETTER KA..TIBETAN SUBJOINED LETTER JA
+0F99..0FBC    ; Tibetan # Mn  [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA
+0FBE..0FC5    ; Tibetan # So   [8] TIBETAN KU RU KHA..TIBETAN SYMBOL RDO RJE
+0FC6          ; Tibetan # Mn       TIBETAN SYMBOL PADMA GDAN
+0FC7..0FCC    ; Tibetan # So   [6] TIBETAN SYMBOL RDO RJE RGYA GRAM..TIBETAN SYMBOL NOR BU BZHI -KHYIL
+0FCE..0FCF    ; Tibetan # So   [2] TIBETAN SIGN RDEL NAG RDEL DKAR..TIBETAN SIGN RDEL NAG GSUM
+0FD0..0FD4    ; Tibetan # Po   [5] TIBETAN MARK BSKA- SHOG GI MGO RGYAN..TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA
+
+# Total code points: 201
+
+# ================================================
+
+1000..102A    ; Myanmar # Lo  [43] MYANMAR LETTER KA..MYANMAR LETTER AU
+102B..102C    ; Myanmar # Mc   [2] MYANMAR VOWEL SIGN TALL AA..MYANMAR VOWEL SIGN AA
+102D..1030    ; Myanmar # Mn   [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU
+1031          ; Myanmar # Mc       MYANMAR VOWEL SIGN E
+1032..1037    ; Myanmar # Mn   [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW
+1038          ; Myanmar # Mc       MYANMAR SIGN VISARGA
+1039..103A    ; Myanmar # Mn   [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+103B..103C    ; Myanmar # Mc   [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA
+103D..103E    ; Myanmar # Mn   [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA
+103F          ; Myanmar # Lo       MYANMAR LETTER GREAT SA
+1040..1049    ; Myanmar # Nd  [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE
+104A..104F    ; Myanmar # Po   [6] MYANMAR SIGN LITTLE SECTION..MYANMAR SYMBOL GENITIVE
+1050..1055    ; Myanmar # Lo   [6] MYANMAR LETTER SHA..MYANMAR LETTER VOCALIC LL
+1056..1057    ; Myanmar # Mc   [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR
+1058..1059    ; Myanmar # Mn   [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
+105A..105D    ; Myanmar # Lo   [4] MYANMAR LETTER MON NGA..MYANMAR LETTER MON BBE
+105E..1060    ; Myanmar # Mn   [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
+1061          ; Myanmar # Lo       MYANMAR LETTER SGAW KAREN SHA
+1062..1064    ; Myanmar # Mc   [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO
+1065..1066    ; Myanmar # Lo   [2] MYANMAR LETTER WESTERN PWO KAREN THA..MYANMAR LETTER WESTERN PWO KAREN PWA
+1067..106D    ; Myanmar # Mc   [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5
+106E..1070    ; Myanmar # Lo   [3] MYANMAR LETTER EASTERN PWO KAREN NNA..MYANMAR LETTER EASTERN PWO KAREN GHWA
+1071..1074    ; Myanmar # Mn   [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE
+1075..1081    ; Myanmar # Lo  [13] MYANMAR LETTER SHAN KA..MYANMAR LETTER SHAN HA
+1082          ; Myanmar # Mn       MYANMAR CONSONANT SIGN SHAN MEDIAL WA
+1083..1084    ; Myanmar # Mc   [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E
+1085..1086    ; Myanmar # Mn   [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y
+1087..108C    ; Myanmar # Mc   [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
+108D          ; Myanmar # Mn       MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
+108E          ; Myanmar # Lo       MYANMAR LETTER RUMAI PALAUNG FA
+108F          ; Myanmar # Mc       MYANMAR SIGN RUMAI PALAUNG TONE-5
+1090..1099    ; Myanmar # Nd  [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE
+109E..109F    ; Myanmar # So   [2] MYANMAR SYMBOL SHAN ONE..MYANMAR SYMBOL SHAN EXCLAMATION
+
+# Total code points: 156
+
+# ================================================
+
+10A0..10C5    ; Georgian # L&  [38] GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE
+10D0..10FA    ; Georgian # Lo  [43] GEORGIAN LETTER AN..GEORGIAN LETTER AIN
+10FC          ; Georgian # Lm       MODIFIER LETTER GEORGIAN NAR
+2D00..2D25    ; Georgian # L&  [38] GEORGIAN SMALL LETTER AN..GEORGIAN SMALL LETTER HOE
+
+# Total code points: 120
+
+# ================================================
+
+1100..1159    ; Hangul # Lo  [90] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG YEORINHIEUH
+115F..11A2    ; Hangul # Lo  [68] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG SSANGARAEA
+11A8..11F9    ; Hangul # Lo  [82] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG YEORINHIEUH
+3131..318E    ; Hangul # Lo  [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE
+3200..321E    ; Hangul # So  [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU
+3260..327E    ; Hangul # So  [31] CIRCLED HANGUL KIYEOK..CIRCLED HANGUL IEUNG U
+AC00..D7A3    ; Hangul # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH
+FFA0..FFBE    ; Hangul # Lo  [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH
+FFC2..FFC7    ; Hangul # Lo   [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E
+FFCA..FFCF    ; Hangul # Lo   [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE
+FFD2..FFD7    ; Hangul # Lo   [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU
+FFDA..FFDC    ; Hangul # Lo   [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I
+
+# Total code points: 11620
+
+# ================================================
+
+1200..1248    ; Ethiopic # Lo  [73] ETHIOPIC SYLLABLE HA..ETHIOPIC SYLLABLE QWA
+124A..124D    ; Ethiopic # Lo   [4] ETHIOPIC SYLLABLE QWI..ETHIOPIC SYLLABLE QWE
+1250..1256    ; Ethiopic # Lo   [7] ETHIOPIC SYLLABLE QHA..ETHIOPIC SYLLABLE QHO
+1258          ; Ethiopic # Lo       ETHIOPIC SYLLABLE QHWA
+125A..125D    ; Ethiopic # Lo   [4] ETHIOPIC SYLLABLE QHWI..ETHIOPIC SYLLABLE QHWE
+1260..1288    ; Ethiopic # Lo  [41] ETHIOPIC SYLLABLE BA..ETHIOPIC SYLLABLE XWA
+128A..128D    ; Ethiopic # Lo   [4] ETHIOPIC SYLLABLE XWI..ETHIOPIC SYLLABLE XWE
+1290..12B0    ; Ethiopic # Lo  [33] ETHIOPIC SYLLABLE NA..ETHIOPIC SYLLABLE KWA
+12B2..12B5    ; Ethiopic # Lo   [4] ETHIOPIC SYLLABLE KWI..ETHIOPIC SYLLABLE KWE
+12B8..12BE    ; Ethiopic # Lo   [7] ETHIOPIC SYLLABLE KXA..ETHIOPIC SYLLABLE KXO
+12C0          ; Ethiopic # Lo       ETHIOPIC SYLLABLE KXWA
+12C2..12C5    ; Ethiopic # Lo   [4] ETHIOPIC SYLLABLE KXWI..ETHIOPIC SYLLABLE KXWE
+12C8..12D6    ; Ethiopic # Lo  [15] ETHIOPIC SYLLABLE WA..ETHIOPIC SYLLABLE PHARYNGEAL O
+12D8..1310    ; Ethiopic # Lo  [57] ETHIOPIC SYLLABLE ZA..ETHIOPIC SYLLABLE GWA
+1312..1315    ; Ethiopic # Lo   [4] ETHIOPIC SYLLABLE GWI..ETHIOPIC SYLLABLE GWE
+1318..135A    ; Ethiopic # Lo  [67] ETHIOPIC SYLLABLE GGA..ETHIOPIC SYLLABLE FYA
+135F          ; Ethiopic # Mn       ETHIOPIC COMBINING GEMINATION MARK
+1360          ; Ethiopic # So       ETHIOPIC SECTION MARK
+1361..1368    ; Ethiopic # Po   [8] ETHIOPIC WORDSPACE..ETHIOPIC PARAGRAPH SEPARATOR
+1369..137C    ; Ethiopic # No  [20] ETHIOPIC DIGIT ONE..ETHIOPIC NUMBER TEN THOUSAND
+1380..138F    ; Ethiopic # Lo  [16] ETHIOPIC SYLLABLE SEBATBEIT MWA..ETHIOPIC SYLLABLE PWE
+1390..1399    ; Ethiopic # So  [10] ETHIOPIC TONAL MARK YIZET..ETHIOPIC TONAL MARK KURT
+2D80..2D96    ; Ethiopic # Lo  [23] ETHIOPIC SYLLABLE LOA..ETHIOPIC SYLLABLE GGWE
+2DA0..2DA6    ; Ethiopic # Lo   [7] ETHIOPIC SYLLABLE SSA..ETHIOPIC SYLLABLE SSO
+2DA8..2DAE    ; Ethiopic # Lo   [7] ETHIOPIC SYLLABLE CCA..ETHIOPIC SYLLABLE CCO
+2DB0..2DB6    ; Ethiopic # Lo   [7] ETHIOPIC SYLLABLE ZZA..ETHIOPIC SYLLABLE ZZO
+2DB8..2DBE    ; Ethiopic # Lo   [7] ETHIOPIC SYLLABLE CCHA..ETHIOPIC SYLLABLE CCHO
+2DC0..2DC6    ; Ethiopic # Lo   [7] ETHIOPIC SYLLABLE QYA..ETHIOPIC SYLLABLE QYO
+2DC8..2DCE    ; Ethiopic # Lo   [7] ETHIOPIC SYLLABLE KYA..ETHIOPIC SYLLABLE KYO
+2DD0..2DD6    ; Ethiopic # Lo   [7] ETHIOPIC SYLLABLE XYA..ETHIOPIC SYLLABLE XYO
+2DD8..2DDE    ; Ethiopic # Lo   [7] ETHIOPIC SYLLABLE GYA..ETHIOPIC SYLLABLE GYO
+
+# Total code points: 461
+
+# ================================================
+
+13A0..13F4    ; Cherokee # Lo  [85] CHEROKEE LETTER A..CHEROKEE LETTER YV
+
+# Total code points: 85
+
+# ================================================
+
+1401..166C    ; Canadian_Aboriginal # Lo [620] CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA
+166D..166E    ; Canadian_Aboriginal # Po   [2] CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP
+166F..1676    ; Canadian_Aboriginal # Lo   [8] CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS NNGAA
+
+# Total code points: 630
+
+# ================================================
+
+1680          ; Ogham # Zs       OGHAM SPACE MARK
+1681..169A    ; Ogham # Lo  [26] OGHAM LETTER BEITH..OGHAM LETTER PEITH
+169B          ; Ogham # Ps       OGHAM FEATHER MARK
+169C          ; Ogham # Pe       OGHAM REVERSED FEATHER MARK
+
+# Total code points: 29
+
+# ================================================
+
+16A0..16EA    ; Runic # Lo  [75] RUNIC LETTER FEHU FEOH FE F..RUNIC LETTER X
+16EE..16F0    ; Runic # Nl   [3] RUNIC ARLAUG SYMBOL..RUNIC BELGTHOR SYMBOL
+
+# Total code points: 78
+
+# ================================================
+
+1780..17B3    ; Khmer # Lo  [52] KHMER LETTER KA..KHMER INDEPENDENT VOWEL QAU
+17B4..17B5    ; Khmer # Cf   [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
+17B6          ; Khmer # Mc       KHMER VOWEL SIGN AA
+17B7..17BD    ; Khmer # Mn   [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA
+17BE..17C5    ; Khmer # Mc   [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU
+17C6          ; Khmer # Mn       KHMER SIGN NIKAHIT
+17C7..17C8    ; Khmer # Mc   [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU
+17C9..17D3    ; Khmer # Mn  [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT
+17D4..17D6    ; Khmer # Po   [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH
+17D7          ; Khmer # Lm       KHMER SIGN LEK TOO
+17D8..17DA    ; Khmer # Po   [3] KHMER SIGN BEYYAL..KHMER SIGN KOOMUUT
+17DB          ; Khmer # Sc       KHMER CURRENCY SYMBOL RIEL
+17DC          ; Khmer # Lo       KHMER SIGN AVAKRAHASANYA
+17DD          ; Khmer # Mn       KHMER SIGN ATTHACAN
+17E0..17E9    ; Khmer # Nd  [10] KHMER DIGIT ZERO..KHMER DIGIT NINE
+17F0..17F9    ; Khmer # No  [10] KHMER SYMBOL LEK ATTAK SON..KHMER SYMBOL LEK ATTAK PRAM-BUON
+19E0..19FF    ; Khmer # So  [32] KHMER SYMBOL PATHAMASAT..KHMER SYMBOL DAP-PRAM ROC
+
+# Total code points: 146
+
+# ================================================
+
+1800..1801    ; Mongolian # Po   [2] MONGOLIAN BIRGA..MONGOLIAN ELLIPSIS
+1804          ; Mongolian # Po       MONGOLIAN COLON
+1806          ; Mongolian # Pd       MONGOLIAN TODO SOFT HYPHEN
+1807..180A    ; Mongolian # Po   [4] MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER..MONGOLIAN NIRUGU
+180B..180D    ; Mongolian # Mn   [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
+180E          ; Mongolian # Zs       MONGOLIAN VOWEL SEPARATOR
+1810..1819    ; Mongolian # Nd  [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE
+1820..1842    ; Mongolian # Lo  [35] MONGOLIAN LETTER A..MONGOLIAN LETTER CHI
+1843          ; Mongolian # Lm       MONGOLIAN LETTER TODO LONG VOWEL SIGN
+1844..1877    ; Mongolian # Lo  [52] MONGOLIAN LETTER TODO E..MONGOLIAN LETTER MANCHU ZHA
+1880..18A8    ; Mongolian # Lo  [41] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER MANCHU ALI GALI BHA
+18A9          ; Mongolian # Mn       MONGOLIAN LETTER ALI GALI DAGALGA
+18AA          ; Mongolian # Lo       MONGOLIAN LETTER MANCHU ALI GALI LHA
+
+# Total code points: 153
+
+# ================================================
+
+3041..3096    ; Hiragana # Lo  [86] HIRAGANA LETTER SMALL A..HIRAGANA LETTER SMALL KE
+309D..309E    ; Hiragana # Lm   [2] HIRAGANA ITERATION MARK..HIRAGANA VOICED ITERATION MARK
+309F          ; Hiragana # Lo       HIRAGANA DIGRAPH YORI
+
+# Total code points: 89
+
+# ================================================
+
+30A1..30FA    ; Katakana # Lo  [90] KATAKANA LETTER SMALL A..KATAKANA LETTER VO
+30FD..30FE    ; Katakana # Lm   [2] KATAKANA ITERATION MARK..KATAKANA VOICED ITERATION MARK
+30FF          ; Katakana # Lo       KATAKANA DIGRAPH KOTO
+31F0..31FF    ; Katakana # Lo  [16] KATAKANA LETTER SMALL KU..KATAKANA LETTER SMALL RO
+32D0..32FE    ; Katakana # So  [47] CIRCLED KATAKANA A..CIRCLED KATAKANA WO
+3300..3357    ; Katakana # So  [88] SQUARE APAATO..SQUARE WATTO
+FF66..FF6F    ; Katakana # Lo  [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU
+FF71..FF9D    ; Katakana # Lo  [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N
+
+# Total code points: 299
+
+# ================================================
+
+3105..312D    ; Bopomofo # Lo  [41] BOPOMOFO LETTER B..BOPOMOFO LETTER IH
+31A0..31B7    ; Bopomofo # Lo  [24] BOPOMOFO LETTER BU..BOPOMOFO FINAL LETTER H
+
+# Total code points: 65
+
+# ================================================
+
+2E80..2E99    ; Han # So  [26] CJK RADICAL REPEAT..CJK RADICAL RAP
+2E9B..2EF3    ; Han # So  [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE
+2F00..2FD5    ; Han # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE
+3005          ; Han # Lm       IDEOGRAPHIC ITERATION MARK
+3007          ; Han # Nl       IDEOGRAPHIC NUMBER ZERO
+3021..3029    ; Han # Nl   [9] HANGZHOU NUMERAL ONE..HANGZHOU NUMERAL NINE
+3038..303A    ; Han # Nl   [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY
+303B          ; Han # Lm       VERTICAL IDEOGRAPHIC ITERATION MARK
+3400..4DB5    ; Han # Lo [6582] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DB5
+4E00..9FC3    ; Han # Lo [20932] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FC3
+F900..FA2D    ; Han # Lo [302] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA2D
+FA30..FA6A    ; Han # Lo  [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A
+FA70..FAD9    ; Han # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9
+20000..2A6D6  ; Han # Lo [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6
+2F800..2FA1D  ; Han # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+
+# Total code points: 71578
+
+# ================================================
+
+A000..A014    ; Yi # Lo  [21] YI SYLLABLE IT..YI SYLLABLE E
+A015          ; Yi # Lm       YI SYLLABLE WU
+A016..A48C    ; Yi # Lo [1143] YI SYLLABLE BIT..YI SYLLABLE YYR
+A490..A4C6    ; Yi # So  [55] YI RADICAL QOT..YI RADICAL KE
+
+# Total code points: 1220
+
+# ================================================
+
+10300..1031E  ; Old_Italic # Lo  [31] OLD ITALIC LETTER A..OLD ITALIC LETTER UU
+10320..10323  ; Old_Italic # No   [4] OLD ITALIC NUMERAL ONE..OLD ITALIC NUMERAL FIFTY
+
+# Total code points: 35
+
+# ================================================
+
+10330..10340  ; Gothic # Lo  [17] GOTHIC LETTER AHSA..GOTHIC LETTER PAIRTHRA
+10341         ; Gothic # Nl       GOTHIC LETTER NINETY
+10342..10349  ; Gothic # Lo   [8] GOTHIC LETTER RAIDA..GOTHIC LETTER OTHAL
+1034A         ; Gothic # Nl       GOTHIC LETTER NINE HUNDRED
+
+# Total code points: 27
+
+# ================================================
+
+10400..1044F  ; Deseret # L&  [80] DESERET CAPITAL LETTER LONG I..DESERET SMALL LETTER EW
+
+# Total code points: 80
+
+# ================================================
+
+0300..036F    ; Inherited # Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X
+064B..0655    ; Inherited # Mn  [11] ARABIC FATHATAN..ARABIC HAMZA BELOW
+0670          ; Inherited # Mn       ARABIC LETTER SUPERSCRIPT ALEF
+0951..0952    ; Inherited # Mn   [2] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI STRESS SIGN ANUDATTA
+1DC0..1DE6    ; Inherited # Mn  [39] COMBINING DOTTED GRAVE ACCENT..COMBINING LATIN SMALL LETTER Z
+1DFE..1DFF    ; Inherited # Mn   [2] COMBINING LEFT ARROWHEAD ABOVE..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW
+200C..200D    ; Inherited # Cf   [2] ZERO WIDTH NON-JOINER..ZERO WIDTH JOINER
+20D0..20DC    ; Inherited # Mn  [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
+20DD..20E0    ; Inherited # Me   [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH
+20E1          ; Inherited # Mn       COMBINING LEFT RIGHT ARROW ABOVE
+20E2..20E4    ; Inherited # Me   [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE
+20E5..20F0    ; Inherited # Mn  [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE
+302A..302F    ; Inherited # Mn   [6] IDEOGRAPHIC LEVEL TONE MARK..HANGUL DOUBLE DOT TONE MARK
+3099..309A    ; Inherited # Mn   [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+FE00..FE0F    ; Inherited # Mn  [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
+FE20..FE26    ; Inherited # Mn   [7] COMBINING LIGATURE LEFT HALF..COMBINING CONJOINING MACRON
+101FD         ; Inherited # Mn       PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE
+1D167..1D169  ; Inherited # Mn   [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
+1D17B..1D182  ; Inherited # Mn   [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
+1D185..1D18B  ; Inherited # Mn   [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
+1D1AA..1D1AD  ; Inherited # Mn   [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
+E0100..E01EF  ; Inherited # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
+
+# Total code points: 496
+
+# ================================================
+
+1700..170C    ; Tagalog # Lo  [13] TAGALOG LETTER A..TAGALOG LETTER YA
+170E..1711    ; Tagalog # Lo   [4] TAGALOG LETTER LA..TAGALOG LETTER HA
+1712..1714    ; Tagalog # Mn   [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA
+
+# Total code points: 20
+
+# ================================================
+
+1720..1731    ; Hanunoo # Lo  [18] HANUNOO LETTER A..HANUNOO LETTER HA
+1732..1734    ; Hanunoo # Mn   [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD
+
+# Total code points: 21
+
+# ================================================
+
+1740..1751    ; Buhid # Lo  [18] BUHID LETTER A..BUHID LETTER HA
+1752..1753    ; Buhid # Mn   [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
+
+# Total code points: 20
+
+# ================================================
+
+1760..176C    ; Tagbanwa # Lo  [13] TAGBANWA LETTER A..TAGBANWA LETTER YA
+176E..1770    ; Tagbanwa # Lo   [3] TAGBANWA LETTER LA..TAGBANWA LETTER SA
+1772..1773    ; Tagbanwa # Mn   [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U
+
+# Total code points: 18
+
+# ================================================
+
+1900..191C    ; Limbu # Lo  [29] LIMBU VOWEL-CARRIER LETTER..LIMBU LETTER HA
+1920..1922    ; Limbu # Mn   [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U
+1923..1926    ; Limbu # Mc   [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU
+1927..1928    ; Limbu # Mn   [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O
+1929..192B    ; Limbu # Mc   [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA
+1930..1931    ; Limbu # Mc   [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA
+1932          ; Limbu # Mn       LIMBU SMALL LETTER ANUSVARA
+1933..1938    ; Limbu # Mc   [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA
+1939..193B    ; Limbu # Mn   [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I
+1940          ; Limbu # So       LIMBU SIGN LOO
+1944..1945    ; Limbu # Po   [2] LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK
+1946..194F    ; Limbu # Nd  [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE
+
+# Total code points: 66
+
+# ================================================
+
+1950..196D    ; Tai_Le # Lo  [30] TAI LE LETTER KA..TAI LE LETTER AI
+1970..1974    ; Tai_Le # Lo   [5] TAI LE LETTER TONE-2..TAI LE LETTER TONE-6
+
+# Total code points: 35
+
+# ================================================
+
+10000..1000B  ; Linear_B # Lo  [12] LINEAR B SYLLABLE B008 A..LINEAR B SYLLABLE B046 JE
+1000D..10026  ; Linear_B # Lo  [26] LINEAR B SYLLABLE B036 JO..LINEAR B SYLLABLE B032 QO
+10028..1003A  ; Linear_B # Lo  [19] LINEAR B SYLLABLE B060 RA..LINEAR B SYLLABLE B042 WO
+1003C..1003D  ; Linear_B # Lo   [2] LINEAR B SYLLABLE B017 ZA..LINEAR B SYLLABLE B074 ZE
+1003F..1004D  ; Linear_B # Lo  [15] LINEAR B SYLLABLE B020 ZO..LINEAR B SYLLABLE B091 TWO
+10050..1005D  ; Linear_B # Lo  [14] LINEAR B SYMBOL B018..LINEAR B SYMBOL B089
+10080..100FA  ; Linear_B # Lo [123] LINEAR B IDEOGRAM B100 MAN..LINEAR B IDEOGRAM VESSEL B305
+
+# Total code points: 211
+
+# ================================================
+
+10380..1039D  ; Ugaritic # Lo  [30] UGARITIC LETTER ALPA..UGARITIC LETTER SSU
+1039F         ; Ugaritic # Po       UGARITIC WORD DIVIDER
+
+# Total code points: 31
+
+# ================================================
+
+10450..1047F  ; Shavian # Lo  [48] SHAVIAN LETTER PEEP..SHAVIAN LETTER YEW
+
+# Total code points: 48
+
+# ================================================
+
+10480..1049D  ; Osmanya # Lo  [30] OSMANYA LETTER ALEF..OSMANYA LETTER OO
+104A0..104A9  ; Osmanya # Nd  [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE
+
+# Total code points: 40
+
+# ================================================
+
+10800..10805  ; Cypriot # Lo   [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA
+10808         ; Cypriot # Lo       CYPRIOT SYLLABLE JO
+1080A..10835  ; Cypriot # Lo  [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO
+10837..10838  ; Cypriot # Lo   [2] CYPRIOT SYLLABLE XA..CYPRIOT SYLLABLE XE
+1083C         ; Cypriot # Lo       CYPRIOT SYLLABLE ZA
+1083F         ; Cypriot # Lo       CYPRIOT SYLLABLE ZO
+
+# Total code points: 55
+
+# ================================================
+
+2800..28FF    ; Braille # So [256] BRAILLE PATTERN BLANK..BRAILLE PATTERN DOTS-12345678
+
+# Total code points: 256
+
+# ================================================
+
+1A00..1A16    ; Buginese # Lo  [23] BUGINESE LETTER KA..BUGINESE LETTER HA
+1A17..1A18    ; Buginese # Mn   [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U
+1A19..1A1B    ; Buginese # Mc   [3] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN AE
+1A1E..1A1F    ; Buginese # Po   [2] BUGINESE PALLAWA..BUGINESE END OF SECTION
+
+# Total code points: 30
+
+# ================================================
+
+03E2..03EF    ; Coptic # L&  [14] COPTIC CAPITAL LETTER SHEI..COPTIC SMALL LETTER DEI
+2C80..2CE4    ; Coptic # L& [101] COPTIC CAPITAL LETTER ALFA..COPTIC SYMBOL KAI
+2CE5..2CEA    ; Coptic # So   [6] COPTIC SYMBOL MI RO..COPTIC SYMBOL SHIMA SIMA
+2CF9..2CFC    ; Coptic # Po   [4] COPTIC OLD NUBIAN FULL STOP..COPTIC OLD NUBIAN VERSE DIVIDER
+2CFD          ; Coptic # No       COPTIC FRACTION ONE HALF
+2CFE..2CFF    ; Coptic # Po   [2] COPTIC FULL STOP..COPTIC MORPHOLOGICAL DIVIDER
+
+# Total code points: 128
+
+# ================================================
+
+1980..19A9    ; New_Tai_Lue # Lo  [42] NEW TAI LUE LETTER HIGH QA..NEW TAI LUE LETTER LOW XVA
+19B0..19C0    ; New_Tai_Lue # Mc  [17] NEW TAI LUE VOWEL SIGN VOWEL SHORTENER..NEW TAI LUE VOWEL SIGN IY
+19C1..19C7    ; New_Tai_Lue # Lo   [7] NEW TAI LUE LETTER FINAL V..NEW TAI LUE LETTER FINAL B
+19C8..19C9    ; New_Tai_Lue # Mc   [2] NEW TAI LUE TONE MARK-1..NEW TAI LUE TONE MARK-2
+19D0..19D9    ; New_Tai_Lue # Nd  [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE
+19DE..19DF    ; New_Tai_Lue # Po   [2] NEW TAI LUE SIGN LAE..NEW TAI LUE SIGN LAEV
+
+# Total code points: 80
+
+# ================================================
+
+2C00..2C2E    ; Glagolitic # L&  [47] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE
+2C30..2C5E    ; Glagolitic # L&  [47] GLAGOLITIC SMALL LETTER AZU..GLAGOLITIC SMALL LETTER LATINATE MYSLITE
+
+# Total code points: 94
+
+# ================================================
+
+2D30..2D65    ; Tifinagh # Lo  [54] TIFINAGH LETTER YA..TIFINAGH LETTER YAZZ
+2D6F          ; Tifinagh # Lm       TIFINAGH MODIFIER LETTER LABIALIZATION MARK
+
+# Total code points: 55
+
+# ================================================
+
+A800..A801    ; Syloti_Nagri # Lo   [2] SYLOTI NAGRI LETTER A..SYLOTI NAGRI LETTER I
+A802          ; Syloti_Nagri # Mn       SYLOTI NAGRI SIGN DVISVARA
+A803..A805    ; Syloti_Nagri # Lo   [3] SYLOTI NAGRI LETTER U..SYLOTI NAGRI LETTER O
+A806          ; Syloti_Nagri # Mn       SYLOTI NAGRI SIGN HASANTA
+A807..A80A    ; Syloti_Nagri # Lo   [4] SYLOTI NAGRI LETTER KO..SYLOTI NAGRI LETTER GHO
+A80B          ; Syloti_Nagri # Mn       SYLOTI NAGRI SIGN ANUSVARA
+A80C..A822    ; Syloti_Nagri # Lo  [23] SYLOTI NAGRI LETTER CO..SYLOTI NAGRI LETTER HO
+A823..A824    ; Syloti_Nagri # Mc   [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
+A825..A826    ; Syloti_Nagri # Mn   [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E
+A827          ; Syloti_Nagri # Mc       SYLOTI NAGRI VOWEL SIGN OO
+A828..A82B    ; Syloti_Nagri # So   [4] SYLOTI NAGRI POETRY MARK-1..SYLOTI NAGRI POETRY MARK-4
+
+# Total code points: 44
+
+# ================================================
+
+103A0..103C3  ; Old_Persian # Lo  [36] OLD PERSIAN SIGN A..OLD PERSIAN SIGN HA
+103C8..103CF  ; Old_Persian # Lo   [8] OLD PERSIAN SIGN AURAMAZDAA..OLD PERSIAN SIGN BUUMISH
+103D0         ; Old_Persian # Po       OLD PERSIAN WORD DIVIDER
+103D1..103D5  ; Old_Persian # Nl   [5] OLD PERSIAN NUMBER ONE..OLD PERSIAN NUMBER HUNDRED
+
+# Total code points: 50
+
+# ================================================
+
+10A00         ; Kharoshthi # Lo       KHAROSHTHI LETTER A
+10A01..10A03  ; Kharoshthi # Mn   [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R
+10A05..10A06  ; Kharoshthi # Mn   [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O
+10A0C..10A0F  ; Kharoshthi # Mn   [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA
+10A10..10A13  ; Kharoshthi # Lo   [4] KHAROSHTHI LETTER KA..KHAROSHTHI LETTER GHA
+10A15..10A17  ; Kharoshthi # Lo   [3] KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA
+10A19..10A33  ; Kharoshthi # Lo  [27] KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER TTTHA
+10A38..10A3A  ; Kharoshthi # Mn   [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW
+10A3F         ; Kharoshthi # Mn       KHAROSHTHI VIRAMA
+10A40..10A47  ; Kharoshthi # No   [8] KHAROSHTHI DIGIT ONE..KHAROSHTHI NUMBER ONE THOUSAND
+10A50..10A58  ; Kharoshthi # Po   [9] KHAROSHTHI PUNCTUATION DOT..KHAROSHTHI PUNCTUATION LINES
+
+# Total code points: 65
+
+# ================================================
+
+1B00..1B03    ; Balinese # Mn   [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
+1B04          ; Balinese # Mc       BALINESE SIGN BISAH
+1B05..1B33    ; Balinese # Lo  [47] BALINESE LETTER AKARA..BALINESE LETTER HA
+1B34          ; Balinese # Mn       BALINESE SIGN REREKAN
+1B35          ; Balinese # Mc       BALINESE VOWEL SIGN TEDUNG
+1B36..1B3A    ; Balinese # Mn   [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
+1B3B          ; Balinese # Mc       BALINESE VOWEL SIGN RA REPA TEDUNG
+1B3C          ; Balinese # Mn       BALINESE VOWEL SIGN LA LENGA
+1B3D..1B41    ; Balinese # Mc   [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
+1B42          ; Balinese # Mn       BALINESE VOWEL SIGN PEPET
+1B43..1B44    ; Balinese # Mc   [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
+1B45..1B4B    ; Balinese # Lo   [7] BALINESE LETTER KAF SASAK..BALINESE LETTER ASYURA SASAK
+1B50..1B59    ; Balinese # Nd  [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE
+1B5A..1B60    ; Balinese # Po   [7] BALINESE PANTI..BALINESE PAMENENG
+1B61..1B6A    ; Balinese # So  [10] BALINESE MUSICAL SYMBOL DONG..BALINESE MUSICAL SYMBOL DANG GEDE
+1B6B..1B73    ; Balinese # Mn   [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG
+1B74..1B7C    ; Balinese # So   [9] BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG..BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING
+
+# Total code points: 121
+
+# ================================================
+
+12000..1236E  ; Cuneiform # Lo [879] CUNEIFORM SIGN A..CUNEIFORM SIGN ZUM
+12400..12462  ; Cuneiform # Nl  [99] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN OLD ASSYRIAN ONE QUARTER
+12470..12473  ; Cuneiform # Po   [4] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL TRICOLON
+
+# Total code points: 982
+
+# ================================================
+
+10900..10915  ; Phoenician # Lo  [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU
+10916..10919  ; Phoenician # No   [4] PHOENICIAN NUMBER ONE..PHOENICIAN NUMBER ONE HUNDRED
+1091F         ; Phoenician # Po       PHOENICIAN WORD SEPARATOR
+
+# Total code points: 27
+
+# ================================================
+
+A840..A873    ; Phags_Pa # Lo  [52] PHAGS-PA LETTER KA..PHAGS-PA LETTER CANDRABINDU
+A874..A877    ; Phags_Pa # Po   [4] PHAGS-PA SINGLE HEAD MARK..PHAGS-PA MARK DOUBLE SHAD
+
+# Total code points: 56
+
+# ================================================
+
+07C0..07C9    ; Nko # Nd  [10] NKO DIGIT ZERO..NKO DIGIT NINE
+07CA..07EA    ; Nko # Lo  [33] NKO LETTER A..NKO LETTER JONA RA
+07EB..07F3    ; Nko # Mn   [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE
+07F4..07F5    ; Nko # Lm   [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE
+07F6          ; Nko # So       NKO SYMBOL OO DENNEN
+07F7..07F9    ; Nko # Po   [3] NKO SYMBOL GBAKURUNEN..NKO EXCLAMATION MARK
+07FA          ; Nko # Lm       NKO LAJANYALAN
+
+# Total code points: 59
+
+# ================================================
+
+1B80..1B81    ; Sundanese # Mn   [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR
+1B82          ; Sundanese # Mc       SUNDANESE SIGN PANGWISAD
+1B83..1BA0    ; Sundanese # Lo  [30] SUNDANESE LETTER A..SUNDANESE LETTER HA
+1BA1          ; Sundanese # Mc       SUNDANESE CONSONANT SIGN PAMINGKAL
+1BA2..1BA5    ; Sundanese # Mn   [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU
+1BA6..1BA7    ; Sundanese # Mc   [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG
+1BA8..1BA9    ; Sundanese # Mn   [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG
+1BAA          ; Sundanese # Mc       SUNDANESE SIGN PAMAAEH
+1BAE..1BAF    ; Sundanese # Lo   [2] SUNDANESE LETTER KHA..SUNDANESE LETTER SYA
+1BB0..1BB9    ; Sundanese # Nd  [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE
+
+# Total code points: 55
+
+# ================================================
+
+1C00..1C23    ; Lepcha # Lo  [36] LEPCHA LETTER KA..LEPCHA LETTER A
+1C24..1C2B    ; Lepcha # Mc   [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
+1C2C..1C33    ; Lepcha # Mn   [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T
+1C34..1C35    ; Lepcha # Mc   [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
+1C36..1C37    ; Lepcha # Mn   [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA
+1C3B..1C3F    ; Lepcha # Po   [5] LEPCHA PUNCTUATION TA-ROL..LEPCHA PUNCTUATION TSHOOK
+1C40..1C49    ; Lepcha # Nd  [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE
+1C4D..1C4F    ; Lepcha # Lo   [3] LEPCHA LETTER TTA..LEPCHA LETTER DDA
+
+# Total code points: 74
+
+# ================================================
+
+1C50..1C59    ; Ol_Chiki # Nd  [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE
+1C5A..1C77    ; Ol_Chiki # Lo  [30] OL CHIKI LETTER LA..OL CHIKI LETTER OH
+1C78..1C7D    ; Ol_Chiki # Lm   [6] OL CHIKI MU TTUDDAG..OL CHIKI AHAD
+1C7E..1C7F    ; Ol_Chiki # Po   [2] OL CHIKI PUNCTUATION MUCAAD..OL CHIKI PUNCTUATION DOUBLE MUCAAD
+
+# Total code points: 48
+
+# ================================================
+
+A500..A60B    ; Vai # Lo [268] VAI SYLLABLE EE..VAI SYLLABLE NG
+A60C          ; Vai # Lm       VAI SYLLABLE LENGTHENER
+A60D..A60F    ; Vai # Po   [3] VAI COMMA..VAI QUESTION MARK
+A610..A61F    ; Vai # Lo  [16] VAI SYLLABLE NDOLE FA..VAI SYMBOL JONG
+A620..A629    ; Vai # Nd  [10] VAI DIGIT ZERO..VAI DIGIT NINE
+A62A..A62B    ; Vai # Lo   [2] VAI SYLLABLE NDOLE MA..VAI SYLLABLE NDOLE DO
+
+# Total code points: 300
+
+# ================================================
+
+A880..A881    ; Saurashtra # Mc   [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA
+A882..A8B3    ; Saurashtra # Lo  [50] SAURASHTRA LETTER A..SAURASHTRA LETTER LLA
+A8B4..A8C3    ; Saurashtra # Mc  [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU
+A8C4          ; Saurashtra # Mn       SAURASHTRA SIGN VIRAMA
+A8CE..A8CF    ; Saurashtra # Po   [2] SAURASHTRA DANDA..SAURASHTRA DOUBLE DANDA
+A8D0..A8D9    ; Saurashtra # Nd  [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE
+
+# Total code points: 81
+
+# ================================================
+
+A900..A909    ; Kayah_Li # Nd  [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE
+A90A..A925    ; Kayah_Li # Lo  [28] KAYAH LI LETTER KA..KAYAH LI LETTER OO
+A926..A92D    ; Kayah_Li # Mn   [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU
+A92E..A92F    ; Kayah_Li # Po   [2] KAYAH LI SIGN CWI..KAYAH LI SIGN SHYA
+
+# Total code points: 48
+
+# ================================================
+
+A930..A946    ; Rejang # Lo  [23] REJANG LETTER KA..REJANG LETTER A
+A947..A951    ; Rejang # Mn  [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
+A952..A953    ; Rejang # Mc   [2] REJANG CONSONANT SIGN H..REJANG VIRAMA
+A95F          ; Rejang # Po       REJANG SECTION MARK
+
+# Total code points: 37
+
+# ================================================
+
+10280..1029C  ; Lycian # Lo  [29] LYCIAN LETTER A..LYCIAN LETTER X
+
+# Total code points: 29
+
+# ================================================
+
+102A0..102D0  ; Carian # Lo  [49] CARIAN LETTER A..CARIAN LETTER UUU3
+
+# Total code points: 49
+
+# ================================================
+
+10920..10939  ; Lydian # Lo  [26] LYDIAN LETTER A..LYDIAN LETTER C
+1093F         ; Lydian # Po       LYDIAN TRIANGULAR MARK
+
+# Total code points: 27
+
+# ================================================
+
+AA00..AA28    ; Cham # Lo  [41] CHAM LETTER A..CHAM LETTER HA
+AA29..AA2E    ; Cham # Mn   [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
+AA2F..AA30    ; Cham # Mc   [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
+AA31..AA32    ; Cham # Mn   [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
+AA33..AA34    ; Cham # Mc   [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA
+AA35..AA36    ; Cham # Mn   [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA
+AA40..AA42    ; Cham # Lo   [3] CHAM LETTER FINAL K..CHAM LETTER FINAL NG
+AA43          ; Cham # Mn       CHAM CONSONANT SIGN FINAL NG
+AA44..AA4B    ; Cham # Lo   [8] CHAM LETTER FINAL CH..CHAM LETTER FINAL SS
+AA4C          ; Cham # Mn       CHAM CONSONANT SIGN FINAL M
+AA4D          ; Cham # Mc       CHAM CONSONANT SIGN FINAL H
+AA50..AA59    ; Cham # Nd  [10] CHAM DIGIT ZERO..CHAM DIGIT NINE
+AA5C..AA5F    ; Cham # Po   [4] CHAM PUNCTUATION SPIRAL..CHAM PUNCTUATION TRIPLE DANDA
+
+# Total code points: 83
+
+# EOF
diff --git a/third_party/harfbuzz/contrib/tables/category-parse.py b/third_party/harfbuzz/contrib/tables/category-parse.py
new file mode 100644
index 0000000..6818c1d
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/category-parse.py
@@ -0,0 +1,70 @@
+import sys
+from unicode_parse_common import *
+
+# http://www.unicode.org/Public/5.1.0/ucd/extracted/DerivedGeneralCategory.txt
+
+category_to_harfbuzz = {
+  'Mn': 'HB_Mark_NonSpacing',
+  'Mc': 'HB_Mark_SpacingCombining',
+  'Me': 'HB_Mark_Enclosing',
+
+  'Nd': 'HB_Number_DecimalDigit',
+  'Nl': 'HB_Number_Letter',
+  'No': 'HB_Number_Other',
+
+  'Zs': 'HB_Separator_Space',
+  'Zl': 'HB_Separator_Line',
+  'Zp': 'HB_Separator_Paragraph',
+
+  'Cc': 'HB_Other_Control',
+  'Cf': 'HB_Other_Format',
+  'Cs': 'HB_Other_Surrogate',
+  'Co': 'HB_Other_PrivateUse',
+  'Cn': 'HB_Other_NotAssigned',
+
+  'Lu': 'HB_Letter_Uppercase',
+  'Ll': 'HB_Letter_Lowercase',
+  'Lt': 'HB_Letter_Titlecase',
+  'Lm': 'HB_Letter_Modifier',
+  'Lo': 'HB_Letter_Other',
+
+  'Pc': 'HB_Punctuation_Connector',
+  'Pd': 'HB_Punctuation_Dash',
+  'Ps': 'HB_Punctuation_Open',
+  'Pe': 'HB_Punctuation_Close',
+  'Pi': 'HB_Punctuation_InitialQuote',
+  'Pf': 'HB_Punctuation_FinalQuote',
+  'Po': 'HB_Punctuation_Other',
+
+  'Sm': 'HB_Symbol_Math',
+  'Sc': 'HB_Symbol_Currency',
+  'Sk': 'HB_Symbol_Modifier',
+  'So': 'HB_Symbol_Other',
+}
+
+def main(infile, outfile):
+  ranges = unicode_file_parse(infile, category_to_harfbuzz)
+  ranges = sort_and_merge(ranges)
+
+  print >>outfile, '// Generated from Unicode script tables\n'
+  print >>outfile, '#ifndef CATEGORY_PROPERTIES_H_'
+  print >>outfile, '#define CATEGORY_PROPERTIES_H_\n'
+  print >>outfile, '#include <stdint.h>'
+  print >>outfile, '#include "harfbuzz-external.h"\n'
+  print >>outfile, 'struct category_property {'
+  print >>outfile, '  uint32_t range_start;'
+  print >>outfile, '  uint32_t range_end;'
+  print >>outfile, '  HB_CharCategory category;'
+  print >>outfile, '};\n'
+  print >>outfile, 'static const struct category_property category_properties[] = {'
+  for (start, end, value) in ranges:
+    print >>outfile, '  {0x%x, 0x%x, %s},' % (start, end, value)
+  print >>outfile, '};\n'
+  print >>outfile, 'static const unsigned category_properties_count = %d;\n' % len(ranges)
+  print >>outfile, '#endif  // CATEGORY_PROPERTIES_H_'
+
+if __name__ == '__main__':
+  if len(sys.argv) != 3:
+    print 'Usage: %s <input .txt> <output .h>' % sys.argv[0]
+  else:
+    main(file(sys.argv[1], 'r'), file(sys.argv[2], 'w+'))
diff --git a/third_party/harfbuzz/contrib/tables/category-properties.h b/third_party/harfbuzz/contrib/tables/category-properties.h
new file mode 100644
index 0000000..3b7c7ca
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/category-properties.h
@@ -0,0 +1,2869 @@
+// Generated from Unicode script tables
+
+#ifndef CATEGORY_PROPERTIES_H_
+#define CATEGORY_PROPERTIES_H_
+
+#include <stdint.h>
+#include "harfbuzz-external.h"
+
+struct category_property {
+  uint32_t range_start;
+  uint32_t range_end;
+  HB_CharCategory category;
+};
+
+static const struct category_property category_properties[] = {
+  {0x0, 0x1f, HB_Other_Control},
+  {0x20, 0x20, HB_Separator_Space},
+  {0x21, 0x23, HB_Punctuation_Other},
+  {0x24, 0x24, HB_Symbol_Currency},
+  {0x25, 0x27, HB_Punctuation_Other},
+  {0x28, 0x28, HB_Punctuation_Open},
+  {0x29, 0x29, HB_Punctuation_Close},
+  {0x2a, 0x2a, HB_Punctuation_Other},
+  {0x2b, 0x2b, HB_Symbol_Math},
+  {0x2c, 0x2c, HB_Punctuation_Other},
+  {0x2d, 0x2d, HB_Punctuation_Dash},
+  {0x2e, 0x2f, HB_Punctuation_Other},
+  {0x30, 0x39, HB_Number_DecimalDigit},
+  {0x3a, 0x3b, HB_Punctuation_Other},
+  {0x3c, 0x3e, HB_Symbol_Math},
+  {0x3f, 0x40, HB_Punctuation_Other},
+  {0x41, 0x5a, HB_Letter_Uppercase},
+  {0x5b, 0x5b, HB_Punctuation_Open},
+  {0x5c, 0x5c, HB_Punctuation_Other},
+  {0x5d, 0x5d, HB_Punctuation_Close},
+  {0x5e, 0x5e, HB_Symbol_Modifier},
+  {0x5f, 0x5f, HB_Punctuation_Connector},
+  {0x60, 0x60, HB_Symbol_Modifier},
+  {0x61, 0x7a, HB_Letter_Lowercase},
+  {0x7b, 0x7b, HB_Punctuation_Open},
+  {0x7c, 0x7c, HB_Symbol_Math},
+  {0x7d, 0x7d, HB_Punctuation_Close},
+  {0x7e, 0x7e, HB_Symbol_Math},
+  {0x7f, 0x9f, HB_Other_Control},
+  {0xa0, 0xa0, HB_Separator_Space},
+  {0xa1, 0xa1, HB_Punctuation_Other},
+  {0xa2, 0xa5, HB_Symbol_Currency},
+  {0xa6, 0xa7, HB_Symbol_Other},
+  {0xa8, 0xa8, HB_Symbol_Modifier},
+  {0xa9, 0xa9, HB_Symbol_Other},
+  {0xaa, 0xaa, HB_Letter_Lowercase},
+  {0xab, 0xab, HB_Punctuation_InitialQuote},
+  {0xac, 0xac, HB_Symbol_Math},
+  {0xad, 0xad, HB_Other_Format},
+  {0xae, 0xae, HB_Symbol_Other},
+  {0xaf, 0xaf, HB_Symbol_Modifier},
+  {0xb0, 0xb0, HB_Symbol_Other},
+  {0xb1, 0xb1, HB_Symbol_Math},
+  {0xb2, 0xb3, HB_Number_Other},
+  {0xb4, 0xb4, HB_Symbol_Modifier},
+  {0xb5, 0xb5, HB_Letter_Lowercase},
+  {0xb6, 0xb6, HB_Symbol_Other},
+  {0xb7, 0xb7, HB_Punctuation_Other},
+  {0xb8, 0xb8, HB_Symbol_Modifier},
+  {0xb9, 0xb9, HB_Number_Other},
+  {0xba, 0xba, HB_Letter_Lowercase},
+  {0xbb, 0xbb, HB_Punctuation_FinalQuote},
+  {0xbc, 0xbe, HB_Number_Other},
+  {0xbf, 0xbf, HB_Punctuation_Other},
+  {0xc0, 0xd6, HB_Letter_Uppercase},
+  {0xd7, 0xd7, HB_Symbol_Math},
+  {0xd8, 0xde, HB_Letter_Uppercase},
+  {0xdf, 0xf6, HB_Letter_Lowercase},
+  {0xf7, 0xf7, HB_Symbol_Math},
+  {0xf8, 0xff, HB_Letter_Lowercase},
+  {0x100, 0x100, HB_Letter_Uppercase},
+  {0x101, 0x101, HB_Letter_Lowercase},
+  {0x102, 0x102, HB_Letter_Uppercase},
+  {0x103, 0x103, HB_Letter_Lowercase},
+  {0x104, 0x104, HB_Letter_Uppercase},
+  {0x105, 0x105, HB_Letter_Lowercase},
+  {0x106, 0x106, HB_Letter_Uppercase},
+  {0x107, 0x107, HB_Letter_Lowercase},
+  {0x108, 0x108, HB_Letter_Uppercase},
+  {0x109, 0x109, HB_Letter_Lowercase},
+  {0x10a, 0x10a, HB_Letter_Uppercase},
+  {0x10b, 0x10b, HB_Letter_Lowercase},
+  {0x10c, 0x10c, HB_Letter_Uppercase},
+  {0x10d, 0x10d, HB_Letter_Lowercase},
+  {0x10e, 0x10e, HB_Letter_Uppercase},
+  {0x10f, 0x10f, HB_Letter_Lowercase},
+  {0x110, 0x110, HB_Letter_Uppercase},
+  {0x111, 0x111, HB_Letter_Lowercase},
+  {0x112, 0x112, HB_Letter_Uppercase},
+  {0x113, 0x113, HB_Letter_Lowercase},
+  {0x114, 0x114, HB_Letter_Uppercase},
+  {0x115, 0x115, HB_Letter_Lowercase},
+  {0x116, 0x116, HB_Letter_Uppercase},
+  {0x117, 0x117, HB_Letter_Lowercase},
+  {0x118, 0x118, HB_Letter_Uppercase},
+  {0x119, 0x119, HB_Letter_Lowercase},
+  {0x11a, 0x11a, HB_Letter_Uppercase},
+  {0x11b, 0x11b, HB_Letter_Lowercase},
+  {0x11c, 0x11c, HB_Letter_Uppercase},
+  {0x11d, 0x11d, HB_Letter_Lowercase},
+  {0x11e, 0x11e, HB_Letter_Uppercase},
+  {0x11f, 0x11f, HB_Letter_Lowercase},
+  {0x120, 0x120, HB_Letter_Uppercase},
+  {0x121, 0x121, HB_Letter_Lowercase},
+  {0x122, 0x122, HB_Letter_Uppercase},
+  {0x123, 0x123, HB_Letter_Lowercase},
+  {0x124, 0x124, HB_Letter_Uppercase},
+  {0x125, 0x125, HB_Letter_Lowercase},
+  {0x126, 0x126, HB_Letter_Uppercase},
+  {0x127, 0x127, HB_Letter_Lowercase},
+  {0x128, 0x128, HB_Letter_Uppercase},
+  {0x129, 0x129, HB_Letter_Lowercase},
+  {0x12a, 0x12a, HB_Letter_Uppercase},
+  {0x12b, 0x12b, HB_Letter_Lowercase},
+  {0x12c, 0x12c, HB_Letter_Uppercase},
+  {0x12d, 0x12d, HB_Letter_Lowercase},
+  {0x12e, 0x12e, HB_Letter_Uppercase},
+  {0x12f, 0x12f, HB_Letter_Lowercase},
+  {0x130, 0x130, HB_Letter_Uppercase},
+  {0x131, 0x131, HB_Letter_Lowercase},
+  {0x132, 0x132, HB_Letter_Uppercase},
+  {0x133, 0x133, HB_Letter_Lowercase},
+  {0x134, 0x134, HB_Letter_Uppercase},
+  {0x135, 0x135, HB_Letter_Lowercase},
+  {0x136, 0x136, HB_Letter_Uppercase},
+  {0x137, 0x138, HB_Letter_Lowercase},
+  {0x139, 0x139, HB_Letter_Uppercase},
+  {0x13a, 0x13a, HB_Letter_Lowercase},
+  {0x13b, 0x13b, HB_Letter_Uppercase},
+  {0x13c, 0x13c, HB_Letter_Lowercase},
+  {0x13d, 0x13d, HB_Letter_Uppercase},
+  {0x13e, 0x13e, HB_Letter_Lowercase},
+  {0x13f, 0x13f, HB_Letter_Uppercase},
+  {0x140, 0x140, HB_Letter_Lowercase},
+  {0x141, 0x141, HB_Letter_Uppercase},
+  {0x142, 0x142, HB_Letter_Lowercase},
+  {0x143, 0x143, HB_Letter_Uppercase},
+  {0x144, 0x144, HB_Letter_Lowercase},
+  {0x145, 0x145, HB_Letter_Uppercase},
+  {0x146, 0x146, HB_Letter_Lowercase},
+  {0x147, 0x147, HB_Letter_Uppercase},
+  {0x148, 0x149, HB_Letter_Lowercase},
+  {0x14a, 0x14a, HB_Letter_Uppercase},
+  {0x14b, 0x14b, HB_Letter_Lowercase},
+  {0x14c, 0x14c, HB_Letter_Uppercase},
+  {0x14d, 0x14d, HB_Letter_Lowercase},
+  {0x14e, 0x14e, HB_Letter_Uppercase},
+  {0x14f, 0x14f, HB_Letter_Lowercase},
+  {0x150, 0x150, HB_Letter_Uppercase},
+  {0x151, 0x151, HB_Letter_Lowercase},
+  {0x152, 0x152, HB_Letter_Uppercase},
+  {0x153, 0x153, HB_Letter_Lowercase},
+  {0x154, 0x154, HB_Letter_Uppercase},
+  {0x155, 0x155, HB_Letter_Lowercase},
+  {0x156, 0x156, HB_Letter_Uppercase},
+  {0x157, 0x157, HB_Letter_Lowercase},
+  {0x158, 0x158, HB_Letter_Uppercase},
+  {0x159, 0x159, HB_Letter_Lowercase},
+  {0x15a, 0x15a, HB_Letter_Uppercase},
+  {0x15b, 0x15b, HB_Letter_Lowercase},
+  {0x15c, 0x15c, HB_Letter_Uppercase},
+  {0x15d, 0x15d, HB_Letter_Lowercase},
+  {0x15e, 0x15e, HB_Letter_Uppercase},
+  {0x15f, 0x15f, HB_Letter_Lowercase},
+  {0x160, 0x160, HB_Letter_Uppercase},
+  {0x161, 0x161, HB_Letter_Lowercase},
+  {0x162, 0x162, HB_Letter_Uppercase},
+  {0x163, 0x163, HB_Letter_Lowercase},
+  {0x164, 0x164, HB_Letter_Uppercase},
+  {0x165, 0x165, HB_Letter_Lowercase},
+  {0x166, 0x166, HB_Letter_Uppercase},
+  {0x167, 0x167, HB_Letter_Lowercase},
+  {0x168, 0x168, HB_Letter_Uppercase},
+  {0x169, 0x169, HB_Letter_Lowercase},
+  {0x16a, 0x16a, HB_Letter_Uppercase},
+  {0x16b, 0x16b, HB_Letter_Lowercase},
+  {0x16c, 0x16c, HB_Letter_Uppercase},
+  {0x16d, 0x16d, HB_Letter_Lowercase},
+  {0x16e, 0x16e, HB_Letter_Uppercase},
+  {0x16f, 0x16f, HB_Letter_Lowercase},
+  {0x170, 0x170, HB_Letter_Uppercase},
+  {0x171, 0x171, HB_Letter_Lowercase},
+  {0x172, 0x172, HB_Letter_Uppercase},
+  {0x173, 0x173, HB_Letter_Lowercase},
+  {0x174, 0x174, HB_Letter_Uppercase},
+  {0x175, 0x175, HB_Letter_Lowercase},
+  {0x176, 0x176, HB_Letter_Uppercase},
+  {0x177, 0x177, HB_Letter_Lowercase},
+  {0x178, 0x179, HB_Letter_Uppercase},
+  {0x17a, 0x17a, HB_Letter_Lowercase},
+  {0x17b, 0x17b, HB_Letter_Uppercase},
+  {0x17c, 0x17c, HB_Letter_Lowercase},
+  {0x17d, 0x17d, HB_Letter_Uppercase},
+  {0x17e, 0x180, HB_Letter_Lowercase},
+  {0x181, 0x182, HB_Letter_Uppercase},
+  {0x183, 0x183, HB_Letter_Lowercase},
+  {0x184, 0x184, HB_Letter_Uppercase},
+  {0x185, 0x185, HB_Letter_Lowercase},
+  {0x186, 0x187, HB_Letter_Uppercase},
+  {0x188, 0x188, HB_Letter_Lowercase},
+  {0x189, 0x18b, HB_Letter_Uppercase},
+  {0x18c, 0x18d, HB_Letter_Lowercase},
+  {0x18e, 0x191, HB_Letter_Uppercase},
+  {0x192, 0x192, HB_Letter_Lowercase},
+  {0x193, 0x194, HB_Letter_Uppercase},
+  {0x195, 0x195, HB_Letter_Lowercase},
+  {0x196, 0x198, HB_Letter_Uppercase},
+  {0x199, 0x19b, HB_Letter_Lowercase},
+  {0x19c, 0x19d, HB_Letter_Uppercase},
+  {0x19e, 0x19e, HB_Letter_Lowercase},
+  {0x19f, 0x1a0, HB_Letter_Uppercase},
+  {0x1a1, 0x1a1, HB_Letter_Lowercase},
+  {0x1a2, 0x1a2, HB_Letter_Uppercase},
+  {0x1a3, 0x1a3, HB_Letter_Lowercase},
+  {0x1a4, 0x1a4, HB_Letter_Uppercase},
+  {0x1a5, 0x1a5, HB_Letter_Lowercase},
+  {0x1a6, 0x1a7, HB_Letter_Uppercase},
+  {0x1a8, 0x1a8, HB_Letter_Lowercase},
+  {0x1a9, 0x1a9, HB_Letter_Uppercase},
+  {0x1aa, 0x1ab, HB_Letter_Lowercase},
+  {0x1ac, 0x1ac, HB_Letter_Uppercase},
+  {0x1ad, 0x1ad, HB_Letter_Lowercase},
+  {0x1ae, 0x1af, HB_Letter_Uppercase},
+  {0x1b0, 0x1b0, HB_Letter_Lowercase},
+  {0x1b1, 0x1b3, HB_Letter_Uppercase},
+  {0x1b4, 0x1b4, HB_Letter_Lowercase},
+  {0x1b5, 0x1b5, HB_Letter_Uppercase},
+  {0x1b6, 0x1b6, HB_Letter_Lowercase},
+  {0x1b7, 0x1b8, HB_Letter_Uppercase},
+  {0x1b9, 0x1ba, HB_Letter_Lowercase},
+  {0x1bb, 0x1bb, HB_Letter_Other},
+  {0x1bc, 0x1bc, HB_Letter_Uppercase},
+  {0x1bd, 0x1bf, HB_Letter_Lowercase},
+  {0x1c0, 0x1c3, HB_Letter_Other},
+  {0x1c4, 0x1c4, HB_Letter_Uppercase},
+  {0x1c5, 0x1c5, HB_Letter_Titlecase},
+  {0x1c6, 0x1c6, HB_Letter_Lowercase},
+  {0x1c7, 0x1c7, HB_Letter_Uppercase},
+  {0x1c8, 0x1c8, HB_Letter_Titlecase},
+  {0x1c9, 0x1c9, HB_Letter_Lowercase},
+  {0x1ca, 0x1ca, HB_Letter_Uppercase},
+  {0x1cb, 0x1cb, HB_Letter_Titlecase},
+  {0x1cc, 0x1cc, HB_Letter_Lowercase},
+  {0x1cd, 0x1cd, HB_Letter_Uppercase},
+  {0x1ce, 0x1ce, HB_Letter_Lowercase},
+  {0x1cf, 0x1cf, HB_Letter_Uppercase},
+  {0x1d0, 0x1d0, HB_Letter_Lowercase},
+  {0x1d1, 0x1d1, HB_Letter_Uppercase},
+  {0x1d2, 0x1d2, HB_Letter_Lowercase},
+  {0x1d3, 0x1d3, HB_Letter_Uppercase},
+  {0x1d4, 0x1d4, HB_Letter_Lowercase},
+  {0x1d5, 0x1d5, HB_Letter_Uppercase},
+  {0x1d6, 0x1d6, HB_Letter_Lowercase},
+  {0x1d7, 0x1d7, HB_Letter_Uppercase},
+  {0x1d8, 0x1d8, HB_Letter_Lowercase},
+  {0x1d9, 0x1d9, HB_Letter_Uppercase},
+  {0x1da, 0x1da, HB_Letter_Lowercase},
+  {0x1db, 0x1db, HB_Letter_Uppercase},
+  {0x1dc, 0x1dd, HB_Letter_Lowercase},
+  {0x1de, 0x1de, HB_Letter_Uppercase},
+  {0x1df, 0x1df, HB_Letter_Lowercase},
+  {0x1e0, 0x1e0, HB_Letter_Uppercase},
+  {0x1e1, 0x1e1, HB_Letter_Lowercase},
+  {0x1e2, 0x1e2, HB_Letter_Uppercase},
+  {0x1e3, 0x1e3, HB_Letter_Lowercase},
+  {0x1e4, 0x1e4, HB_Letter_Uppercase},
+  {0x1e5, 0x1e5, HB_Letter_Lowercase},
+  {0x1e6, 0x1e6, HB_Letter_Uppercase},
+  {0x1e7, 0x1e7, HB_Letter_Lowercase},
+  {0x1e8, 0x1e8, HB_Letter_Uppercase},
+  {0x1e9, 0x1e9, HB_Letter_Lowercase},
+  {0x1ea, 0x1ea, HB_Letter_Uppercase},
+  {0x1eb, 0x1eb, HB_Letter_Lowercase},
+  {0x1ec, 0x1ec, HB_Letter_Uppercase},
+  {0x1ed, 0x1ed, HB_Letter_Lowercase},
+  {0x1ee, 0x1ee, HB_Letter_Uppercase},
+  {0x1ef, 0x1f0, HB_Letter_Lowercase},
+  {0x1f1, 0x1f1, HB_Letter_Uppercase},
+  {0x1f2, 0x1f2, HB_Letter_Titlecase},
+  {0x1f3, 0x1f3, HB_Letter_Lowercase},
+  {0x1f4, 0x1f4, HB_Letter_Uppercase},
+  {0x1f5, 0x1f5, HB_Letter_Lowercase},
+  {0x1f6, 0x1f8, HB_Letter_Uppercase},
+  {0x1f9, 0x1f9, HB_Letter_Lowercase},
+  {0x1fa, 0x1fa, HB_Letter_Uppercase},
+  {0x1fb, 0x1fb, HB_Letter_Lowercase},
+  {0x1fc, 0x1fc, HB_Letter_Uppercase},
+  {0x1fd, 0x1fd, HB_Letter_Lowercase},
+  {0x1fe, 0x1fe, HB_Letter_Uppercase},
+  {0x1ff, 0x1ff, HB_Letter_Lowercase},
+  {0x200, 0x200, HB_Letter_Uppercase},
+  {0x201, 0x201, HB_Letter_Lowercase},
+  {0x202, 0x202, HB_Letter_Uppercase},
+  {0x203, 0x203, HB_Letter_Lowercase},
+  {0x204, 0x204, HB_Letter_Uppercase},
+  {0x205, 0x205, HB_Letter_Lowercase},
+  {0x206, 0x206, HB_Letter_Uppercase},
+  {0x207, 0x207, HB_Letter_Lowercase},
+  {0x208, 0x208, HB_Letter_Uppercase},
+  {0x209, 0x209, HB_Letter_Lowercase},
+  {0x20a, 0x20a, HB_Letter_Uppercase},
+  {0x20b, 0x20b, HB_Letter_Lowercase},
+  {0x20c, 0x20c, HB_Letter_Uppercase},
+  {0x20d, 0x20d, HB_Letter_Lowercase},
+  {0x20e, 0x20e, HB_Letter_Uppercase},
+  {0x20f, 0x20f, HB_Letter_Lowercase},
+  {0x210, 0x210, HB_Letter_Uppercase},
+  {0x211, 0x211, HB_Letter_Lowercase},
+  {0x212, 0x212, HB_Letter_Uppercase},
+  {0x213, 0x213, HB_Letter_Lowercase},
+  {0x214, 0x214, HB_Letter_Uppercase},
+  {0x215, 0x215, HB_Letter_Lowercase},
+  {0x216, 0x216, HB_Letter_Uppercase},
+  {0x217, 0x217, HB_Letter_Lowercase},
+  {0x218, 0x218, HB_Letter_Uppercase},
+  {0x219, 0x219, HB_Letter_Lowercase},
+  {0x21a, 0x21a, HB_Letter_Uppercase},
+  {0x21b, 0x21b, HB_Letter_Lowercase},
+  {0x21c, 0x21c, HB_Letter_Uppercase},
+  {0x21d, 0x21d, HB_Letter_Lowercase},
+  {0x21e, 0x21e, HB_Letter_Uppercase},
+  {0x21f, 0x21f, HB_Letter_Lowercase},
+  {0x220, 0x220, HB_Letter_Uppercase},
+  {0x221, 0x221, HB_Letter_Lowercase},
+  {0x222, 0x222, HB_Letter_Uppercase},
+  {0x223, 0x223, HB_Letter_Lowercase},
+  {0x224, 0x224, HB_Letter_Uppercase},
+  {0x225, 0x225, HB_Letter_Lowercase},
+  {0x226, 0x226, HB_Letter_Uppercase},
+  {0x227, 0x227, HB_Letter_Lowercase},
+  {0x228, 0x228, HB_Letter_Uppercase},
+  {0x229, 0x229, HB_Letter_Lowercase},
+  {0x22a, 0x22a, HB_Letter_Uppercase},
+  {0x22b, 0x22b, HB_Letter_Lowercase},
+  {0x22c, 0x22c, HB_Letter_Uppercase},
+  {0x22d, 0x22d, HB_Letter_Lowercase},
+  {0x22e, 0x22e, HB_Letter_Uppercase},
+  {0x22f, 0x22f, HB_Letter_Lowercase},
+  {0x230, 0x230, HB_Letter_Uppercase},
+  {0x231, 0x231, HB_Letter_Lowercase},
+  {0x232, 0x232, HB_Letter_Uppercase},
+  {0x233, 0x239, HB_Letter_Lowercase},
+  {0x23a, 0x23b, HB_Letter_Uppercase},
+  {0x23c, 0x23c, HB_Letter_Lowercase},
+  {0x23d, 0x23e, HB_Letter_Uppercase},
+  {0x23f, 0x240, HB_Letter_Lowercase},
+  {0x241, 0x241, HB_Letter_Uppercase},
+  {0x242, 0x242, HB_Letter_Lowercase},
+  {0x243, 0x246, HB_Letter_Uppercase},
+  {0x247, 0x247, HB_Letter_Lowercase},
+  {0x248, 0x248, HB_Letter_Uppercase},
+  {0x249, 0x249, HB_Letter_Lowercase},
+  {0x24a, 0x24a, HB_Letter_Uppercase},
+  {0x24b, 0x24b, HB_Letter_Lowercase},
+  {0x24c, 0x24c, HB_Letter_Uppercase},
+  {0x24d, 0x24d, HB_Letter_Lowercase},
+  {0x24e, 0x24e, HB_Letter_Uppercase},
+  {0x24f, 0x293, HB_Letter_Lowercase},
+  {0x294, 0x294, HB_Letter_Other},
+  {0x295, 0x2af, HB_Letter_Lowercase},
+  {0x2b0, 0x2c1, HB_Letter_Modifier},
+  {0x2c2, 0x2c5, HB_Symbol_Modifier},
+  {0x2c6, 0x2d1, HB_Letter_Modifier},
+  {0x2d2, 0x2df, HB_Symbol_Modifier},
+  {0x2e0, 0x2e4, HB_Letter_Modifier},
+  {0x2e5, 0x2eb, HB_Symbol_Modifier},
+  {0x2ec, 0x2ec, HB_Letter_Modifier},
+  {0x2ed, 0x2ed, HB_Symbol_Modifier},
+  {0x2ee, 0x2ee, HB_Letter_Modifier},
+  {0x2ef, 0x2ff, HB_Symbol_Modifier},
+  {0x300, 0x36f, HB_Mark_NonSpacing},
+  {0x370, 0x370, HB_Letter_Uppercase},
+  {0x371, 0x371, HB_Letter_Lowercase},
+  {0x372, 0x372, HB_Letter_Uppercase},
+  {0x373, 0x373, HB_Letter_Lowercase},
+  {0x374, 0x374, HB_Letter_Modifier},
+  {0x375, 0x375, HB_Symbol_Modifier},
+  {0x376, 0x376, HB_Letter_Uppercase},
+  {0x377, 0x377, HB_Letter_Lowercase},
+  {0x378, 0x379, HB_Other_NotAssigned},
+  {0x37a, 0x37a, HB_Letter_Modifier},
+  {0x37b, 0x37d, HB_Letter_Lowercase},
+  {0x37e, 0x37e, HB_Punctuation_Other},
+  {0x37f, 0x383, HB_Other_NotAssigned},
+  {0x384, 0x385, HB_Symbol_Modifier},
+  {0x386, 0x386, HB_Letter_Uppercase},
+  {0x387, 0x387, HB_Punctuation_Other},
+  {0x388, 0x38a, HB_Letter_Uppercase},
+  {0x38b, 0x38b, HB_Other_NotAssigned},
+  {0x38c, 0x38c, HB_Letter_Uppercase},
+  {0x38d, 0x38d, HB_Other_NotAssigned},
+  {0x38e, 0x38f, HB_Letter_Uppercase},
+  {0x390, 0x390, HB_Letter_Lowercase},
+  {0x391, 0x3a1, HB_Letter_Uppercase},
+  {0x3a2, 0x3a2, HB_Other_NotAssigned},
+  {0x3a3, 0x3ab, HB_Letter_Uppercase},
+  {0x3ac, 0x3ce, HB_Letter_Lowercase},
+  {0x3cf, 0x3cf, HB_Letter_Uppercase},
+  {0x3d0, 0x3d1, HB_Letter_Lowercase},
+  {0x3d2, 0x3d4, HB_Letter_Uppercase},
+  {0x3d5, 0x3d7, HB_Letter_Lowercase},
+  {0x3d8, 0x3d8, HB_Letter_Uppercase},
+  {0x3d9, 0x3d9, HB_Letter_Lowercase},
+  {0x3da, 0x3da, HB_Letter_Uppercase},
+  {0x3db, 0x3db, HB_Letter_Lowercase},
+  {0x3dc, 0x3dc, HB_Letter_Uppercase},
+  {0x3dd, 0x3dd, HB_Letter_Lowercase},
+  {0x3de, 0x3de, HB_Letter_Uppercase},
+  {0x3df, 0x3df, HB_Letter_Lowercase},
+  {0x3e0, 0x3e0, HB_Letter_Uppercase},
+  {0x3e1, 0x3e1, HB_Letter_Lowercase},
+  {0x3e2, 0x3e2, HB_Letter_Uppercase},
+  {0x3e3, 0x3e3, HB_Letter_Lowercase},
+  {0x3e4, 0x3e4, HB_Letter_Uppercase},
+  {0x3e5, 0x3e5, HB_Letter_Lowercase},
+  {0x3e6, 0x3e6, HB_Letter_Uppercase},
+  {0x3e7, 0x3e7, HB_Letter_Lowercase},
+  {0x3e8, 0x3e8, HB_Letter_Uppercase},
+  {0x3e9, 0x3e9, HB_Letter_Lowercase},
+  {0x3ea, 0x3ea, HB_Letter_Uppercase},
+  {0x3eb, 0x3eb, HB_Letter_Lowercase},
+  {0x3ec, 0x3ec, HB_Letter_Uppercase},
+  {0x3ed, 0x3ed, HB_Letter_Lowercase},
+  {0x3ee, 0x3ee, HB_Letter_Uppercase},
+  {0x3ef, 0x3f3, HB_Letter_Lowercase},
+  {0x3f4, 0x3f4, HB_Letter_Uppercase},
+  {0x3f5, 0x3f5, HB_Letter_Lowercase},
+  {0x3f6, 0x3f6, HB_Symbol_Math},
+  {0x3f7, 0x3f7, HB_Letter_Uppercase},
+  {0x3f8, 0x3f8, HB_Letter_Lowercase},
+  {0x3f9, 0x3fa, HB_Letter_Uppercase},
+  {0x3fb, 0x3fc, HB_Letter_Lowercase},
+  {0x3fd, 0x42f, HB_Letter_Uppercase},
+  {0x430, 0x45f, HB_Letter_Lowercase},
+  {0x460, 0x460, HB_Letter_Uppercase},
+  {0x461, 0x461, HB_Letter_Lowercase},
+  {0x462, 0x462, HB_Letter_Uppercase},
+  {0x463, 0x463, HB_Letter_Lowercase},
+  {0x464, 0x464, HB_Letter_Uppercase},
+  {0x465, 0x465, HB_Letter_Lowercase},
+  {0x466, 0x466, HB_Letter_Uppercase},
+  {0x467, 0x467, HB_Letter_Lowercase},
+  {0x468, 0x468, HB_Letter_Uppercase},
+  {0x469, 0x469, HB_Letter_Lowercase},
+  {0x46a, 0x46a, HB_Letter_Uppercase},
+  {0x46b, 0x46b, HB_Letter_Lowercase},
+  {0x46c, 0x46c, HB_Letter_Uppercase},
+  {0x46d, 0x46d, HB_Letter_Lowercase},
+  {0x46e, 0x46e, HB_Letter_Uppercase},
+  {0x46f, 0x46f, HB_Letter_Lowercase},
+  {0x470, 0x470, HB_Letter_Uppercase},
+  {0x471, 0x471, HB_Letter_Lowercase},
+  {0x472, 0x472, HB_Letter_Uppercase},
+  {0x473, 0x473, HB_Letter_Lowercase},
+  {0x474, 0x474, HB_Letter_Uppercase},
+  {0x475, 0x475, HB_Letter_Lowercase},
+  {0x476, 0x476, HB_Letter_Uppercase},
+  {0x477, 0x477, HB_Letter_Lowercase},
+  {0x478, 0x478, HB_Letter_Uppercase},
+  {0x479, 0x479, HB_Letter_Lowercase},
+  {0x47a, 0x47a, HB_Letter_Uppercase},
+  {0x47b, 0x47b, HB_Letter_Lowercase},
+  {0x47c, 0x47c, HB_Letter_Uppercase},
+  {0x47d, 0x47d, HB_Letter_Lowercase},
+  {0x47e, 0x47e, HB_Letter_Uppercase},
+  {0x47f, 0x47f, HB_Letter_Lowercase},
+  {0x480, 0x480, HB_Letter_Uppercase},
+  {0x481, 0x481, HB_Letter_Lowercase},
+  {0x482, 0x482, HB_Symbol_Other},
+  {0x483, 0x487, HB_Mark_NonSpacing},
+  {0x488, 0x489, HB_Mark_Enclosing},
+  {0x48a, 0x48a, HB_Letter_Uppercase},
+  {0x48b, 0x48b, HB_Letter_Lowercase},
+  {0x48c, 0x48c, HB_Letter_Uppercase},
+  {0x48d, 0x48d, HB_Letter_Lowercase},
+  {0x48e, 0x48e, HB_Letter_Uppercase},
+  {0x48f, 0x48f, HB_Letter_Lowercase},
+  {0x490, 0x490, HB_Letter_Uppercase},
+  {0x491, 0x491, HB_Letter_Lowercase},
+  {0x492, 0x492, HB_Letter_Uppercase},
+  {0x493, 0x493, HB_Letter_Lowercase},
+  {0x494, 0x494, HB_Letter_Uppercase},
+  {0x495, 0x495, HB_Letter_Lowercase},
+  {0x496, 0x496, HB_Letter_Uppercase},
+  {0x497, 0x497, HB_Letter_Lowercase},
+  {0x498, 0x498, HB_Letter_Uppercase},
+  {0x499, 0x499, HB_Letter_Lowercase},
+  {0x49a, 0x49a, HB_Letter_Uppercase},
+  {0x49b, 0x49b, HB_Letter_Lowercase},
+  {0x49c, 0x49c, HB_Letter_Uppercase},
+  {0x49d, 0x49d, HB_Letter_Lowercase},
+  {0x49e, 0x49e, HB_Letter_Uppercase},
+  {0x49f, 0x49f, HB_Letter_Lowercase},
+  {0x4a0, 0x4a0, HB_Letter_Uppercase},
+  {0x4a1, 0x4a1, HB_Letter_Lowercase},
+  {0x4a2, 0x4a2, HB_Letter_Uppercase},
+  {0x4a3, 0x4a3, HB_Letter_Lowercase},
+  {0x4a4, 0x4a4, HB_Letter_Uppercase},
+  {0x4a5, 0x4a5, HB_Letter_Lowercase},
+  {0x4a6, 0x4a6, HB_Letter_Uppercase},
+  {0x4a7, 0x4a7, HB_Letter_Lowercase},
+  {0x4a8, 0x4a8, HB_Letter_Uppercase},
+  {0x4a9, 0x4a9, HB_Letter_Lowercase},
+  {0x4aa, 0x4aa, HB_Letter_Uppercase},
+  {0x4ab, 0x4ab, HB_Letter_Lowercase},
+  {0x4ac, 0x4ac, HB_Letter_Uppercase},
+  {0x4ad, 0x4ad, HB_Letter_Lowercase},
+  {0x4ae, 0x4ae, HB_Letter_Uppercase},
+  {0x4af, 0x4af, HB_Letter_Lowercase},
+  {0x4b0, 0x4b0, HB_Letter_Uppercase},
+  {0x4b1, 0x4b1, HB_Letter_Lowercase},
+  {0x4b2, 0x4b2, HB_Letter_Uppercase},
+  {0x4b3, 0x4b3, HB_Letter_Lowercase},
+  {0x4b4, 0x4b4, HB_Letter_Uppercase},
+  {0x4b5, 0x4b5, HB_Letter_Lowercase},
+  {0x4b6, 0x4b6, HB_Letter_Uppercase},
+  {0x4b7, 0x4b7, HB_Letter_Lowercase},
+  {0x4b8, 0x4b8, HB_Letter_Uppercase},
+  {0x4b9, 0x4b9, HB_Letter_Lowercase},
+  {0x4ba, 0x4ba, HB_Letter_Uppercase},
+  {0x4bb, 0x4bb, HB_Letter_Lowercase},
+  {0x4bc, 0x4bc, HB_Letter_Uppercase},
+  {0x4bd, 0x4bd, HB_Letter_Lowercase},
+  {0x4be, 0x4be, HB_Letter_Uppercase},
+  {0x4bf, 0x4bf, HB_Letter_Lowercase},
+  {0x4c0, 0x4c1, HB_Letter_Uppercase},
+  {0x4c2, 0x4c2, HB_Letter_Lowercase},
+  {0x4c3, 0x4c3, HB_Letter_Uppercase},
+  {0x4c4, 0x4c4, HB_Letter_Lowercase},
+  {0x4c5, 0x4c5, HB_Letter_Uppercase},
+  {0x4c6, 0x4c6, HB_Letter_Lowercase},
+  {0x4c7, 0x4c7, HB_Letter_Uppercase},
+  {0x4c8, 0x4c8, HB_Letter_Lowercase},
+  {0x4c9, 0x4c9, HB_Letter_Uppercase},
+  {0x4ca, 0x4ca, HB_Letter_Lowercase},
+  {0x4cb, 0x4cb, HB_Letter_Uppercase},
+  {0x4cc, 0x4cc, HB_Letter_Lowercase},
+  {0x4cd, 0x4cd, HB_Letter_Uppercase},
+  {0x4ce, 0x4cf, HB_Letter_Lowercase},
+  {0x4d0, 0x4d0, HB_Letter_Uppercase},
+  {0x4d1, 0x4d1, HB_Letter_Lowercase},
+  {0x4d2, 0x4d2, HB_Letter_Uppercase},
+  {0x4d3, 0x4d3, HB_Letter_Lowercase},
+  {0x4d4, 0x4d4, HB_Letter_Uppercase},
+  {0x4d5, 0x4d5, HB_Letter_Lowercase},
+  {0x4d6, 0x4d6, HB_Letter_Uppercase},
+  {0x4d7, 0x4d7, HB_Letter_Lowercase},
+  {0x4d8, 0x4d8, HB_Letter_Uppercase},
+  {0x4d9, 0x4d9, HB_Letter_Lowercase},
+  {0x4da, 0x4da, HB_Letter_Uppercase},
+  {0x4db, 0x4db, HB_Letter_Lowercase},
+  {0x4dc, 0x4dc, HB_Letter_Uppercase},
+  {0x4dd, 0x4dd, HB_Letter_Lowercase},
+  {0x4de, 0x4de, HB_Letter_Uppercase},
+  {0x4df, 0x4df, HB_Letter_Lowercase},
+  {0x4e0, 0x4e0, HB_Letter_Uppercase},
+  {0x4e1, 0x4e1, HB_Letter_Lowercase},
+  {0x4e2, 0x4e2, HB_Letter_Uppercase},
+  {0x4e3, 0x4e3, HB_Letter_Lowercase},
+  {0x4e4, 0x4e4, HB_Letter_Uppercase},
+  {0x4e5, 0x4e5, HB_Letter_Lowercase},
+  {0x4e6, 0x4e6, HB_Letter_Uppercase},
+  {0x4e7, 0x4e7, HB_Letter_Lowercase},
+  {0x4e8, 0x4e8, HB_Letter_Uppercase},
+  {0x4e9, 0x4e9, HB_Letter_Lowercase},
+  {0x4ea, 0x4ea, HB_Letter_Uppercase},
+  {0x4eb, 0x4eb, HB_Letter_Lowercase},
+  {0x4ec, 0x4ec, HB_Letter_Uppercase},
+  {0x4ed, 0x4ed, HB_Letter_Lowercase},
+  {0x4ee, 0x4ee, HB_Letter_Uppercase},
+  {0x4ef, 0x4ef, HB_Letter_Lowercase},
+  {0x4f0, 0x4f0, HB_Letter_Uppercase},
+  {0x4f1, 0x4f1, HB_Letter_Lowercase},
+  {0x4f2, 0x4f2, HB_Letter_Uppercase},
+  {0x4f3, 0x4f3, HB_Letter_Lowercase},
+  {0x4f4, 0x4f4, HB_Letter_Uppercase},
+  {0x4f5, 0x4f5, HB_Letter_Lowercase},
+  {0x4f6, 0x4f6, HB_Letter_Uppercase},
+  {0x4f7, 0x4f7, HB_Letter_Lowercase},
+  {0x4f8, 0x4f8, HB_Letter_Uppercase},
+  {0x4f9, 0x4f9, HB_Letter_Lowercase},
+  {0x4fa, 0x4fa, HB_Letter_Uppercase},
+  {0x4fb, 0x4fb, HB_Letter_Lowercase},
+  {0x4fc, 0x4fc, HB_Letter_Uppercase},
+  {0x4fd, 0x4fd, HB_Letter_Lowercase},
+  {0x4fe, 0x4fe, HB_Letter_Uppercase},
+  {0x4ff, 0x4ff, HB_Letter_Lowercase},
+  {0x500, 0x500, HB_Letter_Uppercase},
+  {0x501, 0x501, HB_Letter_Lowercase},
+  {0x502, 0x502, HB_Letter_Uppercase},
+  {0x503, 0x503, HB_Letter_Lowercase},
+  {0x504, 0x504, HB_Letter_Uppercase},
+  {0x505, 0x505, HB_Letter_Lowercase},
+  {0x506, 0x506, HB_Letter_Uppercase},
+  {0x507, 0x507, HB_Letter_Lowercase},
+  {0x508, 0x508, HB_Letter_Uppercase},
+  {0x509, 0x509, HB_Letter_Lowercase},
+  {0x50a, 0x50a, HB_Letter_Uppercase},
+  {0x50b, 0x50b, HB_Letter_Lowercase},
+  {0x50c, 0x50c, HB_Letter_Uppercase},
+  {0x50d, 0x50d, HB_Letter_Lowercase},
+  {0x50e, 0x50e, HB_Letter_Uppercase},
+  {0x50f, 0x50f, HB_Letter_Lowercase},
+  {0x510, 0x510, HB_Letter_Uppercase},
+  {0x511, 0x511, HB_Letter_Lowercase},
+  {0x512, 0x512, HB_Letter_Uppercase},
+  {0x513, 0x513, HB_Letter_Lowercase},
+  {0x514, 0x514, HB_Letter_Uppercase},
+  {0x515, 0x515, HB_Letter_Lowercase},
+  {0x516, 0x516, HB_Letter_Uppercase},
+  {0x517, 0x517, HB_Letter_Lowercase},
+  {0x518, 0x518, HB_Letter_Uppercase},
+  {0x519, 0x519, HB_Letter_Lowercase},
+  {0x51a, 0x51a, HB_Letter_Uppercase},
+  {0x51b, 0x51b, HB_Letter_Lowercase},
+  {0x51c, 0x51c, HB_Letter_Uppercase},
+  {0x51d, 0x51d, HB_Letter_Lowercase},
+  {0x51e, 0x51e, HB_Letter_Uppercase},
+  {0x51f, 0x51f, HB_Letter_Lowercase},
+  {0x520, 0x520, HB_Letter_Uppercase},
+  {0x521, 0x521, HB_Letter_Lowercase},
+  {0x522, 0x522, HB_Letter_Uppercase},
+  {0x523, 0x523, HB_Letter_Lowercase},
+  {0x524, 0x530, HB_Other_NotAssigned},
+  {0x531, 0x556, HB_Letter_Uppercase},
+  {0x557, 0x558, HB_Other_NotAssigned},
+  {0x559, 0x559, HB_Letter_Modifier},
+  {0x55a, 0x55f, HB_Punctuation_Other},
+  {0x560, 0x560, HB_Other_NotAssigned},
+  {0x561, 0x587, HB_Letter_Lowercase},
+  {0x588, 0x588, HB_Other_NotAssigned},
+  {0x589, 0x589, HB_Punctuation_Other},
+  {0x58a, 0x58a, HB_Punctuation_Dash},
+  {0x58b, 0x590, HB_Other_NotAssigned},
+  {0x591, 0x5bd, HB_Mark_NonSpacing},
+  {0x5be, 0x5be, HB_Punctuation_Dash},
+  {0x5bf, 0x5bf, HB_Mark_NonSpacing},
+  {0x5c0, 0x5c0, HB_Punctuation_Other},
+  {0x5c1, 0x5c2, HB_Mark_NonSpacing},
+  {0x5c3, 0x5c3, HB_Punctuation_Other},
+  {0x5c4, 0x5c5, HB_Mark_NonSpacing},
+  {0x5c6, 0x5c6, HB_Punctuation_Other},
+  {0x5c7, 0x5c7, HB_Mark_NonSpacing},
+  {0x5c8, 0x5cf, HB_Other_NotAssigned},
+  {0x5d0, 0x5ea, HB_Letter_Other},
+  {0x5eb, 0x5ef, HB_Other_NotAssigned},
+  {0x5f0, 0x5f2, HB_Letter_Other},
+  {0x5f3, 0x5f4, HB_Punctuation_Other},
+  {0x5f5, 0x5ff, HB_Other_NotAssigned},
+  {0x600, 0x603, HB_Other_Format},
+  {0x604, 0x605, HB_Other_NotAssigned},
+  {0x606, 0x608, HB_Symbol_Math},
+  {0x609, 0x60a, HB_Punctuation_Other},
+  {0x60b, 0x60b, HB_Symbol_Currency},
+  {0x60c, 0x60d, HB_Punctuation_Other},
+  {0x60e, 0x60f, HB_Symbol_Other},
+  {0x610, 0x61a, HB_Mark_NonSpacing},
+  {0x61b, 0x61b, HB_Punctuation_Other},
+  {0x61c, 0x61d, HB_Other_NotAssigned},
+  {0x61e, 0x61f, HB_Punctuation_Other},
+  {0x620, 0x620, HB_Other_NotAssigned},
+  {0x621, 0x63f, HB_Letter_Other},
+  {0x640, 0x640, HB_Letter_Modifier},
+  {0x641, 0x64a, HB_Letter_Other},
+  {0x64b, 0x65e, HB_Mark_NonSpacing},
+  {0x65f, 0x65f, HB_Other_NotAssigned},
+  {0x660, 0x669, HB_Number_DecimalDigit},
+  {0x66a, 0x66d, HB_Punctuation_Other},
+  {0x66e, 0x66f, HB_Letter_Other},
+  {0x670, 0x670, HB_Mark_NonSpacing},
+  {0x671, 0x6d3, HB_Letter_Other},
+  {0x6d4, 0x6d4, HB_Punctuation_Other},
+  {0x6d5, 0x6d5, HB_Letter_Other},
+  {0x6d6, 0x6dc, HB_Mark_NonSpacing},
+  {0x6dd, 0x6dd, HB_Other_Format},
+  {0x6de, 0x6de, HB_Mark_Enclosing},
+  {0x6df, 0x6e4, HB_Mark_NonSpacing},
+  {0x6e5, 0x6e6, HB_Letter_Modifier},
+  {0x6e7, 0x6e8, HB_Mark_NonSpacing},
+  {0x6e9, 0x6e9, HB_Symbol_Other},
+  {0x6ea, 0x6ed, HB_Mark_NonSpacing},
+  {0x6ee, 0x6ef, HB_Letter_Other},
+  {0x6f0, 0x6f9, HB_Number_DecimalDigit},
+  {0x6fa, 0x6fc, HB_Letter_Other},
+  {0x6fd, 0x6fe, HB_Symbol_Other},
+  {0x6ff, 0x6ff, HB_Letter_Other},
+  {0x700, 0x70d, HB_Punctuation_Other},
+  {0x70e, 0x70e, HB_Other_NotAssigned},
+  {0x70f, 0x70f, HB_Other_Format},
+  {0x710, 0x710, HB_Letter_Other},
+  {0x711, 0x711, HB_Mark_NonSpacing},
+  {0x712, 0x72f, HB_Letter_Other},
+  {0x730, 0x74a, HB_Mark_NonSpacing},
+  {0x74b, 0x74c, HB_Other_NotAssigned},
+  {0x74d, 0x7a5, HB_Letter_Other},
+  {0x7a6, 0x7b0, HB_Mark_NonSpacing},
+  {0x7b1, 0x7b1, HB_Letter_Other},
+  {0x7b2, 0x7bf, HB_Other_NotAssigned},
+  {0x7c0, 0x7c9, HB_Number_DecimalDigit},
+  {0x7ca, 0x7ea, HB_Letter_Other},
+  {0x7eb, 0x7f3, HB_Mark_NonSpacing},
+  {0x7f4, 0x7f5, HB_Letter_Modifier},
+  {0x7f6, 0x7f6, HB_Symbol_Other},
+  {0x7f7, 0x7f9, HB_Punctuation_Other},
+  {0x7fa, 0x7fa, HB_Letter_Modifier},
+  {0x7fb, 0x900, HB_Other_NotAssigned},
+  {0x901, 0x902, HB_Mark_NonSpacing},
+  {0x903, 0x903, HB_Mark_SpacingCombining},
+  {0x904, 0x939, HB_Letter_Other},
+  {0x93a, 0x93b, HB_Other_NotAssigned},
+  {0x93c, 0x93c, HB_Mark_NonSpacing},
+  {0x93d, 0x93d, HB_Letter_Other},
+  {0x93e, 0x940, HB_Mark_SpacingCombining},
+  {0x941, 0x948, HB_Mark_NonSpacing},
+  {0x949, 0x94c, HB_Mark_SpacingCombining},
+  {0x94d, 0x94d, HB_Mark_NonSpacing},
+  {0x94e, 0x94f, HB_Other_NotAssigned},
+  {0x950, 0x950, HB_Letter_Other},
+  {0x951, 0x954, HB_Mark_NonSpacing},
+  {0x955, 0x957, HB_Other_NotAssigned},
+  {0x958, 0x961, HB_Letter_Other},
+  {0x962, 0x963, HB_Mark_NonSpacing},
+  {0x964, 0x965, HB_Punctuation_Other},
+  {0x966, 0x96f, HB_Number_DecimalDigit},
+  {0x970, 0x970, HB_Punctuation_Other},
+  {0x971, 0x971, HB_Letter_Modifier},
+  {0x972, 0x972, HB_Letter_Other},
+  {0x973, 0x97a, HB_Other_NotAssigned},
+  {0x97b, 0x97f, HB_Letter_Other},
+  {0x980, 0x980, HB_Other_NotAssigned},
+  {0x981, 0x981, HB_Mark_NonSpacing},
+  {0x982, 0x983, HB_Mark_SpacingCombining},
+  {0x984, 0x984, HB_Other_NotAssigned},
+  {0x985, 0x98c, HB_Letter_Other},
+  {0x98d, 0x98e, HB_Other_NotAssigned},
+  {0x98f, 0x990, HB_Letter_Other},
+  {0x991, 0x992, HB_Other_NotAssigned},
+  {0x993, 0x9a8, HB_Letter_Other},
+  {0x9a9, 0x9a9, HB_Other_NotAssigned},
+  {0x9aa, 0x9b0, HB_Letter_Other},
+  {0x9b1, 0x9b1, HB_Other_NotAssigned},
+  {0x9b2, 0x9b2, HB_Letter_Other},
+  {0x9b3, 0x9b5, HB_Other_NotAssigned},
+  {0x9b6, 0x9b9, HB_Letter_Other},
+  {0x9ba, 0x9bb, HB_Other_NotAssigned},
+  {0x9bc, 0x9bc, HB_Mark_NonSpacing},
+  {0x9bd, 0x9bd, HB_Letter_Other},
+  {0x9be, 0x9c0, HB_Mark_SpacingCombining},
+  {0x9c1, 0x9c4, HB_Mark_NonSpacing},
+  {0x9c5, 0x9c6, HB_Other_NotAssigned},
+  {0x9c7, 0x9c8, HB_Mark_SpacingCombining},
+  {0x9c9, 0x9ca, HB_Other_NotAssigned},
+  {0x9cb, 0x9cc, HB_Mark_SpacingCombining},
+  {0x9cd, 0x9cd, HB_Mark_NonSpacing},
+  {0x9ce, 0x9ce, HB_Letter_Other},
+  {0x9cf, 0x9d6, HB_Other_NotAssigned},
+  {0x9d7, 0x9d7, HB_Mark_SpacingCombining},
+  {0x9d8, 0x9db, HB_Other_NotAssigned},
+  {0x9dc, 0x9dd, HB_Letter_Other},
+  {0x9de, 0x9de, HB_Other_NotAssigned},
+  {0x9df, 0x9e1, HB_Letter_Other},
+  {0x9e2, 0x9e3, HB_Mark_NonSpacing},
+  {0x9e4, 0x9e5, HB_Other_NotAssigned},
+  {0x9e6, 0x9ef, HB_Number_DecimalDigit},
+  {0x9f0, 0x9f1, HB_Letter_Other},
+  {0x9f2, 0x9f3, HB_Symbol_Currency},
+  {0x9f4, 0x9f9, HB_Number_Other},
+  {0x9fa, 0x9fa, HB_Symbol_Other},
+  {0x9fb, 0xa00, HB_Other_NotAssigned},
+  {0xa01, 0xa02, HB_Mark_NonSpacing},
+  {0xa03, 0xa03, HB_Mark_SpacingCombining},
+  {0xa04, 0xa04, HB_Other_NotAssigned},
+  {0xa05, 0xa0a, HB_Letter_Other},
+  {0xa0b, 0xa0e, HB_Other_NotAssigned},
+  {0xa0f, 0xa10, HB_Letter_Other},
+  {0xa11, 0xa12, HB_Other_NotAssigned},
+  {0xa13, 0xa28, HB_Letter_Other},
+  {0xa29, 0xa29, HB_Other_NotAssigned},
+  {0xa2a, 0xa30, HB_Letter_Other},
+  {0xa31, 0xa31, HB_Other_NotAssigned},
+  {0xa32, 0xa33, HB_Letter_Other},
+  {0xa34, 0xa34, HB_Other_NotAssigned},
+  {0xa35, 0xa36, HB_Letter_Other},
+  {0xa37, 0xa37, HB_Other_NotAssigned},
+  {0xa38, 0xa39, HB_Letter_Other},
+  {0xa3a, 0xa3b, HB_Other_NotAssigned},
+  {0xa3c, 0xa3c, HB_Mark_NonSpacing},
+  {0xa3d, 0xa3d, HB_Other_NotAssigned},
+  {0xa3e, 0xa40, HB_Mark_SpacingCombining},
+  {0xa41, 0xa42, HB_Mark_NonSpacing},
+  {0xa43, 0xa46, HB_Other_NotAssigned},
+  {0xa47, 0xa48, HB_Mark_NonSpacing},
+  {0xa49, 0xa4a, HB_Other_NotAssigned},
+  {0xa4b, 0xa4d, HB_Mark_NonSpacing},
+  {0xa4e, 0xa50, HB_Other_NotAssigned},
+  {0xa51, 0xa51, HB_Mark_NonSpacing},
+  {0xa52, 0xa58, HB_Other_NotAssigned},
+  {0xa59, 0xa5c, HB_Letter_Other},
+  {0xa5d, 0xa5d, HB_Other_NotAssigned},
+  {0xa5e, 0xa5e, HB_Letter_Other},
+  {0xa5f, 0xa65, HB_Other_NotAssigned},
+  {0xa66, 0xa6f, HB_Number_DecimalDigit},
+  {0xa70, 0xa71, HB_Mark_NonSpacing},
+  {0xa72, 0xa74, HB_Letter_Other},
+  {0xa75, 0xa75, HB_Mark_NonSpacing},
+  {0xa76, 0xa80, HB_Other_NotAssigned},
+  {0xa81, 0xa82, HB_Mark_NonSpacing},
+  {0xa83, 0xa83, HB_Mark_SpacingCombining},
+  {0xa84, 0xa84, HB_Other_NotAssigned},
+  {0xa85, 0xa8d, HB_Letter_Other},
+  {0xa8e, 0xa8e, HB_Other_NotAssigned},
+  {0xa8f, 0xa91, HB_Letter_Other},
+  {0xa92, 0xa92, HB_Other_NotAssigned},
+  {0xa93, 0xaa8, HB_Letter_Other},
+  {0xaa9, 0xaa9, HB_Other_NotAssigned},
+  {0xaaa, 0xab0, HB_Letter_Other},
+  {0xab1, 0xab1, HB_Other_NotAssigned},
+  {0xab2, 0xab3, HB_Letter_Other},
+  {0xab4, 0xab4, HB_Other_NotAssigned},
+  {0xab5, 0xab9, HB_Letter_Other},
+  {0xaba, 0xabb, HB_Other_NotAssigned},
+  {0xabc, 0xabc, HB_Mark_NonSpacing},
+  {0xabd, 0xabd, HB_Letter_Other},
+  {0xabe, 0xac0, HB_Mark_SpacingCombining},
+  {0xac1, 0xac5, HB_Mark_NonSpacing},
+  {0xac6, 0xac6, HB_Other_NotAssigned},
+  {0xac7, 0xac8, HB_Mark_NonSpacing},
+  {0xac9, 0xac9, HB_Mark_SpacingCombining},
+  {0xaca, 0xaca, HB_Other_NotAssigned},
+  {0xacb, 0xacc, HB_Mark_SpacingCombining},
+  {0xacd, 0xacd, HB_Mark_NonSpacing},
+  {0xace, 0xacf, HB_Other_NotAssigned},
+  {0xad0, 0xad0, HB_Letter_Other},
+  {0xad1, 0xadf, HB_Other_NotAssigned},
+  {0xae0, 0xae1, HB_Letter_Other},
+  {0xae2, 0xae3, HB_Mark_NonSpacing},
+  {0xae4, 0xae5, HB_Other_NotAssigned},
+  {0xae6, 0xaef, HB_Number_DecimalDigit},
+  {0xaf0, 0xaf0, HB_Other_NotAssigned},
+  {0xaf1, 0xaf1, HB_Symbol_Currency},
+  {0xaf2, 0xb00, HB_Other_NotAssigned},
+  {0xb01, 0xb01, HB_Mark_NonSpacing},
+  {0xb02, 0xb03, HB_Mark_SpacingCombining},
+  {0xb04, 0xb04, HB_Other_NotAssigned},
+  {0xb05, 0xb0c, HB_Letter_Other},
+  {0xb0d, 0xb0e, HB_Other_NotAssigned},
+  {0xb0f, 0xb10, HB_Letter_Other},
+  {0xb11, 0xb12, HB_Other_NotAssigned},
+  {0xb13, 0xb28, HB_Letter_Other},
+  {0xb29, 0xb29, HB_Other_NotAssigned},
+  {0xb2a, 0xb30, HB_Letter_Other},
+  {0xb31, 0xb31, HB_Other_NotAssigned},
+  {0xb32, 0xb33, HB_Letter_Other},
+  {0xb34, 0xb34, HB_Other_NotAssigned},
+  {0xb35, 0xb39, HB_Letter_Other},
+  {0xb3a, 0xb3b, HB_Other_NotAssigned},
+  {0xb3c, 0xb3c, HB_Mark_NonSpacing},
+  {0xb3d, 0xb3d, HB_Letter_Other},
+  {0xb3e, 0xb3e, HB_Mark_SpacingCombining},
+  {0xb3f, 0xb3f, HB_Mark_NonSpacing},
+  {0xb40, 0xb40, HB_Mark_SpacingCombining},
+  {0xb41, 0xb44, HB_Mark_NonSpacing},
+  {0xb45, 0xb46, HB_Other_NotAssigned},
+  {0xb47, 0xb48, HB_Mark_SpacingCombining},
+  {0xb49, 0xb4a, HB_Other_NotAssigned},
+  {0xb4b, 0xb4c, HB_Mark_SpacingCombining},
+  {0xb4d, 0xb4d, HB_Mark_NonSpacing},
+  {0xb4e, 0xb55, HB_Other_NotAssigned},
+  {0xb56, 0xb56, HB_Mark_NonSpacing},
+  {0xb57, 0xb57, HB_Mark_SpacingCombining},
+  {0xb58, 0xb5b, HB_Other_NotAssigned},
+  {0xb5c, 0xb5d, HB_Letter_Other},
+  {0xb5e, 0xb5e, HB_Other_NotAssigned},
+  {0xb5f, 0xb61, HB_Letter_Other},
+  {0xb62, 0xb63, HB_Mark_NonSpacing},
+  {0xb64, 0xb65, HB_Other_NotAssigned},
+  {0xb66, 0xb6f, HB_Number_DecimalDigit},
+  {0xb70, 0xb70, HB_Symbol_Other},
+  {0xb71, 0xb71, HB_Letter_Other},
+  {0xb72, 0xb81, HB_Other_NotAssigned},
+  {0xb82, 0xb82, HB_Mark_NonSpacing},
+  {0xb83, 0xb83, HB_Letter_Other},
+  {0xb84, 0xb84, HB_Other_NotAssigned},
+  {0xb85, 0xb8a, HB_Letter_Other},
+  {0xb8b, 0xb8d, HB_Other_NotAssigned},
+  {0xb8e, 0xb90, HB_Letter_Other},
+  {0xb91, 0xb91, HB_Other_NotAssigned},
+  {0xb92, 0xb95, HB_Letter_Other},
+  {0xb96, 0xb98, HB_Other_NotAssigned},
+  {0xb99, 0xb9a, HB_Letter_Other},
+  {0xb9b, 0xb9b, HB_Other_NotAssigned},
+  {0xb9c, 0xb9c, HB_Letter_Other},
+  {0xb9d, 0xb9d, HB_Other_NotAssigned},
+  {0xb9e, 0xb9f, HB_Letter_Other},
+  {0xba0, 0xba2, HB_Other_NotAssigned},
+  {0xba3, 0xba4, HB_Letter_Other},
+  {0xba5, 0xba7, HB_Other_NotAssigned},
+  {0xba8, 0xbaa, HB_Letter_Other},
+  {0xbab, 0xbad, HB_Other_NotAssigned},
+  {0xbae, 0xbb9, HB_Letter_Other},
+  {0xbba, 0xbbd, HB_Other_NotAssigned},
+  {0xbbe, 0xbbf, HB_Mark_SpacingCombining},
+  {0xbc0, 0xbc0, HB_Mark_NonSpacing},
+  {0xbc1, 0xbc2, HB_Mark_SpacingCombining},
+  {0xbc3, 0xbc5, HB_Other_NotAssigned},
+  {0xbc6, 0xbc8, HB_Mark_SpacingCombining},
+  {0xbc9, 0xbc9, HB_Other_NotAssigned},
+  {0xbca, 0xbcc, HB_Mark_SpacingCombining},
+  {0xbcd, 0xbcd, HB_Mark_NonSpacing},
+  {0xbce, 0xbcf, HB_Other_NotAssigned},
+  {0xbd0, 0xbd0, HB_Letter_Other},
+  {0xbd1, 0xbd6, HB_Other_NotAssigned},
+  {0xbd7, 0xbd7, HB_Mark_SpacingCombining},
+  {0xbd8, 0xbe5, HB_Other_NotAssigned},
+  {0xbe6, 0xbef, HB_Number_DecimalDigit},
+  {0xbf0, 0xbf2, HB_Number_Other},
+  {0xbf3, 0xbf8, HB_Symbol_Other},
+  {0xbf9, 0xbf9, HB_Symbol_Currency},
+  {0xbfa, 0xbfa, HB_Symbol_Other},
+  {0xbfb, 0xc00, HB_Other_NotAssigned},
+  {0xc01, 0xc03, HB_Mark_SpacingCombining},
+  {0xc04, 0xc04, HB_Other_NotAssigned},
+  {0xc05, 0xc0c, HB_Letter_Other},
+  {0xc0d, 0xc0d, HB_Other_NotAssigned},
+  {0xc0e, 0xc10, HB_Letter_Other},
+  {0xc11, 0xc11, HB_Other_NotAssigned},
+  {0xc12, 0xc28, HB_Letter_Other},
+  {0xc29, 0xc29, HB_Other_NotAssigned},
+  {0xc2a, 0xc33, HB_Letter_Other},
+  {0xc34, 0xc34, HB_Other_NotAssigned},
+  {0xc35, 0xc39, HB_Letter_Other},
+  {0xc3a, 0xc3c, HB_Other_NotAssigned},
+  {0xc3d, 0xc3d, HB_Letter_Other},
+  {0xc3e, 0xc40, HB_Mark_NonSpacing},
+  {0xc41, 0xc44, HB_Mark_SpacingCombining},
+  {0xc45, 0xc45, HB_Other_NotAssigned},
+  {0xc46, 0xc48, HB_Mark_NonSpacing},
+  {0xc49, 0xc49, HB_Other_NotAssigned},
+  {0xc4a, 0xc4d, HB_Mark_NonSpacing},
+  {0xc4e, 0xc54, HB_Other_NotAssigned},
+  {0xc55, 0xc56, HB_Mark_NonSpacing},
+  {0xc57, 0xc57, HB_Other_NotAssigned},
+  {0xc58, 0xc59, HB_Letter_Other},
+  {0xc5a, 0xc5f, HB_Other_NotAssigned},
+  {0xc60, 0xc61, HB_Letter_Other},
+  {0xc62, 0xc63, HB_Mark_NonSpacing},
+  {0xc64, 0xc65, HB_Other_NotAssigned},
+  {0xc66, 0xc6f, HB_Number_DecimalDigit},
+  {0xc70, 0xc77, HB_Other_NotAssigned},
+  {0xc78, 0xc7e, HB_Number_Other},
+  {0xc7f, 0xc7f, HB_Symbol_Other},
+  {0xc80, 0xc81, HB_Other_NotAssigned},
+  {0xc82, 0xc83, HB_Mark_SpacingCombining},
+  {0xc84, 0xc84, HB_Other_NotAssigned},
+  {0xc85, 0xc8c, HB_Letter_Other},
+  {0xc8d, 0xc8d, HB_Other_NotAssigned},
+  {0xc8e, 0xc90, HB_Letter_Other},
+  {0xc91, 0xc91, HB_Other_NotAssigned},
+  {0xc92, 0xca8, HB_Letter_Other},
+  {0xca9, 0xca9, HB_Other_NotAssigned},
+  {0xcaa, 0xcb3, HB_Letter_Other},
+  {0xcb4, 0xcb4, HB_Other_NotAssigned},
+  {0xcb5, 0xcb9, HB_Letter_Other},
+  {0xcba, 0xcbb, HB_Other_NotAssigned},
+  {0xcbc, 0xcbc, HB_Mark_NonSpacing},
+  {0xcbd, 0xcbd, HB_Letter_Other},
+  {0xcbe, 0xcbe, HB_Mark_SpacingCombining},
+  {0xcbf, 0xcbf, HB_Mark_NonSpacing},
+  {0xcc0, 0xcc4, HB_Mark_SpacingCombining},
+  {0xcc5, 0xcc5, HB_Other_NotAssigned},
+  {0xcc6, 0xcc6, HB_Mark_NonSpacing},
+  {0xcc7, 0xcc8, HB_Mark_SpacingCombining},
+  {0xcc9, 0xcc9, HB_Other_NotAssigned},
+  {0xcca, 0xccb, HB_Mark_SpacingCombining},
+  {0xccc, 0xccd, HB_Mark_NonSpacing},
+  {0xcce, 0xcd4, HB_Other_NotAssigned},
+  {0xcd5, 0xcd6, HB_Mark_SpacingCombining},
+  {0xcd7, 0xcdd, HB_Other_NotAssigned},
+  {0xcde, 0xcde, HB_Letter_Other},
+  {0xcdf, 0xcdf, HB_Other_NotAssigned},
+  {0xce0, 0xce1, HB_Letter_Other},
+  {0xce2, 0xce3, HB_Mark_NonSpacing},
+  {0xce4, 0xce5, HB_Other_NotAssigned},
+  {0xce6, 0xcef, HB_Number_DecimalDigit},
+  {0xcf0, 0xcf0, HB_Other_NotAssigned},
+  {0xcf1, 0xcf2, HB_Symbol_Other},
+  {0xcf3, 0xd01, HB_Other_NotAssigned},
+  {0xd02, 0xd03, HB_Mark_SpacingCombining},
+  {0xd04, 0xd04, HB_Other_NotAssigned},
+  {0xd05, 0xd0c, HB_Letter_Other},
+  {0xd0d, 0xd0d, HB_Other_NotAssigned},
+  {0xd0e, 0xd10, HB_Letter_Other},
+  {0xd11, 0xd11, HB_Other_NotAssigned},
+  {0xd12, 0xd28, HB_Letter_Other},
+  {0xd29, 0xd29, HB_Other_NotAssigned},
+  {0xd2a, 0xd39, HB_Letter_Other},
+  {0xd3a, 0xd3c, HB_Other_NotAssigned},
+  {0xd3d, 0xd3d, HB_Letter_Other},
+  {0xd3e, 0xd40, HB_Mark_SpacingCombining},
+  {0xd41, 0xd44, HB_Mark_NonSpacing},
+  {0xd45, 0xd45, HB_Other_NotAssigned},
+  {0xd46, 0xd48, HB_Mark_SpacingCombining},
+  {0xd49, 0xd49, HB_Other_NotAssigned},
+  {0xd4a, 0xd4c, HB_Mark_SpacingCombining},
+  {0xd4d, 0xd4d, HB_Mark_NonSpacing},
+  {0xd4e, 0xd56, HB_Other_NotAssigned},
+  {0xd57, 0xd57, HB_Mark_SpacingCombining},
+  {0xd58, 0xd5f, HB_Other_NotAssigned},
+  {0xd60, 0xd61, HB_Letter_Other},
+  {0xd62, 0xd63, HB_Mark_NonSpacing},
+  {0xd64, 0xd65, HB_Other_NotAssigned},
+  {0xd66, 0xd6f, HB_Number_DecimalDigit},
+  {0xd70, 0xd75, HB_Number_Other},
+  {0xd76, 0xd78, HB_Other_NotAssigned},
+  {0xd79, 0xd79, HB_Symbol_Other},
+  {0xd7a, 0xd7f, HB_Letter_Other},
+  {0xd80, 0xd81, HB_Other_NotAssigned},
+  {0xd82, 0xd83, HB_Mark_SpacingCombining},
+  {0xd84, 0xd84, HB_Other_NotAssigned},
+  {0xd85, 0xd96, HB_Letter_Other},
+  {0xd97, 0xd99, HB_Other_NotAssigned},
+  {0xd9a, 0xdb1, HB_Letter_Other},
+  {0xdb2, 0xdb2, HB_Other_NotAssigned},
+  {0xdb3, 0xdbb, HB_Letter_Other},
+  {0xdbc, 0xdbc, HB_Other_NotAssigned},
+  {0xdbd, 0xdbd, HB_Letter_Other},
+  {0xdbe, 0xdbf, HB_Other_NotAssigned},
+  {0xdc0, 0xdc6, HB_Letter_Other},
+  {0xdc7, 0xdc9, HB_Other_NotAssigned},
+  {0xdca, 0xdca, HB_Mark_NonSpacing},
+  {0xdcb, 0xdce, HB_Other_NotAssigned},
+  {0xdcf, 0xdd1, HB_Mark_SpacingCombining},
+  {0xdd2, 0xdd4, HB_Mark_NonSpacing},
+  {0xdd5, 0xdd5, HB_Other_NotAssigned},
+  {0xdd6, 0xdd6, HB_Mark_NonSpacing},
+  {0xdd7, 0xdd7, HB_Other_NotAssigned},
+  {0xdd8, 0xddf, HB_Mark_SpacingCombining},
+  {0xde0, 0xdf1, HB_Other_NotAssigned},
+  {0xdf2, 0xdf3, HB_Mark_SpacingCombining},
+  {0xdf4, 0xdf4, HB_Punctuation_Other},
+  {0xdf5, 0xe00, HB_Other_NotAssigned},
+  {0xe01, 0xe30, HB_Letter_Other},
+  {0xe31, 0xe31, HB_Mark_NonSpacing},
+  {0xe32, 0xe33, HB_Letter_Other},
+  {0xe34, 0xe3a, HB_Mark_NonSpacing},
+  {0xe3b, 0xe3e, HB_Other_NotAssigned},
+  {0xe3f, 0xe3f, HB_Symbol_Currency},
+  {0xe40, 0xe45, HB_Letter_Other},
+  {0xe46, 0xe46, HB_Letter_Modifier},
+  {0xe47, 0xe4e, HB_Mark_NonSpacing},
+  {0xe4f, 0xe4f, HB_Punctuation_Other},
+  {0xe50, 0xe59, HB_Number_DecimalDigit},
+  {0xe5a, 0xe5b, HB_Punctuation_Other},
+  {0xe5c, 0xe80, HB_Other_NotAssigned},
+  {0xe81, 0xe82, HB_Letter_Other},
+  {0xe83, 0xe83, HB_Other_NotAssigned},
+  {0xe84, 0xe84, HB_Letter_Other},
+  {0xe85, 0xe86, HB_Other_NotAssigned},
+  {0xe87, 0xe88, HB_Letter_Other},
+  {0xe89, 0xe89, HB_Other_NotAssigned},
+  {0xe8a, 0xe8a, HB_Letter_Other},
+  {0xe8b, 0xe8c, HB_Other_NotAssigned},
+  {0xe8d, 0xe8d, HB_Letter_Other},
+  {0xe8e, 0xe93, HB_Other_NotAssigned},
+  {0xe94, 0xe97, HB_Letter_Other},
+  {0xe98, 0xe98, HB_Other_NotAssigned},
+  {0xe99, 0xe9f, HB_Letter_Other},
+  {0xea0, 0xea0, HB_Other_NotAssigned},
+  {0xea1, 0xea3, HB_Letter_Other},
+  {0xea4, 0xea4, HB_Other_NotAssigned},
+  {0xea5, 0xea5, HB_Letter_Other},
+  {0xea6, 0xea6, HB_Other_NotAssigned},
+  {0xea7, 0xea7, HB_Letter_Other},
+  {0xea8, 0xea9, HB_Other_NotAssigned},
+  {0xeaa, 0xeab, HB_Letter_Other},
+  {0xeac, 0xeac, HB_Other_NotAssigned},
+  {0xead, 0xeb0, HB_Letter_Other},
+  {0xeb1, 0xeb1, HB_Mark_NonSpacing},
+  {0xeb2, 0xeb3, HB_Letter_Other},
+  {0xeb4, 0xeb9, HB_Mark_NonSpacing},
+  {0xeba, 0xeba, HB_Other_NotAssigned},
+  {0xebb, 0xebc, HB_Mark_NonSpacing},
+  {0xebd, 0xebd, HB_Letter_Other},
+  {0xebe, 0xebf, HB_Other_NotAssigned},
+  {0xec0, 0xec4, HB_Letter_Other},
+  {0xec5, 0xec5, HB_Other_NotAssigned},
+  {0xec6, 0xec6, HB_Letter_Modifier},
+  {0xec7, 0xec7, HB_Other_NotAssigned},
+  {0xec8, 0xecd, HB_Mark_NonSpacing},
+  {0xece, 0xecf, HB_Other_NotAssigned},
+  {0xed0, 0xed9, HB_Number_DecimalDigit},
+  {0xeda, 0xedb, HB_Other_NotAssigned},
+  {0xedc, 0xedd, HB_Letter_Other},
+  {0xede, 0xeff, HB_Other_NotAssigned},
+  {0xf00, 0xf00, HB_Letter_Other},
+  {0xf01, 0xf03, HB_Symbol_Other},
+  {0xf04, 0xf12, HB_Punctuation_Other},
+  {0xf13, 0xf17, HB_Symbol_Other},
+  {0xf18, 0xf19, HB_Mark_NonSpacing},
+  {0xf1a, 0xf1f, HB_Symbol_Other},
+  {0xf20, 0xf29, HB_Number_DecimalDigit},
+  {0xf2a, 0xf33, HB_Number_Other},
+  {0xf34, 0xf34, HB_Symbol_Other},
+  {0xf35, 0xf35, HB_Mark_NonSpacing},
+  {0xf36, 0xf36, HB_Symbol_Other},
+  {0xf37, 0xf37, HB_Mark_NonSpacing},
+  {0xf38, 0xf38, HB_Symbol_Other},
+  {0xf39, 0xf39, HB_Mark_NonSpacing},
+  {0xf3a, 0xf3a, HB_Punctuation_Open},
+  {0xf3b, 0xf3b, HB_Punctuation_Close},
+  {0xf3c, 0xf3c, HB_Punctuation_Open},
+  {0xf3d, 0xf3d, HB_Punctuation_Close},
+  {0xf3e, 0xf3f, HB_Mark_SpacingCombining},
+  {0xf40, 0xf47, HB_Letter_Other},
+  {0xf48, 0xf48, HB_Other_NotAssigned},
+  {0xf49, 0xf6c, HB_Letter_Other},
+  {0xf6d, 0xf70, HB_Other_NotAssigned},
+  {0xf71, 0xf7e, HB_Mark_NonSpacing},
+  {0xf7f, 0xf7f, HB_Mark_SpacingCombining},
+  {0xf80, 0xf84, HB_Mark_NonSpacing},
+  {0xf85, 0xf85, HB_Punctuation_Other},
+  {0xf86, 0xf87, HB_Mark_NonSpacing},
+  {0xf88, 0xf8b, HB_Letter_Other},
+  {0xf8c, 0xf8f, HB_Other_NotAssigned},
+  {0xf90, 0xf97, HB_Mark_NonSpacing},
+  {0xf98, 0xf98, HB_Other_NotAssigned},
+  {0xf99, 0xfbc, HB_Mark_NonSpacing},
+  {0xfbd, 0xfbd, HB_Other_NotAssigned},
+  {0xfbe, 0xfc5, HB_Symbol_Other},
+  {0xfc6, 0xfc6, HB_Mark_NonSpacing},
+  {0xfc7, 0xfcc, HB_Symbol_Other},
+  {0xfcd, 0xfcd, HB_Other_NotAssigned},
+  {0xfce, 0xfcf, HB_Symbol_Other},
+  {0xfd0, 0xfd4, HB_Punctuation_Other},
+  {0xfd5, 0xfff, HB_Other_NotAssigned},
+  {0x1000, 0x102a, HB_Letter_Other},
+  {0x102b, 0x102c, HB_Mark_SpacingCombining},
+  {0x102d, 0x1030, HB_Mark_NonSpacing},
+  {0x1031, 0x1031, HB_Mark_SpacingCombining},
+  {0x1032, 0x1037, HB_Mark_NonSpacing},
+  {0x1038, 0x1038, HB_Mark_SpacingCombining},
+  {0x1039, 0x103a, HB_Mark_NonSpacing},
+  {0x103b, 0x103c, HB_Mark_SpacingCombining},
+  {0x103d, 0x103e, HB_Mark_NonSpacing},
+  {0x103f, 0x103f, HB_Letter_Other},
+  {0x1040, 0x1049, HB_Number_DecimalDigit},
+  {0x104a, 0x104f, HB_Punctuation_Other},
+  {0x1050, 0x1055, HB_Letter_Other},
+  {0x1056, 0x1057, HB_Mark_SpacingCombining},
+  {0x1058, 0x1059, HB_Mark_NonSpacing},
+  {0x105a, 0x105d, HB_Letter_Other},
+  {0x105e, 0x1060, HB_Mark_NonSpacing},
+  {0x1061, 0x1061, HB_Letter_Other},
+  {0x1062, 0x1064, HB_Mark_SpacingCombining},
+  {0x1065, 0x1066, HB_Letter_Other},
+  {0x1067, 0x106d, HB_Mark_SpacingCombining},
+  {0x106e, 0x1070, HB_Letter_Other},
+  {0x1071, 0x1074, HB_Mark_NonSpacing},
+  {0x1075, 0x1081, HB_Letter_Other},
+  {0x1082, 0x1082, HB_Mark_NonSpacing},
+  {0x1083, 0x1084, HB_Mark_SpacingCombining},
+  {0x1085, 0x1086, HB_Mark_NonSpacing},
+  {0x1087, 0x108c, HB_Mark_SpacingCombining},
+  {0x108d, 0x108d, HB_Mark_NonSpacing},
+  {0x108e, 0x108e, HB_Letter_Other},
+  {0x108f, 0x108f, HB_Mark_SpacingCombining},
+  {0x1090, 0x1099, HB_Number_DecimalDigit},
+  {0x109a, 0x109d, HB_Other_NotAssigned},
+  {0x109e, 0x109f, HB_Symbol_Other},
+  {0x10a0, 0x10c5, HB_Letter_Uppercase},
+  {0x10c6, 0x10cf, HB_Other_NotAssigned},
+  {0x10d0, 0x10fa, HB_Letter_Other},
+  {0x10fb, 0x10fb, HB_Punctuation_Other},
+  {0x10fc, 0x10fc, HB_Letter_Modifier},
+  {0x10fd, 0x10ff, HB_Other_NotAssigned},
+  {0x1100, 0x1159, HB_Letter_Other},
+  {0x115a, 0x115e, HB_Other_NotAssigned},
+  {0x115f, 0x11a2, HB_Letter_Other},
+  {0x11a3, 0x11a7, HB_Other_NotAssigned},
+  {0x11a8, 0x11f9, HB_Letter_Other},
+  {0x11fa, 0x11ff, HB_Other_NotAssigned},
+  {0x1200, 0x1248, HB_Letter_Other},
+  {0x1249, 0x1249, HB_Other_NotAssigned},
+  {0x124a, 0x124d, HB_Letter_Other},
+  {0x124e, 0x124f, HB_Other_NotAssigned},
+  {0x1250, 0x1256, HB_Letter_Other},
+  {0x1257, 0x1257, HB_Other_NotAssigned},
+  {0x1258, 0x1258, HB_Letter_Other},
+  {0x1259, 0x1259, HB_Other_NotAssigned},
+  {0x125a, 0x125d, HB_Letter_Other},
+  {0x125e, 0x125f, HB_Other_NotAssigned},
+  {0x1260, 0x1288, HB_Letter_Other},
+  {0x1289, 0x1289, HB_Other_NotAssigned},
+  {0x128a, 0x128d, HB_Letter_Other},
+  {0x128e, 0x128f, HB_Other_NotAssigned},
+  {0x1290, 0x12b0, HB_Letter_Other},
+  {0x12b1, 0x12b1, HB_Other_NotAssigned},
+  {0x12b2, 0x12b5, HB_Letter_Other},
+  {0x12b6, 0x12b7, HB_Other_NotAssigned},
+  {0x12b8, 0x12be, HB_Letter_Other},
+  {0x12bf, 0x12bf, HB_Other_NotAssigned},
+  {0x12c0, 0x12c0, HB_Letter_Other},
+  {0x12c1, 0x12c1, HB_Other_NotAssigned},
+  {0x12c2, 0x12c5, HB_Letter_Other},
+  {0x12c6, 0x12c7, HB_Other_NotAssigned},
+  {0x12c8, 0x12d6, HB_Letter_Other},
+  {0x12d7, 0x12d7, HB_Other_NotAssigned},
+  {0x12d8, 0x1310, HB_Letter_Other},
+  {0x1311, 0x1311, HB_Other_NotAssigned},
+  {0x1312, 0x1315, HB_Letter_Other},
+  {0x1316, 0x1317, HB_Other_NotAssigned},
+  {0x1318, 0x135a, HB_Letter_Other},
+  {0x135b, 0x135e, HB_Other_NotAssigned},
+  {0x135f, 0x135f, HB_Mark_NonSpacing},
+  {0x1360, 0x1360, HB_Symbol_Other},
+  {0x1361, 0x1368, HB_Punctuation_Other},
+  {0x1369, 0x137c, HB_Number_Other},
+  {0x137d, 0x137f, HB_Other_NotAssigned},
+  {0x1380, 0x138f, HB_Letter_Other},
+  {0x1390, 0x1399, HB_Symbol_Other},
+  {0x139a, 0x139f, HB_Other_NotAssigned},
+  {0x13a0, 0x13f4, HB_Letter_Other},
+  {0x13f5, 0x1400, HB_Other_NotAssigned},
+  {0x1401, 0x166c, HB_Letter_Other},
+  {0x166d, 0x166e, HB_Punctuation_Other},
+  {0x166f, 0x1676, HB_Letter_Other},
+  {0x1677, 0x167f, HB_Other_NotAssigned},
+  {0x1680, 0x1680, HB_Separator_Space},
+  {0x1681, 0x169a, HB_Letter_Other},
+  {0x169b, 0x169b, HB_Punctuation_Open},
+  {0x169c, 0x169c, HB_Punctuation_Close},
+  {0x169d, 0x169f, HB_Other_NotAssigned},
+  {0x16a0, 0x16ea, HB_Letter_Other},
+  {0x16eb, 0x16ed, HB_Punctuation_Other},
+  {0x16ee, 0x16f0, HB_Number_Letter},
+  {0x16f1, 0x16ff, HB_Other_NotAssigned},
+  {0x1700, 0x170c, HB_Letter_Other},
+  {0x170d, 0x170d, HB_Other_NotAssigned},
+  {0x170e, 0x1711, HB_Letter_Other},
+  {0x1712, 0x1714, HB_Mark_NonSpacing},
+  {0x1715, 0x171f, HB_Other_NotAssigned},
+  {0x1720, 0x1731, HB_Letter_Other},
+  {0x1732, 0x1734, HB_Mark_NonSpacing},
+  {0x1735, 0x1736, HB_Punctuation_Other},
+  {0x1737, 0x173f, HB_Other_NotAssigned},
+  {0x1740, 0x1751, HB_Letter_Other},
+  {0x1752, 0x1753, HB_Mark_NonSpacing},
+  {0x1754, 0x175f, HB_Other_NotAssigned},
+  {0x1760, 0x176c, HB_Letter_Other},
+  {0x176d, 0x176d, HB_Other_NotAssigned},
+  {0x176e, 0x1770, HB_Letter_Other},
+  {0x1771, 0x1771, HB_Other_NotAssigned},
+  {0x1772, 0x1773, HB_Mark_NonSpacing},
+  {0x1774, 0x177f, HB_Other_NotAssigned},
+  {0x1780, 0x17b3, HB_Letter_Other},
+  {0x17b4, 0x17b5, HB_Other_Format},
+  {0x17b6, 0x17b6, HB_Mark_SpacingCombining},
+  {0x17b7, 0x17bd, HB_Mark_NonSpacing},
+  {0x17be, 0x17c5, HB_Mark_SpacingCombining},
+  {0x17c6, 0x17c6, HB_Mark_NonSpacing},
+  {0x17c7, 0x17c8, HB_Mark_SpacingCombining},
+  {0x17c9, 0x17d3, HB_Mark_NonSpacing},
+  {0x17d4, 0x17d6, HB_Punctuation_Other},
+  {0x17d7, 0x17d7, HB_Letter_Modifier},
+  {0x17d8, 0x17da, HB_Punctuation_Other},
+  {0x17db, 0x17db, HB_Symbol_Currency},
+  {0x17dc, 0x17dc, HB_Letter_Other},
+  {0x17dd, 0x17dd, HB_Mark_NonSpacing},
+  {0x17de, 0x17df, HB_Other_NotAssigned},
+  {0x17e0, 0x17e9, HB_Number_DecimalDigit},
+  {0x17ea, 0x17ef, HB_Other_NotAssigned},
+  {0x17f0, 0x17f9, HB_Number_Other},
+  {0x17fa, 0x17ff, HB_Other_NotAssigned},
+  {0x1800, 0x1805, HB_Punctuation_Other},
+  {0x1806, 0x1806, HB_Punctuation_Dash},
+  {0x1807, 0x180a, HB_Punctuation_Other},
+  {0x180b, 0x180d, HB_Mark_NonSpacing},
+  {0x180e, 0x180e, HB_Separator_Space},
+  {0x180f, 0x180f, HB_Other_NotAssigned},
+  {0x1810, 0x1819, HB_Number_DecimalDigit},
+  {0x181a, 0x181f, HB_Other_NotAssigned},
+  {0x1820, 0x1842, HB_Letter_Other},
+  {0x1843, 0x1843, HB_Letter_Modifier},
+  {0x1844, 0x1877, HB_Letter_Other},
+  {0x1878, 0x187f, HB_Other_NotAssigned},
+  {0x1880, 0x18a8, HB_Letter_Other},
+  {0x18a9, 0x18a9, HB_Mark_NonSpacing},
+  {0x18aa, 0x18aa, HB_Letter_Other},
+  {0x18ab, 0x18ff, HB_Other_NotAssigned},
+  {0x1900, 0x191c, HB_Letter_Other},
+  {0x191d, 0x191f, HB_Other_NotAssigned},
+  {0x1920, 0x1922, HB_Mark_NonSpacing},
+  {0x1923, 0x1926, HB_Mark_SpacingCombining},
+  {0x1927, 0x1928, HB_Mark_NonSpacing},
+  {0x1929, 0x192b, HB_Mark_SpacingCombining},
+  {0x192c, 0x192f, HB_Other_NotAssigned},
+  {0x1930, 0x1931, HB_Mark_SpacingCombining},
+  {0x1932, 0x1932, HB_Mark_NonSpacing},
+  {0x1933, 0x1938, HB_Mark_SpacingCombining},
+  {0x1939, 0x193b, HB_Mark_NonSpacing},
+  {0x193c, 0x193f, HB_Other_NotAssigned},
+  {0x1940, 0x1940, HB_Symbol_Other},
+  {0x1941, 0x1943, HB_Other_NotAssigned},
+  {0x1944, 0x1945, HB_Punctuation_Other},
+  {0x1946, 0x194f, HB_Number_DecimalDigit},
+  {0x1950, 0x196d, HB_Letter_Other},
+  {0x196e, 0x196f, HB_Other_NotAssigned},
+  {0x1970, 0x1974, HB_Letter_Other},
+  {0x1975, 0x197f, HB_Other_NotAssigned},
+  {0x1980, 0x19a9, HB_Letter_Other},
+  {0x19aa, 0x19af, HB_Other_NotAssigned},
+  {0x19b0, 0x19c0, HB_Mark_SpacingCombining},
+  {0x19c1, 0x19c7, HB_Letter_Other},
+  {0x19c8, 0x19c9, HB_Mark_SpacingCombining},
+  {0x19ca, 0x19cf, HB_Other_NotAssigned},
+  {0x19d0, 0x19d9, HB_Number_DecimalDigit},
+  {0x19da, 0x19dd, HB_Other_NotAssigned},
+  {0x19de, 0x19df, HB_Punctuation_Other},
+  {0x19e0, 0x19ff, HB_Symbol_Other},
+  {0x1a00, 0x1a16, HB_Letter_Other},
+  {0x1a17, 0x1a18, HB_Mark_NonSpacing},
+  {0x1a19, 0x1a1b, HB_Mark_SpacingCombining},
+  {0x1a1c, 0x1a1d, HB_Other_NotAssigned},
+  {0x1a1e, 0x1a1f, HB_Punctuation_Other},
+  {0x1a20, 0x1aff, HB_Other_NotAssigned},
+  {0x1b00, 0x1b03, HB_Mark_NonSpacing},
+  {0x1b04, 0x1b04, HB_Mark_SpacingCombining},
+  {0x1b05, 0x1b33, HB_Letter_Other},
+  {0x1b34, 0x1b34, HB_Mark_NonSpacing},
+  {0x1b35, 0x1b35, HB_Mark_SpacingCombining},
+  {0x1b36, 0x1b3a, HB_Mark_NonSpacing},
+  {0x1b3b, 0x1b3b, HB_Mark_SpacingCombining},
+  {0x1b3c, 0x1b3c, HB_Mark_NonSpacing},
+  {0x1b3d, 0x1b41, HB_Mark_SpacingCombining},
+  {0x1b42, 0x1b42, HB_Mark_NonSpacing},
+  {0x1b43, 0x1b44, HB_Mark_SpacingCombining},
+  {0x1b45, 0x1b4b, HB_Letter_Other},
+  {0x1b4c, 0x1b4f, HB_Other_NotAssigned},
+  {0x1b50, 0x1b59, HB_Number_DecimalDigit},
+  {0x1b5a, 0x1b60, HB_Punctuation_Other},
+  {0x1b61, 0x1b6a, HB_Symbol_Other},
+  {0x1b6b, 0x1b73, HB_Mark_NonSpacing},
+  {0x1b74, 0x1b7c, HB_Symbol_Other},
+  {0x1b7d, 0x1b7f, HB_Other_NotAssigned},
+  {0x1b80, 0x1b81, HB_Mark_NonSpacing},
+  {0x1b82, 0x1b82, HB_Mark_SpacingCombining},
+  {0x1b83, 0x1ba0, HB_Letter_Other},
+  {0x1ba1, 0x1ba1, HB_Mark_SpacingCombining},
+  {0x1ba2, 0x1ba5, HB_Mark_NonSpacing},
+  {0x1ba6, 0x1ba7, HB_Mark_SpacingCombining},
+  {0x1ba8, 0x1ba9, HB_Mark_NonSpacing},
+  {0x1baa, 0x1baa, HB_Mark_SpacingCombining},
+  {0x1bab, 0x1bad, HB_Other_NotAssigned},
+  {0x1bae, 0x1baf, HB_Letter_Other},
+  {0x1bb0, 0x1bb9, HB_Number_DecimalDigit},
+  {0x1bba, 0x1bff, HB_Other_NotAssigned},
+  {0x1c00, 0x1c23, HB_Letter_Other},
+  {0x1c24, 0x1c2b, HB_Mark_SpacingCombining},
+  {0x1c2c, 0x1c33, HB_Mark_NonSpacing},
+  {0x1c34, 0x1c35, HB_Mark_SpacingCombining},
+  {0x1c36, 0x1c37, HB_Mark_NonSpacing},
+  {0x1c38, 0x1c3a, HB_Other_NotAssigned},
+  {0x1c3b, 0x1c3f, HB_Punctuation_Other},
+  {0x1c40, 0x1c49, HB_Number_DecimalDigit},
+  {0x1c4a, 0x1c4c, HB_Other_NotAssigned},
+  {0x1c4d, 0x1c4f, HB_Letter_Other},
+  {0x1c50, 0x1c59, HB_Number_DecimalDigit},
+  {0x1c5a, 0x1c77, HB_Letter_Other},
+  {0x1c78, 0x1c7d, HB_Letter_Modifier},
+  {0x1c7e, 0x1c7f, HB_Punctuation_Other},
+  {0x1c80, 0x1cff, HB_Other_NotAssigned},
+  {0x1d00, 0x1d2b, HB_Letter_Lowercase},
+  {0x1d2c, 0x1d61, HB_Letter_Modifier},
+  {0x1d62, 0x1d77, HB_Letter_Lowercase},
+  {0x1d78, 0x1d78, HB_Letter_Modifier},
+  {0x1d79, 0x1d9a, HB_Letter_Lowercase},
+  {0x1d9b, 0x1dbf, HB_Letter_Modifier},
+  {0x1dc0, 0x1de6, HB_Mark_NonSpacing},
+  {0x1de7, 0x1dfd, HB_Other_NotAssigned},
+  {0x1dfe, 0x1dff, HB_Mark_NonSpacing},
+  {0x1e00, 0x1e00, HB_Letter_Uppercase},
+  {0x1e01, 0x1e01, HB_Letter_Lowercase},
+  {0x1e02, 0x1e02, HB_Letter_Uppercase},
+  {0x1e03, 0x1e03, HB_Letter_Lowercase},
+  {0x1e04, 0x1e04, HB_Letter_Uppercase},
+  {0x1e05, 0x1e05, HB_Letter_Lowercase},
+  {0x1e06, 0x1e06, HB_Letter_Uppercase},
+  {0x1e07, 0x1e07, HB_Letter_Lowercase},
+  {0x1e08, 0x1e08, HB_Letter_Uppercase},
+  {0x1e09, 0x1e09, HB_Letter_Lowercase},
+  {0x1e0a, 0x1e0a, HB_Letter_Uppercase},
+  {0x1e0b, 0x1e0b, HB_Letter_Lowercase},
+  {0x1e0c, 0x1e0c, HB_Letter_Uppercase},
+  {0x1e0d, 0x1e0d, HB_Letter_Lowercase},
+  {0x1e0e, 0x1e0e, HB_Letter_Uppercase},
+  {0x1e0f, 0x1e0f, HB_Letter_Lowercase},
+  {0x1e10, 0x1e10, HB_Letter_Uppercase},
+  {0x1e11, 0x1e11, HB_Letter_Lowercase},
+  {0x1e12, 0x1e12, HB_Letter_Uppercase},
+  {0x1e13, 0x1e13, HB_Letter_Lowercase},
+  {0x1e14, 0x1e14, HB_Letter_Uppercase},
+  {0x1e15, 0x1e15, HB_Letter_Lowercase},
+  {0x1e16, 0x1e16, HB_Letter_Uppercase},
+  {0x1e17, 0x1e17, HB_Letter_Lowercase},
+  {0x1e18, 0x1e18, HB_Letter_Uppercase},
+  {0x1e19, 0x1e19, HB_Letter_Lowercase},
+  {0x1e1a, 0x1e1a, HB_Letter_Uppercase},
+  {0x1e1b, 0x1e1b, HB_Letter_Lowercase},
+  {0x1e1c, 0x1e1c, HB_Letter_Uppercase},
+  {0x1e1d, 0x1e1d, HB_Letter_Lowercase},
+  {0x1e1e, 0x1e1e, HB_Letter_Uppercase},
+  {0x1e1f, 0x1e1f, HB_Letter_Lowercase},
+  {0x1e20, 0x1e20, HB_Letter_Uppercase},
+  {0x1e21, 0x1e21, HB_Letter_Lowercase},
+  {0x1e22, 0x1e22, HB_Letter_Uppercase},
+  {0x1e23, 0x1e23, HB_Letter_Lowercase},
+  {0x1e24, 0x1e24, HB_Letter_Uppercase},
+  {0x1e25, 0x1e25, HB_Letter_Lowercase},
+  {0x1e26, 0x1e26, HB_Letter_Uppercase},
+  {0x1e27, 0x1e27, HB_Letter_Lowercase},
+  {0x1e28, 0x1e28, HB_Letter_Uppercase},
+  {0x1e29, 0x1e29, HB_Letter_Lowercase},
+  {0x1e2a, 0x1e2a, HB_Letter_Uppercase},
+  {0x1e2b, 0x1e2b, HB_Letter_Lowercase},
+  {0x1e2c, 0x1e2c, HB_Letter_Uppercase},
+  {0x1e2d, 0x1e2d, HB_Letter_Lowercase},
+  {0x1e2e, 0x1e2e, HB_Letter_Uppercase},
+  {0x1e2f, 0x1e2f, HB_Letter_Lowercase},
+  {0x1e30, 0x1e30, HB_Letter_Uppercase},
+  {0x1e31, 0x1e31, HB_Letter_Lowercase},
+  {0x1e32, 0x1e32, HB_Letter_Uppercase},
+  {0x1e33, 0x1e33, HB_Letter_Lowercase},
+  {0x1e34, 0x1e34, HB_Letter_Uppercase},
+  {0x1e35, 0x1e35, HB_Letter_Lowercase},
+  {0x1e36, 0x1e36, HB_Letter_Uppercase},
+  {0x1e37, 0x1e37, HB_Letter_Lowercase},
+  {0x1e38, 0x1e38, HB_Letter_Uppercase},
+  {0x1e39, 0x1e39, HB_Letter_Lowercase},
+  {0x1e3a, 0x1e3a, HB_Letter_Uppercase},
+  {0x1e3b, 0x1e3b, HB_Letter_Lowercase},
+  {0x1e3c, 0x1e3c, HB_Letter_Uppercase},
+  {0x1e3d, 0x1e3d, HB_Letter_Lowercase},
+  {0x1e3e, 0x1e3e, HB_Letter_Uppercase},
+  {0x1e3f, 0x1e3f, HB_Letter_Lowercase},
+  {0x1e40, 0x1e40, HB_Letter_Uppercase},
+  {0x1e41, 0x1e41, HB_Letter_Lowercase},
+  {0x1e42, 0x1e42, HB_Letter_Uppercase},
+  {0x1e43, 0x1e43, HB_Letter_Lowercase},
+  {0x1e44, 0x1e44, HB_Letter_Uppercase},
+  {0x1e45, 0x1e45, HB_Letter_Lowercase},
+  {0x1e46, 0x1e46, HB_Letter_Uppercase},
+  {0x1e47, 0x1e47, HB_Letter_Lowercase},
+  {0x1e48, 0x1e48, HB_Letter_Uppercase},
+  {0x1e49, 0x1e49, HB_Letter_Lowercase},
+  {0x1e4a, 0x1e4a, HB_Letter_Uppercase},
+  {0x1e4b, 0x1e4b, HB_Letter_Lowercase},
+  {0x1e4c, 0x1e4c, HB_Letter_Uppercase},
+  {0x1e4d, 0x1e4d, HB_Letter_Lowercase},
+  {0x1e4e, 0x1e4e, HB_Letter_Uppercase},
+  {0x1e4f, 0x1e4f, HB_Letter_Lowercase},
+  {0x1e50, 0x1e50, HB_Letter_Uppercase},
+  {0x1e51, 0x1e51, HB_Letter_Lowercase},
+  {0x1e52, 0x1e52, HB_Letter_Uppercase},
+  {0x1e53, 0x1e53, HB_Letter_Lowercase},
+  {0x1e54, 0x1e54, HB_Letter_Uppercase},
+  {0x1e55, 0x1e55, HB_Letter_Lowercase},
+  {0x1e56, 0x1e56, HB_Letter_Uppercase},
+  {0x1e57, 0x1e57, HB_Letter_Lowercase},
+  {0x1e58, 0x1e58, HB_Letter_Uppercase},
+  {0x1e59, 0x1e59, HB_Letter_Lowercase},
+  {0x1e5a, 0x1e5a, HB_Letter_Uppercase},
+  {0x1e5b, 0x1e5b, HB_Letter_Lowercase},
+  {0x1e5c, 0x1e5c, HB_Letter_Uppercase},
+  {0x1e5d, 0x1e5d, HB_Letter_Lowercase},
+  {0x1e5e, 0x1e5e, HB_Letter_Uppercase},
+  {0x1e5f, 0x1e5f, HB_Letter_Lowercase},
+  {0x1e60, 0x1e60, HB_Letter_Uppercase},
+  {0x1e61, 0x1e61, HB_Letter_Lowercase},
+  {0x1e62, 0x1e62, HB_Letter_Uppercase},
+  {0x1e63, 0x1e63, HB_Letter_Lowercase},
+  {0x1e64, 0x1e64, HB_Letter_Uppercase},
+  {0x1e65, 0x1e65, HB_Letter_Lowercase},
+  {0x1e66, 0x1e66, HB_Letter_Uppercase},
+  {0x1e67, 0x1e67, HB_Letter_Lowercase},
+  {0x1e68, 0x1e68, HB_Letter_Uppercase},
+  {0x1e69, 0x1e69, HB_Letter_Lowercase},
+  {0x1e6a, 0x1e6a, HB_Letter_Uppercase},
+  {0x1e6b, 0x1e6b, HB_Letter_Lowercase},
+  {0x1e6c, 0x1e6c, HB_Letter_Uppercase},
+  {0x1e6d, 0x1e6d, HB_Letter_Lowercase},
+  {0x1e6e, 0x1e6e, HB_Letter_Uppercase},
+  {0x1e6f, 0x1e6f, HB_Letter_Lowercase},
+  {0x1e70, 0x1e70, HB_Letter_Uppercase},
+  {0x1e71, 0x1e71, HB_Letter_Lowercase},
+  {0x1e72, 0x1e72, HB_Letter_Uppercase},
+  {0x1e73, 0x1e73, HB_Letter_Lowercase},
+  {0x1e74, 0x1e74, HB_Letter_Uppercase},
+  {0x1e75, 0x1e75, HB_Letter_Lowercase},
+  {0x1e76, 0x1e76, HB_Letter_Uppercase},
+  {0x1e77, 0x1e77, HB_Letter_Lowercase},
+  {0x1e78, 0x1e78, HB_Letter_Uppercase},
+  {0x1e79, 0x1e79, HB_Letter_Lowercase},
+  {0x1e7a, 0x1e7a, HB_Letter_Uppercase},
+  {0x1e7b, 0x1e7b, HB_Letter_Lowercase},
+  {0x1e7c, 0x1e7c, HB_Letter_Uppercase},
+  {0x1e7d, 0x1e7d, HB_Letter_Lowercase},
+  {0x1e7e, 0x1e7e, HB_Letter_Uppercase},
+  {0x1e7f, 0x1e7f, HB_Letter_Lowercase},
+  {0x1e80, 0x1e80, HB_Letter_Uppercase},
+  {0x1e81, 0x1e81, HB_Letter_Lowercase},
+  {0x1e82, 0x1e82, HB_Letter_Uppercase},
+  {0x1e83, 0x1e83, HB_Letter_Lowercase},
+  {0x1e84, 0x1e84, HB_Letter_Uppercase},
+  {0x1e85, 0x1e85, HB_Letter_Lowercase},
+  {0x1e86, 0x1e86, HB_Letter_Uppercase},
+  {0x1e87, 0x1e87, HB_Letter_Lowercase},
+  {0x1e88, 0x1e88, HB_Letter_Uppercase},
+  {0x1e89, 0x1e89, HB_Letter_Lowercase},
+  {0x1e8a, 0x1e8a, HB_Letter_Uppercase},
+  {0x1e8b, 0x1e8b, HB_Letter_Lowercase},
+  {0x1e8c, 0x1e8c, HB_Letter_Uppercase},
+  {0x1e8d, 0x1e8d, HB_Letter_Lowercase},
+  {0x1e8e, 0x1e8e, HB_Letter_Uppercase},
+  {0x1e8f, 0x1e8f, HB_Letter_Lowercase},
+  {0x1e90, 0x1e90, HB_Letter_Uppercase},
+  {0x1e91, 0x1e91, HB_Letter_Lowercase},
+  {0x1e92, 0x1e92, HB_Letter_Uppercase},
+  {0x1e93, 0x1e93, HB_Letter_Lowercase},
+  {0x1e94, 0x1e94, HB_Letter_Uppercase},
+  {0x1e95, 0x1e9d, HB_Letter_Lowercase},
+  {0x1e9e, 0x1e9e, HB_Letter_Uppercase},
+  {0x1e9f, 0x1e9f, HB_Letter_Lowercase},
+  {0x1ea0, 0x1ea0, HB_Letter_Uppercase},
+  {0x1ea1, 0x1ea1, HB_Letter_Lowercase},
+  {0x1ea2, 0x1ea2, HB_Letter_Uppercase},
+  {0x1ea3, 0x1ea3, HB_Letter_Lowercase},
+  {0x1ea4, 0x1ea4, HB_Letter_Uppercase},
+  {0x1ea5, 0x1ea5, HB_Letter_Lowercase},
+  {0x1ea6, 0x1ea6, HB_Letter_Uppercase},
+  {0x1ea7, 0x1ea7, HB_Letter_Lowercase},
+  {0x1ea8, 0x1ea8, HB_Letter_Uppercase},
+  {0x1ea9, 0x1ea9, HB_Letter_Lowercase},
+  {0x1eaa, 0x1eaa, HB_Letter_Uppercase},
+  {0x1eab, 0x1eab, HB_Letter_Lowercase},
+  {0x1eac, 0x1eac, HB_Letter_Uppercase},
+  {0x1ead, 0x1ead, HB_Letter_Lowercase},
+  {0x1eae, 0x1eae, HB_Letter_Uppercase},
+  {0x1eaf, 0x1eaf, HB_Letter_Lowercase},
+  {0x1eb0, 0x1eb0, HB_Letter_Uppercase},
+  {0x1eb1, 0x1eb1, HB_Letter_Lowercase},
+  {0x1eb2, 0x1eb2, HB_Letter_Uppercase},
+  {0x1eb3, 0x1eb3, HB_Letter_Lowercase},
+  {0x1eb4, 0x1eb4, HB_Letter_Uppercase},
+  {0x1eb5, 0x1eb5, HB_Letter_Lowercase},
+  {0x1eb6, 0x1eb6, HB_Letter_Uppercase},
+  {0x1eb7, 0x1eb7, HB_Letter_Lowercase},
+  {0x1eb8, 0x1eb8, HB_Letter_Uppercase},
+  {0x1eb9, 0x1eb9, HB_Letter_Lowercase},
+  {0x1eba, 0x1eba, HB_Letter_Uppercase},
+  {0x1ebb, 0x1ebb, HB_Letter_Lowercase},
+  {0x1ebc, 0x1ebc, HB_Letter_Uppercase},
+  {0x1ebd, 0x1ebd, HB_Letter_Lowercase},
+  {0x1ebe, 0x1ebe, HB_Letter_Uppercase},
+  {0x1ebf, 0x1ebf, HB_Letter_Lowercase},
+  {0x1ec0, 0x1ec0, HB_Letter_Uppercase},
+  {0x1ec1, 0x1ec1, HB_Letter_Lowercase},
+  {0x1ec2, 0x1ec2, HB_Letter_Uppercase},
+  {0x1ec3, 0x1ec3, HB_Letter_Lowercase},
+  {0x1ec4, 0x1ec4, HB_Letter_Uppercase},
+  {0x1ec5, 0x1ec5, HB_Letter_Lowercase},
+  {0x1ec6, 0x1ec6, HB_Letter_Uppercase},
+  {0x1ec7, 0x1ec7, HB_Letter_Lowercase},
+  {0x1ec8, 0x1ec8, HB_Letter_Uppercase},
+  {0x1ec9, 0x1ec9, HB_Letter_Lowercase},
+  {0x1eca, 0x1eca, HB_Letter_Uppercase},
+  {0x1ecb, 0x1ecb, HB_Letter_Lowercase},
+  {0x1ecc, 0x1ecc, HB_Letter_Uppercase},
+  {0x1ecd, 0x1ecd, HB_Letter_Lowercase},
+  {0x1ece, 0x1ece, HB_Letter_Uppercase},
+  {0x1ecf, 0x1ecf, HB_Letter_Lowercase},
+  {0x1ed0, 0x1ed0, HB_Letter_Uppercase},
+  {0x1ed1, 0x1ed1, HB_Letter_Lowercase},
+  {0x1ed2, 0x1ed2, HB_Letter_Uppercase},
+  {0x1ed3, 0x1ed3, HB_Letter_Lowercase},
+  {0x1ed4, 0x1ed4, HB_Letter_Uppercase},
+  {0x1ed5, 0x1ed5, HB_Letter_Lowercase},
+  {0x1ed6, 0x1ed6, HB_Letter_Uppercase},
+  {0x1ed7, 0x1ed7, HB_Letter_Lowercase},
+  {0x1ed8, 0x1ed8, HB_Letter_Uppercase},
+  {0x1ed9, 0x1ed9, HB_Letter_Lowercase},
+  {0x1eda, 0x1eda, HB_Letter_Uppercase},
+  {0x1edb, 0x1edb, HB_Letter_Lowercase},
+  {0x1edc, 0x1edc, HB_Letter_Uppercase},
+  {0x1edd, 0x1edd, HB_Letter_Lowercase},
+  {0x1ede, 0x1ede, HB_Letter_Uppercase},
+  {0x1edf, 0x1edf, HB_Letter_Lowercase},
+  {0x1ee0, 0x1ee0, HB_Letter_Uppercase},
+  {0x1ee1, 0x1ee1, HB_Letter_Lowercase},
+  {0x1ee2, 0x1ee2, HB_Letter_Uppercase},
+  {0x1ee3, 0x1ee3, HB_Letter_Lowercase},
+  {0x1ee4, 0x1ee4, HB_Letter_Uppercase},
+  {0x1ee5, 0x1ee5, HB_Letter_Lowercase},
+  {0x1ee6, 0x1ee6, HB_Letter_Uppercase},
+  {0x1ee7, 0x1ee7, HB_Letter_Lowercase},
+  {0x1ee8, 0x1ee8, HB_Letter_Uppercase},
+  {0x1ee9, 0x1ee9, HB_Letter_Lowercase},
+  {0x1eea, 0x1eea, HB_Letter_Uppercase},
+  {0x1eeb, 0x1eeb, HB_Letter_Lowercase},
+  {0x1eec, 0x1eec, HB_Letter_Uppercase},
+  {0x1eed, 0x1eed, HB_Letter_Lowercase},
+  {0x1eee, 0x1eee, HB_Letter_Uppercase},
+  {0x1eef, 0x1eef, HB_Letter_Lowercase},
+  {0x1ef0, 0x1ef0, HB_Letter_Uppercase},
+  {0x1ef1, 0x1ef1, HB_Letter_Lowercase},
+  {0x1ef2, 0x1ef2, HB_Letter_Uppercase},
+  {0x1ef3, 0x1ef3, HB_Letter_Lowercase},
+  {0x1ef4, 0x1ef4, HB_Letter_Uppercase},
+  {0x1ef5, 0x1ef5, HB_Letter_Lowercase},
+  {0x1ef6, 0x1ef6, HB_Letter_Uppercase},
+  {0x1ef7, 0x1ef7, HB_Letter_Lowercase},
+  {0x1ef8, 0x1ef8, HB_Letter_Uppercase},
+  {0x1ef9, 0x1ef9, HB_Letter_Lowercase},
+  {0x1efa, 0x1efa, HB_Letter_Uppercase},
+  {0x1efb, 0x1efb, HB_Letter_Lowercase},
+  {0x1efc, 0x1efc, HB_Letter_Uppercase},
+  {0x1efd, 0x1efd, HB_Letter_Lowercase},
+  {0x1efe, 0x1efe, HB_Letter_Uppercase},
+  {0x1eff, 0x1f07, HB_Letter_Lowercase},
+  {0x1f08, 0x1f0f, HB_Letter_Uppercase},
+  {0x1f10, 0x1f15, HB_Letter_Lowercase},
+  {0x1f16, 0x1f17, HB_Other_NotAssigned},
+  {0x1f18, 0x1f1d, HB_Letter_Uppercase},
+  {0x1f1e, 0x1f1f, HB_Other_NotAssigned},
+  {0x1f20, 0x1f27, HB_Letter_Lowercase},
+  {0x1f28, 0x1f2f, HB_Letter_Uppercase},
+  {0x1f30, 0x1f37, HB_Letter_Lowercase},
+  {0x1f38, 0x1f3f, HB_Letter_Uppercase},
+  {0x1f40, 0x1f45, HB_Letter_Lowercase},
+  {0x1f46, 0x1f47, HB_Other_NotAssigned},
+  {0x1f48, 0x1f4d, HB_Letter_Uppercase},
+  {0x1f4e, 0x1f4f, HB_Other_NotAssigned},
+  {0x1f50, 0x1f57, HB_Letter_Lowercase},
+  {0x1f58, 0x1f58, HB_Other_NotAssigned},
+  {0x1f59, 0x1f59, HB_Letter_Uppercase},
+  {0x1f5a, 0x1f5a, HB_Other_NotAssigned},
+  {0x1f5b, 0x1f5b, HB_Letter_Uppercase},
+  {0x1f5c, 0x1f5c, HB_Other_NotAssigned},
+  {0x1f5d, 0x1f5d, HB_Letter_Uppercase},
+  {0x1f5e, 0x1f5e, HB_Other_NotAssigned},
+  {0x1f5f, 0x1f5f, HB_Letter_Uppercase},
+  {0x1f60, 0x1f67, HB_Letter_Lowercase},
+  {0x1f68, 0x1f6f, HB_Letter_Uppercase},
+  {0x1f70, 0x1f7d, HB_Letter_Lowercase},
+  {0x1f7e, 0x1f7f, HB_Other_NotAssigned},
+  {0x1f80, 0x1f87, HB_Letter_Lowercase},
+  {0x1f88, 0x1f8f, HB_Letter_Titlecase},
+  {0x1f90, 0x1f97, HB_Letter_Lowercase},
+  {0x1f98, 0x1f9f, HB_Letter_Titlecase},
+  {0x1fa0, 0x1fa7, HB_Letter_Lowercase},
+  {0x1fa8, 0x1faf, HB_Letter_Titlecase},
+  {0x1fb0, 0x1fb4, HB_Letter_Lowercase},
+  {0x1fb5, 0x1fb5, HB_Other_NotAssigned},
+  {0x1fb6, 0x1fb7, HB_Letter_Lowercase},
+  {0x1fb8, 0x1fbb, HB_Letter_Uppercase},
+  {0x1fbc, 0x1fbc, HB_Letter_Titlecase},
+  {0x1fbd, 0x1fbd, HB_Symbol_Modifier},
+  {0x1fbe, 0x1fbe, HB_Letter_Lowercase},
+  {0x1fbf, 0x1fc1, HB_Symbol_Modifier},
+  {0x1fc2, 0x1fc4, HB_Letter_Lowercase},
+  {0x1fc5, 0x1fc5, HB_Other_NotAssigned},
+  {0x1fc6, 0x1fc7, HB_Letter_Lowercase},
+  {0x1fc8, 0x1fcb, HB_Letter_Uppercase},
+  {0x1fcc, 0x1fcc, HB_Letter_Titlecase},
+  {0x1fcd, 0x1fcf, HB_Symbol_Modifier},
+  {0x1fd0, 0x1fd3, HB_Letter_Lowercase},
+  {0x1fd4, 0x1fd5, HB_Other_NotAssigned},
+  {0x1fd6, 0x1fd7, HB_Letter_Lowercase},
+  {0x1fd8, 0x1fdb, HB_Letter_Uppercase},
+  {0x1fdc, 0x1fdc, HB_Other_NotAssigned},
+  {0x1fdd, 0x1fdf, HB_Symbol_Modifier},
+  {0x1fe0, 0x1fe7, HB_Letter_Lowercase},
+  {0x1fe8, 0x1fec, HB_Letter_Uppercase},
+  {0x1fed, 0x1fef, HB_Symbol_Modifier},
+  {0x1ff0, 0x1ff1, HB_Other_NotAssigned},
+  {0x1ff2, 0x1ff4, HB_Letter_Lowercase},
+  {0x1ff5, 0x1ff5, HB_Other_NotAssigned},
+  {0x1ff6, 0x1ff7, HB_Letter_Lowercase},
+  {0x1ff8, 0x1ffb, HB_Letter_Uppercase},
+  {0x1ffc, 0x1ffc, HB_Letter_Titlecase},
+  {0x1ffd, 0x1ffe, HB_Symbol_Modifier},
+  {0x1fff, 0x1fff, HB_Other_NotAssigned},
+  {0x2000, 0x200a, HB_Separator_Space},
+  {0x200b, 0x200f, HB_Other_Format},
+  {0x2010, 0x2015, HB_Punctuation_Dash},
+  {0x2016, 0x2017, HB_Punctuation_Other},
+  {0x2018, 0x2018, HB_Punctuation_InitialQuote},
+  {0x2019, 0x2019, HB_Punctuation_FinalQuote},
+  {0x201a, 0x201a, HB_Punctuation_Open},
+  {0x201b, 0x201c, HB_Punctuation_InitialQuote},
+  {0x201d, 0x201d, HB_Punctuation_FinalQuote},
+  {0x201e, 0x201e, HB_Punctuation_Open},
+  {0x201f, 0x201f, HB_Punctuation_InitialQuote},
+  {0x2020, 0x2027, HB_Punctuation_Other},
+  {0x2028, 0x2028, HB_Separator_Line},
+  {0x2029, 0x2029, HB_Separator_Paragraph},
+  {0x202a, 0x202e, HB_Other_Format},
+  {0x202f, 0x202f, HB_Separator_Space},
+  {0x2030, 0x2038, HB_Punctuation_Other},
+  {0x2039, 0x2039, HB_Punctuation_InitialQuote},
+  {0x203a, 0x203a, HB_Punctuation_FinalQuote},
+  {0x203b, 0x203e, HB_Punctuation_Other},
+  {0x203f, 0x2040, HB_Punctuation_Connector},
+  {0x2041, 0x2043, HB_Punctuation_Other},
+  {0x2044, 0x2044, HB_Symbol_Math},
+  {0x2045, 0x2045, HB_Punctuation_Open},
+  {0x2046, 0x2046, HB_Punctuation_Close},
+  {0x2047, 0x2051, HB_Punctuation_Other},
+  {0x2052, 0x2052, HB_Symbol_Math},
+  {0x2053, 0x2053, HB_Punctuation_Other},
+  {0x2054, 0x2054, HB_Punctuation_Connector},
+  {0x2055, 0x205e, HB_Punctuation_Other},
+  {0x205f, 0x205f, HB_Separator_Space},
+  {0x2060, 0x2064, HB_Other_Format},
+  {0x2065, 0x2069, HB_Other_NotAssigned},
+  {0x206a, 0x206f, HB_Other_Format},
+  {0x2070, 0x2070, HB_Number_Other},
+  {0x2071, 0x2071, HB_Letter_Lowercase},
+  {0x2072, 0x2073, HB_Other_NotAssigned},
+  {0x2074, 0x2079, HB_Number_Other},
+  {0x207a, 0x207c, HB_Symbol_Math},
+  {0x207d, 0x207d, HB_Punctuation_Open},
+  {0x207e, 0x207e, HB_Punctuation_Close},
+  {0x207f, 0x207f, HB_Letter_Lowercase},
+  {0x2080, 0x2089, HB_Number_Other},
+  {0x208a, 0x208c, HB_Symbol_Math},
+  {0x208d, 0x208d, HB_Punctuation_Open},
+  {0x208e, 0x208e, HB_Punctuation_Close},
+  {0x208f, 0x208f, HB_Other_NotAssigned},
+  {0x2090, 0x2094, HB_Letter_Modifier},
+  {0x2095, 0x209f, HB_Other_NotAssigned},
+  {0x20a0, 0x20b5, HB_Symbol_Currency},
+  {0x20b6, 0x20cf, HB_Other_NotAssigned},
+  {0x20d0, 0x20dc, HB_Mark_NonSpacing},
+  {0x20dd, 0x20e0, HB_Mark_Enclosing},
+  {0x20e1, 0x20e1, HB_Mark_NonSpacing},
+  {0x20e2, 0x20e4, HB_Mark_Enclosing},
+  {0x20e5, 0x20f0, HB_Mark_NonSpacing},
+  {0x20f1, 0x20ff, HB_Other_NotAssigned},
+  {0x2100, 0x2101, HB_Symbol_Other},
+  {0x2102, 0x2102, HB_Letter_Uppercase},
+  {0x2103, 0x2106, HB_Symbol_Other},
+  {0x2107, 0x2107, HB_Letter_Uppercase},
+  {0x2108, 0x2109, HB_Symbol_Other},
+  {0x210a, 0x210a, HB_Letter_Lowercase},
+  {0x210b, 0x210d, HB_Letter_Uppercase},
+  {0x210e, 0x210f, HB_Letter_Lowercase},
+  {0x2110, 0x2112, HB_Letter_Uppercase},
+  {0x2113, 0x2113, HB_Letter_Lowercase},
+  {0x2114, 0x2114, HB_Symbol_Other},
+  {0x2115, 0x2115, HB_Letter_Uppercase},
+  {0x2116, 0x2118, HB_Symbol_Other},
+  {0x2119, 0x211d, HB_Letter_Uppercase},
+  {0x211e, 0x2123, HB_Symbol_Other},
+  {0x2124, 0x2124, HB_Letter_Uppercase},
+  {0x2125, 0x2125, HB_Symbol_Other},
+  {0x2126, 0x2126, HB_Letter_Uppercase},
+  {0x2127, 0x2127, HB_Symbol_Other},
+  {0x2128, 0x2128, HB_Letter_Uppercase},
+  {0x2129, 0x2129, HB_Symbol_Other},
+  {0x212a, 0x212d, HB_Letter_Uppercase},
+  {0x212e, 0x212e, HB_Symbol_Other},
+  {0x212f, 0x212f, HB_Letter_Lowercase},
+  {0x2130, 0x2133, HB_Letter_Uppercase},
+  {0x2134, 0x2134, HB_Letter_Lowercase},
+  {0x2135, 0x2138, HB_Letter_Other},
+  {0x2139, 0x2139, HB_Letter_Lowercase},
+  {0x213a, 0x213b, HB_Symbol_Other},
+  {0x213c, 0x213d, HB_Letter_Lowercase},
+  {0x213e, 0x213f, HB_Letter_Uppercase},
+  {0x2140, 0x2144, HB_Symbol_Math},
+  {0x2145, 0x2145, HB_Letter_Uppercase},
+  {0x2146, 0x2149, HB_Letter_Lowercase},
+  {0x214a, 0x214a, HB_Symbol_Other},
+  {0x214b, 0x214b, HB_Symbol_Math},
+  {0x214c, 0x214d, HB_Symbol_Other},
+  {0x214e, 0x214e, HB_Letter_Lowercase},
+  {0x214f, 0x214f, HB_Symbol_Other},
+  {0x2150, 0x2152, HB_Other_NotAssigned},
+  {0x2153, 0x215f, HB_Number_Other},
+  {0x2160, 0x2182, HB_Number_Letter},
+  {0x2183, 0x2183, HB_Letter_Uppercase},
+  {0x2184, 0x2184, HB_Letter_Lowercase},
+  {0x2185, 0x2188, HB_Number_Letter},
+  {0x2189, 0x218f, HB_Other_NotAssigned},
+  {0x2190, 0x2194, HB_Symbol_Math},
+  {0x2195, 0x2199, HB_Symbol_Other},
+  {0x219a, 0x219b, HB_Symbol_Math},
+  {0x219c, 0x219f, HB_Symbol_Other},
+  {0x21a0, 0x21a0, HB_Symbol_Math},
+  {0x21a1, 0x21a2, HB_Symbol_Other},
+  {0x21a3, 0x21a3, HB_Symbol_Math},
+  {0x21a4, 0x21a5, HB_Symbol_Other},
+  {0x21a6, 0x21a6, HB_Symbol_Math},
+  {0x21a7, 0x21ad, HB_Symbol_Other},
+  {0x21ae, 0x21ae, HB_Symbol_Math},
+  {0x21af, 0x21cd, HB_Symbol_Other},
+  {0x21ce, 0x21cf, HB_Symbol_Math},
+  {0x21d0, 0x21d1, HB_Symbol_Other},
+  {0x21d2, 0x21d2, HB_Symbol_Math},
+  {0x21d3, 0x21d3, HB_Symbol_Other},
+  {0x21d4, 0x21d4, HB_Symbol_Math},
+  {0x21d5, 0x21f3, HB_Symbol_Other},
+  {0x21f4, 0x22ff, HB_Symbol_Math},
+  {0x2300, 0x2307, HB_Symbol_Other},
+  {0x2308, 0x230b, HB_Symbol_Math},
+  {0x230c, 0x231f, HB_Symbol_Other},
+  {0x2320, 0x2321, HB_Symbol_Math},
+  {0x2322, 0x2328, HB_Symbol_Other},
+  {0x2329, 0x2329, HB_Punctuation_Open},
+  {0x232a, 0x232a, HB_Punctuation_Close},
+  {0x232b, 0x237b, HB_Symbol_Other},
+  {0x237c, 0x237c, HB_Symbol_Math},
+  {0x237d, 0x239a, HB_Symbol_Other},
+  {0x239b, 0x23b3, HB_Symbol_Math},
+  {0x23b4, 0x23db, HB_Symbol_Other},
+  {0x23dc, 0x23e1, HB_Symbol_Math},
+  {0x23e2, 0x23e7, HB_Symbol_Other},
+  {0x23e8, 0x23ff, HB_Other_NotAssigned},
+  {0x2400, 0x2426, HB_Symbol_Other},
+  {0x2427, 0x243f, HB_Other_NotAssigned},
+  {0x2440, 0x244a, HB_Symbol_Other},
+  {0x244b, 0x245f, HB_Other_NotAssigned},
+  {0x2460, 0x249b, HB_Number_Other},
+  {0x249c, 0x24e9, HB_Symbol_Other},
+  {0x24ea, 0x24ff, HB_Number_Other},
+  {0x2500, 0x25b6, HB_Symbol_Other},
+  {0x25b7, 0x25b7, HB_Symbol_Math},
+  {0x25b8, 0x25c0, HB_Symbol_Other},
+  {0x25c1, 0x25c1, HB_Symbol_Math},
+  {0x25c2, 0x25f7, HB_Symbol_Other},
+  {0x25f8, 0x25ff, HB_Symbol_Math},
+  {0x2600, 0x266e, HB_Symbol_Other},
+  {0x266f, 0x266f, HB_Symbol_Math},
+  {0x2670, 0x269d, HB_Symbol_Other},
+  {0x269e, 0x269f, HB_Other_NotAssigned},
+  {0x26a0, 0x26bc, HB_Symbol_Other},
+  {0x26bd, 0x26bf, HB_Other_NotAssigned},
+  {0x26c0, 0x26c3, HB_Symbol_Other},
+  {0x26c4, 0x2700, HB_Other_NotAssigned},
+  {0x2701, 0x2704, HB_Symbol_Other},
+  {0x2705, 0x2705, HB_Other_NotAssigned},
+  {0x2706, 0x2709, HB_Symbol_Other},
+  {0x270a, 0x270b, HB_Other_NotAssigned},
+  {0x270c, 0x2727, HB_Symbol_Other},
+  {0x2728, 0x2728, HB_Other_NotAssigned},
+  {0x2729, 0x274b, HB_Symbol_Other},
+  {0x274c, 0x274c, HB_Other_NotAssigned},
+  {0x274d, 0x274d, HB_Symbol_Other},
+  {0x274e, 0x274e, HB_Other_NotAssigned},
+  {0x274f, 0x2752, HB_Symbol_Other},
+  {0x2753, 0x2755, HB_Other_NotAssigned},
+  {0x2756, 0x2756, HB_Symbol_Other},
+  {0x2757, 0x2757, HB_Other_NotAssigned},
+  {0x2758, 0x275e, HB_Symbol_Other},
+  {0x275f, 0x2760, HB_Other_NotAssigned},
+  {0x2761, 0x2767, HB_Symbol_Other},
+  {0x2768, 0x2768, HB_Punctuation_Open},
+  {0x2769, 0x2769, HB_Punctuation_Close},
+  {0x276a, 0x276a, HB_Punctuation_Open},
+  {0x276b, 0x276b, HB_Punctuation_Close},
+  {0x276c, 0x276c, HB_Punctuation_Open},
+  {0x276d, 0x276d, HB_Punctuation_Close},
+  {0x276e, 0x276e, HB_Punctuation_Open},
+  {0x276f, 0x276f, HB_Punctuation_Close},
+  {0x2770, 0x2770, HB_Punctuation_Open},
+  {0x2771, 0x2771, HB_Punctuation_Close},
+  {0x2772, 0x2772, HB_Punctuation_Open},
+  {0x2773, 0x2773, HB_Punctuation_Close},
+  {0x2774, 0x2774, HB_Punctuation_Open},
+  {0x2775, 0x2775, HB_Punctuation_Close},
+  {0x2776, 0x2793, HB_Number_Other},
+  {0x2794, 0x2794, HB_Symbol_Other},
+  {0x2795, 0x2797, HB_Other_NotAssigned},
+  {0x2798, 0x27af, HB_Symbol_Other},
+  {0x27b0, 0x27b0, HB_Other_NotAssigned},
+  {0x27b1, 0x27be, HB_Symbol_Other},
+  {0x27bf, 0x27bf, HB_Other_NotAssigned},
+  {0x27c0, 0x27c4, HB_Symbol_Math},
+  {0x27c5, 0x27c5, HB_Punctuation_Open},
+  {0x27c6, 0x27c6, HB_Punctuation_Close},
+  {0x27c7, 0x27ca, HB_Symbol_Math},
+  {0x27cb, 0x27cb, HB_Other_NotAssigned},
+  {0x27cc, 0x27cc, HB_Symbol_Math},
+  {0x27cd, 0x27cf, HB_Other_NotAssigned},
+  {0x27d0, 0x27e5, HB_Symbol_Math},
+  {0x27e6, 0x27e6, HB_Punctuation_Open},
+  {0x27e7, 0x27e7, HB_Punctuation_Close},
+  {0x27e8, 0x27e8, HB_Punctuation_Open},
+  {0x27e9, 0x27e9, HB_Punctuation_Close},
+  {0x27ea, 0x27ea, HB_Punctuation_Open},
+  {0x27eb, 0x27eb, HB_Punctuation_Close},
+  {0x27ec, 0x27ec, HB_Punctuation_Open},
+  {0x27ed, 0x27ed, HB_Punctuation_Close},
+  {0x27ee, 0x27ee, HB_Punctuation_Open},
+  {0x27ef, 0x27ef, HB_Punctuation_Close},
+  {0x27f0, 0x27ff, HB_Symbol_Math},
+  {0x2800, 0x28ff, HB_Symbol_Other},
+  {0x2900, 0x2982, HB_Symbol_Math},
+  {0x2983, 0x2983, HB_Punctuation_Open},
+  {0x2984, 0x2984, HB_Punctuation_Close},
+  {0x2985, 0x2985, HB_Punctuation_Open},
+  {0x2986, 0x2986, HB_Punctuation_Close},
+  {0x2987, 0x2987, HB_Punctuation_Open},
+  {0x2988, 0x2988, HB_Punctuation_Close},
+  {0x2989, 0x2989, HB_Punctuation_Open},
+  {0x298a, 0x298a, HB_Punctuation_Close},
+  {0x298b, 0x298b, HB_Punctuation_Open},
+  {0x298c, 0x298c, HB_Punctuation_Close},
+  {0x298d, 0x298d, HB_Punctuation_Open},
+  {0x298e, 0x298e, HB_Punctuation_Close},
+  {0x298f, 0x298f, HB_Punctuation_Open},
+  {0x2990, 0x2990, HB_Punctuation_Close},
+  {0x2991, 0x2991, HB_Punctuation_Open},
+  {0x2992, 0x2992, HB_Punctuation_Close},
+  {0x2993, 0x2993, HB_Punctuation_Open},
+  {0x2994, 0x2994, HB_Punctuation_Close},
+  {0x2995, 0x2995, HB_Punctuation_Open},
+  {0x2996, 0x2996, HB_Punctuation_Close},
+  {0x2997, 0x2997, HB_Punctuation_Open},
+  {0x2998, 0x2998, HB_Punctuation_Close},
+  {0x2999, 0x29d7, HB_Symbol_Math},
+  {0x29d8, 0x29d8, HB_Punctuation_Open},
+  {0x29d9, 0x29d9, HB_Punctuation_Close},
+  {0x29da, 0x29da, HB_Punctuation_Open},
+  {0x29db, 0x29db, HB_Punctuation_Close},
+  {0x29dc, 0x29fb, HB_Symbol_Math},
+  {0x29fc, 0x29fc, HB_Punctuation_Open},
+  {0x29fd, 0x29fd, HB_Punctuation_Close},
+  {0x29fe, 0x2aff, HB_Symbol_Math},
+  {0x2b00, 0x2b2f, HB_Symbol_Other},
+  {0x2b30, 0x2b44, HB_Symbol_Math},
+  {0x2b45, 0x2b46, HB_Symbol_Other},
+  {0x2b47, 0x2b4c, HB_Symbol_Math},
+  {0x2b4d, 0x2b4f, HB_Other_NotAssigned},
+  {0x2b50, 0x2b54, HB_Symbol_Other},
+  {0x2b55, 0x2bff, HB_Other_NotAssigned},
+  {0x2c00, 0x2c2e, HB_Letter_Uppercase},
+  {0x2c2f, 0x2c2f, HB_Other_NotAssigned},
+  {0x2c30, 0x2c5e, HB_Letter_Lowercase},
+  {0x2c5f, 0x2c5f, HB_Other_NotAssigned},
+  {0x2c60, 0x2c60, HB_Letter_Uppercase},
+  {0x2c61, 0x2c61, HB_Letter_Lowercase},
+  {0x2c62, 0x2c64, HB_Letter_Uppercase},
+  {0x2c65, 0x2c66, HB_Letter_Lowercase},
+  {0x2c67, 0x2c67, HB_Letter_Uppercase},
+  {0x2c68, 0x2c68, HB_Letter_Lowercase},
+  {0x2c69, 0x2c69, HB_Letter_Uppercase},
+  {0x2c6a, 0x2c6a, HB_Letter_Lowercase},
+  {0x2c6b, 0x2c6b, HB_Letter_Uppercase},
+  {0x2c6c, 0x2c6c, HB_Letter_Lowercase},
+  {0x2c6d, 0x2c6f, HB_Letter_Uppercase},
+  {0x2c70, 0x2c70, HB_Other_NotAssigned},
+  {0x2c71, 0x2c71, HB_Letter_Lowercase},
+  {0x2c72, 0x2c72, HB_Letter_Uppercase},
+  {0x2c73, 0x2c74, HB_Letter_Lowercase},
+  {0x2c75, 0x2c75, HB_Letter_Uppercase},
+  {0x2c76, 0x2c7c, HB_Letter_Lowercase},
+  {0x2c7d, 0x2c7d, HB_Letter_Modifier},
+  {0x2c7e, 0x2c7f, HB_Other_NotAssigned},
+  {0x2c80, 0x2c80, HB_Letter_Uppercase},
+  {0x2c81, 0x2c81, HB_Letter_Lowercase},
+  {0x2c82, 0x2c82, HB_Letter_Uppercase},
+  {0x2c83, 0x2c83, HB_Letter_Lowercase},
+  {0x2c84, 0x2c84, HB_Letter_Uppercase},
+  {0x2c85, 0x2c85, HB_Letter_Lowercase},
+  {0x2c86, 0x2c86, HB_Letter_Uppercase},
+  {0x2c87, 0x2c87, HB_Letter_Lowercase},
+  {0x2c88, 0x2c88, HB_Letter_Uppercase},
+  {0x2c89, 0x2c89, HB_Letter_Lowercase},
+  {0x2c8a, 0x2c8a, HB_Letter_Uppercase},
+  {0x2c8b, 0x2c8b, HB_Letter_Lowercase},
+  {0x2c8c, 0x2c8c, HB_Letter_Uppercase},
+  {0x2c8d, 0x2c8d, HB_Letter_Lowercase},
+  {0x2c8e, 0x2c8e, HB_Letter_Uppercase},
+  {0x2c8f, 0x2c8f, HB_Letter_Lowercase},
+  {0x2c90, 0x2c90, HB_Letter_Uppercase},
+  {0x2c91, 0x2c91, HB_Letter_Lowercase},
+  {0x2c92, 0x2c92, HB_Letter_Uppercase},
+  {0x2c93, 0x2c93, HB_Letter_Lowercase},
+  {0x2c94, 0x2c94, HB_Letter_Uppercase},
+  {0x2c95, 0x2c95, HB_Letter_Lowercase},
+  {0x2c96, 0x2c96, HB_Letter_Uppercase},
+  {0x2c97, 0x2c97, HB_Letter_Lowercase},
+  {0x2c98, 0x2c98, HB_Letter_Uppercase},
+  {0x2c99, 0x2c99, HB_Letter_Lowercase},
+  {0x2c9a, 0x2c9a, HB_Letter_Uppercase},
+  {0x2c9b, 0x2c9b, HB_Letter_Lowercase},
+  {0x2c9c, 0x2c9c, HB_Letter_Uppercase},
+  {0x2c9d, 0x2c9d, HB_Letter_Lowercase},
+  {0x2c9e, 0x2c9e, HB_Letter_Uppercase},
+  {0x2c9f, 0x2c9f, HB_Letter_Lowercase},
+  {0x2ca0, 0x2ca0, HB_Letter_Uppercase},
+  {0x2ca1, 0x2ca1, HB_Letter_Lowercase},
+  {0x2ca2, 0x2ca2, HB_Letter_Uppercase},
+  {0x2ca3, 0x2ca3, HB_Letter_Lowercase},
+  {0x2ca4, 0x2ca4, HB_Letter_Uppercase},
+  {0x2ca5, 0x2ca5, HB_Letter_Lowercase},
+  {0x2ca6, 0x2ca6, HB_Letter_Uppercase},
+  {0x2ca7, 0x2ca7, HB_Letter_Lowercase},
+  {0x2ca8, 0x2ca8, HB_Letter_Uppercase},
+  {0x2ca9, 0x2ca9, HB_Letter_Lowercase},
+  {0x2caa, 0x2caa, HB_Letter_Uppercase},
+  {0x2cab, 0x2cab, HB_Letter_Lowercase},
+  {0x2cac, 0x2cac, HB_Letter_Uppercase},
+  {0x2cad, 0x2cad, HB_Letter_Lowercase},
+  {0x2cae, 0x2cae, HB_Letter_Uppercase},
+  {0x2caf, 0x2caf, HB_Letter_Lowercase},
+  {0x2cb0, 0x2cb0, HB_Letter_Uppercase},
+  {0x2cb1, 0x2cb1, HB_Letter_Lowercase},
+  {0x2cb2, 0x2cb2, HB_Letter_Uppercase},
+  {0x2cb3, 0x2cb3, HB_Letter_Lowercase},
+  {0x2cb4, 0x2cb4, HB_Letter_Uppercase},
+  {0x2cb5, 0x2cb5, HB_Letter_Lowercase},
+  {0x2cb6, 0x2cb6, HB_Letter_Uppercase},
+  {0x2cb7, 0x2cb7, HB_Letter_Lowercase},
+  {0x2cb8, 0x2cb8, HB_Letter_Uppercase},
+  {0x2cb9, 0x2cb9, HB_Letter_Lowercase},
+  {0x2cba, 0x2cba, HB_Letter_Uppercase},
+  {0x2cbb, 0x2cbb, HB_Letter_Lowercase},
+  {0x2cbc, 0x2cbc, HB_Letter_Uppercase},
+  {0x2cbd, 0x2cbd, HB_Letter_Lowercase},
+  {0x2cbe, 0x2cbe, HB_Letter_Uppercase},
+  {0x2cbf, 0x2cbf, HB_Letter_Lowercase},
+  {0x2cc0, 0x2cc0, HB_Letter_Uppercase},
+  {0x2cc1, 0x2cc1, HB_Letter_Lowercase},
+  {0x2cc2, 0x2cc2, HB_Letter_Uppercase},
+  {0x2cc3, 0x2cc3, HB_Letter_Lowercase},
+  {0x2cc4, 0x2cc4, HB_Letter_Uppercase},
+  {0x2cc5, 0x2cc5, HB_Letter_Lowercase},
+  {0x2cc6, 0x2cc6, HB_Letter_Uppercase},
+  {0x2cc7, 0x2cc7, HB_Letter_Lowercase},
+  {0x2cc8, 0x2cc8, HB_Letter_Uppercase},
+  {0x2cc9, 0x2cc9, HB_Letter_Lowercase},
+  {0x2cca, 0x2cca, HB_Letter_Uppercase},
+  {0x2ccb, 0x2ccb, HB_Letter_Lowercase},
+  {0x2ccc, 0x2ccc, HB_Letter_Uppercase},
+  {0x2ccd, 0x2ccd, HB_Letter_Lowercase},
+  {0x2cce, 0x2cce, HB_Letter_Uppercase},
+  {0x2ccf, 0x2ccf, HB_Letter_Lowercase},
+  {0x2cd0, 0x2cd0, HB_Letter_Uppercase},
+  {0x2cd1, 0x2cd1, HB_Letter_Lowercase},
+  {0x2cd2, 0x2cd2, HB_Letter_Uppercase},
+  {0x2cd3, 0x2cd3, HB_Letter_Lowercase},
+  {0x2cd4, 0x2cd4, HB_Letter_Uppercase},
+  {0x2cd5, 0x2cd5, HB_Letter_Lowercase},
+  {0x2cd6, 0x2cd6, HB_Letter_Uppercase},
+  {0x2cd7, 0x2cd7, HB_Letter_Lowercase},
+  {0x2cd8, 0x2cd8, HB_Letter_Uppercase},
+  {0x2cd9, 0x2cd9, HB_Letter_Lowercase},
+  {0x2cda, 0x2cda, HB_Letter_Uppercase},
+  {0x2cdb, 0x2cdb, HB_Letter_Lowercase},
+  {0x2cdc, 0x2cdc, HB_Letter_Uppercase},
+  {0x2cdd, 0x2cdd, HB_Letter_Lowercase},
+  {0x2cde, 0x2cde, HB_Letter_Uppercase},
+  {0x2cdf, 0x2cdf, HB_Letter_Lowercase},
+  {0x2ce0, 0x2ce0, HB_Letter_Uppercase},
+  {0x2ce1, 0x2ce1, HB_Letter_Lowercase},
+  {0x2ce2, 0x2ce2, HB_Letter_Uppercase},
+  {0x2ce3, 0x2ce4, HB_Letter_Lowercase},
+  {0x2ce5, 0x2cea, HB_Symbol_Other},
+  {0x2ceb, 0x2cf8, HB_Other_NotAssigned},
+  {0x2cf9, 0x2cfc, HB_Punctuation_Other},
+  {0x2cfd, 0x2cfd, HB_Number_Other},
+  {0x2cfe, 0x2cff, HB_Punctuation_Other},
+  {0x2d00, 0x2d25, HB_Letter_Lowercase},
+  {0x2d26, 0x2d2f, HB_Other_NotAssigned},
+  {0x2d30, 0x2d65, HB_Letter_Other},
+  {0x2d66, 0x2d6e, HB_Other_NotAssigned},
+  {0x2d6f, 0x2d6f, HB_Letter_Modifier},
+  {0x2d70, 0x2d7f, HB_Other_NotAssigned},
+  {0x2d80, 0x2d96, HB_Letter_Other},
+  {0x2d97, 0x2d9f, HB_Other_NotAssigned},
+  {0x2da0, 0x2da6, HB_Letter_Other},
+  {0x2da7, 0x2da7, HB_Other_NotAssigned},
+  {0x2da8, 0x2dae, HB_Letter_Other},
+  {0x2daf, 0x2daf, HB_Other_NotAssigned},
+  {0x2db0, 0x2db6, HB_Letter_Other},
+  {0x2db7, 0x2db7, HB_Other_NotAssigned},
+  {0x2db8, 0x2dbe, HB_Letter_Other},
+  {0x2dbf, 0x2dbf, HB_Other_NotAssigned},
+  {0x2dc0, 0x2dc6, HB_Letter_Other},
+  {0x2dc7, 0x2dc7, HB_Other_NotAssigned},
+  {0x2dc8, 0x2dce, HB_Letter_Other},
+  {0x2dcf, 0x2dcf, HB_Other_NotAssigned},
+  {0x2dd0, 0x2dd6, HB_Letter_Other},
+  {0x2dd7, 0x2dd7, HB_Other_NotAssigned},
+  {0x2dd8, 0x2dde, HB_Letter_Other},
+  {0x2ddf, 0x2ddf, HB_Other_NotAssigned},
+  {0x2de0, 0x2dff, HB_Mark_NonSpacing},
+  {0x2e00, 0x2e01, HB_Punctuation_Other},
+  {0x2e02, 0x2e02, HB_Punctuation_InitialQuote},
+  {0x2e03, 0x2e03, HB_Punctuation_FinalQuote},
+  {0x2e04, 0x2e04, HB_Punctuation_InitialQuote},
+  {0x2e05, 0x2e05, HB_Punctuation_FinalQuote},
+  {0x2e06, 0x2e08, HB_Punctuation_Other},
+  {0x2e09, 0x2e09, HB_Punctuation_InitialQuote},
+  {0x2e0a, 0x2e0a, HB_Punctuation_FinalQuote},
+  {0x2e0b, 0x2e0b, HB_Punctuation_Other},
+  {0x2e0c, 0x2e0c, HB_Punctuation_InitialQuote},
+  {0x2e0d, 0x2e0d, HB_Punctuation_FinalQuote},
+  {0x2e0e, 0x2e16, HB_Punctuation_Other},
+  {0x2e17, 0x2e17, HB_Punctuation_Dash},
+  {0x2e18, 0x2e19, HB_Punctuation_Other},
+  {0x2e1a, 0x2e1a, HB_Punctuation_Dash},
+  {0x2e1b, 0x2e1b, HB_Punctuation_Other},
+  {0x2e1c, 0x2e1c, HB_Punctuation_InitialQuote},
+  {0x2e1d, 0x2e1d, HB_Punctuation_FinalQuote},
+  {0x2e1e, 0x2e1f, HB_Punctuation_Other},
+  {0x2e20, 0x2e20, HB_Punctuation_InitialQuote},
+  {0x2e21, 0x2e21, HB_Punctuation_FinalQuote},
+  {0x2e22, 0x2e22, HB_Punctuation_Open},
+  {0x2e23, 0x2e23, HB_Punctuation_Close},
+  {0x2e24, 0x2e24, HB_Punctuation_Open},
+  {0x2e25, 0x2e25, HB_Punctuation_Close},
+  {0x2e26, 0x2e26, HB_Punctuation_Open},
+  {0x2e27, 0x2e27, HB_Punctuation_Close},
+  {0x2e28, 0x2e28, HB_Punctuation_Open},
+  {0x2e29, 0x2e29, HB_Punctuation_Close},
+  {0x2e2a, 0x2e2e, HB_Punctuation_Other},
+  {0x2e2f, 0x2e2f, HB_Letter_Modifier},
+  {0x2e30, 0x2e30, HB_Punctuation_Other},
+  {0x2e31, 0x2e7f, HB_Other_NotAssigned},
+  {0x2e80, 0x2e99, HB_Symbol_Other},
+  {0x2e9a, 0x2e9a, HB_Other_NotAssigned},
+  {0x2e9b, 0x2ef3, HB_Symbol_Other},
+  {0x2ef4, 0x2eff, HB_Other_NotAssigned},
+  {0x2f00, 0x2fd5, HB_Symbol_Other},
+  {0x2fd6, 0x2fef, HB_Other_NotAssigned},
+  {0x2ff0, 0x2ffb, HB_Symbol_Other},
+  {0x2ffc, 0x2fff, HB_Other_NotAssigned},
+  {0x3000, 0x3000, HB_Separator_Space},
+  {0x3001, 0x3003, HB_Punctuation_Other},
+  {0x3004, 0x3004, HB_Symbol_Other},
+  {0x3005, 0x3005, HB_Letter_Modifier},
+  {0x3006, 0x3006, HB_Letter_Other},
+  {0x3007, 0x3007, HB_Number_Letter},
+  {0x3008, 0x3008, HB_Punctuation_Open},
+  {0x3009, 0x3009, HB_Punctuation_Close},
+  {0x300a, 0x300a, HB_Punctuation_Open},
+  {0x300b, 0x300b, HB_Punctuation_Close},
+  {0x300c, 0x300c, HB_Punctuation_Open},
+  {0x300d, 0x300d, HB_Punctuation_Close},
+  {0x300e, 0x300e, HB_Punctuation_Open},
+  {0x300f, 0x300f, HB_Punctuation_Close},
+  {0x3010, 0x3010, HB_Punctuation_Open},
+  {0x3011, 0x3011, HB_Punctuation_Close},
+  {0x3012, 0x3013, HB_Symbol_Other},
+  {0x3014, 0x3014, HB_Punctuation_Open},
+  {0x3015, 0x3015, HB_Punctuation_Close},
+  {0x3016, 0x3016, HB_Punctuation_Open},
+  {0x3017, 0x3017, HB_Punctuation_Close},
+  {0x3018, 0x3018, HB_Punctuation_Open},
+  {0x3019, 0x3019, HB_Punctuation_Close},
+  {0x301a, 0x301a, HB_Punctuation_Open},
+  {0x301b, 0x301b, HB_Punctuation_Close},
+  {0x301c, 0x301c, HB_Punctuation_Dash},
+  {0x301d, 0x301d, HB_Punctuation_Open},
+  {0x301e, 0x301f, HB_Punctuation_Close},
+  {0x3020, 0x3020, HB_Symbol_Other},
+  {0x3021, 0x3029, HB_Number_Letter},
+  {0x302a, 0x302f, HB_Mark_NonSpacing},
+  {0x3030, 0x3030, HB_Punctuation_Dash},
+  {0x3031, 0x3035, HB_Letter_Modifier},
+  {0x3036, 0x3037, HB_Symbol_Other},
+  {0x3038, 0x303a, HB_Number_Letter},
+  {0x303b, 0x303b, HB_Letter_Modifier},
+  {0x303c, 0x303c, HB_Letter_Other},
+  {0x303d, 0x303d, HB_Punctuation_Other},
+  {0x303e, 0x303f, HB_Symbol_Other},
+  {0x3040, 0x3040, HB_Other_NotAssigned},
+  {0x3041, 0x3096, HB_Letter_Other},
+  {0x3097, 0x3098, HB_Other_NotAssigned},
+  {0x3099, 0x309a, HB_Mark_NonSpacing},
+  {0x309b, 0x309c, HB_Symbol_Modifier},
+  {0x309d, 0x309e, HB_Letter_Modifier},
+  {0x309f, 0x309f, HB_Letter_Other},
+  {0x30a0, 0x30a0, HB_Punctuation_Dash},
+  {0x30a1, 0x30fa, HB_Letter_Other},
+  {0x30fb, 0x30fb, HB_Punctuation_Other},
+  {0x30fc, 0x30fe, HB_Letter_Modifier},
+  {0x30ff, 0x30ff, HB_Letter_Other},
+  {0x3100, 0x3104, HB_Other_NotAssigned},
+  {0x3105, 0x312d, HB_Letter_Other},
+  {0x312e, 0x3130, HB_Other_NotAssigned},
+  {0x3131, 0x318e, HB_Letter_Other},
+  {0x318f, 0x318f, HB_Other_NotAssigned},
+  {0x3190, 0x3191, HB_Symbol_Other},
+  {0x3192, 0x3195, HB_Number_Other},
+  {0x3196, 0x319f, HB_Symbol_Other},
+  {0x31a0, 0x31b7, HB_Letter_Other},
+  {0x31b8, 0x31bf, HB_Other_NotAssigned},
+  {0x31c0, 0x31e3, HB_Symbol_Other},
+  {0x31e4, 0x31ef, HB_Other_NotAssigned},
+  {0x31f0, 0x31ff, HB_Letter_Other},
+  {0x3200, 0x321e, HB_Symbol_Other},
+  {0x321f, 0x321f, HB_Other_NotAssigned},
+  {0x3220, 0x3229, HB_Number_Other},
+  {0x322a, 0x3243, HB_Symbol_Other},
+  {0x3244, 0x324f, HB_Other_NotAssigned},
+  {0x3250, 0x3250, HB_Symbol_Other},
+  {0x3251, 0x325f, HB_Number_Other},
+  {0x3260, 0x327f, HB_Symbol_Other},
+  {0x3280, 0x3289, HB_Number_Other},
+  {0x328a, 0x32b0, HB_Symbol_Other},
+  {0x32b1, 0x32bf, HB_Number_Other},
+  {0x32c0, 0x32fe, HB_Symbol_Other},
+  {0x32ff, 0x32ff, HB_Other_NotAssigned},
+  {0x3300, 0x33ff, HB_Symbol_Other},
+  {0x3400, 0x4db5, HB_Letter_Other},
+  {0x4db6, 0x4dbf, HB_Other_NotAssigned},
+  {0x4dc0, 0x4dff, HB_Symbol_Other},
+  {0x4e00, 0x9fc3, HB_Letter_Other},
+  {0x9fc4, 0x9fff, HB_Other_NotAssigned},
+  {0xa000, 0xa014, HB_Letter_Other},
+  {0xa015, 0xa015, HB_Letter_Modifier},
+  {0xa016, 0xa48c, HB_Letter_Other},
+  {0xa48d, 0xa48f, HB_Other_NotAssigned},
+  {0xa490, 0xa4c6, HB_Symbol_Other},
+  {0xa4c7, 0xa4ff, HB_Other_NotAssigned},
+  {0xa500, 0xa60b, HB_Letter_Other},
+  {0xa60c, 0xa60c, HB_Letter_Modifier},
+  {0xa60d, 0xa60f, HB_Punctuation_Other},
+  {0xa610, 0xa61f, HB_Letter_Other},
+  {0xa620, 0xa629, HB_Number_DecimalDigit},
+  {0xa62a, 0xa62b, HB_Letter_Other},
+  {0xa62c, 0xa63f, HB_Other_NotAssigned},
+  {0xa640, 0xa640, HB_Letter_Uppercase},
+  {0xa641, 0xa641, HB_Letter_Lowercase},
+  {0xa642, 0xa642, HB_Letter_Uppercase},
+  {0xa643, 0xa643, HB_Letter_Lowercase},
+  {0xa644, 0xa644, HB_Letter_Uppercase},
+  {0xa645, 0xa645, HB_Letter_Lowercase},
+  {0xa646, 0xa646, HB_Letter_Uppercase},
+  {0xa647, 0xa647, HB_Letter_Lowercase},
+  {0xa648, 0xa648, HB_Letter_Uppercase},
+  {0xa649, 0xa649, HB_Letter_Lowercase},
+  {0xa64a, 0xa64a, HB_Letter_Uppercase},
+  {0xa64b, 0xa64b, HB_Letter_Lowercase},
+  {0xa64c, 0xa64c, HB_Letter_Uppercase},
+  {0xa64d, 0xa64d, HB_Letter_Lowercase},
+  {0xa64e, 0xa64e, HB_Letter_Uppercase},
+  {0xa64f, 0xa64f, HB_Letter_Lowercase},
+  {0xa650, 0xa650, HB_Letter_Uppercase},
+  {0xa651, 0xa651, HB_Letter_Lowercase},
+  {0xa652, 0xa652, HB_Letter_Uppercase},
+  {0xa653, 0xa653, HB_Letter_Lowercase},
+  {0xa654, 0xa654, HB_Letter_Uppercase},
+  {0xa655, 0xa655, HB_Letter_Lowercase},
+  {0xa656, 0xa656, HB_Letter_Uppercase},
+  {0xa657, 0xa657, HB_Letter_Lowercase},
+  {0xa658, 0xa658, HB_Letter_Uppercase},
+  {0xa659, 0xa659, HB_Letter_Lowercase},
+  {0xa65a, 0xa65a, HB_Letter_Uppercase},
+  {0xa65b, 0xa65b, HB_Letter_Lowercase},
+  {0xa65c, 0xa65c, HB_Letter_Uppercase},
+  {0xa65d, 0xa65d, HB_Letter_Lowercase},
+  {0xa65e, 0xa65e, HB_Letter_Uppercase},
+  {0xa65f, 0xa65f, HB_Letter_Lowercase},
+  {0xa660, 0xa661, HB_Other_NotAssigned},
+  {0xa662, 0xa662, HB_Letter_Uppercase},
+  {0xa663, 0xa663, HB_Letter_Lowercase},
+  {0xa664, 0xa664, HB_Letter_Uppercase},
+  {0xa665, 0xa665, HB_Letter_Lowercase},
+  {0xa666, 0xa666, HB_Letter_Uppercase},
+  {0xa667, 0xa667, HB_Letter_Lowercase},
+  {0xa668, 0xa668, HB_Letter_Uppercase},
+  {0xa669, 0xa669, HB_Letter_Lowercase},
+  {0xa66a, 0xa66a, HB_Letter_Uppercase},
+  {0xa66b, 0xa66b, HB_Letter_Lowercase},
+  {0xa66c, 0xa66c, HB_Letter_Uppercase},
+  {0xa66d, 0xa66d, HB_Letter_Lowercase},
+  {0xa66e, 0xa66e, HB_Letter_Other},
+  {0xa66f, 0xa66f, HB_Mark_NonSpacing},
+  {0xa670, 0xa672, HB_Mark_Enclosing},
+  {0xa673, 0xa673, HB_Punctuation_Other},
+  {0xa674, 0xa67b, HB_Other_NotAssigned},
+  {0xa67c, 0xa67d, HB_Mark_NonSpacing},
+  {0xa67e, 0xa67e, HB_Punctuation_Other},
+  {0xa67f, 0xa67f, HB_Letter_Modifier},
+  {0xa680, 0xa680, HB_Letter_Uppercase},
+  {0xa681, 0xa681, HB_Letter_Lowercase},
+  {0xa682, 0xa682, HB_Letter_Uppercase},
+  {0xa683, 0xa683, HB_Letter_Lowercase},
+  {0xa684, 0xa684, HB_Letter_Uppercase},
+  {0xa685, 0xa685, HB_Letter_Lowercase},
+  {0xa686, 0xa686, HB_Letter_Uppercase},
+  {0xa687, 0xa687, HB_Letter_Lowercase},
+  {0xa688, 0xa688, HB_Letter_Uppercase},
+  {0xa689, 0xa689, HB_Letter_Lowercase},
+  {0xa68a, 0xa68a, HB_Letter_Uppercase},
+  {0xa68b, 0xa68b, HB_Letter_Lowercase},
+  {0xa68c, 0xa68c, HB_Letter_Uppercase},
+  {0xa68d, 0xa68d, HB_Letter_Lowercase},
+  {0xa68e, 0xa68e, HB_Letter_Uppercase},
+  {0xa68f, 0xa68f, HB_Letter_Lowercase},
+  {0xa690, 0xa690, HB_Letter_Uppercase},
+  {0xa691, 0xa691, HB_Letter_Lowercase},
+  {0xa692, 0xa692, HB_Letter_Uppercase},
+  {0xa693, 0xa693, HB_Letter_Lowercase},
+  {0xa694, 0xa694, HB_Letter_Uppercase},
+  {0xa695, 0xa695, HB_Letter_Lowercase},
+  {0xa696, 0xa696, HB_Letter_Uppercase},
+  {0xa697, 0xa697, HB_Letter_Lowercase},
+  {0xa698, 0xa6ff, HB_Other_NotAssigned},
+  {0xa700, 0xa716, HB_Symbol_Modifier},
+  {0xa717, 0xa71f, HB_Letter_Modifier},
+  {0xa720, 0xa721, HB_Symbol_Modifier},
+  {0xa722, 0xa722, HB_Letter_Uppercase},
+  {0xa723, 0xa723, HB_Letter_Lowercase},
+  {0xa724, 0xa724, HB_Letter_Uppercase},
+  {0xa725, 0xa725, HB_Letter_Lowercase},
+  {0xa726, 0xa726, HB_Letter_Uppercase},
+  {0xa727, 0xa727, HB_Letter_Lowercase},
+  {0xa728, 0xa728, HB_Letter_Uppercase},
+  {0xa729, 0xa729, HB_Letter_Lowercase},
+  {0xa72a, 0xa72a, HB_Letter_Uppercase},
+  {0xa72b, 0xa72b, HB_Letter_Lowercase},
+  {0xa72c, 0xa72c, HB_Letter_Uppercase},
+  {0xa72d, 0xa72d, HB_Letter_Lowercase},
+  {0xa72e, 0xa72e, HB_Letter_Uppercase},
+  {0xa72f, 0xa731, HB_Letter_Lowercase},
+  {0xa732, 0xa732, HB_Letter_Uppercase},
+  {0xa733, 0xa733, HB_Letter_Lowercase},
+  {0xa734, 0xa734, HB_Letter_Uppercase},
+  {0xa735, 0xa735, HB_Letter_Lowercase},
+  {0xa736, 0xa736, HB_Letter_Uppercase},
+  {0xa737, 0xa737, HB_Letter_Lowercase},
+  {0xa738, 0xa738, HB_Letter_Uppercase},
+  {0xa739, 0xa739, HB_Letter_Lowercase},
+  {0xa73a, 0xa73a, HB_Letter_Uppercase},
+  {0xa73b, 0xa73b, HB_Letter_Lowercase},
+  {0xa73c, 0xa73c, HB_Letter_Uppercase},
+  {0xa73d, 0xa73d, HB_Letter_Lowercase},
+  {0xa73e, 0xa73e, HB_Letter_Uppercase},
+  {0xa73f, 0xa73f, HB_Letter_Lowercase},
+  {0xa740, 0xa740, HB_Letter_Uppercase},
+  {0xa741, 0xa741, HB_Letter_Lowercase},
+  {0xa742, 0xa742, HB_Letter_Uppercase},
+  {0xa743, 0xa743, HB_Letter_Lowercase},
+  {0xa744, 0xa744, HB_Letter_Uppercase},
+  {0xa745, 0xa745, HB_Letter_Lowercase},
+  {0xa746, 0xa746, HB_Letter_Uppercase},
+  {0xa747, 0xa747, HB_Letter_Lowercase},
+  {0xa748, 0xa748, HB_Letter_Uppercase},
+  {0xa749, 0xa749, HB_Letter_Lowercase},
+  {0xa74a, 0xa74a, HB_Letter_Uppercase},
+  {0xa74b, 0xa74b, HB_Letter_Lowercase},
+  {0xa74c, 0xa74c, HB_Letter_Uppercase},
+  {0xa74d, 0xa74d, HB_Letter_Lowercase},
+  {0xa74e, 0xa74e, HB_Letter_Uppercase},
+  {0xa74f, 0xa74f, HB_Letter_Lowercase},
+  {0xa750, 0xa750, HB_Letter_Uppercase},
+  {0xa751, 0xa751, HB_Letter_Lowercase},
+  {0xa752, 0xa752, HB_Letter_Uppercase},
+  {0xa753, 0xa753, HB_Letter_Lowercase},
+  {0xa754, 0xa754, HB_Letter_Uppercase},
+  {0xa755, 0xa755, HB_Letter_Lowercase},
+  {0xa756, 0xa756, HB_Letter_Uppercase},
+  {0xa757, 0xa757, HB_Letter_Lowercase},
+  {0xa758, 0xa758, HB_Letter_Uppercase},
+  {0xa759, 0xa759, HB_Letter_Lowercase},
+  {0xa75a, 0xa75a, HB_Letter_Uppercase},
+  {0xa75b, 0xa75b, HB_Letter_Lowercase},
+  {0xa75c, 0xa75c, HB_Letter_Uppercase},
+  {0xa75d, 0xa75d, HB_Letter_Lowercase},
+  {0xa75e, 0xa75e, HB_Letter_Uppercase},
+  {0xa75f, 0xa75f, HB_Letter_Lowercase},
+  {0xa760, 0xa760, HB_Letter_Uppercase},
+  {0xa761, 0xa761, HB_Letter_Lowercase},
+  {0xa762, 0xa762, HB_Letter_Uppercase},
+  {0xa763, 0xa763, HB_Letter_Lowercase},
+  {0xa764, 0xa764, HB_Letter_Uppercase},
+  {0xa765, 0xa765, HB_Letter_Lowercase},
+  {0xa766, 0xa766, HB_Letter_Uppercase},
+  {0xa767, 0xa767, HB_Letter_Lowercase},
+  {0xa768, 0xa768, HB_Letter_Uppercase},
+  {0xa769, 0xa769, HB_Letter_Lowercase},
+  {0xa76a, 0xa76a, HB_Letter_Uppercase},
+  {0xa76b, 0xa76b, HB_Letter_Lowercase},
+  {0xa76c, 0xa76c, HB_Letter_Uppercase},
+  {0xa76d, 0xa76d, HB_Letter_Lowercase},
+  {0xa76e, 0xa76e, HB_Letter_Uppercase},
+  {0xa76f, 0xa76f, HB_Letter_Lowercase},
+  {0xa770, 0xa770, HB_Letter_Modifier},
+  {0xa771, 0xa778, HB_Letter_Lowercase},
+  {0xa779, 0xa779, HB_Letter_Uppercase},
+  {0xa77a, 0xa77a, HB_Letter_Lowercase},
+  {0xa77b, 0xa77b, HB_Letter_Uppercase},
+  {0xa77c, 0xa77c, HB_Letter_Lowercase},
+  {0xa77d, 0xa77e, HB_Letter_Uppercase},
+  {0xa77f, 0xa77f, HB_Letter_Lowercase},
+  {0xa780, 0xa780, HB_Letter_Uppercase},
+  {0xa781, 0xa781, HB_Letter_Lowercase},
+  {0xa782, 0xa782, HB_Letter_Uppercase},
+  {0xa783, 0xa783, HB_Letter_Lowercase},
+  {0xa784, 0xa784, HB_Letter_Uppercase},
+  {0xa785, 0xa785, HB_Letter_Lowercase},
+  {0xa786, 0xa786, HB_Letter_Uppercase},
+  {0xa787, 0xa787, HB_Letter_Lowercase},
+  {0xa788, 0xa788, HB_Letter_Modifier},
+  {0xa789, 0xa78a, HB_Symbol_Modifier},
+  {0xa78b, 0xa78b, HB_Letter_Uppercase},
+  {0xa78c, 0xa78c, HB_Letter_Lowercase},
+  {0xa78d, 0xa7fa, HB_Other_NotAssigned},
+  {0xa7fb, 0xa801, HB_Letter_Other},
+  {0xa802, 0xa802, HB_Mark_NonSpacing},
+  {0xa803, 0xa805, HB_Letter_Other},
+  {0xa806, 0xa806, HB_Mark_NonSpacing},
+  {0xa807, 0xa80a, HB_Letter_Other},
+  {0xa80b, 0xa80b, HB_Mark_NonSpacing},
+  {0xa80c, 0xa822, HB_Letter_Other},
+  {0xa823, 0xa824, HB_Mark_SpacingCombining},
+  {0xa825, 0xa826, HB_Mark_NonSpacing},
+  {0xa827, 0xa827, HB_Mark_SpacingCombining},
+  {0xa828, 0xa82b, HB_Symbol_Other},
+  {0xa82c, 0xa83f, HB_Other_NotAssigned},
+  {0xa840, 0xa873, HB_Letter_Other},
+  {0xa874, 0xa877, HB_Punctuation_Other},
+  {0xa878, 0xa87f, HB_Other_NotAssigned},
+  {0xa880, 0xa881, HB_Mark_SpacingCombining},
+  {0xa882, 0xa8b3, HB_Letter_Other},
+  {0xa8b4, 0xa8c3, HB_Mark_SpacingCombining},
+  {0xa8c4, 0xa8c4, HB_Mark_NonSpacing},
+  {0xa8c5, 0xa8cd, HB_Other_NotAssigned},
+  {0xa8ce, 0xa8cf, HB_Punctuation_Other},
+  {0xa8d0, 0xa8d9, HB_Number_DecimalDigit},
+  {0xa8da, 0xa8ff, HB_Other_NotAssigned},
+  {0xa900, 0xa909, HB_Number_DecimalDigit},
+  {0xa90a, 0xa925, HB_Letter_Other},
+  {0xa926, 0xa92d, HB_Mark_NonSpacing},
+  {0xa92e, 0xa92f, HB_Punctuation_Other},
+  {0xa930, 0xa946, HB_Letter_Other},
+  {0xa947, 0xa951, HB_Mark_NonSpacing},
+  {0xa952, 0xa953, HB_Mark_SpacingCombining},
+  {0xa954, 0xa95e, HB_Other_NotAssigned},
+  {0xa95f, 0xa95f, HB_Punctuation_Other},
+  {0xa960, 0xa9ff, HB_Other_NotAssigned},
+  {0xaa00, 0xaa28, HB_Letter_Other},
+  {0xaa29, 0xaa2e, HB_Mark_NonSpacing},
+  {0xaa2f, 0xaa30, HB_Mark_SpacingCombining},
+  {0xaa31, 0xaa32, HB_Mark_NonSpacing},
+  {0xaa33, 0xaa34, HB_Mark_SpacingCombining},
+  {0xaa35, 0xaa36, HB_Mark_NonSpacing},
+  {0xaa37, 0xaa3f, HB_Other_NotAssigned},
+  {0xaa40, 0xaa42, HB_Letter_Other},
+  {0xaa43, 0xaa43, HB_Mark_NonSpacing},
+  {0xaa44, 0xaa4b, HB_Letter_Other},
+  {0xaa4c, 0xaa4c, HB_Mark_NonSpacing},
+  {0xaa4d, 0xaa4d, HB_Mark_SpacingCombining},
+  {0xaa4e, 0xaa4f, HB_Other_NotAssigned},
+  {0xaa50, 0xaa59, HB_Number_DecimalDigit},
+  {0xaa5a, 0xaa5b, HB_Other_NotAssigned},
+  {0xaa5c, 0xaa5f, HB_Punctuation_Other},
+  {0xaa60, 0xabff, HB_Other_NotAssigned},
+  {0xac00, 0xd7a3, HB_Letter_Other},
+  {0xd7a4, 0xd7ff, HB_Other_NotAssigned},
+  {0xd800, 0xdfff, HB_Other_Surrogate},
+  {0xe000, 0xf8ff, HB_Other_PrivateUse},
+  {0xf900, 0xfa2d, HB_Letter_Other},
+  {0xfa2e, 0xfa2f, HB_Other_NotAssigned},
+  {0xfa30, 0xfa6a, HB_Letter_Other},
+  {0xfa6b, 0xfa6f, HB_Other_NotAssigned},
+  {0xfa70, 0xfad9, HB_Letter_Other},
+  {0xfada, 0xfaff, HB_Other_NotAssigned},
+  {0xfb00, 0xfb06, HB_Letter_Lowercase},
+  {0xfb07, 0xfb12, HB_Other_NotAssigned},
+  {0xfb13, 0xfb17, HB_Letter_Lowercase},
+  {0xfb18, 0xfb1c, HB_Other_NotAssigned},
+  {0xfb1d, 0xfb1d, HB_Letter_Other},
+  {0xfb1e, 0xfb1e, HB_Mark_NonSpacing},
+  {0xfb1f, 0xfb28, HB_Letter_Other},
+  {0xfb29, 0xfb29, HB_Symbol_Math},
+  {0xfb2a, 0xfb36, HB_Letter_Other},
+  {0xfb37, 0xfb37, HB_Other_NotAssigned},
+  {0xfb38, 0xfb3c, HB_Letter_Other},
+  {0xfb3d, 0xfb3d, HB_Other_NotAssigned},
+  {0xfb3e, 0xfb3e, HB_Letter_Other},
+  {0xfb3f, 0xfb3f, HB_Other_NotAssigned},
+  {0xfb40, 0xfb41, HB_Letter_Other},
+  {0xfb42, 0xfb42, HB_Other_NotAssigned},
+  {0xfb43, 0xfb44, HB_Letter_Other},
+  {0xfb45, 0xfb45, HB_Other_NotAssigned},
+  {0xfb46, 0xfbb1, HB_Letter_Other},
+  {0xfbb2, 0xfbd2, HB_Other_NotAssigned},
+  {0xfbd3, 0xfd3d, HB_Letter_Other},
+  {0xfd3e, 0xfd3e, HB_Punctuation_Open},
+  {0xfd3f, 0xfd3f, HB_Punctuation_Close},
+  {0xfd40, 0xfd4f, HB_Other_NotAssigned},
+  {0xfd50, 0xfd8f, HB_Letter_Other},
+  {0xfd90, 0xfd91, HB_Other_NotAssigned},
+  {0xfd92, 0xfdc7, HB_Letter_Other},
+  {0xfdc8, 0xfdef, HB_Other_NotAssigned},
+  {0xfdf0, 0xfdfb, HB_Letter_Other},
+  {0xfdfc, 0xfdfc, HB_Symbol_Currency},
+  {0xfdfd, 0xfdfd, HB_Symbol_Other},
+  {0xfdfe, 0xfdff, HB_Other_NotAssigned},
+  {0xfe00, 0xfe0f, HB_Mark_NonSpacing},
+  {0xfe10, 0xfe16, HB_Punctuation_Other},
+  {0xfe17, 0xfe17, HB_Punctuation_Open},
+  {0xfe18, 0xfe18, HB_Punctuation_Close},
+  {0xfe19, 0xfe19, HB_Punctuation_Other},
+  {0xfe1a, 0xfe1f, HB_Other_NotAssigned},
+  {0xfe20, 0xfe26, HB_Mark_NonSpacing},
+  {0xfe27, 0xfe2f, HB_Other_NotAssigned},
+  {0xfe30, 0xfe30, HB_Punctuation_Other},
+  {0xfe31, 0xfe32, HB_Punctuation_Dash},
+  {0xfe33, 0xfe34, HB_Punctuation_Connector},
+  {0xfe35, 0xfe35, HB_Punctuation_Open},
+  {0xfe36, 0xfe36, HB_Punctuation_Close},
+  {0xfe37, 0xfe37, HB_Punctuation_Open},
+  {0xfe38, 0xfe38, HB_Punctuation_Close},
+  {0xfe39, 0xfe39, HB_Punctuation_Open},
+  {0xfe3a, 0xfe3a, HB_Punctuation_Close},
+  {0xfe3b, 0xfe3b, HB_Punctuation_Open},
+  {0xfe3c, 0xfe3c, HB_Punctuation_Close},
+  {0xfe3d, 0xfe3d, HB_Punctuation_Open},
+  {0xfe3e, 0xfe3e, HB_Punctuation_Close},
+  {0xfe3f, 0xfe3f, HB_Punctuation_Open},
+  {0xfe40, 0xfe40, HB_Punctuation_Close},
+  {0xfe41, 0xfe41, HB_Punctuation_Open},
+  {0xfe42, 0xfe42, HB_Punctuation_Close},
+  {0xfe43, 0xfe43, HB_Punctuation_Open},
+  {0xfe44, 0xfe44, HB_Punctuation_Close},
+  {0xfe45, 0xfe46, HB_Punctuation_Other},
+  {0xfe47, 0xfe47, HB_Punctuation_Open},
+  {0xfe48, 0xfe48, HB_Punctuation_Close},
+  {0xfe49, 0xfe4c, HB_Punctuation_Other},
+  {0xfe4d, 0xfe4f, HB_Punctuation_Connector},
+  {0xfe50, 0xfe52, HB_Punctuation_Other},
+  {0xfe53, 0xfe53, HB_Other_NotAssigned},
+  {0xfe54, 0xfe57, HB_Punctuation_Other},
+  {0xfe58, 0xfe58, HB_Punctuation_Dash},
+  {0xfe59, 0xfe59, HB_Punctuation_Open},
+  {0xfe5a, 0xfe5a, HB_Punctuation_Close},
+  {0xfe5b, 0xfe5b, HB_Punctuation_Open},
+  {0xfe5c, 0xfe5c, HB_Punctuation_Close},
+  {0xfe5d, 0xfe5d, HB_Punctuation_Open},
+  {0xfe5e, 0xfe5e, HB_Punctuation_Close},
+  {0xfe5f, 0xfe61, HB_Punctuation_Other},
+  {0xfe62, 0xfe62, HB_Symbol_Math},
+  {0xfe63, 0xfe63, HB_Punctuation_Dash},
+  {0xfe64, 0xfe66, HB_Symbol_Math},
+  {0xfe67, 0xfe67, HB_Other_NotAssigned},
+  {0xfe68, 0xfe68, HB_Punctuation_Other},
+  {0xfe69, 0xfe69, HB_Symbol_Currency},
+  {0xfe6a, 0xfe6b, HB_Punctuation_Other},
+  {0xfe6c, 0xfe6f, HB_Other_NotAssigned},
+  {0xfe70, 0xfe74, HB_Letter_Other},
+  {0xfe75, 0xfe75, HB_Other_NotAssigned},
+  {0xfe76, 0xfefc, HB_Letter_Other},
+  {0xfefd, 0xfefe, HB_Other_NotAssigned},
+  {0xfeff, 0xfeff, HB_Other_Format},
+  {0xff00, 0xff00, HB_Other_NotAssigned},
+  {0xff01, 0xff03, HB_Punctuation_Other},
+  {0xff04, 0xff04, HB_Symbol_Currency},
+  {0xff05, 0xff07, HB_Punctuation_Other},
+  {0xff08, 0xff08, HB_Punctuation_Open},
+  {0xff09, 0xff09, HB_Punctuation_Close},
+  {0xff0a, 0xff0a, HB_Punctuation_Other},
+  {0xff0b, 0xff0b, HB_Symbol_Math},
+  {0xff0c, 0xff0c, HB_Punctuation_Other},
+  {0xff0d, 0xff0d, HB_Punctuation_Dash},
+  {0xff0e, 0xff0f, HB_Punctuation_Other},
+  {0xff10, 0xff19, HB_Number_DecimalDigit},
+  {0xff1a, 0xff1b, HB_Punctuation_Other},
+  {0xff1c, 0xff1e, HB_Symbol_Math},
+  {0xff1f, 0xff20, HB_Punctuation_Other},
+  {0xff21, 0xff3a, HB_Letter_Uppercase},
+  {0xff3b, 0xff3b, HB_Punctuation_Open},
+  {0xff3c, 0xff3c, HB_Punctuation_Other},
+  {0xff3d, 0xff3d, HB_Punctuation_Close},
+  {0xff3e, 0xff3e, HB_Symbol_Modifier},
+  {0xff3f, 0xff3f, HB_Punctuation_Connector},
+  {0xff40, 0xff40, HB_Symbol_Modifier},
+  {0xff41, 0xff5a, HB_Letter_Lowercase},
+  {0xff5b, 0xff5b, HB_Punctuation_Open},
+  {0xff5c, 0xff5c, HB_Symbol_Math},
+  {0xff5d, 0xff5d, HB_Punctuation_Close},
+  {0xff5e, 0xff5e, HB_Symbol_Math},
+  {0xff5f, 0xff5f, HB_Punctuation_Open},
+  {0xff60, 0xff60, HB_Punctuation_Close},
+  {0xff61, 0xff61, HB_Punctuation_Other},
+  {0xff62, 0xff62, HB_Punctuation_Open},
+  {0xff63, 0xff63, HB_Punctuation_Close},
+  {0xff64, 0xff65, HB_Punctuation_Other},
+  {0xff66, 0xff6f, HB_Letter_Other},
+  {0xff70, 0xff70, HB_Letter_Modifier},
+  {0xff71, 0xff9d, HB_Letter_Other},
+  {0xff9e, 0xff9f, HB_Letter_Modifier},
+  {0xffa0, 0xffbe, HB_Letter_Other},
+  {0xffbf, 0xffc1, HB_Other_NotAssigned},
+  {0xffc2, 0xffc7, HB_Letter_Other},
+  {0xffc8, 0xffc9, HB_Other_NotAssigned},
+  {0xffca, 0xffcf, HB_Letter_Other},
+  {0xffd0, 0xffd1, HB_Other_NotAssigned},
+  {0xffd2, 0xffd7, HB_Letter_Other},
+  {0xffd8, 0xffd9, HB_Other_NotAssigned},
+  {0xffda, 0xffdc, HB_Letter_Other},
+  {0xffdd, 0xffdf, HB_Other_NotAssigned},
+  {0xffe0, 0xffe1, HB_Symbol_Currency},
+  {0xffe2, 0xffe2, HB_Symbol_Math},
+  {0xffe3, 0xffe3, HB_Symbol_Modifier},
+  {0xffe4, 0xffe4, HB_Symbol_Other},
+  {0xffe5, 0xffe6, HB_Symbol_Currency},
+  {0xffe7, 0xffe7, HB_Other_NotAssigned},
+  {0xffe8, 0xffe8, HB_Symbol_Other},
+  {0xffe9, 0xffec, HB_Symbol_Math},
+  {0xffed, 0xffee, HB_Symbol_Other},
+  {0xffef, 0xfff8, HB_Other_NotAssigned},
+  {0xfff9, 0xfffb, HB_Other_Format},
+  {0xfffc, 0xfffd, HB_Symbol_Other},
+  {0xfffe, 0xffff, HB_Other_NotAssigned},
+  {0x10000, 0x1000b, HB_Letter_Other},
+  {0x1000c, 0x1000c, HB_Other_NotAssigned},
+  {0x1000d, 0x10026, HB_Letter_Other},
+  {0x10027, 0x10027, HB_Other_NotAssigned},
+  {0x10028, 0x1003a, HB_Letter_Other},
+  {0x1003b, 0x1003b, HB_Other_NotAssigned},
+  {0x1003c, 0x1003d, HB_Letter_Other},
+  {0x1003e, 0x1003e, HB_Other_NotAssigned},
+  {0x1003f, 0x1004d, HB_Letter_Other},
+  {0x1004e, 0x1004f, HB_Other_NotAssigned},
+  {0x10050, 0x1005d, HB_Letter_Other},
+  {0x1005e, 0x1007f, HB_Other_NotAssigned},
+  {0x10080, 0x100fa, HB_Letter_Other},
+  {0x100fb, 0x100ff, HB_Other_NotAssigned},
+  {0x10100, 0x10101, HB_Punctuation_Other},
+  {0x10102, 0x10102, HB_Symbol_Other},
+  {0x10103, 0x10106, HB_Other_NotAssigned},
+  {0x10107, 0x10133, HB_Number_Other},
+  {0x10134, 0x10136, HB_Other_NotAssigned},
+  {0x10137, 0x1013f, HB_Symbol_Other},
+  {0x10140, 0x10174, HB_Number_Letter},
+  {0x10175, 0x10178, HB_Number_Other},
+  {0x10179, 0x10189, HB_Symbol_Other},
+  {0x1018a, 0x1018a, HB_Number_Other},
+  {0x1018b, 0x1018f, HB_Other_NotAssigned},
+  {0x10190, 0x1019b, HB_Symbol_Other},
+  {0x1019c, 0x101cf, HB_Other_NotAssigned},
+  {0x101d0, 0x101fc, HB_Symbol_Other},
+  {0x101fd, 0x101fd, HB_Mark_NonSpacing},
+  {0x101fe, 0x1027f, HB_Other_NotAssigned},
+  {0x10280, 0x1029c, HB_Letter_Other},
+  {0x1029d, 0x1029f, HB_Other_NotAssigned},
+  {0x102a0, 0x102d0, HB_Letter_Other},
+  {0x102d1, 0x102ff, HB_Other_NotAssigned},
+  {0x10300, 0x1031e, HB_Letter_Other},
+  {0x1031f, 0x1031f, HB_Other_NotAssigned},
+  {0x10320, 0x10323, HB_Number_Other},
+  {0x10324, 0x1032f, HB_Other_NotAssigned},
+  {0x10330, 0x10340, HB_Letter_Other},
+  {0x10341, 0x10341, HB_Number_Letter},
+  {0x10342, 0x10349, HB_Letter_Other},
+  {0x1034a, 0x1034a, HB_Number_Letter},
+  {0x1034b, 0x1037f, HB_Other_NotAssigned},
+  {0x10380, 0x1039d, HB_Letter_Other},
+  {0x1039e, 0x1039e, HB_Other_NotAssigned},
+  {0x1039f, 0x1039f, HB_Punctuation_Other},
+  {0x103a0, 0x103c3, HB_Letter_Other},
+  {0x103c4, 0x103c7, HB_Other_NotAssigned},
+  {0x103c8, 0x103cf, HB_Letter_Other},
+  {0x103d0, 0x103d0, HB_Punctuation_Other},
+  {0x103d1, 0x103d5, HB_Number_Letter},
+  {0x103d6, 0x103ff, HB_Other_NotAssigned},
+  {0x10400, 0x10427, HB_Letter_Uppercase},
+  {0x10428, 0x1044f, HB_Letter_Lowercase},
+  {0x10450, 0x1049d, HB_Letter_Other},
+  {0x1049e, 0x1049f, HB_Other_NotAssigned},
+  {0x104a0, 0x104a9, HB_Number_DecimalDigit},
+  {0x104aa, 0x107ff, HB_Other_NotAssigned},
+  {0x10800, 0x10805, HB_Letter_Other},
+  {0x10806, 0x10807, HB_Other_NotAssigned},
+  {0x10808, 0x10808, HB_Letter_Other},
+  {0x10809, 0x10809, HB_Other_NotAssigned},
+  {0x1080a, 0x10835, HB_Letter_Other},
+  {0x10836, 0x10836, HB_Other_NotAssigned},
+  {0x10837, 0x10838, HB_Letter_Other},
+  {0x10839, 0x1083b, HB_Other_NotAssigned},
+  {0x1083c, 0x1083c, HB_Letter_Other},
+  {0x1083d, 0x1083e, HB_Other_NotAssigned},
+  {0x1083f, 0x1083f, HB_Letter_Other},
+  {0x10840, 0x108ff, HB_Other_NotAssigned},
+  {0x10900, 0x10915, HB_Letter_Other},
+  {0x10916, 0x10919, HB_Number_Other},
+  {0x1091a, 0x1091e, HB_Other_NotAssigned},
+  {0x1091f, 0x1091f, HB_Punctuation_Other},
+  {0x10920, 0x10939, HB_Letter_Other},
+  {0x1093a, 0x1093e, HB_Other_NotAssigned},
+  {0x1093f, 0x1093f, HB_Punctuation_Other},
+  {0x10940, 0x109ff, HB_Other_NotAssigned},
+  {0x10a00, 0x10a00, HB_Letter_Other},
+  {0x10a01, 0x10a03, HB_Mark_NonSpacing},
+  {0x10a04, 0x10a04, HB_Other_NotAssigned},
+  {0x10a05, 0x10a06, HB_Mark_NonSpacing},
+  {0x10a07, 0x10a0b, HB_Other_NotAssigned},
+  {0x10a0c, 0x10a0f, HB_Mark_NonSpacing},
+  {0x10a10, 0x10a13, HB_Letter_Other},
+  {0x10a14, 0x10a14, HB_Other_NotAssigned},
+  {0x10a15, 0x10a17, HB_Letter_Other},
+  {0x10a18, 0x10a18, HB_Other_NotAssigned},
+  {0x10a19, 0x10a33, HB_Letter_Other},
+  {0x10a34, 0x10a37, HB_Other_NotAssigned},
+  {0x10a38, 0x10a3a, HB_Mark_NonSpacing},
+  {0x10a3b, 0x10a3e, HB_Other_NotAssigned},
+  {0x10a3f, 0x10a3f, HB_Mark_NonSpacing},
+  {0x10a40, 0x10a47, HB_Number_Other},
+  {0x10a48, 0x10a4f, HB_Other_NotAssigned},
+  {0x10a50, 0x10a58, HB_Punctuation_Other},
+  {0x10a59, 0x11fff, HB_Other_NotAssigned},
+  {0x12000, 0x1236e, HB_Letter_Other},
+  {0x1236f, 0x123ff, HB_Other_NotAssigned},
+  {0x12400, 0x12462, HB_Number_Letter},
+  {0x12463, 0x1246f, HB_Other_NotAssigned},
+  {0x12470, 0x12473, HB_Punctuation_Other},
+  {0x12474, 0x1cfff, HB_Other_NotAssigned},
+  {0x1d000, 0x1d0f5, HB_Symbol_Other},
+  {0x1d0f6, 0x1d0ff, HB_Other_NotAssigned},
+  {0x1d100, 0x1d126, HB_Symbol_Other},
+  {0x1d127, 0x1d128, HB_Other_NotAssigned},
+  {0x1d129, 0x1d164, HB_Symbol_Other},
+  {0x1d165, 0x1d166, HB_Mark_SpacingCombining},
+  {0x1d167, 0x1d169, HB_Mark_NonSpacing},
+  {0x1d16a, 0x1d16c, HB_Symbol_Other},
+  {0x1d16d, 0x1d172, HB_Mark_SpacingCombining},
+  {0x1d173, 0x1d17a, HB_Other_Format},
+  {0x1d17b, 0x1d182, HB_Mark_NonSpacing},
+  {0x1d183, 0x1d184, HB_Symbol_Other},
+  {0x1d185, 0x1d18b, HB_Mark_NonSpacing},
+  {0x1d18c, 0x1d1a9, HB_Symbol_Other},
+  {0x1d1aa, 0x1d1ad, HB_Mark_NonSpacing},
+  {0x1d1ae, 0x1d1dd, HB_Symbol_Other},
+  {0x1d1de, 0x1d1ff, HB_Other_NotAssigned},
+  {0x1d200, 0x1d241, HB_Symbol_Other},
+  {0x1d242, 0x1d244, HB_Mark_NonSpacing},
+  {0x1d245, 0x1d245, HB_Symbol_Other},
+  {0x1d246, 0x1d2ff, HB_Other_NotAssigned},
+  {0x1d300, 0x1d356, HB_Symbol_Other},
+  {0x1d357, 0x1d35f, HB_Other_NotAssigned},
+  {0x1d360, 0x1d371, HB_Number_Other},
+  {0x1d372, 0x1d3ff, HB_Other_NotAssigned},
+  {0x1d400, 0x1d419, HB_Letter_Uppercase},
+  {0x1d41a, 0x1d433, HB_Letter_Lowercase},
+  {0x1d434, 0x1d44d, HB_Letter_Uppercase},
+  {0x1d44e, 0x1d454, HB_Letter_Lowercase},
+  {0x1d455, 0x1d455, HB_Other_NotAssigned},
+  {0x1d456, 0x1d467, HB_Letter_Lowercase},
+  {0x1d468, 0x1d481, HB_Letter_Uppercase},
+  {0x1d482, 0x1d49b, HB_Letter_Lowercase},
+  {0x1d49c, 0x1d49c, HB_Letter_Uppercase},
+  {0x1d49d, 0x1d49d, HB_Other_NotAssigned},
+  {0x1d49e, 0x1d49f, HB_Letter_Uppercase},
+  {0x1d4a0, 0x1d4a1, HB_Other_NotAssigned},
+  {0x1d4a2, 0x1d4a2, HB_Letter_Uppercase},
+  {0x1d4a3, 0x1d4a4, HB_Other_NotAssigned},
+  {0x1d4a5, 0x1d4a6, HB_Letter_Uppercase},
+  {0x1d4a7, 0x1d4a8, HB_Other_NotAssigned},
+  {0x1d4a9, 0x1d4ac, HB_Letter_Uppercase},
+  {0x1d4ad, 0x1d4ad, HB_Other_NotAssigned},
+  {0x1d4ae, 0x1d4b5, HB_Letter_Uppercase},
+  {0x1d4b6, 0x1d4b9, HB_Letter_Lowercase},
+  {0x1d4ba, 0x1d4ba, HB_Other_NotAssigned},
+  {0x1d4bb, 0x1d4bb, HB_Letter_Lowercase},
+  {0x1d4bc, 0x1d4bc, HB_Other_NotAssigned},
+  {0x1d4bd, 0x1d4c3, HB_Letter_Lowercase},
+  {0x1d4c4, 0x1d4c4, HB_Other_NotAssigned},
+  {0x1d4c5, 0x1d4cf, HB_Letter_Lowercase},
+  {0x1d4d0, 0x1d4e9, HB_Letter_Uppercase},
+  {0x1d4ea, 0x1d503, HB_Letter_Lowercase},
+  {0x1d504, 0x1d505, HB_Letter_Uppercase},
+  {0x1d506, 0x1d506, HB_Other_NotAssigned},
+  {0x1d507, 0x1d50a, HB_Letter_Uppercase},
+  {0x1d50b, 0x1d50c, HB_Other_NotAssigned},
+  {0x1d50d, 0x1d514, HB_Letter_Uppercase},
+  {0x1d515, 0x1d515, HB_Other_NotAssigned},
+  {0x1d516, 0x1d51c, HB_Letter_Uppercase},
+  {0x1d51d, 0x1d51d, HB_Other_NotAssigned},
+  {0x1d51e, 0x1d537, HB_Letter_Lowercase},
+  {0x1d538, 0x1d539, HB_Letter_Uppercase},
+  {0x1d53a, 0x1d53a, HB_Other_NotAssigned},
+  {0x1d53b, 0x1d53e, HB_Letter_Uppercase},
+  {0x1d53f, 0x1d53f, HB_Other_NotAssigned},
+  {0x1d540, 0x1d544, HB_Letter_Uppercase},
+  {0x1d545, 0x1d545, HB_Other_NotAssigned},
+  {0x1d546, 0x1d546, HB_Letter_Uppercase},
+  {0x1d547, 0x1d549, HB_Other_NotAssigned},
+  {0x1d54a, 0x1d550, HB_Letter_Uppercase},
+  {0x1d551, 0x1d551, HB_Other_NotAssigned},
+  {0x1d552, 0x1d56b, HB_Letter_Lowercase},
+  {0x1d56c, 0x1d585, HB_Letter_Uppercase},
+  {0x1d586, 0x1d59f, HB_Letter_Lowercase},
+  {0x1d5a0, 0x1d5b9, HB_Letter_Uppercase},
+  {0x1d5ba, 0x1d5d3, HB_Letter_Lowercase},
+  {0x1d5d4, 0x1d5ed, HB_Letter_Uppercase},
+  {0x1d5ee, 0x1d607, HB_Letter_Lowercase},
+  {0x1d608, 0x1d621, HB_Letter_Uppercase},
+  {0x1d622, 0x1d63b, HB_Letter_Lowercase},
+  {0x1d63c, 0x1d655, HB_Letter_Uppercase},
+  {0x1d656, 0x1d66f, HB_Letter_Lowercase},
+  {0x1d670, 0x1d689, HB_Letter_Uppercase},
+  {0x1d68a, 0x1d6a5, HB_Letter_Lowercase},
+  {0x1d6a6, 0x1d6a7, HB_Other_NotAssigned},
+  {0x1d6a8, 0x1d6c0, HB_Letter_Uppercase},
+  {0x1d6c1, 0x1d6c1, HB_Symbol_Math},
+  {0x1d6c2, 0x1d6da, HB_Letter_Lowercase},
+  {0x1d6db, 0x1d6db, HB_Symbol_Math},
+  {0x1d6dc, 0x1d6e1, HB_Letter_Lowercase},
+  {0x1d6e2, 0x1d6fa, HB_Letter_Uppercase},
+  {0x1d6fb, 0x1d6fb, HB_Symbol_Math},
+  {0x1d6fc, 0x1d714, HB_Letter_Lowercase},
+  {0x1d715, 0x1d715, HB_Symbol_Math},
+  {0x1d716, 0x1d71b, HB_Letter_Lowercase},
+  {0x1d71c, 0x1d734, HB_Letter_Uppercase},
+  {0x1d735, 0x1d735, HB_Symbol_Math},
+  {0x1d736, 0x1d74e, HB_Letter_Lowercase},
+  {0x1d74f, 0x1d74f, HB_Symbol_Math},
+  {0x1d750, 0x1d755, HB_Letter_Lowercase},
+  {0x1d756, 0x1d76e, HB_Letter_Uppercase},
+  {0x1d76f, 0x1d76f, HB_Symbol_Math},
+  {0x1d770, 0x1d788, HB_Letter_Lowercase},
+  {0x1d789, 0x1d789, HB_Symbol_Math},
+  {0x1d78a, 0x1d78f, HB_Letter_Lowercase},
+  {0x1d790, 0x1d7a8, HB_Letter_Uppercase},
+  {0x1d7a9, 0x1d7a9, HB_Symbol_Math},
+  {0x1d7aa, 0x1d7c2, HB_Letter_Lowercase},
+  {0x1d7c3, 0x1d7c3, HB_Symbol_Math},
+  {0x1d7c4, 0x1d7c9, HB_Letter_Lowercase},
+  {0x1d7ca, 0x1d7ca, HB_Letter_Uppercase},
+  {0x1d7cb, 0x1d7cb, HB_Letter_Lowercase},
+  {0x1d7cc, 0x1d7cd, HB_Other_NotAssigned},
+  {0x1d7ce, 0x1d7ff, HB_Number_DecimalDigit},
+  {0x1d800, 0x1efff, HB_Other_NotAssigned},
+  {0x1f000, 0x1f02b, HB_Symbol_Other},
+  {0x1f02c, 0x1f02f, HB_Other_NotAssigned},
+  {0x1f030, 0x1f093, HB_Symbol_Other},
+  {0x1f094, 0x1ffff, HB_Other_NotAssigned},
+  {0x20000, 0x2a6d6, HB_Letter_Other},
+  {0x2a6d7, 0x2f7ff, HB_Other_NotAssigned},
+  {0x2f800, 0x2fa1d, HB_Letter_Other},
+  {0x2fa1e, 0xe0000, HB_Other_NotAssigned},
+  {0xe0001, 0xe0001, HB_Other_Format},
+  {0xe0002, 0xe001f, HB_Other_NotAssigned},
+  {0xe0020, 0xe007f, HB_Other_Format},
+  {0xe0080, 0xe00ff, HB_Other_NotAssigned},
+  {0xe0100, 0xe01ef, HB_Mark_NonSpacing},
+  {0xe01f0, 0xeffff, HB_Other_NotAssigned},
+  {0xf0000, 0xffffd, HB_Other_PrivateUse},
+  {0xffffe, 0xfffff, HB_Other_NotAssigned},
+  {0x100000, 0x10fffd, HB_Other_PrivateUse},
+  {0x10fffe, 0x10ffff, HB_Other_NotAssigned},
+};
+
+static const unsigned category_properties_count = 2849;
+
+#endif  // CATEGORY_PROPERTIES_H_
diff --git a/third_party/harfbuzz/contrib/tables/combining-class-parse.py b/third_party/harfbuzz/contrib/tables/combining-class-parse.py
new file mode 100644
index 0000000..c591ddd
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/combining-class-parse.py
@@ -0,0 +1,34 @@
+import sys
+from unicode_parse_common import *
+
+# http://www.unicode.org/Public/5.1.0/ucd/extracted/DerivedCombiningClass.txt
+
+class IdentityMap(object):
+  def __getitem__(_, key):
+    return key
+
+def main(infile, outfile):
+  ranges = unicode_file_parse(infile, IdentityMap(), '0')
+  ranges = sort_and_merge(ranges)
+
+  print >>outfile, '// Generated from Unicode tables\n'
+  print >>outfile, '#ifndef COMBINING_PROPERTIES_H_'
+  print >>outfile, '#define COMBINING_PROPERTIES_H_\n'
+  print >>outfile, '#include <stdint.h>'
+  print >>outfile, 'struct combining_property {'
+  print >>outfile, '  uint32_t range_start;'
+  print >>outfile, '  uint32_t range_end;'
+  print >>outfile, '  uint8_t klass;'
+  print >>outfile, '};\n'
+  print >>outfile, 'static const struct combining_property combining_properties[] = {'
+  for (start, end, value) in ranges:
+    print >>outfile, '  {0x%x, 0x%x, %s},' % (start, end, value)
+  print >>outfile, '};\n'
+  print >>outfile, 'static const unsigned combining_properties_count = %d;\n' % len(ranges)
+  print >>outfile, '#endif  // COMBINING_PROPERTIES_H_'
+
+if __name__ == '__main__':
+  if len(sys.argv) != 3:
+    print 'Usage: %s <input .txt> <output .h>' % sys.argv[0]
+  else:
+    main(file(sys.argv[1], 'r'), file(sys.argv[2], 'w+'))
diff --git a/third_party/harfbuzz/contrib/tables/combining-properties.h b/third_party/harfbuzz/contrib/tables/combining-properties.h
new file mode 100644
index 0000000..552ed35
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/combining-properties.h
@@ -0,0 +1,247 @@
+// Generated from Unicode tables
+
+#ifndef COMBINING_PROPERTIES_H_
+#define COMBINING_PROPERTIES_H_
+
+#include <stdint.h>
+struct combining_property {
+  uint32_t range_start;
+  uint32_t range_end;
+  uint8_t klass;
+};
+
+static const struct combining_property combining_properties[] = {
+  {0x300, 0x314, 230},
+  {0x315, 0x315, 232},
+  {0x316, 0x319, 220},
+  {0x31a, 0x31a, 232},
+  {0x31b, 0x31b, 216},
+  {0x31c, 0x320, 220},
+  {0x321, 0x322, 202},
+  {0x323, 0x326, 220},
+  {0x327, 0x328, 202},
+  {0x329, 0x333, 220},
+  {0x334, 0x338, 1},
+  {0x339, 0x33c, 220},
+  {0x33d, 0x344, 230},
+  {0x345, 0x345, 240},
+  {0x346, 0x346, 230},
+  {0x347, 0x349, 220},
+  {0x34a, 0x34c, 230},
+  {0x34d, 0x34e, 220},
+  {0x350, 0x352, 230},
+  {0x353, 0x356, 220},
+  {0x357, 0x357, 230},
+  {0x358, 0x358, 232},
+  {0x359, 0x35a, 220},
+  {0x35b, 0x35b, 230},
+  {0x35c, 0x35c, 233},
+  {0x35d, 0x35e, 234},
+  {0x35f, 0x35f, 233},
+  {0x360, 0x361, 234},
+  {0x362, 0x362, 233},
+  {0x363, 0x36f, 230},
+  {0x483, 0x487, 230},
+  {0x591, 0x591, 220},
+  {0x592, 0x595, 230},
+  {0x596, 0x596, 220},
+  {0x597, 0x599, 230},
+  {0x59a, 0x59a, 222},
+  {0x59b, 0x59b, 220},
+  {0x59c, 0x5a1, 230},
+  {0x5a2, 0x5a7, 220},
+  {0x5a8, 0x5a9, 230},
+  {0x5aa, 0x5aa, 220},
+  {0x5ab, 0x5ac, 230},
+  {0x5ad, 0x5ad, 222},
+  {0x5ae, 0x5ae, 228},
+  {0x5af, 0x5af, 230},
+  {0x5b0, 0x5b0, 10},
+  {0x5b1, 0x5b1, 11},
+  {0x5b2, 0x5b2, 12},
+  {0x5b3, 0x5b3, 13},
+  {0x5b4, 0x5b4, 14},
+  {0x5b5, 0x5b5, 15},
+  {0x5b6, 0x5b6, 16},
+  {0x5b7, 0x5b7, 17},
+  {0x5b8, 0x5b8, 18},
+  {0x5b9, 0x5ba, 19},
+  {0x5bb, 0x5bb, 20},
+  {0x5bc, 0x5bc, 21},
+  {0x5bd, 0x5bd, 22},
+  {0x5bf, 0x5bf, 23},
+  {0x5c1, 0x5c1, 24},
+  {0x5c2, 0x5c2, 25},
+  {0x5c4, 0x5c4, 230},
+  {0x5c5, 0x5c5, 220},
+  {0x5c7, 0x5c7, 18},
+  {0x610, 0x617, 230},
+  {0x618, 0x618, 30},
+  {0x619, 0x619, 31},
+  {0x61a, 0x61a, 32},
+  {0x64b, 0x64b, 27},
+  {0x64c, 0x64c, 28},
+  {0x64d, 0x64d, 29},
+  {0x64e, 0x64e, 30},
+  {0x64f, 0x64f, 31},
+  {0x650, 0x650, 32},
+  {0x651, 0x651, 33},
+  {0x652, 0x652, 34},
+  {0x653, 0x654, 230},
+  {0x655, 0x656, 220},
+  {0x657, 0x65b, 230},
+  {0x65c, 0x65c, 220},
+  {0x65d, 0x65e, 230},
+  {0x670, 0x670, 35},
+  {0x6d6, 0x6dc, 230},
+  {0x6df, 0x6e2, 230},
+  {0x6e3, 0x6e3, 220},
+  {0x6e4, 0x6e4, 230},
+  {0x6e7, 0x6e8, 230},
+  {0x6ea, 0x6ea, 220},
+  {0x6eb, 0x6ec, 230},
+  {0x6ed, 0x6ed, 220},
+  {0x711, 0x711, 36},
+  {0x730, 0x730, 230},
+  {0x731, 0x731, 220},
+  {0x732, 0x733, 230},
+  {0x734, 0x734, 220},
+  {0x735, 0x736, 230},
+  {0x737, 0x739, 220},
+  {0x73a, 0x73a, 230},
+  {0x73b, 0x73c, 220},
+  {0x73d, 0x73d, 230},
+  {0x73e, 0x73e, 220},
+  {0x73f, 0x741, 230},
+  {0x742, 0x742, 220},
+  {0x743, 0x743, 230},
+  {0x744, 0x744, 220},
+  {0x745, 0x745, 230},
+  {0x746, 0x746, 220},
+  {0x747, 0x747, 230},
+  {0x748, 0x748, 220},
+  {0x749, 0x74a, 230},
+  {0x7eb, 0x7f1, 230},
+  {0x7f2, 0x7f2, 220},
+  {0x7f3, 0x7f3, 230},
+  {0x93c, 0x93c, 7},
+  {0x94d, 0x94d, 9},
+  {0x951, 0x951, 230},
+  {0x952, 0x952, 220},
+  {0x953, 0x954, 230},
+  {0x9bc, 0x9bc, 7},
+  {0x9cd, 0x9cd, 9},
+  {0xa3c, 0xa3c, 7},
+  {0xa4d, 0xa4d, 9},
+  {0xabc, 0xabc, 7},
+  {0xacd, 0xacd, 9},
+  {0xb3c, 0xb3c, 7},
+  {0xb4d, 0xb4d, 9},
+  {0xbcd, 0xbcd, 9},
+  {0xc4d, 0xc4d, 9},
+  {0xc55, 0xc55, 84},
+  {0xc56, 0xc56, 91},
+  {0xcbc, 0xcbc, 7},
+  {0xccd, 0xccd, 9},
+  {0xd4d, 0xd4d, 9},
+  {0xdca, 0xdca, 9},
+  {0xe38, 0xe39, 103},
+  {0xe3a, 0xe3a, 9},
+  {0xe48, 0xe4b, 107},
+  {0xeb8, 0xeb9, 118},
+  {0xec8, 0xecb, 122},
+  {0xf18, 0xf19, 220},
+  {0xf35, 0xf35, 220},
+  {0xf37, 0xf37, 220},
+  {0xf39, 0xf39, 216},
+  {0xf71, 0xf71, 129},
+  {0xf72, 0xf72, 130},
+  {0xf74, 0xf74, 132},
+  {0xf7a, 0xf7d, 130},
+  {0xf80, 0xf80, 130},
+  {0xf82, 0xf83, 230},
+  {0xf84, 0xf84, 9},
+  {0xf86, 0xf87, 230},
+  {0xfc6, 0xfc6, 220},
+  {0x1037, 0x1037, 7},
+  {0x1039, 0x103a, 9},
+  {0x108d, 0x108d, 220},
+  {0x135f, 0x135f, 230},
+  {0x1714, 0x1714, 9},
+  {0x1734, 0x1734, 9},
+  {0x17d2, 0x17d2, 9},
+  {0x17dd, 0x17dd, 230},
+  {0x18a9, 0x18a9, 228},
+  {0x1939, 0x1939, 222},
+  {0x193a, 0x193a, 230},
+  {0x193b, 0x193b, 220},
+  {0x1a17, 0x1a17, 230},
+  {0x1a18, 0x1a18, 220},
+  {0x1b34, 0x1b34, 7},
+  {0x1b44, 0x1b44, 9},
+  {0x1b6b, 0x1b6b, 230},
+  {0x1b6c, 0x1b6c, 220},
+  {0x1b6d, 0x1b73, 230},
+  {0x1baa, 0x1baa, 9},
+  {0x1c37, 0x1c37, 7},
+  {0x1dc0, 0x1dc1, 230},
+  {0x1dc2, 0x1dc2, 220},
+  {0x1dc3, 0x1dc9, 230},
+  {0x1dca, 0x1dca, 220},
+  {0x1dcb, 0x1dcc, 230},
+  {0x1dcd, 0x1dcd, 234},
+  {0x1dce, 0x1dce, 214},
+  {0x1dcf, 0x1dcf, 220},
+  {0x1dd0, 0x1dd0, 202},
+  {0x1dd1, 0x1de6, 230},
+  {0x1dfe, 0x1dfe, 230},
+  {0x1dff, 0x1dff, 220},
+  {0x20d0, 0x20d1, 230},
+  {0x20d2, 0x20d3, 1},
+  {0x20d4, 0x20d7, 230},
+  {0x20d8, 0x20da, 1},
+  {0x20db, 0x20dc, 230},
+  {0x20e1, 0x20e1, 230},
+  {0x20e5, 0x20e6, 1},
+  {0x20e7, 0x20e7, 230},
+  {0x20e8, 0x20e8, 220},
+  {0x20e9, 0x20e9, 230},
+  {0x20ea, 0x20eb, 1},
+  {0x20ec, 0x20ef, 220},
+  {0x20f0, 0x20f0, 230},
+  {0x2de0, 0x2dff, 230},
+  {0x302a, 0x302a, 218},
+  {0x302b, 0x302b, 228},
+  {0x302c, 0x302c, 232},
+  {0x302d, 0x302d, 222},
+  {0x302e, 0x302f, 224},
+  {0x3099, 0x309a, 8},
+  {0xa66f, 0xa66f, 230},
+  {0xa67c, 0xa67d, 230},
+  {0xa806, 0xa806, 9},
+  {0xa8c4, 0xa8c4, 9},
+  {0xa92b, 0xa92d, 220},
+  {0xa953, 0xa953, 9},
+  {0xfb1e, 0xfb1e, 26},
+  {0xfe20, 0xfe26, 230},
+  {0x101fd, 0x101fd, 220},
+  {0x10a0d, 0x10a0d, 220},
+  {0x10a0f, 0x10a0f, 230},
+  {0x10a38, 0x10a38, 230},
+  {0x10a39, 0x10a39, 1},
+  {0x10a3a, 0x10a3a, 220},
+  {0x10a3f, 0x10a3f, 9},
+  {0x1d165, 0x1d166, 216},
+  {0x1d167, 0x1d169, 1},
+  {0x1d16d, 0x1d16d, 226},
+  {0x1d16e, 0x1d172, 216},
+  {0x1d17b, 0x1d182, 220},
+  {0x1d185, 0x1d189, 230},
+  {0x1d18a, 0x1d18b, 220},
+  {0x1d1aa, 0x1d1ad, 230},
+  {0x1d242, 0x1d244, 230},
+};
+
+static const unsigned combining_properties_count = 229;
+
+#endif  // COMBINING_PROPERTIES_H_
diff --git a/third_party/harfbuzz/contrib/tables/grapheme-break-parse.py b/third_party/harfbuzz/contrib/tables/grapheme-break-parse.py
new file mode 100644
index 0000000..a4b3534
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/grapheme-break-parse.py
@@ -0,0 +1,45 @@
+import sys
+from unicode_parse_common import *
+
+# http://www.unicode.org/Public/UNIDATA/auxiliary/GraphemeBreakProperty.txt
+
+property_to_harfbuzz = {
+  'CR': 'HB_Grapheme_CR',
+  'LF': 'HB_Grapheme_LF',
+  'Control': 'HB_Grapheme_Control',
+  'Extend': 'HB_Grapheme_Extend',
+  'Prepend': 'HB_Grapheme_Other',
+  'SpacingMark': 'HB_Grapheme_Other',
+  'L': 'HB_Grapheme_L',
+  'V': 'HB_Grapheme_V',
+  'T': 'HB_Grapheme_T',
+  'LV': 'HB_Grapheme_LV',
+  'LVT': 'HB_Grapheme_LVT',
+}
+
+def main(infile, outfile):
+  ranges = unicode_file_parse(infile, property_to_harfbuzz)
+  ranges.sort()
+
+  print >>outfile, '// Generated from Unicode Grapheme break tables\n'
+  print >>outfile, '#ifndef GRAPHEME_BREAK_PROPERTY_H_'
+  print >>outfile, '#define GRAPHEME_BREAK_PROPERTY_H_\n'
+  print >>outfile, '#include <stdint.h>'
+  print >>outfile, '#include "harfbuzz-external.h"\n'
+  print >>outfile, 'struct grapheme_break_property {'
+  print >>outfile, '  uint32_t range_start;'
+  print >>outfile, '  uint32_t range_end;'
+  print >>outfile, '  HB_GraphemeClass klass;'
+  print >>outfile, '};\n'
+  print >>outfile, 'static const struct grapheme_break_property grapheme_break_properties[] = {'
+  for (start, end, value) in ranges:
+    print >>outfile, '  {0x%x, 0x%x, %s},' % (start, end, value)
+  print >>outfile, '};\n'
+  print >>outfile, 'static const unsigned grapheme_break_properties_count = %d;\n' % len(ranges)
+  print >>outfile, '#endif  // GRAPHEME_BREAK_PROPERTY_H_'
+
+if __name__ == '__main__':
+  if len(sys.argv) != 3:
+    print 'Usage: %s <input .txt> <output .h>' % sys.argv[0]
+  else:
+    main(file(sys.argv[1], 'r'), file(sys.argv[2], 'w+'))
diff --git a/third_party/harfbuzz/contrib/tables/grapheme-break-properties.h b/third_party/harfbuzz/contrib/tables/grapheme-break-properties.h
new file mode 100644
index 0000000..73f47d4
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/grapheme-break-properties.h
@@ -0,0 +1,1113 @@
+// Generated from Unicode Grapheme break tables
+
+#ifndef GRAPHEME_BREAK_PROPERTY_H_
+#define GRAPHEME_BREAK_PROPERTY_H_
+
+#include <stdint.h>
+#include "harfbuzz-external.h"
+
+struct grapheme_break_property {
+  uint32_t range_start;
+  uint32_t range_end;
+  HB_GraphemeClass klass;
+};
+
+static const struct grapheme_break_property grapheme_break_properties[] = {
+  {0x0, 0x9, HB_Grapheme_Control},
+  {0xa, 0xa, HB_Grapheme_LF},
+  {0xb, 0xc, HB_Grapheme_Control},
+  {0xd, 0xd, HB_Grapheme_CR},
+  {0xe, 0x1f, HB_Grapheme_Control},
+  {0x7f, 0x9f, HB_Grapheme_Control},
+  {0xad, 0xad, HB_Grapheme_Control},
+  {0x300, 0x36f, HB_Grapheme_Extend},
+  {0x483, 0x487, HB_Grapheme_Extend},
+  {0x488, 0x489, HB_Grapheme_Extend},
+  {0x591, 0x5bd, HB_Grapheme_Extend},
+  {0x5bf, 0x5bf, HB_Grapheme_Extend},
+  {0x5c1, 0x5c2, HB_Grapheme_Extend},
+  {0x5c4, 0x5c5, HB_Grapheme_Extend},
+  {0x5c7, 0x5c7, HB_Grapheme_Extend},
+  {0x600, 0x603, HB_Grapheme_Control},
+  {0x610, 0x61a, HB_Grapheme_Extend},
+  {0x64b, 0x65e, HB_Grapheme_Extend},
+  {0x670, 0x670, HB_Grapheme_Extend},
+  {0x6d6, 0x6dc, HB_Grapheme_Extend},
+  {0x6dd, 0x6dd, HB_Grapheme_Control},
+  {0x6de, 0x6de, HB_Grapheme_Extend},
+  {0x6df, 0x6e4, HB_Grapheme_Extend},
+  {0x6e7, 0x6e8, HB_Grapheme_Extend},
+  {0x6ea, 0x6ed, HB_Grapheme_Extend},
+  {0x70f, 0x70f, HB_Grapheme_Control},
+  {0x711, 0x711, HB_Grapheme_Extend},
+  {0x730, 0x74a, HB_Grapheme_Extend},
+  {0x7a6, 0x7b0, HB_Grapheme_Extend},
+  {0x7eb, 0x7f3, HB_Grapheme_Extend},
+  {0x901, 0x902, HB_Grapheme_Extend},
+  {0x903, 0x903, HB_Grapheme_Other},
+  {0x93c, 0x93c, HB_Grapheme_Extend},
+  {0x93e, 0x940, HB_Grapheme_Other},
+  {0x941, 0x948, HB_Grapheme_Extend},
+  {0x949, 0x94c, HB_Grapheme_Other},
+  {0x94d, 0x94d, HB_Grapheme_Extend},
+  {0x951, 0x954, HB_Grapheme_Extend},
+  {0x962, 0x963, HB_Grapheme_Extend},
+  {0x981, 0x981, HB_Grapheme_Extend},
+  {0x982, 0x983, HB_Grapheme_Other},
+  {0x9bc, 0x9bc, HB_Grapheme_Extend},
+  {0x9be, 0x9be, HB_Grapheme_Extend},
+  {0x9bf, 0x9c0, HB_Grapheme_Other},
+  {0x9c1, 0x9c4, HB_Grapheme_Extend},
+  {0x9c7, 0x9c8, HB_Grapheme_Other},
+  {0x9cb, 0x9cc, HB_Grapheme_Other},
+  {0x9cd, 0x9cd, HB_Grapheme_Extend},
+  {0x9d7, 0x9d7, HB_Grapheme_Extend},
+  {0x9e2, 0x9e3, HB_Grapheme_Extend},
+  {0xa01, 0xa02, HB_Grapheme_Extend},
+  {0xa03, 0xa03, HB_Grapheme_Other},
+  {0xa3c, 0xa3c, HB_Grapheme_Extend},
+  {0xa3e, 0xa40, HB_Grapheme_Other},
+  {0xa41, 0xa42, HB_Grapheme_Extend},
+  {0xa47, 0xa48, HB_Grapheme_Extend},
+  {0xa4b, 0xa4d, HB_Grapheme_Extend},
+  {0xa51, 0xa51, HB_Grapheme_Extend},
+  {0xa70, 0xa71, HB_Grapheme_Extend},
+  {0xa75, 0xa75, HB_Grapheme_Extend},
+  {0xa81, 0xa82, HB_Grapheme_Extend},
+  {0xa83, 0xa83, HB_Grapheme_Other},
+  {0xabc, 0xabc, HB_Grapheme_Extend},
+  {0xabe, 0xac0, HB_Grapheme_Other},
+  {0xac1, 0xac5, HB_Grapheme_Extend},
+  {0xac7, 0xac8, HB_Grapheme_Extend},
+  {0xac9, 0xac9, HB_Grapheme_Other},
+  {0xacb, 0xacc, HB_Grapheme_Other},
+  {0xacd, 0xacd, HB_Grapheme_Extend},
+  {0xae2, 0xae3, HB_Grapheme_Extend},
+  {0xb01, 0xb01, HB_Grapheme_Extend},
+  {0xb02, 0xb03, HB_Grapheme_Other},
+  {0xb3c, 0xb3c, HB_Grapheme_Extend},
+  {0xb3e, 0xb3e, HB_Grapheme_Extend},
+  {0xb3f, 0xb3f, HB_Grapheme_Extend},
+  {0xb40, 0xb40, HB_Grapheme_Other},
+  {0xb41, 0xb44, HB_Grapheme_Extend},
+  {0xb47, 0xb48, HB_Grapheme_Other},
+  {0xb4b, 0xb4c, HB_Grapheme_Other},
+  {0xb4d, 0xb4d, HB_Grapheme_Extend},
+  {0xb56, 0xb56, HB_Grapheme_Extend},
+  {0xb57, 0xb57, HB_Grapheme_Extend},
+  {0xb62, 0xb63, HB_Grapheme_Extend},
+  {0xb82, 0xb82, HB_Grapheme_Extend},
+  {0xbbe, 0xbbe, HB_Grapheme_Extend},
+  {0xbbf, 0xbbf, HB_Grapheme_Other},
+  {0xbc0, 0xbc0, HB_Grapheme_Extend},
+  {0xbc1, 0xbc2, HB_Grapheme_Other},
+  {0xbc6, 0xbc8, HB_Grapheme_Other},
+  {0xbca, 0xbcc, HB_Grapheme_Other},
+  {0xbcd, 0xbcd, HB_Grapheme_Extend},
+  {0xbd7, 0xbd7, HB_Grapheme_Extend},
+  {0xc01, 0xc03, HB_Grapheme_Other},
+  {0xc3e, 0xc40, HB_Grapheme_Extend},
+  {0xc41, 0xc44, HB_Grapheme_Other},
+  {0xc46, 0xc48, HB_Grapheme_Extend},
+  {0xc4a, 0xc4d, HB_Grapheme_Extend},
+  {0xc55, 0xc56, HB_Grapheme_Extend},
+  {0xc62, 0xc63, HB_Grapheme_Extend},
+  {0xc82, 0xc83, HB_Grapheme_Other},
+  {0xcbc, 0xcbc, HB_Grapheme_Extend},
+  {0xcbe, 0xcbe, HB_Grapheme_Other},
+  {0xcbf, 0xcbf, HB_Grapheme_Extend},
+  {0xcc0, 0xcc1, HB_Grapheme_Other},
+  {0xcc2, 0xcc2, HB_Grapheme_Extend},
+  {0xcc3, 0xcc4, HB_Grapheme_Other},
+  {0xcc6, 0xcc6, HB_Grapheme_Extend},
+  {0xcc7, 0xcc8, HB_Grapheme_Other},
+  {0xcca, 0xccb, HB_Grapheme_Other},
+  {0xccc, 0xccd, HB_Grapheme_Extend},
+  {0xcd5, 0xcd6, HB_Grapheme_Extend},
+  {0xce2, 0xce3, HB_Grapheme_Extend},
+  {0xd02, 0xd03, HB_Grapheme_Other},
+  {0xd3e, 0xd3e, HB_Grapheme_Extend},
+  {0xd3f, 0xd40, HB_Grapheme_Other},
+  {0xd41, 0xd44, HB_Grapheme_Extend},
+  {0xd46, 0xd48, HB_Grapheme_Other},
+  {0xd4a, 0xd4c, HB_Grapheme_Other},
+  {0xd4d, 0xd4d, HB_Grapheme_Extend},
+  {0xd57, 0xd57, HB_Grapheme_Extend},
+  {0xd62, 0xd63, HB_Grapheme_Extend},
+  {0xd82, 0xd83, HB_Grapheme_Other},
+  {0xdca, 0xdca, HB_Grapheme_Extend},
+  {0xdcf, 0xdcf, HB_Grapheme_Extend},
+  {0xdd0, 0xdd1, HB_Grapheme_Other},
+  {0xdd2, 0xdd4, HB_Grapheme_Extend},
+  {0xdd6, 0xdd6, HB_Grapheme_Extend},
+  {0xdd8, 0xdde, HB_Grapheme_Other},
+  {0xddf, 0xddf, HB_Grapheme_Extend},
+  {0xdf2, 0xdf3, HB_Grapheme_Other},
+  {0xe30, 0xe30, HB_Grapheme_Extend},
+  {0xe31, 0xe31, HB_Grapheme_Extend},
+  {0xe32, 0xe33, HB_Grapheme_Extend},
+  {0xe34, 0xe3a, HB_Grapheme_Extend},
+  {0xe40, 0xe44, HB_Grapheme_Other},
+  {0xe45, 0xe45, HB_Grapheme_Extend},
+  {0xe47, 0xe4e, HB_Grapheme_Extend},
+  {0xeb0, 0xeb0, HB_Grapheme_Extend},
+  {0xeb1, 0xeb1, HB_Grapheme_Extend},
+  {0xeb2, 0xeb3, HB_Grapheme_Extend},
+  {0xeb4, 0xeb9, HB_Grapheme_Extend},
+  {0xebb, 0xebc, HB_Grapheme_Extend},
+  {0xec0, 0xec4, HB_Grapheme_Other},
+  {0xec8, 0xecd, HB_Grapheme_Extend},
+  {0xf18, 0xf19, HB_Grapheme_Extend},
+  {0xf35, 0xf35, HB_Grapheme_Extend},
+  {0xf37, 0xf37, HB_Grapheme_Extend},
+  {0xf39, 0xf39, HB_Grapheme_Extend},
+  {0xf3e, 0xf3f, HB_Grapheme_Other},
+  {0xf71, 0xf7e, HB_Grapheme_Extend},
+  {0xf7f, 0xf7f, HB_Grapheme_Other},
+  {0xf80, 0xf84, HB_Grapheme_Extend},
+  {0xf86, 0xf87, HB_Grapheme_Extend},
+  {0xf90, 0xf97, HB_Grapheme_Extend},
+  {0xf99, 0xfbc, HB_Grapheme_Extend},
+  {0xfc6, 0xfc6, HB_Grapheme_Extend},
+  {0x102b, 0x102c, HB_Grapheme_Other},
+  {0x102d, 0x1030, HB_Grapheme_Extend},
+  {0x1031, 0x1031, HB_Grapheme_Other},
+  {0x1032, 0x1037, HB_Grapheme_Extend},
+  {0x1038, 0x1038, HB_Grapheme_Other},
+  {0x1039, 0x103a, HB_Grapheme_Extend},
+  {0x103b, 0x103c, HB_Grapheme_Other},
+  {0x103d, 0x103e, HB_Grapheme_Extend},
+  {0x1056, 0x1057, HB_Grapheme_Other},
+  {0x1058, 0x1059, HB_Grapheme_Extend},
+  {0x105e, 0x1060, HB_Grapheme_Extend},
+  {0x1062, 0x1064, HB_Grapheme_Other},
+  {0x1067, 0x106d, HB_Grapheme_Other},
+  {0x1071, 0x1074, HB_Grapheme_Extend},
+  {0x1082, 0x1082, HB_Grapheme_Extend},
+  {0x1083, 0x1084, HB_Grapheme_Other},
+  {0x1085, 0x1086, HB_Grapheme_Extend},
+  {0x1087, 0x108c, HB_Grapheme_Other},
+  {0x108d, 0x108d, HB_Grapheme_Extend},
+  {0x108f, 0x108f, HB_Grapheme_Other},
+  {0x1100, 0x1159, HB_Grapheme_L},
+  {0x115f, 0x115f, HB_Grapheme_L},
+  {0x1160, 0x11a2, HB_Grapheme_V},
+  {0x11a8, 0x11f9, HB_Grapheme_T},
+  {0x135f, 0x135f, HB_Grapheme_Extend},
+  {0x1712, 0x1714, HB_Grapheme_Extend},
+  {0x1732, 0x1734, HB_Grapheme_Extend},
+  {0x1752, 0x1753, HB_Grapheme_Extend},
+  {0x1772, 0x1773, HB_Grapheme_Extend},
+  {0x17b4, 0x17b5, HB_Grapheme_Control},
+  {0x17b6, 0x17b6, HB_Grapheme_Other},
+  {0x17b7, 0x17bd, HB_Grapheme_Extend},
+  {0x17be, 0x17c5, HB_Grapheme_Other},
+  {0x17c6, 0x17c6, HB_Grapheme_Extend},
+  {0x17c7, 0x17c8, HB_Grapheme_Other},
+  {0x17c9, 0x17d3, HB_Grapheme_Extend},
+  {0x17dd, 0x17dd, HB_Grapheme_Extend},
+  {0x180b, 0x180d, HB_Grapheme_Extend},
+  {0x18a9, 0x18a9, HB_Grapheme_Extend},
+  {0x1920, 0x1922, HB_Grapheme_Extend},
+  {0x1923, 0x1926, HB_Grapheme_Other},
+  {0x1927, 0x1928, HB_Grapheme_Extend},
+  {0x1929, 0x192b, HB_Grapheme_Other},
+  {0x1930, 0x1931, HB_Grapheme_Other},
+  {0x1932, 0x1932, HB_Grapheme_Extend},
+  {0x1933, 0x1938, HB_Grapheme_Other},
+  {0x1939, 0x193b, HB_Grapheme_Extend},
+  {0x19b0, 0x19c0, HB_Grapheme_Other},
+  {0x19c8, 0x19c9, HB_Grapheme_Other},
+  {0x1a17, 0x1a18, HB_Grapheme_Extend},
+  {0x1a19, 0x1a1b, HB_Grapheme_Other},
+  {0x1b00, 0x1b03, HB_Grapheme_Extend},
+  {0x1b04, 0x1b04, HB_Grapheme_Other},
+  {0x1b34, 0x1b34, HB_Grapheme_Extend},
+  {0x1b35, 0x1b35, HB_Grapheme_Other},
+  {0x1b36, 0x1b3a, HB_Grapheme_Extend},
+  {0x1b3b, 0x1b3b, HB_Grapheme_Other},
+  {0x1b3c, 0x1b3c, HB_Grapheme_Extend},
+  {0x1b3d, 0x1b41, HB_Grapheme_Other},
+  {0x1b42, 0x1b42, HB_Grapheme_Extend},
+  {0x1b43, 0x1b44, HB_Grapheme_Other},
+  {0x1b6b, 0x1b73, HB_Grapheme_Extend},
+  {0x1b80, 0x1b81, HB_Grapheme_Extend},
+  {0x1b82, 0x1b82, HB_Grapheme_Other},
+  {0x1ba1, 0x1ba1, HB_Grapheme_Other},
+  {0x1ba2, 0x1ba5, HB_Grapheme_Extend},
+  {0x1ba6, 0x1ba7, HB_Grapheme_Other},
+  {0x1ba8, 0x1ba9, HB_Grapheme_Extend},
+  {0x1baa, 0x1baa, HB_Grapheme_Other},
+  {0x1c24, 0x1c2b, HB_Grapheme_Other},
+  {0x1c2c, 0x1c33, HB_Grapheme_Extend},
+  {0x1c34, 0x1c35, HB_Grapheme_Other},
+  {0x1c36, 0x1c37, HB_Grapheme_Extend},
+  {0x1dc0, 0x1de6, HB_Grapheme_Extend},
+  {0x1dfe, 0x1dff, HB_Grapheme_Extend},
+  {0x200b, 0x200b, HB_Grapheme_Control},
+  {0x200c, 0x200d, HB_Grapheme_Extend},
+  {0x200e, 0x200f, HB_Grapheme_Control},
+  {0x2028, 0x2028, HB_Grapheme_Control},
+  {0x2029, 0x2029, HB_Grapheme_Control},
+  {0x202a, 0x202e, HB_Grapheme_Control},
+  {0x2060, 0x2064, HB_Grapheme_Control},
+  {0x206a, 0x206f, HB_Grapheme_Control},
+  {0x20d0, 0x20dc, HB_Grapheme_Extend},
+  {0x20dd, 0x20e0, HB_Grapheme_Extend},
+  {0x20e1, 0x20e1, HB_Grapheme_Extend},
+  {0x20e2, 0x20e4, HB_Grapheme_Extend},
+  {0x20e5, 0x20f0, HB_Grapheme_Extend},
+  {0x2de0, 0x2dff, HB_Grapheme_Extend},
+  {0x302a, 0x302f, HB_Grapheme_Extend},
+  {0x3099, 0x309a, HB_Grapheme_Extend},
+  {0xa66f, 0xa66f, HB_Grapheme_Extend},
+  {0xa670, 0xa672, HB_Grapheme_Extend},
+  {0xa67c, 0xa67d, HB_Grapheme_Extend},
+  {0xa802, 0xa802, HB_Grapheme_Extend},
+  {0xa806, 0xa806, HB_Grapheme_Extend},
+  {0xa80b, 0xa80b, HB_Grapheme_Extend},
+  {0xa823, 0xa824, HB_Grapheme_Other},
+  {0xa825, 0xa826, HB_Grapheme_Extend},
+  {0xa827, 0xa827, HB_Grapheme_Other},
+  {0xa880, 0xa881, HB_Grapheme_Other},
+  {0xa8b4, 0xa8c3, HB_Grapheme_Other},
+  {0xa8c4, 0xa8c4, HB_Grapheme_Extend},
+  {0xa926, 0xa92d, HB_Grapheme_Extend},
+  {0xa947, 0xa951, HB_Grapheme_Extend},
+  {0xa952, 0xa953, HB_Grapheme_Other},
+  {0xaa29, 0xaa2e, HB_Grapheme_Extend},
+  {0xaa2f, 0xaa30, HB_Grapheme_Other},
+  {0xaa31, 0xaa32, HB_Grapheme_Extend},
+  {0xaa33, 0xaa34, HB_Grapheme_Other},
+  {0xaa35, 0xaa36, HB_Grapheme_Extend},
+  {0xaa43, 0xaa43, HB_Grapheme_Extend},
+  {0xaa4c, 0xaa4c, HB_Grapheme_Extend},
+  {0xaa4d, 0xaa4d, HB_Grapheme_Other},
+  {0xac00, 0xac00, HB_Grapheme_LV},
+  {0xac01, 0xac1b, HB_Grapheme_LVT},
+  {0xac1c, 0xac1c, HB_Grapheme_LV},
+  {0xac1d, 0xac37, HB_Grapheme_LVT},
+  {0xac38, 0xac38, HB_Grapheme_LV},
+  {0xac39, 0xac53, HB_Grapheme_LVT},
+  {0xac54, 0xac54, HB_Grapheme_LV},
+  {0xac55, 0xac6f, HB_Grapheme_LVT},
+  {0xac70, 0xac70, HB_Grapheme_LV},
+  {0xac71, 0xac8b, HB_Grapheme_LVT},
+  {0xac8c, 0xac8c, HB_Grapheme_LV},
+  {0xac8d, 0xaca7, HB_Grapheme_LVT},
+  {0xaca8, 0xaca8, HB_Grapheme_LV},
+  {0xaca9, 0xacc3, HB_Grapheme_LVT},
+  {0xacc4, 0xacc4, HB_Grapheme_LV},
+  {0xacc5, 0xacdf, HB_Grapheme_LVT},
+  {0xace0, 0xace0, HB_Grapheme_LV},
+  {0xace1, 0xacfb, HB_Grapheme_LVT},
+  {0xacfc, 0xacfc, HB_Grapheme_LV},
+  {0xacfd, 0xad17, HB_Grapheme_LVT},
+  {0xad18, 0xad18, HB_Grapheme_LV},
+  {0xad19, 0xad33, HB_Grapheme_LVT},
+  {0xad34, 0xad34, HB_Grapheme_LV},
+  {0xad35, 0xad4f, HB_Grapheme_LVT},
+  {0xad50, 0xad50, HB_Grapheme_LV},
+  {0xad51, 0xad6b, HB_Grapheme_LVT},
+  {0xad6c, 0xad6c, HB_Grapheme_LV},
+  {0xad6d, 0xad87, HB_Grapheme_LVT},
+  {0xad88, 0xad88, HB_Grapheme_LV},
+  {0xad89, 0xada3, HB_Grapheme_LVT},
+  {0xada4, 0xada4, HB_Grapheme_LV},
+  {0xada5, 0xadbf, HB_Grapheme_LVT},
+  {0xadc0, 0xadc0, HB_Grapheme_LV},
+  {0xadc1, 0xaddb, HB_Grapheme_LVT},
+  {0xaddc, 0xaddc, HB_Grapheme_LV},
+  {0xaddd, 0xadf7, HB_Grapheme_LVT},
+  {0xadf8, 0xadf8, HB_Grapheme_LV},
+  {0xadf9, 0xae13, HB_Grapheme_LVT},
+  {0xae14, 0xae14, HB_Grapheme_LV},
+  {0xae15, 0xae2f, HB_Grapheme_LVT},
+  {0xae30, 0xae30, HB_Grapheme_LV},
+  {0xae31, 0xae4b, HB_Grapheme_LVT},
+  {0xae4c, 0xae4c, HB_Grapheme_LV},
+  {0xae4d, 0xae67, HB_Grapheme_LVT},
+  {0xae68, 0xae68, HB_Grapheme_LV},
+  {0xae69, 0xae83, HB_Grapheme_LVT},
+  {0xae84, 0xae84, HB_Grapheme_LV},
+  {0xae85, 0xae9f, HB_Grapheme_LVT},
+  {0xaea0, 0xaea0, HB_Grapheme_LV},
+  {0xaea1, 0xaebb, HB_Grapheme_LVT},
+  {0xaebc, 0xaebc, HB_Grapheme_LV},
+  {0xaebd, 0xaed7, HB_Grapheme_LVT},
+  {0xaed8, 0xaed8, HB_Grapheme_LV},
+  {0xaed9, 0xaef3, HB_Grapheme_LVT},
+  {0xaef4, 0xaef4, HB_Grapheme_LV},
+  {0xaef5, 0xaf0f, HB_Grapheme_LVT},
+  {0xaf10, 0xaf10, HB_Grapheme_LV},
+  {0xaf11, 0xaf2b, HB_Grapheme_LVT},
+  {0xaf2c, 0xaf2c, HB_Grapheme_LV},
+  {0xaf2d, 0xaf47, HB_Grapheme_LVT},
+  {0xaf48, 0xaf48, HB_Grapheme_LV},
+  {0xaf49, 0xaf63, HB_Grapheme_LVT},
+  {0xaf64, 0xaf64, HB_Grapheme_LV},
+  {0xaf65, 0xaf7f, HB_Grapheme_LVT},
+  {0xaf80, 0xaf80, HB_Grapheme_LV},
+  {0xaf81, 0xaf9b, HB_Grapheme_LVT},
+  {0xaf9c, 0xaf9c, HB_Grapheme_LV},
+  {0xaf9d, 0xafb7, HB_Grapheme_LVT},
+  {0xafb8, 0xafb8, HB_Grapheme_LV},
+  {0xafb9, 0xafd3, HB_Grapheme_LVT},
+  {0xafd4, 0xafd4, HB_Grapheme_LV},
+  {0xafd5, 0xafef, HB_Grapheme_LVT},
+  {0xaff0, 0xaff0, HB_Grapheme_LV},
+  {0xaff1, 0xb00b, HB_Grapheme_LVT},
+  {0xb00c, 0xb00c, HB_Grapheme_LV},
+  {0xb00d, 0xb027, HB_Grapheme_LVT},
+  {0xb028, 0xb028, HB_Grapheme_LV},
+  {0xb029, 0xb043, HB_Grapheme_LVT},
+  {0xb044, 0xb044, HB_Grapheme_LV},
+  {0xb045, 0xb05f, HB_Grapheme_LVT},
+  {0xb060, 0xb060, HB_Grapheme_LV},
+  {0xb061, 0xb07b, HB_Grapheme_LVT},
+  {0xb07c, 0xb07c, HB_Grapheme_LV},
+  {0xb07d, 0xb097, HB_Grapheme_LVT},
+  {0xb098, 0xb098, HB_Grapheme_LV},
+  {0xb099, 0xb0b3, HB_Grapheme_LVT},
+  {0xb0b4, 0xb0b4, HB_Grapheme_LV},
+  {0xb0b5, 0xb0cf, HB_Grapheme_LVT},
+  {0xb0d0, 0xb0d0, HB_Grapheme_LV},
+  {0xb0d1, 0xb0eb, HB_Grapheme_LVT},
+  {0xb0ec, 0xb0ec, HB_Grapheme_LV},
+  {0xb0ed, 0xb107, HB_Grapheme_LVT},
+  {0xb108, 0xb108, HB_Grapheme_LV},
+  {0xb109, 0xb123, HB_Grapheme_LVT},
+  {0xb124, 0xb124, HB_Grapheme_LV},
+  {0xb125, 0xb13f, HB_Grapheme_LVT},
+  {0xb140, 0xb140, HB_Grapheme_LV},
+  {0xb141, 0xb15b, HB_Grapheme_LVT},
+  {0xb15c, 0xb15c, HB_Grapheme_LV},
+  {0xb15d, 0xb177, HB_Grapheme_LVT},
+  {0xb178, 0xb178, HB_Grapheme_LV},
+  {0xb179, 0xb193, HB_Grapheme_LVT},
+  {0xb194, 0xb194, HB_Grapheme_LV},
+  {0xb195, 0xb1af, HB_Grapheme_LVT},
+  {0xb1b0, 0xb1b0, HB_Grapheme_LV},
+  {0xb1b1, 0xb1cb, HB_Grapheme_LVT},
+  {0xb1cc, 0xb1cc, HB_Grapheme_LV},
+  {0xb1cd, 0xb1e7, HB_Grapheme_LVT},
+  {0xb1e8, 0xb1e8, HB_Grapheme_LV},
+  {0xb1e9, 0xb203, HB_Grapheme_LVT},
+  {0xb204, 0xb204, HB_Grapheme_LV},
+  {0xb205, 0xb21f, HB_Grapheme_LVT},
+  {0xb220, 0xb220, HB_Grapheme_LV},
+  {0xb221, 0xb23b, HB_Grapheme_LVT},
+  {0xb23c, 0xb23c, HB_Grapheme_LV},
+  {0xb23d, 0xb257, HB_Grapheme_LVT},
+  {0xb258, 0xb258, HB_Grapheme_LV},
+  {0xb259, 0xb273, HB_Grapheme_LVT},
+  {0xb274, 0xb274, HB_Grapheme_LV},
+  {0xb275, 0xb28f, HB_Grapheme_LVT},
+  {0xb290, 0xb290, HB_Grapheme_LV},
+  {0xb291, 0xb2ab, HB_Grapheme_LVT},
+  {0xb2ac, 0xb2ac, HB_Grapheme_LV},
+  {0xb2ad, 0xb2c7, HB_Grapheme_LVT},
+  {0xb2c8, 0xb2c8, HB_Grapheme_LV},
+  {0xb2c9, 0xb2e3, HB_Grapheme_LVT},
+  {0xb2e4, 0xb2e4, HB_Grapheme_LV},
+  {0xb2e5, 0xb2ff, HB_Grapheme_LVT},
+  {0xb300, 0xb300, HB_Grapheme_LV},
+  {0xb301, 0xb31b, HB_Grapheme_LVT},
+  {0xb31c, 0xb31c, HB_Grapheme_LV},
+  {0xb31d, 0xb337, HB_Grapheme_LVT},
+  {0xb338, 0xb338, HB_Grapheme_LV},
+  {0xb339, 0xb353, HB_Grapheme_LVT},
+  {0xb354, 0xb354, HB_Grapheme_LV},
+  {0xb355, 0xb36f, HB_Grapheme_LVT},
+  {0xb370, 0xb370, HB_Grapheme_LV},
+  {0xb371, 0xb38b, HB_Grapheme_LVT},
+  {0xb38c, 0xb38c, HB_Grapheme_LV},
+  {0xb38d, 0xb3a7, HB_Grapheme_LVT},
+  {0xb3a8, 0xb3a8, HB_Grapheme_LV},
+  {0xb3a9, 0xb3c3, HB_Grapheme_LVT},
+  {0xb3c4, 0xb3c4, HB_Grapheme_LV},
+  {0xb3c5, 0xb3df, HB_Grapheme_LVT},
+  {0xb3e0, 0xb3e0, HB_Grapheme_LV},
+  {0xb3e1, 0xb3fb, HB_Grapheme_LVT},
+  {0xb3fc, 0xb3fc, HB_Grapheme_LV},
+  {0xb3fd, 0xb417, HB_Grapheme_LVT},
+  {0xb418, 0xb418, HB_Grapheme_LV},
+  {0xb419, 0xb433, HB_Grapheme_LVT},
+  {0xb434, 0xb434, HB_Grapheme_LV},
+  {0xb435, 0xb44f, HB_Grapheme_LVT},
+  {0xb450, 0xb450, HB_Grapheme_LV},
+  {0xb451, 0xb46b, HB_Grapheme_LVT},
+  {0xb46c, 0xb46c, HB_Grapheme_LV},
+  {0xb46d, 0xb487, HB_Grapheme_LVT},
+  {0xb488, 0xb488, HB_Grapheme_LV},
+  {0xb489, 0xb4a3, HB_Grapheme_LVT},
+  {0xb4a4, 0xb4a4, HB_Grapheme_LV},
+  {0xb4a5, 0xb4bf, HB_Grapheme_LVT},
+  {0xb4c0, 0xb4c0, HB_Grapheme_LV},
+  {0xb4c1, 0xb4db, HB_Grapheme_LVT},
+  {0xb4dc, 0xb4dc, HB_Grapheme_LV},
+  {0xb4dd, 0xb4f7, HB_Grapheme_LVT},
+  {0xb4f8, 0xb4f8, HB_Grapheme_LV},
+  {0xb4f9, 0xb513, HB_Grapheme_LVT},
+  {0xb514, 0xb514, HB_Grapheme_LV},
+  {0xb515, 0xb52f, HB_Grapheme_LVT},
+  {0xb530, 0xb530, HB_Grapheme_LV},
+  {0xb531, 0xb54b, HB_Grapheme_LVT},
+  {0xb54c, 0xb54c, HB_Grapheme_LV},
+  {0xb54d, 0xb567, HB_Grapheme_LVT},
+  {0xb568, 0xb568, HB_Grapheme_LV},
+  {0xb569, 0xb583, HB_Grapheme_LVT},
+  {0xb584, 0xb584, HB_Grapheme_LV},
+  {0xb585, 0xb59f, HB_Grapheme_LVT},
+  {0xb5a0, 0xb5a0, HB_Grapheme_LV},
+  {0xb5a1, 0xb5bb, HB_Grapheme_LVT},
+  {0xb5bc, 0xb5bc, HB_Grapheme_LV},
+  {0xb5bd, 0xb5d7, HB_Grapheme_LVT},
+  {0xb5d8, 0xb5d8, HB_Grapheme_LV},
+  {0xb5d9, 0xb5f3, HB_Grapheme_LVT},
+  {0xb5f4, 0xb5f4, HB_Grapheme_LV},
+  {0xb5f5, 0xb60f, HB_Grapheme_LVT},
+  {0xb610, 0xb610, HB_Grapheme_LV},
+  {0xb611, 0xb62b, HB_Grapheme_LVT},
+  {0xb62c, 0xb62c, HB_Grapheme_LV},
+  {0xb62d, 0xb647, HB_Grapheme_LVT},
+  {0xb648, 0xb648, HB_Grapheme_LV},
+  {0xb649, 0xb663, HB_Grapheme_LVT},
+  {0xb664, 0xb664, HB_Grapheme_LV},
+  {0xb665, 0xb67f, HB_Grapheme_LVT},
+  {0xb680, 0xb680, HB_Grapheme_LV},
+  {0xb681, 0xb69b, HB_Grapheme_LVT},
+  {0xb69c, 0xb69c, HB_Grapheme_LV},
+  {0xb69d, 0xb6b7, HB_Grapheme_LVT},
+  {0xb6b8, 0xb6b8, HB_Grapheme_LV},
+  {0xb6b9, 0xb6d3, HB_Grapheme_LVT},
+  {0xb6d4, 0xb6d4, HB_Grapheme_LV},
+  {0xb6d5, 0xb6ef, HB_Grapheme_LVT},
+  {0xb6f0, 0xb6f0, HB_Grapheme_LV},
+  {0xb6f1, 0xb70b, HB_Grapheme_LVT},
+  {0xb70c, 0xb70c, HB_Grapheme_LV},
+  {0xb70d, 0xb727, HB_Grapheme_LVT},
+  {0xb728, 0xb728, HB_Grapheme_LV},
+  {0xb729, 0xb743, HB_Grapheme_LVT},
+  {0xb744, 0xb744, HB_Grapheme_LV},
+  {0xb745, 0xb75f, HB_Grapheme_LVT},
+  {0xb760, 0xb760, HB_Grapheme_LV},
+  {0xb761, 0xb77b, HB_Grapheme_LVT},
+  {0xb77c, 0xb77c, HB_Grapheme_LV},
+  {0xb77d, 0xb797, HB_Grapheme_LVT},
+  {0xb798, 0xb798, HB_Grapheme_LV},
+  {0xb799, 0xb7b3, HB_Grapheme_LVT},
+  {0xb7b4, 0xb7b4, HB_Grapheme_LV},
+  {0xb7b5, 0xb7cf, HB_Grapheme_LVT},
+  {0xb7d0, 0xb7d0, HB_Grapheme_LV},
+  {0xb7d1, 0xb7eb, HB_Grapheme_LVT},
+  {0xb7ec, 0xb7ec, HB_Grapheme_LV},
+  {0xb7ed, 0xb807, HB_Grapheme_LVT},
+  {0xb808, 0xb808, HB_Grapheme_LV},
+  {0xb809, 0xb823, HB_Grapheme_LVT},
+  {0xb824, 0xb824, HB_Grapheme_LV},
+  {0xb825, 0xb83f, HB_Grapheme_LVT},
+  {0xb840, 0xb840, HB_Grapheme_LV},
+  {0xb841, 0xb85b, HB_Grapheme_LVT},
+  {0xb85c, 0xb85c, HB_Grapheme_LV},
+  {0xb85d, 0xb877, HB_Grapheme_LVT},
+  {0xb878, 0xb878, HB_Grapheme_LV},
+  {0xb879, 0xb893, HB_Grapheme_LVT},
+  {0xb894, 0xb894, HB_Grapheme_LV},
+  {0xb895, 0xb8af, HB_Grapheme_LVT},
+  {0xb8b0, 0xb8b0, HB_Grapheme_LV},
+  {0xb8b1, 0xb8cb, HB_Grapheme_LVT},
+  {0xb8cc, 0xb8cc, HB_Grapheme_LV},
+  {0xb8cd, 0xb8e7, HB_Grapheme_LVT},
+  {0xb8e8, 0xb8e8, HB_Grapheme_LV},
+  {0xb8e9, 0xb903, HB_Grapheme_LVT},
+  {0xb904, 0xb904, HB_Grapheme_LV},
+  {0xb905, 0xb91f, HB_Grapheme_LVT},
+  {0xb920, 0xb920, HB_Grapheme_LV},
+  {0xb921, 0xb93b, HB_Grapheme_LVT},
+  {0xb93c, 0xb93c, HB_Grapheme_LV},
+  {0xb93d, 0xb957, HB_Grapheme_LVT},
+  {0xb958, 0xb958, HB_Grapheme_LV},
+  {0xb959, 0xb973, HB_Grapheme_LVT},
+  {0xb974, 0xb974, HB_Grapheme_LV},
+  {0xb975, 0xb98f, HB_Grapheme_LVT},
+  {0xb990, 0xb990, HB_Grapheme_LV},
+  {0xb991, 0xb9ab, HB_Grapheme_LVT},
+  {0xb9ac, 0xb9ac, HB_Grapheme_LV},
+  {0xb9ad, 0xb9c7, HB_Grapheme_LVT},
+  {0xb9c8, 0xb9c8, HB_Grapheme_LV},
+  {0xb9c9, 0xb9e3, HB_Grapheme_LVT},
+  {0xb9e4, 0xb9e4, HB_Grapheme_LV},
+  {0xb9e5, 0xb9ff, HB_Grapheme_LVT},
+  {0xba00, 0xba00, HB_Grapheme_LV},
+  {0xba01, 0xba1b, HB_Grapheme_LVT},
+  {0xba1c, 0xba1c, HB_Grapheme_LV},
+  {0xba1d, 0xba37, HB_Grapheme_LVT},
+  {0xba38, 0xba38, HB_Grapheme_LV},
+  {0xba39, 0xba53, HB_Grapheme_LVT},
+  {0xba54, 0xba54, HB_Grapheme_LV},
+  {0xba55, 0xba6f, HB_Grapheme_LVT},
+  {0xba70, 0xba70, HB_Grapheme_LV},
+  {0xba71, 0xba8b, HB_Grapheme_LVT},
+  {0xba8c, 0xba8c, HB_Grapheme_LV},
+  {0xba8d, 0xbaa7, HB_Grapheme_LVT},
+  {0xbaa8, 0xbaa8, HB_Grapheme_LV},
+  {0xbaa9, 0xbac3, HB_Grapheme_LVT},
+  {0xbac4, 0xbac4, HB_Grapheme_LV},
+  {0xbac5, 0xbadf, HB_Grapheme_LVT},
+  {0xbae0, 0xbae0, HB_Grapheme_LV},
+  {0xbae1, 0xbafb, HB_Grapheme_LVT},
+  {0xbafc, 0xbafc, HB_Grapheme_LV},
+  {0xbafd, 0xbb17, HB_Grapheme_LVT},
+  {0xbb18, 0xbb18, HB_Grapheme_LV},
+  {0xbb19, 0xbb33, HB_Grapheme_LVT},
+  {0xbb34, 0xbb34, HB_Grapheme_LV},
+  {0xbb35, 0xbb4f, HB_Grapheme_LVT},
+  {0xbb50, 0xbb50, HB_Grapheme_LV},
+  {0xbb51, 0xbb6b, HB_Grapheme_LVT},
+  {0xbb6c, 0xbb6c, HB_Grapheme_LV},
+  {0xbb6d, 0xbb87, HB_Grapheme_LVT},
+  {0xbb88, 0xbb88, HB_Grapheme_LV},
+  {0xbb89, 0xbba3, HB_Grapheme_LVT},
+  {0xbba4, 0xbba4, HB_Grapheme_LV},
+  {0xbba5, 0xbbbf, HB_Grapheme_LVT},
+  {0xbbc0, 0xbbc0, HB_Grapheme_LV},
+  {0xbbc1, 0xbbdb, HB_Grapheme_LVT},
+  {0xbbdc, 0xbbdc, HB_Grapheme_LV},
+  {0xbbdd, 0xbbf7, HB_Grapheme_LVT},
+  {0xbbf8, 0xbbf8, HB_Grapheme_LV},
+  {0xbbf9, 0xbc13, HB_Grapheme_LVT},
+  {0xbc14, 0xbc14, HB_Grapheme_LV},
+  {0xbc15, 0xbc2f, HB_Grapheme_LVT},
+  {0xbc30, 0xbc30, HB_Grapheme_LV},
+  {0xbc31, 0xbc4b, HB_Grapheme_LVT},
+  {0xbc4c, 0xbc4c, HB_Grapheme_LV},
+  {0xbc4d, 0xbc67, HB_Grapheme_LVT},
+  {0xbc68, 0xbc68, HB_Grapheme_LV},
+  {0xbc69, 0xbc83, HB_Grapheme_LVT},
+  {0xbc84, 0xbc84, HB_Grapheme_LV},
+  {0xbc85, 0xbc9f, HB_Grapheme_LVT},
+  {0xbca0, 0xbca0, HB_Grapheme_LV},
+  {0xbca1, 0xbcbb, HB_Grapheme_LVT},
+  {0xbcbc, 0xbcbc, HB_Grapheme_LV},
+  {0xbcbd, 0xbcd7, HB_Grapheme_LVT},
+  {0xbcd8, 0xbcd8, HB_Grapheme_LV},
+  {0xbcd9, 0xbcf3, HB_Grapheme_LVT},
+  {0xbcf4, 0xbcf4, HB_Grapheme_LV},
+  {0xbcf5, 0xbd0f, HB_Grapheme_LVT},
+  {0xbd10, 0xbd10, HB_Grapheme_LV},
+  {0xbd11, 0xbd2b, HB_Grapheme_LVT},
+  {0xbd2c, 0xbd2c, HB_Grapheme_LV},
+  {0xbd2d, 0xbd47, HB_Grapheme_LVT},
+  {0xbd48, 0xbd48, HB_Grapheme_LV},
+  {0xbd49, 0xbd63, HB_Grapheme_LVT},
+  {0xbd64, 0xbd64, HB_Grapheme_LV},
+  {0xbd65, 0xbd7f, HB_Grapheme_LVT},
+  {0xbd80, 0xbd80, HB_Grapheme_LV},
+  {0xbd81, 0xbd9b, HB_Grapheme_LVT},
+  {0xbd9c, 0xbd9c, HB_Grapheme_LV},
+  {0xbd9d, 0xbdb7, HB_Grapheme_LVT},
+  {0xbdb8, 0xbdb8, HB_Grapheme_LV},
+  {0xbdb9, 0xbdd3, HB_Grapheme_LVT},
+  {0xbdd4, 0xbdd4, HB_Grapheme_LV},
+  {0xbdd5, 0xbdef, HB_Grapheme_LVT},
+  {0xbdf0, 0xbdf0, HB_Grapheme_LV},
+  {0xbdf1, 0xbe0b, HB_Grapheme_LVT},
+  {0xbe0c, 0xbe0c, HB_Grapheme_LV},
+  {0xbe0d, 0xbe27, HB_Grapheme_LVT},
+  {0xbe28, 0xbe28, HB_Grapheme_LV},
+  {0xbe29, 0xbe43, HB_Grapheme_LVT},
+  {0xbe44, 0xbe44, HB_Grapheme_LV},
+  {0xbe45, 0xbe5f, HB_Grapheme_LVT},
+  {0xbe60, 0xbe60, HB_Grapheme_LV},
+  {0xbe61, 0xbe7b, HB_Grapheme_LVT},
+  {0xbe7c, 0xbe7c, HB_Grapheme_LV},
+  {0xbe7d, 0xbe97, HB_Grapheme_LVT},
+  {0xbe98, 0xbe98, HB_Grapheme_LV},
+  {0xbe99, 0xbeb3, HB_Grapheme_LVT},
+  {0xbeb4, 0xbeb4, HB_Grapheme_LV},
+  {0xbeb5, 0xbecf, HB_Grapheme_LVT},
+  {0xbed0, 0xbed0, HB_Grapheme_LV},
+  {0xbed1, 0xbeeb, HB_Grapheme_LVT},
+  {0xbeec, 0xbeec, HB_Grapheme_LV},
+  {0xbeed, 0xbf07, HB_Grapheme_LVT},
+  {0xbf08, 0xbf08, HB_Grapheme_LV},
+  {0xbf09, 0xbf23, HB_Grapheme_LVT},
+  {0xbf24, 0xbf24, HB_Grapheme_LV},
+  {0xbf25, 0xbf3f, HB_Grapheme_LVT},
+  {0xbf40, 0xbf40, HB_Grapheme_LV},
+  {0xbf41, 0xbf5b, HB_Grapheme_LVT},
+  {0xbf5c, 0xbf5c, HB_Grapheme_LV},
+  {0xbf5d, 0xbf77, HB_Grapheme_LVT},
+  {0xbf78, 0xbf78, HB_Grapheme_LV},
+  {0xbf79, 0xbf93, HB_Grapheme_LVT},
+  {0xbf94, 0xbf94, HB_Grapheme_LV},
+  {0xbf95, 0xbfaf, HB_Grapheme_LVT},
+  {0xbfb0, 0xbfb0, HB_Grapheme_LV},
+  {0xbfb1, 0xbfcb, HB_Grapheme_LVT},
+  {0xbfcc, 0xbfcc, HB_Grapheme_LV},
+  {0xbfcd, 0xbfe7, HB_Grapheme_LVT},
+  {0xbfe8, 0xbfe8, HB_Grapheme_LV},
+  {0xbfe9, 0xc003, HB_Grapheme_LVT},
+  {0xc004, 0xc004, HB_Grapheme_LV},
+  {0xc005, 0xc01f, HB_Grapheme_LVT},
+  {0xc020, 0xc020, HB_Grapheme_LV},
+  {0xc021, 0xc03b, HB_Grapheme_LVT},
+  {0xc03c, 0xc03c, HB_Grapheme_LV},
+  {0xc03d, 0xc057, HB_Grapheme_LVT},
+  {0xc058, 0xc058, HB_Grapheme_LV},
+  {0xc059, 0xc073, HB_Grapheme_LVT},
+  {0xc074, 0xc074, HB_Grapheme_LV},
+  {0xc075, 0xc08f, HB_Grapheme_LVT},
+  {0xc090, 0xc090, HB_Grapheme_LV},
+  {0xc091, 0xc0ab, HB_Grapheme_LVT},
+  {0xc0ac, 0xc0ac, HB_Grapheme_LV},
+  {0xc0ad, 0xc0c7, HB_Grapheme_LVT},
+  {0xc0c8, 0xc0c8, HB_Grapheme_LV},
+  {0xc0c9, 0xc0e3, HB_Grapheme_LVT},
+  {0xc0e4, 0xc0e4, HB_Grapheme_LV},
+  {0xc0e5, 0xc0ff, HB_Grapheme_LVT},
+  {0xc100, 0xc100, HB_Grapheme_LV},
+  {0xc101, 0xc11b, HB_Grapheme_LVT},
+  {0xc11c, 0xc11c, HB_Grapheme_LV},
+  {0xc11d, 0xc137, HB_Grapheme_LVT},
+  {0xc138, 0xc138, HB_Grapheme_LV},
+  {0xc139, 0xc153, HB_Grapheme_LVT},
+  {0xc154, 0xc154, HB_Grapheme_LV},
+  {0xc155, 0xc16f, HB_Grapheme_LVT},
+  {0xc170, 0xc170, HB_Grapheme_LV},
+  {0xc171, 0xc18b, HB_Grapheme_LVT},
+  {0xc18c, 0xc18c, HB_Grapheme_LV},
+  {0xc18d, 0xc1a7, HB_Grapheme_LVT},
+  {0xc1a8, 0xc1a8, HB_Grapheme_LV},
+  {0xc1a9, 0xc1c3, HB_Grapheme_LVT},
+  {0xc1c4, 0xc1c4, HB_Grapheme_LV},
+  {0xc1c5, 0xc1df, HB_Grapheme_LVT},
+  {0xc1e0, 0xc1e0, HB_Grapheme_LV},
+  {0xc1e1, 0xc1fb, HB_Grapheme_LVT},
+  {0xc1fc, 0xc1fc, HB_Grapheme_LV},
+  {0xc1fd, 0xc217, HB_Grapheme_LVT},
+  {0xc218, 0xc218, HB_Grapheme_LV},
+  {0xc219, 0xc233, HB_Grapheme_LVT},
+  {0xc234, 0xc234, HB_Grapheme_LV},
+  {0xc235, 0xc24f, HB_Grapheme_LVT},
+  {0xc250, 0xc250, HB_Grapheme_LV},
+  {0xc251, 0xc26b, HB_Grapheme_LVT},
+  {0xc26c, 0xc26c, HB_Grapheme_LV},
+  {0xc26d, 0xc287, HB_Grapheme_LVT},
+  {0xc288, 0xc288, HB_Grapheme_LV},
+  {0xc289, 0xc2a3, HB_Grapheme_LVT},
+  {0xc2a4, 0xc2a4, HB_Grapheme_LV},
+  {0xc2a5, 0xc2bf, HB_Grapheme_LVT},
+  {0xc2c0, 0xc2c0, HB_Grapheme_LV},
+  {0xc2c1, 0xc2db, HB_Grapheme_LVT},
+  {0xc2dc, 0xc2dc, HB_Grapheme_LV},
+  {0xc2dd, 0xc2f7, HB_Grapheme_LVT},
+  {0xc2f8, 0xc2f8, HB_Grapheme_LV},
+  {0xc2f9, 0xc313, HB_Grapheme_LVT},
+  {0xc314, 0xc314, HB_Grapheme_LV},
+  {0xc315, 0xc32f, HB_Grapheme_LVT},
+  {0xc330, 0xc330, HB_Grapheme_LV},
+  {0xc331, 0xc34b, HB_Grapheme_LVT},
+  {0xc34c, 0xc34c, HB_Grapheme_LV},
+  {0xc34d, 0xc367, HB_Grapheme_LVT},
+  {0xc368, 0xc368, HB_Grapheme_LV},
+  {0xc369, 0xc383, HB_Grapheme_LVT},
+  {0xc384, 0xc384, HB_Grapheme_LV},
+  {0xc385, 0xc39f, HB_Grapheme_LVT},
+  {0xc3a0, 0xc3a0, HB_Grapheme_LV},
+  {0xc3a1, 0xc3bb, HB_Grapheme_LVT},
+  {0xc3bc, 0xc3bc, HB_Grapheme_LV},
+  {0xc3bd, 0xc3d7, HB_Grapheme_LVT},
+  {0xc3d8, 0xc3d8, HB_Grapheme_LV},
+  {0xc3d9, 0xc3f3, HB_Grapheme_LVT},
+  {0xc3f4, 0xc3f4, HB_Grapheme_LV},
+  {0xc3f5, 0xc40f, HB_Grapheme_LVT},
+  {0xc410, 0xc410, HB_Grapheme_LV},
+  {0xc411, 0xc42b, HB_Grapheme_LVT},
+  {0xc42c, 0xc42c, HB_Grapheme_LV},
+  {0xc42d, 0xc447, HB_Grapheme_LVT},
+  {0xc448, 0xc448, HB_Grapheme_LV},
+  {0xc449, 0xc463, HB_Grapheme_LVT},
+  {0xc464, 0xc464, HB_Grapheme_LV},
+  {0xc465, 0xc47f, HB_Grapheme_LVT},
+  {0xc480, 0xc480, HB_Grapheme_LV},
+  {0xc481, 0xc49b, HB_Grapheme_LVT},
+  {0xc49c, 0xc49c, HB_Grapheme_LV},
+  {0xc49d, 0xc4b7, HB_Grapheme_LVT},
+  {0xc4b8, 0xc4b8, HB_Grapheme_LV},
+  {0xc4b9, 0xc4d3, HB_Grapheme_LVT},
+  {0xc4d4, 0xc4d4, HB_Grapheme_LV},
+  {0xc4d5, 0xc4ef, HB_Grapheme_LVT},
+  {0xc4f0, 0xc4f0, HB_Grapheme_LV},
+  {0xc4f1, 0xc50b, HB_Grapheme_LVT},
+  {0xc50c, 0xc50c, HB_Grapheme_LV},
+  {0xc50d, 0xc527, HB_Grapheme_LVT},
+  {0xc528, 0xc528, HB_Grapheme_LV},
+  {0xc529, 0xc543, HB_Grapheme_LVT},
+  {0xc544, 0xc544, HB_Grapheme_LV},
+  {0xc545, 0xc55f, HB_Grapheme_LVT},
+  {0xc560, 0xc560, HB_Grapheme_LV},
+  {0xc561, 0xc57b, HB_Grapheme_LVT},
+  {0xc57c, 0xc57c, HB_Grapheme_LV},
+  {0xc57d, 0xc597, HB_Grapheme_LVT},
+  {0xc598, 0xc598, HB_Grapheme_LV},
+  {0xc599, 0xc5b3, HB_Grapheme_LVT},
+  {0xc5b4, 0xc5b4, HB_Grapheme_LV},
+  {0xc5b5, 0xc5cf, HB_Grapheme_LVT},
+  {0xc5d0, 0xc5d0, HB_Grapheme_LV},
+  {0xc5d1, 0xc5eb, HB_Grapheme_LVT},
+  {0xc5ec, 0xc5ec, HB_Grapheme_LV},
+  {0xc5ed, 0xc607, HB_Grapheme_LVT},
+  {0xc608, 0xc608, HB_Grapheme_LV},
+  {0xc609, 0xc623, HB_Grapheme_LVT},
+  {0xc624, 0xc624, HB_Grapheme_LV},
+  {0xc625, 0xc63f, HB_Grapheme_LVT},
+  {0xc640, 0xc640, HB_Grapheme_LV},
+  {0xc641, 0xc65b, HB_Grapheme_LVT},
+  {0xc65c, 0xc65c, HB_Grapheme_LV},
+  {0xc65d, 0xc677, HB_Grapheme_LVT},
+  {0xc678, 0xc678, HB_Grapheme_LV},
+  {0xc679, 0xc693, HB_Grapheme_LVT},
+  {0xc694, 0xc694, HB_Grapheme_LV},
+  {0xc695, 0xc6af, HB_Grapheme_LVT},
+  {0xc6b0, 0xc6b0, HB_Grapheme_LV},
+  {0xc6b1, 0xc6cb, HB_Grapheme_LVT},
+  {0xc6cc, 0xc6cc, HB_Grapheme_LV},
+  {0xc6cd, 0xc6e7, HB_Grapheme_LVT},
+  {0xc6e8, 0xc6e8, HB_Grapheme_LV},
+  {0xc6e9, 0xc703, HB_Grapheme_LVT},
+  {0xc704, 0xc704, HB_Grapheme_LV},
+  {0xc705, 0xc71f, HB_Grapheme_LVT},
+  {0xc720, 0xc720, HB_Grapheme_LV},
+  {0xc721, 0xc73b, HB_Grapheme_LVT},
+  {0xc73c, 0xc73c, HB_Grapheme_LV},
+  {0xc73d, 0xc757, HB_Grapheme_LVT},
+  {0xc758, 0xc758, HB_Grapheme_LV},
+  {0xc759, 0xc773, HB_Grapheme_LVT},
+  {0xc774, 0xc774, HB_Grapheme_LV},
+  {0xc775, 0xc78f, HB_Grapheme_LVT},
+  {0xc790, 0xc790, HB_Grapheme_LV},
+  {0xc791, 0xc7ab, HB_Grapheme_LVT},
+  {0xc7ac, 0xc7ac, HB_Grapheme_LV},
+  {0xc7ad, 0xc7c7, HB_Grapheme_LVT},
+  {0xc7c8, 0xc7c8, HB_Grapheme_LV},
+  {0xc7c9, 0xc7e3, HB_Grapheme_LVT},
+  {0xc7e4, 0xc7e4, HB_Grapheme_LV},
+  {0xc7e5, 0xc7ff, HB_Grapheme_LVT},
+  {0xc800, 0xc800, HB_Grapheme_LV},
+  {0xc801, 0xc81b, HB_Grapheme_LVT},
+  {0xc81c, 0xc81c, HB_Grapheme_LV},
+  {0xc81d, 0xc837, HB_Grapheme_LVT},
+  {0xc838, 0xc838, HB_Grapheme_LV},
+  {0xc839, 0xc853, HB_Grapheme_LVT},
+  {0xc854, 0xc854, HB_Grapheme_LV},
+  {0xc855, 0xc86f, HB_Grapheme_LVT},
+  {0xc870, 0xc870, HB_Grapheme_LV},
+  {0xc871, 0xc88b, HB_Grapheme_LVT},
+  {0xc88c, 0xc88c, HB_Grapheme_LV},
+  {0xc88d, 0xc8a7, HB_Grapheme_LVT},
+  {0xc8a8, 0xc8a8, HB_Grapheme_LV},
+  {0xc8a9, 0xc8c3, HB_Grapheme_LVT},
+  {0xc8c4, 0xc8c4, HB_Grapheme_LV},
+  {0xc8c5, 0xc8df, HB_Grapheme_LVT},
+  {0xc8e0, 0xc8e0, HB_Grapheme_LV},
+  {0xc8e1, 0xc8fb, HB_Grapheme_LVT},
+  {0xc8fc, 0xc8fc, HB_Grapheme_LV},
+  {0xc8fd, 0xc917, HB_Grapheme_LVT},
+  {0xc918, 0xc918, HB_Grapheme_LV},
+  {0xc919, 0xc933, HB_Grapheme_LVT},
+  {0xc934, 0xc934, HB_Grapheme_LV},
+  {0xc935, 0xc94f, HB_Grapheme_LVT},
+  {0xc950, 0xc950, HB_Grapheme_LV},
+  {0xc951, 0xc96b, HB_Grapheme_LVT},
+  {0xc96c, 0xc96c, HB_Grapheme_LV},
+  {0xc96d, 0xc987, HB_Grapheme_LVT},
+  {0xc988, 0xc988, HB_Grapheme_LV},
+  {0xc989, 0xc9a3, HB_Grapheme_LVT},
+  {0xc9a4, 0xc9a4, HB_Grapheme_LV},
+  {0xc9a5, 0xc9bf, HB_Grapheme_LVT},
+  {0xc9c0, 0xc9c0, HB_Grapheme_LV},
+  {0xc9c1, 0xc9db, HB_Grapheme_LVT},
+  {0xc9dc, 0xc9dc, HB_Grapheme_LV},
+  {0xc9dd, 0xc9f7, HB_Grapheme_LVT},
+  {0xc9f8, 0xc9f8, HB_Grapheme_LV},
+  {0xc9f9, 0xca13, HB_Grapheme_LVT},
+  {0xca14, 0xca14, HB_Grapheme_LV},
+  {0xca15, 0xca2f, HB_Grapheme_LVT},
+  {0xca30, 0xca30, HB_Grapheme_LV},
+  {0xca31, 0xca4b, HB_Grapheme_LVT},
+  {0xca4c, 0xca4c, HB_Grapheme_LV},
+  {0xca4d, 0xca67, HB_Grapheme_LVT},
+  {0xca68, 0xca68, HB_Grapheme_LV},
+  {0xca69, 0xca83, HB_Grapheme_LVT},
+  {0xca84, 0xca84, HB_Grapheme_LV},
+  {0xca85, 0xca9f, HB_Grapheme_LVT},
+  {0xcaa0, 0xcaa0, HB_Grapheme_LV},
+  {0xcaa1, 0xcabb, HB_Grapheme_LVT},
+  {0xcabc, 0xcabc, HB_Grapheme_LV},
+  {0xcabd, 0xcad7, HB_Grapheme_LVT},
+  {0xcad8, 0xcad8, HB_Grapheme_LV},
+  {0xcad9, 0xcaf3, HB_Grapheme_LVT},
+  {0xcaf4, 0xcaf4, HB_Grapheme_LV},
+  {0xcaf5, 0xcb0f, HB_Grapheme_LVT},
+  {0xcb10, 0xcb10, HB_Grapheme_LV},
+  {0xcb11, 0xcb2b, HB_Grapheme_LVT},
+  {0xcb2c, 0xcb2c, HB_Grapheme_LV},
+  {0xcb2d, 0xcb47, HB_Grapheme_LVT},
+  {0xcb48, 0xcb48, HB_Grapheme_LV},
+  {0xcb49, 0xcb63, HB_Grapheme_LVT},
+  {0xcb64, 0xcb64, HB_Grapheme_LV},
+  {0xcb65, 0xcb7f, HB_Grapheme_LVT},
+  {0xcb80, 0xcb80, HB_Grapheme_LV},
+  {0xcb81, 0xcb9b, HB_Grapheme_LVT},
+  {0xcb9c, 0xcb9c, HB_Grapheme_LV},
+  {0xcb9d, 0xcbb7, HB_Grapheme_LVT},
+  {0xcbb8, 0xcbb8, HB_Grapheme_LV},
+  {0xcbb9, 0xcbd3, HB_Grapheme_LVT},
+  {0xcbd4, 0xcbd4, HB_Grapheme_LV},
+  {0xcbd5, 0xcbef, HB_Grapheme_LVT},
+  {0xcbf0, 0xcbf0, HB_Grapheme_LV},
+  {0xcbf1, 0xcc0b, HB_Grapheme_LVT},
+  {0xcc0c, 0xcc0c, HB_Grapheme_LV},
+  {0xcc0d, 0xcc27, HB_Grapheme_LVT},
+  {0xcc28, 0xcc28, HB_Grapheme_LV},
+  {0xcc29, 0xcc43, HB_Grapheme_LVT},
+  {0xcc44, 0xcc44, HB_Grapheme_LV},
+  {0xcc45, 0xcc5f, HB_Grapheme_LVT},
+  {0xcc60, 0xcc60, HB_Grapheme_LV},
+  {0xcc61, 0xcc7b, HB_Grapheme_LVT},
+  {0xcc7c, 0xcc7c, HB_Grapheme_LV},
+  {0xcc7d, 0xcc97, HB_Grapheme_LVT},
+  {0xcc98, 0xcc98, HB_Grapheme_LV},
+  {0xcc99, 0xccb3, HB_Grapheme_LVT},
+  {0xccb4, 0xccb4, HB_Grapheme_LV},
+  {0xccb5, 0xcccf, HB_Grapheme_LVT},
+  {0xccd0, 0xccd0, HB_Grapheme_LV},
+  {0xccd1, 0xcceb, HB_Grapheme_LVT},
+  {0xccec, 0xccec, HB_Grapheme_LV},
+  {0xcced, 0xcd07, HB_Grapheme_LVT},
+  {0xcd08, 0xcd08, HB_Grapheme_LV},
+  {0xcd09, 0xcd23, HB_Grapheme_LVT},
+  {0xcd24, 0xcd24, HB_Grapheme_LV},
+  {0xcd25, 0xcd3f, HB_Grapheme_LVT},
+  {0xcd40, 0xcd40, HB_Grapheme_LV},
+  {0xcd41, 0xcd5b, HB_Grapheme_LVT},
+  {0xcd5c, 0xcd5c, HB_Grapheme_LV},
+  {0xcd5d, 0xcd77, HB_Grapheme_LVT},
+  {0xcd78, 0xcd78, HB_Grapheme_LV},
+  {0xcd79, 0xcd93, HB_Grapheme_LVT},
+  {0xcd94, 0xcd94, HB_Grapheme_LV},
+  {0xcd95, 0xcdaf, HB_Grapheme_LVT},
+  {0xcdb0, 0xcdb0, HB_Grapheme_LV},
+  {0xcdb1, 0xcdcb, HB_Grapheme_LVT},
+  {0xcdcc, 0xcdcc, HB_Grapheme_LV},
+  {0xcdcd, 0xcde7, HB_Grapheme_LVT},
+  {0xcde8, 0xcde8, HB_Grapheme_LV},
+  {0xcde9, 0xce03, HB_Grapheme_LVT},
+  {0xce04, 0xce04, HB_Grapheme_LV},
+  {0xce05, 0xce1f, HB_Grapheme_LVT},
+  {0xce20, 0xce20, HB_Grapheme_LV},
+  {0xce21, 0xce3b, HB_Grapheme_LVT},
+  {0xce3c, 0xce3c, HB_Grapheme_LV},
+  {0xce3d, 0xce57, HB_Grapheme_LVT},
+  {0xce58, 0xce58, HB_Grapheme_LV},
+  {0xce59, 0xce73, HB_Grapheme_LVT},
+  {0xce74, 0xce74, HB_Grapheme_LV},
+  {0xce75, 0xce8f, HB_Grapheme_LVT},
+  {0xce90, 0xce90, HB_Grapheme_LV},
+  {0xce91, 0xceab, HB_Grapheme_LVT},
+  {0xceac, 0xceac, HB_Grapheme_LV},
+  {0xcead, 0xcec7, HB_Grapheme_LVT},
+  {0xcec8, 0xcec8, HB_Grapheme_LV},
+  {0xcec9, 0xcee3, HB_Grapheme_LVT},
+  {0xcee4, 0xcee4, HB_Grapheme_LV},
+  {0xcee5, 0xceff, HB_Grapheme_LVT},
+  {0xcf00, 0xcf00, HB_Grapheme_LV},
+  {0xcf01, 0xcf1b, HB_Grapheme_LVT},
+  {0xcf1c, 0xcf1c, HB_Grapheme_LV},
+  {0xcf1d, 0xcf37, HB_Grapheme_LVT},
+  {0xcf38, 0xcf38, HB_Grapheme_LV},
+  {0xcf39, 0xcf53, HB_Grapheme_LVT},
+  {0xcf54, 0xcf54, HB_Grapheme_LV},
+  {0xcf55, 0xcf6f, HB_Grapheme_LVT},
+  {0xcf70, 0xcf70, HB_Grapheme_LV},
+  {0xcf71, 0xcf8b, HB_Grapheme_LVT},
+  {0xcf8c, 0xcf8c, HB_Grapheme_LV},
+  {0xcf8d, 0xcfa7, HB_Grapheme_LVT},
+  {0xcfa8, 0xcfa8, HB_Grapheme_LV},
+  {0xcfa9, 0xcfc3, HB_Grapheme_LVT},
+  {0xcfc4, 0xcfc4, HB_Grapheme_LV},
+  {0xcfc5, 0xcfdf, HB_Grapheme_LVT},
+  {0xcfe0, 0xcfe0, HB_Grapheme_LV},
+  {0xcfe1, 0xcffb, HB_Grapheme_LVT},
+  {0xcffc, 0xcffc, HB_Grapheme_LV},
+  {0xcffd, 0xd017, HB_Grapheme_LVT},
+  {0xd018, 0xd018, HB_Grapheme_LV},
+  {0xd019, 0xd033, HB_Grapheme_LVT},
+  {0xd034, 0xd034, HB_Grapheme_LV},
+  {0xd035, 0xd04f, HB_Grapheme_LVT},
+  {0xd050, 0xd050, HB_Grapheme_LV},
+  {0xd051, 0xd06b, HB_Grapheme_LVT},
+  {0xd06c, 0xd06c, HB_Grapheme_LV},
+  {0xd06d, 0xd087, HB_Grapheme_LVT},
+  {0xd088, 0xd088, HB_Grapheme_LV},
+  {0xd089, 0xd0a3, HB_Grapheme_LVT},
+  {0xd0a4, 0xd0a4, HB_Grapheme_LV},
+  {0xd0a5, 0xd0bf, HB_Grapheme_LVT},
+  {0xd0c0, 0xd0c0, HB_Grapheme_LV},
+  {0xd0c1, 0xd0db, HB_Grapheme_LVT},
+  {0xd0dc, 0xd0dc, HB_Grapheme_LV},
+  {0xd0dd, 0xd0f7, HB_Grapheme_LVT},
+  {0xd0f8, 0xd0f8, HB_Grapheme_LV},
+  {0xd0f9, 0xd113, HB_Grapheme_LVT},
+  {0xd114, 0xd114, HB_Grapheme_LV},
+  {0xd115, 0xd12f, HB_Grapheme_LVT},
+  {0xd130, 0xd130, HB_Grapheme_LV},
+  {0xd131, 0xd14b, HB_Grapheme_LVT},
+  {0xd14c, 0xd14c, HB_Grapheme_LV},
+  {0xd14d, 0xd167, HB_Grapheme_LVT},
+  {0xd168, 0xd168, HB_Grapheme_LV},
+  {0xd169, 0xd183, HB_Grapheme_LVT},
+  {0xd184, 0xd184, HB_Grapheme_LV},
+  {0xd185, 0xd19f, HB_Grapheme_LVT},
+  {0xd1a0, 0xd1a0, HB_Grapheme_LV},
+  {0xd1a1, 0xd1bb, HB_Grapheme_LVT},
+  {0xd1bc, 0xd1bc, HB_Grapheme_LV},
+  {0xd1bd, 0xd1d7, HB_Grapheme_LVT},
+  {0xd1d8, 0xd1d8, HB_Grapheme_LV},
+  {0xd1d9, 0xd1f3, HB_Grapheme_LVT},
+  {0xd1f4, 0xd1f4, HB_Grapheme_LV},
+  {0xd1f5, 0xd20f, HB_Grapheme_LVT},
+  {0xd210, 0xd210, HB_Grapheme_LV},
+  {0xd211, 0xd22b, HB_Grapheme_LVT},
+  {0xd22c, 0xd22c, HB_Grapheme_LV},
+  {0xd22d, 0xd247, HB_Grapheme_LVT},
+  {0xd248, 0xd248, HB_Grapheme_LV},
+  {0xd249, 0xd263, HB_Grapheme_LVT},
+  {0xd264, 0xd264, HB_Grapheme_LV},
+  {0xd265, 0xd27f, HB_Grapheme_LVT},
+  {0xd280, 0xd280, HB_Grapheme_LV},
+  {0xd281, 0xd29b, HB_Grapheme_LVT},
+  {0xd29c, 0xd29c, HB_Grapheme_LV},
+  {0xd29d, 0xd2b7, HB_Grapheme_LVT},
+  {0xd2b8, 0xd2b8, HB_Grapheme_LV},
+  {0xd2b9, 0xd2d3, HB_Grapheme_LVT},
+  {0xd2d4, 0xd2d4, HB_Grapheme_LV},
+  {0xd2d5, 0xd2ef, HB_Grapheme_LVT},
+  {0xd2f0, 0xd2f0, HB_Grapheme_LV},
+  {0xd2f1, 0xd30b, HB_Grapheme_LVT},
+  {0xd30c, 0xd30c, HB_Grapheme_LV},
+  {0xd30d, 0xd327, HB_Grapheme_LVT},
+  {0xd328, 0xd328, HB_Grapheme_LV},
+  {0xd329, 0xd343, HB_Grapheme_LVT},
+  {0xd344, 0xd344, HB_Grapheme_LV},
+  {0xd345, 0xd35f, HB_Grapheme_LVT},
+  {0xd360, 0xd360, HB_Grapheme_LV},
+  {0xd361, 0xd37b, HB_Grapheme_LVT},
+  {0xd37c, 0xd37c, HB_Grapheme_LV},
+  {0xd37d, 0xd397, HB_Grapheme_LVT},
+  {0xd398, 0xd398, HB_Grapheme_LV},
+  {0xd399, 0xd3b3, HB_Grapheme_LVT},
+  {0xd3b4, 0xd3b4, HB_Grapheme_LV},
+  {0xd3b5, 0xd3cf, HB_Grapheme_LVT},
+  {0xd3d0, 0xd3d0, HB_Grapheme_LV},
+  {0xd3d1, 0xd3eb, HB_Grapheme_LVT},
+  {0xd3ec, 0xd3ec, HB_Grapheme_LV},
+  {0xd3ed, 0xd407, HB_Grapheme_LVT},
+  {0xd408, 0xd408, HB_Grapheme_LV},
+  {0xd409, 0xd423, HB_Grapheme_LVT},
+  {0xd424, 0xd424, HB_Grapheme_LV},
+  {0xd425, 0xd43f, HB_Grapheme_LVT},
+  {0xd440, 0xd440, HB_Grapheme_LV},
+  {0xd441, 0xd45b, HB_Grapheme_LVT},
+  {0xd45c, 0xd45c, HB_Grapheme_LV},
+  {0xd45d, 0xd477, HB_Grapheme_LVT},
+  {0xd478, 0xd478, HB_Grapheme_LV},
+  {0xd479, 0xd493, HB_Grapheme_LVT},
+  {0xd494, 0xd494, HB_Grapheme_LV},
+  {0xd495, 0xd4af, HB_Grapheme_LVT},
+  {0xd4b0, 0xd4b0, HB_Grapheme_LV},
+  {0xd4b1, 0xd4cb, HB_Grapheme_LVT},
+  {0xd4cc, 0xd4cc, HB_Grapheme_LV},
+  {0xd4cd, 0xd4e7, HB_Grapheme_LVT},
+  {0xd4e8, 0xd4e8, HB_Grapheme_LV},
+  {0xd4e9, 0xd503, HB_Grapheme_LVT},
+  {0xd504, 0xd504, HB_Grapheme_LV},
+  {0xd505, 0xd51f, HB_Grapheme_LVT},
+  {0xd520, 0xd520, HB_Grapheme_LV},
+  {0xd521, 0xd53b, HB_Grapheme_LVT},
+  {0xd53c, 0xd53c, HB_Grapheme_LV},
+  {0xd53d, 0xd557, HB_Grapheme_LVT},
+  {0xd558, 0xd558, HB_Grapheme_LV},
+  {0xd559, 0xd573, HB_Grapheme_LVT},
+  {0xd574, 0xd574, HB_Grapheme_LV},
+  {0xd575, 0xd58f, HB_Grapheme_LVT},
+  {0xd590, 0xd590, HB_Grapheme_LV},
+  {0xd591, 0xd5ab, HB_Grapheme_LVT},
+  {0xd5ac, 0xd5ac, HB_Grapheme_LV},
+  {0xd5ad, 0xd5c7, HB_Grapheme_LVT},
+  {0xd5c8, 0xd5c8, HB_Grapheme_LV},
+  {0xd5c9, 0xd5e3, HB_Grapheme_LVT},
+  {0xd5e4, 0xd5e4, HB_Grapheme_LV},
+  {0xd5e5, 0xd5ff, HB_Grapheme_LVT},
+  {0xd600, 0xd600, HB_Grapheme_LV},
+  {0xd601, 0xd61b, HB_Grapheme_LVT},
+  {0xd61c, 0xd61c, HB_Grapheme_LV},
+  {0xd61d, 0xd637, HB_Grapheme_LVT},
+  {0xd638, 0xd638, HB_Grapheme_LV},
+  {0xd639, 0xd653, HB_Grapheme_LVT},
+  {0xd654, 0xd654, HB_Grapheme_LV},
+  {0xd655, 0xd66f, HB_Grapheme_LVT},
+  {0xd670, 0xd670, HB_Grapheme_LV},
+  {0xd671, 0xd68b, HB_Grapheme_LVT},
+  {0xd68c, 0xd68c, HB_Grapheme_LV},
+  {0xd68d, 0xd6a7, HB_Grapheme_LVT},
+  {0xd6a8, 0xd6a8, HB_Grapheme_LV},
+  {0xd6a9, 0xd6c3, HB_Grapheme_LVT},
+  {0xd6c4, 0xd6c4, HB_Grapheme_LV},
+  {0xd6c5, 0xd6df, HB_Grapheme_LVT},
+  {0xd6e0, 0xd6e0, HB_Grapheme_LV},
+  {0xd6e1, 0xd6fb, HB_Grapheme_LVT},
+  {0xd6fc, 0xd6fc, HB_Grapheme_LV},
+  {0xd6fd, 0xd717, HB_Grapheme_LVT},
+  {0xd718, 0xd718, HB_Grapheme_LV},
+  {0xd719, 0xd733, HB_Grapheme_LVT},
+  {0xd734, 0xd734, HB_Grapheme_LV},
+  {0xd735, 0xd74f, HB_Grapheme_LVT},
+  {0xd750, 0xd750, HB_Grapheme_LV},
+  {0xd751, 0xd76b, HB_Grapheme_LVT},
+  {0xd76c, 0xd76c, HB_Grapheme_LV},
+  {0xd76d, 0xd787, HB_Grapheme_LVT},
+  {0xd788, 0xd788, HB_Grapheme_LV},
+  {0xd789, 0xd7a3, HB_Grapheme_LVT},
+  {0xfb1e, 0xfb1e, HB_Grapheme_Extend},
+  {0xfe00, 0xfe0f, HB_Grapheme_Extend},
+  {0xfe20, 0xfe26, HB_Grapheme_Extend},
+  {0xfeff, 0xfeff, HB_Grapheme_Control},
+  {0xff9e, 0xff9f, HB_Grapheme_Extend},
+  {0xfff9, 0xfffb, HB_Grapheme_Control},
+  {0x101fd, 0x101fd, HB_Grapheme_Extend},
+  {0x10a01, 0x10a03, HB_Grapheme_Extend},
+  {0x10a05, 0x10a06, HB_Grapheme_Extend},
+  {0x10a0c, 0x10a0f, HB_Grapheme_Extend},
+  {0x10a38, 0x10a3a, HB_Grapheme_Extend},
+  {0x10a3f, 0x10a3f, HB_Grapheme_Extend},
+  {0x1d165, 0x1d165, HB_Grapheme_Extend},
+  {0x1d166, 0x1d166, HB_Grapheme_Other},
+  {0x1d167, 0x1d169, HB_Grapheme_Extend},
+  {0x1d16d, 0x1d16d, HB_Grapheme_Other},
+  {0x1d16e, 0x1d172, HB_Grapheme_Extend},
+  {0x1d173, 0x1d17a, HB_Grapheme_Control},
+  {0x1d17b, 0x1d182, HB_Grapheme_Extend},
+  {0x1d185, 0x1d18b, HB_Grapheme_Extend},
+  {0x1d1aa, 0x1d1ad, HB_Grapheme_Extend},
+  {0x1d242, 0x1d244, HB_Grapheme_Extend},
+  {0xe0001, 0xe0001, HB_Grapheme_Control},
+  {0xe0020, 0xe007f, HB_Grapheme_Control},
+  {0xe0100, 0xe01ef, HB_Grapheme_Extend},
+};
+
+static const unsigned grapheme_break_properties_count = 1093;
+
+#endif  // GRAPHEME_BREAK_PROPERTY_H_
diff --git a/third_party/harfbuzz/contrib/tables/mirroring-parse.py b/third_party/harfbuzz/contrib/tables/mirroring-parse.py
new file mode 100644
index 0000000..5724e19
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/mirroring-parse.py
@@ -0,0 +1,50 @@
+import sys
+
+# http://www.unicode.org/Public/UNIDATA/auxiliary/BidiMirroring.txt
+
+# This parses a file in the format of the above file and outputs a table
+# suitable for bsearch(3). This table maps Unicode code points to their
+# 'mirror'. (Mirroring is used when rendering RTL characters, see the Unicode
+# standard). By convention, this mapping should be commutative, but this code
+# doesn't enforce or check this.
+
+def main(infile, outfile):
+  pairs = []
+  for line in infile:
+    line = line[:-1]
+    if len(line) == 0 or line[0] == '#':
+      continue
+    if '#' in line:
+      (data, _) = line.split('#', 1)
+    else:
+      data = line
+    if ';' not in data:
+      continue
+    (a, b) = data.split(';', 1)
+    a = int(a, 16)
+    b = int(b, 16)
+
+    pairs.append((a, b))
+
+  pairs.sort()
+
+  print >>outfile, '// Generated from Unicode Bidi Mirroring tables\n'
+  print >>outfile, '#ifndef MIRRORING_PROPERTY_H_'
+  print >>outfile, '#define MIRRORING_PROPERTY_H_\n'
+  print >>outfile, '#include <stdint.h>'
+  print >>outfile, 'struct mirroring_property {'
+  print >>outfile, '  uint32_t a;'
+  print >>outfile, '  uint32_t b;'
+  print >>outfile, '};\n'
+  print >>outfile, 'static const struct mirroring_property mirroring_properties[] = {'
+  for pair in pairs:
+    print >>outfile, '  {0x%x, 0x%x},' % pair
+  print >>outfile, '};\n'
+  print >>outfile, 'static const unsigned mirroring_properties_count = %d;\n' % len(pairs)
+  print >>outfile, '#endif  // MIRRORING_PROPERTY_H_'
+
+if __name__ == '__main__':
+  if len(sys.argv) != 3:
+    print 'Usage: %s <input .txt> <output .h>' % sys.argv[0]
+  else:
+    main(file(sys.argv[1], 'r'), file(sys.argv[2], 'w+'))
diff --git a/third_party/harfbuzz/contrib/tables/mirroring-properties.h b/third_party/harfbuzz/contrib/tables/mirroring-properties.h
new file mode 100644
index 0000000..f9be2f6
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/mirroring-properties.h
@@ -0,0 +1,379 @@
+// Generated from Unicode Bidi Mirroring tables
+
+#ifndef MIRRORING_PROPERTY_H_
+#define MIRRORING_PROPERTY_H_
+
+#include <stdint.h>
+struct mirroring_property {
+  uint32_t a;
+  uint32_t b;
+};
+
+static const struct mirroring_property mirroring_properties[] = {
+  {0x28, 0x29},
+  {0x29, 0x28},
+  {0x3c, 0x3e},
+  {0x3e, 0x3c},
+  {0x5b, 0x5d},
+  {0x5d, 0x5b},
+  {0x7b, 0x7d},
+  {0x7d, 0x7b},
+  {0xab, 0xbb},
+  {0xbb, 0xab},
+  {0xf3a, 0xf3b},
+  {0xf3b, 0xf3a},
+  {0xf3c, 0xf3d},
+  {0xf3d, 0xf3c},
+  {0x169b, 0x169c},
+  {0x169c, 0x169b},
+  {0x2039, 0x203a},
+  {0x203a, 0x2039},
+  {0x2045, 0x2046},
+  {0x2046, 0x2045},
+  {0x207d, 0x207e},
+  {0x207e, 0x207d},
+  {0x208d, 0x208e},
+  {0x208e, 0x208d},
+  {0x2208, 0x220b},
+  {0x2209, 0x220c},
+  {0x220a, 0x220d},
+  {0x220b, 0x2208},
+  {0x220c, 0x2209},
+  {0x220d, 0x220a},
+  {0x2215, 0x29f5},
+  {0x223c, 0x223d},
+  {0x223d, 0x223c},
+  {0x2243, 0x22cd},
+  {0x2252, 0x2253},
+  {0x2253, 0x2252},
+  {0x2254, 0x2255},
+  {0x2255, 0x2254},
+  {0x2264, 0x2265},
+  {0x2265, 0x2264},
+  {0x2266, 0x2267},
+  {0x2267, 0x2266},
+  {0x2268, 0x2269},
+  {0x2269, 0x2268},
+  {0x226a, 0x226b},
+  {0x226b, 0x226a},
+  {0x226e, 0x226f},
+  {0x226f, 0x226e},
+  {0x2270, 0x2271},
+  {0x2271, 0x2270},
+  {0x2272, 0x2273},
+  {0x2273, 0x2272},
+  {0x2274, 0x2275},
+  {0x2275, 0x2274},
+  {0x2276, 0x2277},
+  {0x2277, 0x2276},
+  {0x2278, 0x2279},
+  {0x2279, 0x2278},
+  {0x227a, 0x227b},
+  {0x227b, 0x227a},
+  {0x227c, 0x227d},
+  {0x227d, 0x227c},
+  {0x227e, 0x227f},
+  {0x227f, 0x227e},
+  {0x2280, 0x2281},
+  {0x2281, 0x2280},
+  {0x2282, 0x2283},
+  {0x2283, 0x2282},
+  {0x2284, 0x2285},
+  {0x2285, 0x2284},
+  {0x2286, 0x2287},
+  {0x2287, 0x2286},
+  {0x2288, 0x2289},
+  {0x2289, 0x2288},
+  {0x228a, 0x228b},
+  {0x228b, 0x228a},
+  {0x228f, 0x2290},
+  {0x2290, 0x228f},
+  {0x2291, 0x2292},
+  {0x2292, 0x2291},
+  {0x2298, 0x29b8},
+  {0x22a2, 0x22a3},
+  {0x22a3, 0x22a2},
+  {0x22a6, 0x2ade},
+  {0x22a8, 0x2ae4},
+  {0x22a9, 0x2ae3},
+  {0x22ab, 0x2ae5},
+  {0x22b0, 0x22b1},
+  {0x22b1, 0x22b0},
+  {0x22b2, 0x22b3},
+  {0x22b3, 0x22b2},
+  {0x22b4, 0x22b5},
+  {0x22b5, 0x22b4},
+  {0x22b6, 0x22b7},
+  {0x22b7, 0x22b6},
+  {0x22c9, 0x22ca},
+  {0x22ca, 0x22c9},
+  {0x22cb, 0x22cc},
+  {0x22cc, 0x22cb},
+  {0x22cd, 0x2243},
+  {0x22d0, 0x22d1},
+  {0x22d1, 0x22d0},
+  {0x22d6, 0x22d7},
+  {0x22d7, 0x22d6},
+  {0x22d8, 0x22d9},
+  {0x22d9, 0x22d8},
+  {0x22da, 0x22db},
+  {0x22db, 0x22da},
+  {0x22dc, 0x22dd},
+  {0x22dd, 0x22dc},
+  {0x22de, 0x22df},
+  {0x22df, 0x22de},
+  {0x22e0, 0x22e1},
+  {0x22e1, 0x22e0},
+  {0x22e2, 0x22e3},
+  {0x22e3, 0x22e2},
+  {0x22e4, 0x22e5},
+  {0x22e5, 0x22e4},
+  {0x22e6, 0x22e7},
+  {0x22e7, 0x22e6},
+  {0x22e8, 0x22e9},
+  {0x22e9, 0x22e8},
+  {0x22ea, 0x22eb},
+  {0x22eb, 0x22ea},
+  {0x22ec, 0x22ed},
+  {0x22ed, 0x22ec},
+  {0x22f0, 0x22f1},
+  {0x22f1, 0x22f0},
+  {0x22f2, 0x22fa},
+  {0x22f3, 0x22fb},
+  {0x22f4, 0x22fc},
+  {0x22f6, 0x22fd},
+  {0x22f7, 0x22fe},
+  {0x22fa, 0x22f2},
+  {0x22fb, 0x22f3},
+  {0x22fc, 0x22f4},
+  {0x22fd, 0x22f6},
+  {0x22fe, 0x22f7},
+  {0x2308, 0x2309},
+  {0x2309, 0x2308},
+  {0x230a, 0x230b},
+  {0x230b, 0x230a},
+  {0x2329, 0x232a},
+  {0x232a, 0x2329},
+  {0x2768, 0x2769},
+  {0x2769, 0x2768},
+  {0x276a, 0x276b},
+  {0x276b, 0x276a},
+  {0x276c, 0x276d},
+  {0x276d, 0x276c},
+  {0x276e, 0x276f},
+  {0x276f, 0x276e},
+  {0x2770, 0x2771},
+  {0x2771, 0x2770},
+  {0x2772, 0x2773},
+  {0x2773, 0x2772},
+  {0x2774, 0x2775},
+  {0x2775, 0x2774},
+  {0x27c3, 0x27c4},
+  {0x27c4, 0x27c3},
+  {0x27c5, 0x27c6},
+  {0x27c6, 0x27c5},
+  {0x27c8, 0x27c9},
+  {0x27c9, 0x27c8},
+  {0x27d5, 0x27d6},
+  {0x27d6, 0x27d5},
+  {0x27dd, 0x27de},
+  {0x27de, 0x27dd},
+  {0x27e2, 0x27e3},
+  {0x27e3, 0x27e2},
+  {0x27e4, 0x27e5},
+  {0x27e5, 0x27e4},
+  {0x27e6, 0x27e7},
+  {0x27e7, 0x27e6},
+  {0x27e8, 0x27e9},
+  {0x27e9, 0x27e8},
+  {0x27ea, 0x27eb},
+  {0x27eb, 0x27ea},
+  {0x27ec, 0x27ed},
+  {0x27ed, 0x27ec},
+  {0x27ee, 0x27ef},
+  {0x27ef, 0x27ee},
+  {0x2983, 0x2984},
+  {0x2984, 0x2983},
+  {0x2985, 0x2986},
+  {0x2986, 0x2985},
+  {0x2987, 0x2988},
+  {0x2988, 0x2987},
+  {0x2989, 0x298a},
+  {0x298a, 0x2989},
+  {0x298b, 0x298c},
+  {0x298c, 0x298b},
+  {0x298d, 0x2990},
+  {0x298e, 0x298f},
+  {0x298f, 0x298e},
+  {0x2990, 0x298d},
+  {0x2991, 0x2992},
+  {0x2992, 0x2991},
+  {0x2993, 0x2994},
+  {0x2994, 0x2993},
+  {0x2995, 0x2996},
+  {0x2996, 0x2995},
+  {0x2997, 0x2998},
+  {0x2998, 0x2997},
+  {0x29b8, 0x2298},
+  {0x29c0, 0x29c1},
+  {0x29c1, 0x29c0},
+  {0x29c4, 0x29c5},
+  {0x29c5, 0x29c4},
+  {0x29cf, 0x29d0},
+  {0x29d0, 0x29cf},
+  {0x29d1, 0x29d2},
+  {0x29d2, 0x29d1},
+  {0x29d4, 0x29d5},
+  {0x29d5, 0x29d4},
+  {0x29d8, 0x29d9},
+  {0x29d9, 0x29d8},
+  {0x29da, 0x29db},
+  {0x29db, 0x29da},
+  {0x29f5, 0x2215},
+  {0x29f8, 0x29f9},
+  {0x29f9, 0x29f8},
+  {0x29fc, 0x29fd},
+  {0x29fd, 0x29fc},
+  {0x2a2b, 0x2a2c},
+  {0x2a2c, 0x2a2b},
+  {0x2a2d, 0x2a2e},
+  {0x2a2e, 0x2a2d},
+  {0x2a34, 0x2a35},
+  {0x2a35, 0x2a34},
+  {0x2a3c, 0x2a3d},
+  {0x2a3d, 0x2a3c},
+  {0x2a64, 0x2a65},
+  {0x2a65, 0x2a64},
+  {0x2a79, 0x2a7a},
+  {0x2a7a, 0x2a79},
+  {0x2a7d, 0x2a7e},
+  {0x2a7e, 0x2a7d},
+  {0x2a7f, 0x2a80},
+  {0x2a80, 0x2a7f},
+  {0x2a81, 0x2a82},
+  {0x2a82, 0x2a81},
+  {0x2a83, 0x2a84},
+  {0x2a84, 0x2a83},
+  {0x2a8b, 0x2a8c},
+  {0x2a8c, 0x2a8b},
+  {0x2a91, 0x2a92},
+  {0x2a92, 0x2a91},
+  {0x2a93, 0x2a94},
+  {0x2a94, 0x2a93},
+  {0x2a95, 0x2a96},
+  {0x2a96, 0x2a95},
+  {0x2a97, 0x2a98},
+  {0x2a98, 0x2a97},
+  {0x2a99, 0x2a9a},
+  {0x2a9a, 0x2a99},
+  {0x2a9b, 0x2a9c},
+  {0x2a9c, 0x2a9b},
+  {0x2aa1, 0x2aa2},
+  {0x2aa2, 0x2aa1},
+  {0x2aa6, 0x2aa7},
+  {0x2aa7, 0x2aa6},
+  {0x2aa8, 0x2aa9},
+  {0x2aa9, 0x2aa8},
+  {0x2aaa, 0x2aab},
+  {0x2aab, 0x2aaa},
+  {0x2aac, 0x2aad},
+  {0x2aad, 0x2aac},
+  {0x2aaf, 0x2ab0},
+  {0x2ab0, 0x2aaf},
+  {0x2ab3, 0x2ab4},
+  {0x2ab4, 0x2ab3},
+  {0x2abb, 0x2abc},
+  {0x2abc, 0x2abb},
+  {0x2abd, 0x2abe},
+  {0x2abe, 0x2abd},
+  {0x2abf, 0x2ac0},
+  {0x2ac0, 0x2abf},
+  {0x2ac1, 0x2ac2},
+  {0x2ac2, 0x2ac1},
+  {0x2ac3, 0x2ac4},
+  {0x2ac4, 0x2ac3},
+  {0x2ac5, 0x2ac6},
+  {0x2ac6, 0x2ac5},
+  {0x2acd, 0x2ace},
+  {0x2ace, 0x2acd},
+  {0x2acf, 0x2ad0},
+  {0x2ad0, 0x2acf},
+  {0x2ad1, 0x2ad2},
+  {0x2ad2, 0x2ad1},
+  {0x2ad3, 0x2ad4},
+  {0x2ad4, 0x2ad3},
+  {0x2ad5, 0x2ad6},
+  {0x2ad6, 0x2ad5},
+  {0x2ade, 0x22a6},
+  {0x2ae3, 0x22a9},
+  {0x2ae4, 0x22a8},
+  {0x2ae5, 0x22ab},
+  {0x2aec, 0x2aed},
+  {0x2aed, 0x2aec},
+  {0x2af7, 0x2af8},
+  {0x2af8, 0x2af7},
+  {0x2af9, 0x2afa},
+  {0x2afa, 0x2af9},
+  {0x2e02, 0x2e03},
+  {0x2e03, 0x2e02},
+  {0x2e04, 0x2e05},
+  {0x2e05, 0x2e04},
+  {0x2e09, 0x2e0a},
+  {0x2e0a, 0x2e09},
+  {0x2e0c, 0x2e0d},
+  {0x2e0d, 0x2e0c},
+  {0x2e1c, 0x2e1d},
+  {0x2e1d, 0x2e1c},
+  {0x2e20, 0x2e21},
+  {0x2e21, 0x2e20},
+  {0x2e22, 0x2e23},
+  {0x2e23, 0x2e22},
+  {0x2e24, 0x2e25},
+  {0x2e25, 0x2e24},
+  {0x2e26, 0x2e27},
+  {0x2e27, 0x2e26},
+  {0x2e28, 0x2e29},
+  {0x2e29, 0x2e28},
+  {0x3008, 0x3009},
+  {0x3009, 0x3008},
+  {0x300a, 0x300b},
+  {0x300b, 0x300a},
+  {0x300c, 0x300d},
+  {0x300d, 0x300c},
+  {0x300e, 0x300f},
+  {0x300f, 0x300e},
+  {0x3010, 0x3011},
+  {0x3011, 0x3010},
+  {0x3014, 0x3015},
+  {0x3015, 0x3014},
+  {0x3016, 0x3017},
+  {0x3017, 0x3016},
+  {0x3018, 0x3019},
+  {0x3019, 0x3018},
+  {0x301a, 0x301b},
+  {0x301b, 0x301a},
+  {0xfe59, 0xfe5a},
+  {0xfe5a, 0xfe59},
+  {0xfe5b, 0xfe5c},
+  {0xfe5c, 0xfe5b},
+  {0xfe5d, 0xfe5e},
+  {0xfe5e, 0xfe5d},
+  {0xfe64, 0xfe65},
+  {0xfe65, 0xfe64},
+  {0xff08, 0xff09},
+  {0xff09, 0xff08},
+  {0xff1c, 0xff1e},
+  {0xff1e, 0xff1c},
+  {0xff3b, 0xff3d},
+  {0xff3d, 0xff3b},
+  {0xff5b, 0xff5d},
+  {0xff5d, 0xff5b},
+  {0xff5f, 0xff60},
+  {0xff60, 0xff5f},
+  {0xff62, 0xff63},
+  {0xff63, 0xff62},
+};
+
+static const unsigned mirroring_properties_count = 362;
+
+#endif  // MIRRORING_PROPERTY_H_
diff --git a/third_party/harfbuzz/contrib/tables/script-properties.h b/third_party/harfbuzz/contrib/tables/script-properties.h
new file mode 100644
index 0000000..a6ff50b
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/script-properties.h
@@ -0,0 +1,297 @@
+// Generated from Unicode script tables
+
+#ifndef SCRIPT_PROPERTIES_H_
+#define SCRIPT_PROPERTIES_H_
+
+#include <stdint.h>
+#include "harfbuzz-shaper.h"
+
+struct script_property {
+  uint32_t range_start;
+  uint32_t range_end;
+  HB_Script script;
+};
+
+static const struct script_property script_properties[] = {
+  {0x300, 0x36f, HB_Script_Inherited},
+  {0x370, 0x373, HB_Script_Greek},
+  {0x375, 0x377, HB_Script_Greek},
+  {0x37a, 0x37d, HB_Script_Greek},
+  {0x384, 0x384, HB_Script_Greek},
+  {0x386, 0x386, HB_Script_Greek},
+  {0x388, 0x38a, HB_Script_Greek},
+  {0x38c, 0x38c, HB_Script_Greek},
+  {0x38e, 0x3a1, HB_Script_Greek},
+  {0x3a3, 0x3e1, HB_Script_Greek},
+  {0x3f0, 0x3ff, HB_Script_Greek},
+  {0x400, 0x523, HB_Script_Cyrillic},
+  {0x531, 0x556, HB_Script_Armenian},
+  {0x559, 0x55f, HB_Script_Armenian},
+  {0x561, 0x587, HB_Script_Armenian},
+  {0x58a, 0x58a, HB_Script_Armenian},
+  {0x591, 0x5c7, HB_Script_Hebrew},
+  {0x5d0, 0x5ea, HB_Script_Hebrew},
+  {0x5f0, 0x5f4, HB_Script_Hebrew},
+  {0x606, 0x60b, HB_Script_Arabic},
+  {0x60d, 0x61a, HB_Script_Arabic},
+  {0x61e, 0x61e, HB_Script_Arabic},
+  {0x621, 0x63f, HB_Script_Arabic},
+  {0x641, 0x64a, HB_Script_Arabic},
+  {0x64b, 0x655, HB_Script_Inherited},
+  {0x656, 0x65e, HB_Script_Arabic},
+  {0x66a, 0x66f, HB_Script_Arabic},
+  {0x670, 0x670, HB_Script_Inherited},
+  {0x671, 0x6dc, HB_Script_Arabic},
+  {0x6de, 0x6ff, HB_Script_Arabic},
+  {0x700, 0x70d, HB_Script_Syriac},
+  {0x70f, 0x74a, HB_Script_Syriac},
+  {0x74d, 0x74f, HB_Script_Syriac},
+  {0x750, 0x77f, HB_Script_Arabic},
+  {0x780, 0x7b1, HB_Script_Thaana},
+  {0x901, 0x939, HB_Script_Devanagari},
+  {0x93c, 0x94d, HB_Script_Devanagari},
+  {0x950, 0x950, HB_Script_Devanagari},
+  {0x951, 0x952, HB_Script_Inherited},
+  {0x953, 0x954, HB_Script_Devanagari},
+  {0x958, 0x963, HB_Script_Devanagari},
+  {0x966, 0x96f, HB_Script_Devanagari},
+  {0x971, 0x972, HB_Script_Devanagari},
+  {0x97b, 0x97f, HB_Script_Devanagari},
+  {0x981, 0x983, HB_Script_Bengali},
+  {0x985, 0x98c, HB_Script_Bengali},
+  {0x98f, 0x990, HB_Script_Bengali},
+  {0x993, 0x9a8, HB_Script_Bengali},
+  {0x9aa, 0x9b0, HB_Script_Bengali},
+  {0x9b2, 0x9b2, HB_Script_Bengali},
+  {0x9b6, 0x9b9, HB_Script_Bengali},
+  {0x9bc, 0x9c4, HB_Script_Bengali},
+  {0x9c7, 0x9c8, HB_Script_Bengali},
+  {0x9cb, 0x9ce, HB_Script_Bengali},
+  {0x9d7, 0x9d7, HB_Script_Bengali},
+  {0x9dc, 0x9dd, HB_Script_Bengali},
+  {0x9df, 0x9e3, HB_Script_Bengali},
+  {0x9e6, 0x9fa, HB_Script_Bengali},
+  {0xa01, 0xa03, HB_Script_Gurmukhi},
+  {0xa05, 0xa0a, HB_Script_Gurmukhi},
+  {0xa0f, 0xa10, HB_Script_Gurmukhi},
+  {0xa13, 0xa28, HB_Script_Gurmukhi},
+  {0xa2a, 0xa30, HB_Script_Gurmukhi},
+  {0xa32, 0xa33, HB_Script_Gurmukhi},
+  {0xa35, 0xa36, HB_Script_Gurmukhi},
+  {0xa38, 0xa39, HB_Script_Gurmukhi},
+  {0xa3c, 0xa3c, HB_Script_Gurmukhi},
+  {0xa3e, 0xa42, HB_Script_Gurmukhi},
+  {0xa47, 0xa48, HB_Script_Gurmukhi},
+  {0xa4b, 0xa4d, HB_Script_Gurmukhi},
+  {0xa51, 0xa51, HB_Script_Gurmukhi},
+  {0xa59, 0xa5c, HB_Script_Gurmukhi},
+  {0xa5e, 0xa5e, HB_Script_Gurmukhi},
+  {0xa66, 0xa75, HB_Script_Gurmukhi},
+  {0xa81, 0xa83, HB_Script_Gujarati},
+  {0xa85, 0xa8d, HB_Script_Gujarati},
+  {0xa8f, 0xa91, HB_Script_Gujarati},
+  {0xa93, 0xaa8, HB_Script_Gujarati},
+  {0xaaa, 0xab0, HB_Script_Gujarati},
+  {0xab2, 0xab3, HB_Script_Gujarati},
+  {0xab5, 0xab9, HB_Script_Gujarati},
+  {0xabc, 0xac5, HB_Script_Gujarati},
+  {0xac7, 0xac9, HB_Script_Gujarati},
+  {0xacb, 0xacd, HB_Script_Gujarati},
+  {0xad0, 0xad0, HB_Script_Gujarati},
+  {0xae0, 0xae3, HB_Script_Gujarati},
+  {0xae6, 0xaef, HB_Script_Gujarati},
+  {0xaf1, 0xaf1, HB_Script_Gujarati},
+  {0xb01, 0xb03, HB_Script_Oriya},
+  {0xb05, 0xb0c, HB_Script_Oriya},
+  {0xb0f, 0xb10, HB_Script_Oriya},
+  {0xb13, 0xb28, HB_Script_Oriya},
+  {0xb2a, 0xb30, HB_Script_Oriya},
+  {0xb32, 0xb33, HB_Script_Oriya},
+  {0xb35, 0xb39, HB_Script_Oriya},
+  {0xb3c, 0xb44, HB_Script_Oriya},
+  {0xb47, 0xb48, HB_Script_Oriya},
+  {0xb4b, 0xb4d, HB_Script_Oriya},
+  {0xb56, 0xb57, HB_Script_Oriya},
+  {0xb5c, 0xb5d, HB_Script_Oriya},
+  {0xb5f, 0xb63, HB_Script_Oriya},
+  {0xb66, 0xb71, HB_Script_Oriya},
+  {0xb82, 0xb83, HB_Script_Tamil},
+  {0xb85, 0xb8a, HB_Script_Tamil},
+  {0xb8e, 0xb90, HB_Script_Tamil},
+  {0xb92, 0xb95, HB_Script_Tamil},
+  {0xb99, 0xb9a, HB_Script_Tamil},
+  {0xb9c, 0xb9c, HB_Script_Tamil},
+  {0xb9e, 0xb9f, HB_Script_Tamil},
+  {0xba3, 0xba4, HB_Script_Tamil},
+  {0xba8, 0xbaa, HB_Script_Tamil},
+  {0xbae, 0xbb9, HB_Script_Tamil},
+  {0xbbe, 0xbc2, HB_Script_Tamil},
+  {0xbc6, 0xbc8, HB_Script_Tamil},
+  {0xbca, 0xbcd, HB_Script_Tamil},
+  {0xbd0, 0xbd0, HB_Script_Tamil},
+  {0xbd7, 0xbd7, HB_Script_Tamil},
+  {0xbe6, 0xbfa, HB_Script_Tamil},
+  {0xc01, 0xc03, HB_Script_Telugu},
+  {0xc05, 0xc0c, HB_Script_Telugu},
+  {0xc0e, 0xc10, HB_Script_Telugu},
+  {0xc12, 0xc28, HB_Script_Telugu},
+  {0xc2a, 0xc33, HB_Script_Telugu},
+  {0xc35, 0xc39, HB_Script_Telugu},
+  {0xc3d, 0xc44, HB_Script_Telugu},
+  {0xc46, 0xc48, HB_Script_Telugu},
+  {0xc4a, 0xc4d, HB_Script_Telugu},
+  {0xc55, 0xc56, HB_Script_Telugu},
+  {0xc58, 0xc59, HB_Script_Telugu},
+  {0xc60, 0xc63, HB_Script_Telugu},
+  {0xc66, 0xc6f, HB_Script_Telugu},
+  {0xc78, 0xc7f, HB_Script_Telugu},
+  {0xc82, 0xc83, HB_Script_Kannada},
+  {0xc85, 0xc8c, HB_Script_Kannada},
+  {0xc8e, 0xc90, HB_Script_Kannada},
+  {0xc92, 0xca8, HB_Script_Kannada},
+  {0xcaa, 0xcb3, HB_Script_Kannada},
+  {0xcb5, 0xcb9, HB_Script_Kannada},
+  {0xcbc, 0xcc4, HB_Script_Kannada},
+  {0xcc6, 0xcc8, HB_Script_Kannada},
+  {0xcca, 0xccd, HB_Script_Kannada},
+  {0xcd5, 0xcd6, HB_Script_Kannada},
+  {0xcde, 0xcde, HB_Script_Kannada},
+  {0xce0, 0xce3, HB_Script_Kannada},
+  {0xce6, 0xcef, HB_Script_Kannada},
+  {0xd02, 0xd03, HB_Script_Malayalam},
+  {0xd05, 0xd0c, HB_Script_Malayalam},
+  {0xd0e, 0xd10, HB_Script_Malayalam},
+  {0xd12, 0xd28, HB_Script_Malayalam},
+  {0xd2a, 0xd39, HB_Script_Malayalam},
+  {0xd3d, 0xd44, HB_Script_Malayalam},
+  {0xd46, 0xd48, HB_Script_Malayalam},
+  {0xd4a, 0xd4d, HB_Script_Malayalam},
+  {0xd57, 0xd57, HB_Script_Malayalam},
+  {0xd60, 0xd63, HB_Script_Malayalam},
+  {0xd66, 0xd75, HB_Script_Malayalam},
+  {0xd79, 0xd7f, HB_Script_Malayalam},
+  {0xd82, 0xd83, HB_Script_Sinhala},
+  {0xd85, 0xd96, HB_Script_Sinhala},
+  {0xd9a, 0xdb1, HB_Script_Sinhala},
+  {0xdb3, 0xdbb, HB_Script_Sinhala},
+  {0xdbd, 0xdbd, HB_Script_Sinhala},
+  {0xdc0, 0xdc6, HB_Script_Sinhala},
+  {0xdca, 0xdca, HB_Script_Sinhala},
+  {0xdcf, 0xdd4, HB_Script_Sinhala},
+  {0xdd6, 0xdd6, HB_Script_Sinhala},
+  {0xdd8, 0xddf, HB_Script_Sinhala},
+  {0xdf2, 0xdf4, HB_Script_Sinhala},
+  {0xe01, 0xe3a, HB_Script_Thai},
+  {0xe40, 0xe5b, HB_Script_Thai},
+  {0xe81, 0xe82, HB_Script_Lao},
+  {0xe84, 0xe84, HB_Script_Lao},
+  {0xe87, 0xe88, HB_Script_Lao},
+  {0xe8a, 0xe8a, HB_Script_Lao},
+  {0xe8d, 0xe8d, HB_Script_Lao},
+  {0xe94, 0xe97, HB_Script_Lao},
+  {0xe99, 0xe9f, HB_Script_Lao},
+  {0xea1, 0xea3, HB_Script_Lao},
+  {0xea5, 0xea5, HB_Script_Lao},
+  {0xea7, 0xea7, HB_Script_Lao},
+  {0xeaa, 0xeab, HB_Script_Lao},
+  {0xead, 0xeb9, HB_Script_Lao},
+  {0xebb, 0xebd, HB_Script_Lao},
+  {0xec0, 0xec4, HB_Script_Lao},
+  {0xec6, 0xec6, HB_Script_Lao},
+  {0xec8, 0xecd, HB_Script_Lao},
+  {0xed0, 0xed9, HB_Script_Lao},
+  {0xedc, 0xedd, HB_Script_Lao},
+  {0xf00, 0xf47, HB_Script_Tibetan},
+  {0xf49, 0xf6c, HB_Script_Tibetan},
+  {0xf71, 0xf8b, HB_Script_Tibetan},
+  {0xf90, 0xf97, HB_Script_Tibetan},
+  {0xf99, 0xfbc, HB_Script_Tibetan},
+  {0xfbe, 0xfcc, HB_Script_Tibetan},
+  {0xfce, 0xfd4, HB_Script_Tibetan},
+  {0x1000, 0x1099, HB_Script_Myanmar},
+  {0x109e, 0x109f, HB_Script_Myanmar},
+  {0x10a0, 0x10c5, HB_Script_Georgian},
+  {0x10d0, 0x10fa, HB_Script_Georgian},
+  {0x10fc, 0x10fc, HB_Script_Georgian},
+  {0x1100, 0x1159, HB_Script_Hangul},
+  {0x115f, 0x11a2, HB_Script_Hangul},
+  {0x11a8, 0x11f9, HB_Script_Hangul},
+  {0x1680, 0x169c, HB_Script_Ogham},
+  {0x16a0, 0x16ea, HB_Script_Runic},
+  {0x16ee, 0x16f0, HB_Script_Runic},
+  {0x1780, 0x17dd, HB_Script_Khmer},
+  {0x17e0, 0x17e9, HB_Script_Khmer},
+  {0x17f0, 0x17f9, HB_Script_Khmer},
+  {0x19e0, 0x19ff, HB_Script_Khmer},
+  {0x1d26, 0x1d2a, HB_Script_Greek},
+  {0x1d2b, 0x1d2b, HB_Script_Cyrillic},
+  {0x1d5d, 0x1d61, HB_Script_Greek},
+  {0x1d66, 0x1d6a, HB_Script_Greek},
+  {0x1d78, 0x1d78, HB_Script_Cyrillic},
+  {0x1dbf, 0x1dbf, HB_Script_Greek},
+  {0x1dc0, 0x1de6, HB_Script_Inherited},
+  {0x1dfe, 0x1dff, HB_Script_Inherited},
+  {0x1f00, 0x1f15, HB_Script_Greek},
+  {0x1f18, 0x1f1d, HB_Script_Greek},
+  {0x1f20, 0x1f45, HB_Script_Greek},
+  {0x1f48, 0x1f4d, HB_Script_Greek},
+  {0x1f50, 0x1f57, HB_Script_Greek},
+  {0x1f59, 0x1f59, HB_Script_Greek},
+  {0x1f5b, 0x1f5b, HB_Script_Greek},
+  {0x1f5d, 0x1f5d, HB_Script_Greek},
+  {0x1f5f, 0x1f7d, HB_Script_Greek},
+  {0x1f80, 0x1fb4, HB_Script_Greek},
+  {0x1fb6, 0x1fc4, HB_Script_Greek},
+  {0x1fc6, 0x1fd3, HB_Script_Greek},
+  {0x1fd6, 0x1fdb, HB_Script_Greek},
+  {0x1fdd, 0x1fef, HB_Script_Greek},
+  {0x1ff2, 0x1ff4, HB_Script_Greek},
+  {0x1ff6, 0x1ffe, HB_Script_Greek},
+  {0x200c, 0x200d, HB_Script_Inherited},
+  {0x20d0, 0x20f0, HB_Script_Inherited},
+  {0x2126, 0x2126, HB_Script_Greek},
+  {0x2d00, 0x2d25, HB_Script_Georgian},
+  {0x2de0, 0x2dff, HB_Script_Cyrillic},
+  {0x302a, 0x302f, HB_Script_Inherited},
+  {0x3099, 0x309a, HB_Script_Inherited},
+  {0x3131, 0x318e, HB_Script_Hangul},
+  {0x3200, 0x321e, HB_Script_Hangul},
+  {0x3260, 0x327e, HB_Script_Hangul},
+  {0xa640, 0xa65f, HB_Script_Cyrillic},
+  {0xa662, 0xa673, HB_Script_Cyrillic},
+  {0xa67c, 0xa697, HB_Script_Cyrillic},
+  {0xac00, 0xd7a3, HB_Script_Hangul},
+  {0xfb13, 0xfb17, HB_Script_Armenian},
+  {0xfb1d, 0xfb36, HB_Script_Hebrew},
+  {0xfb38, 0xfb3c, HB_Script_Hebrew},
+  {0xfb3e, 0xfb3e, HB_Script_Hebrew},
+  {0xfb40, 0xfb41, HB_Script_Hebrew},
+  {0xfb43, 0xfb44, HB_Script_Hebrew},
+  {0xfb46, 0xfb4f, HB_Script_Hebrew},
+  {0xfb50, 0xfbb1, HB_Script_Arabic},
+  {0xfbd3, 0xfd3d, HB_Script_Arabic},
+  {0xfd50, 0xfd8f, HB_Script_Arabic},
+  {0xfd92, 0xfdc7, HB_Script_Arabic},
+  {0xfdf0, 0xfdfc, HB_Script_Arabic},
+  {0xfe00, 0xfe0f, HB_Script_Inherited},
+  {0xfe20, 0xfe26, HB_Script_Inherited},
+  {0xfe70, 0xfe74, HB_Script_Arabic},
+  {0xfe76, 0xfefc, HB_Script_Arabic},
+  {0xffa0, 0xffbe, HB_Script_Hangul},
+  {0xffc2, 0xffc7, HB_Script_Hangul},
+  {0xffca, 0xffcf, HB_Script_Hangul},
+  {0xffd2, 0xffd7, HB_Script_Hangul},
+  {0xffda, 0xffdc, HB_Script_Hangul},
+  {0x10140, 0x1018a, HB_Script_Greek},
+  {0x101fd, 0x101fd, HB_Script_Inherited},
+  {0x1d167, 0x1d169, HB_Script_Inherited},
+  {0x1d17b, 0x1d182, HB_Script_Inherited},
+  {0x1d185, 0x1d18b, HB_Script_Inherited},
+  {0x1d1aa, 0x1d1ad, HB_Script_Inherited},
+  {0x1d200, 0x1d245, HB_Script_Greek},
+  {0xe0100, 0xe01ef, HB_Script_Inherited},
+};
+
+static const unsigned script_properties_count = 277;
+
+#endif  // SCRIPT_PROPERTIES_H_
diff --git a/third_party/harfbuzz/contrib/tables/scripts-parse.py b/third_party/harfbuzz/contrib/tables/scripts-parse.py
new file mode 100644
index 0000000..23bac10
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/scripts-parse.py
@@ -0,0 +1,75 @@
+import sys
+from unicode_parse_common import *
+
+# http://www.unicode.org/Public/5.1.0/ucd/Scripts.txt
+
+script_to_harfbuzz = {
+  # This is the list of HB_Script_* at the time of writing
+  'Common': 'HB_Script_Common',
+  'Greek': 'HB_Script_Greek',
+  'Cyrillic': 'HB_Script_Cyrillic',
+  'Armenian': 'HB_Script_Armenian',
+  'Hebrew': 'HB_Script_Hebrew',
+  'Arabic': 'HB_Script_Arabic',
+  'Syriac': 'HB_Script_Syriac',
+  'Thaana': 'HB_Script_Thaana',
+  'Devanagari': 'HB_Script_Devanagari',
+  'Bengali': 'HB_Script_Bengali',
+  'Gurmukhi': 'HB_Script_Gurmukhi',
+  'Gujarati': 'HB_Script_Gujarati',
+  'Oriya': 'HB_Script_Oriya',
+  'Tamil': 'HB_Script_Tamil',
+  'Telugu': 'HB_Script_Telugu',
+  'Kannada': 'HB_Script_Kannada',
+  'Malayalam': 'HB_Script_Malayalam',
+  'Sinhala': 'HB_Script_Sinhala',
+  'Thai': 'HB_Script_Thai',
+  'Lao': 'HB_Script_Lao',
+  'Tibetan': 'HB_Script_Tibetan',
+  'Myanmar': 'HB_Script_Myanmar',
+  'Georgian': 'HB_Script_Georgian',
+  'Hangul': 'HB_Script_Hangul',
+  'Ogham': 'HB_Script_Ogham',
+  'Runic': 'HB_Script_Runic',
+  'Khmer': 'HB_Script_Khmer',
+  'Inherited': 'HB_Script_Inherited',
+}
+
+class ScriptDict(object):
+  def __init__(self, base):
+    self.base = base
+
+  def __getitem__(self, key):
+    r = self.base.get(key, None)
+    if r is None:
+      return 'HB_Script_Common'
+    return r
+
+def main(infile, outfile):
+  ranges = unicode_file_parse(infile,
+                              ScriptDict(script_to_harfbuzz),
+                              'HB_Script_Common')
+  ranges = sort_and_merge(ranges)
+
+  print >>outfile, '// Generated from Unicode script tables\n'
+  print >>outfile, '#ifndef SCRIPT_PROPERTIES_H_'
+  print >>outfile, '#define SCRIPT_PROPERTIES_H_\n'
+  print >>outfile, '#include <stdint.h>'
+  print >>outfile, '#include "harfbuzz-shaper.h"\n'
+  print >>outfile, 'struct script_property {'
+  print >>outfile, '  uint32_t range_start;'
+  print >>outfile, '  uint32_t range_end;'
+  print >>outfile, '  HB_Script script;'
+  print >>outfile, '};\n'
+  print >>outfile, 'static const struct script_property script_properties[] = {'
+  for (start, end, value) in ranges:
+    print >>outfile, '  {0x%x, 0x%x, %s},' % (start, end, value)
+  print >>outfile, '};\n'
+  print >>outfile, 'static const unsigned script_properties_count = %d;\n' % len(ranges)
+  print >>outfile, '#endif  // SCRIPT_PROPERTIES_H_'
+
+if __name__ == '__main__':
+  if len(sys.argv) != 3:
+    print 'Usage: %s <input .txt> <output .h>' % sys.argv[0]
+  else:
+    main(file(sys.argv[1], 'r'), file(sys.argv[2], 'w+'))
diff --git a/third_party/harfbuzz/contrib/tables/unicode_parse_common.py b/third_party/harfbuzz/contrib/tables/unicode_parse_common.py
new file mode 100644
index 0000000..ac26eca
--- /dev/null
+++ b/third_party/harfbuzz/contrib/tables/unicode_parse_common.py
@@ -0,0 +1,70 @@
+def lines_get(f):
+  '''Parse a file like object, removing comments and returning a list of
+     lines.'''
+  def cut_comment(line):
+    first_hash = line.find('#')
+    if first_hash == -1:
+      return line
+    return line[:first_hash]
+
+  return [x for x in [cut_comment(x[:-1]) for x in f.readlines()] if len(x)]
+
+def line_split(line):
+  '''Split a line based on a semicolon separator.'''
+  def normalise(word):
+    return word.lstrip().rstrip()
+  return [normalise(x) for x in line.split(';')]
+
+def codepoints_parse(token):
+  '''Parse a Unicode style code-point range. Return either a single value or a
+     tuple of (start, end) for a range of code-points.'''
+  def fromHex(token):
+    return int(token, 16)
+  parts = token.split('..')
+  if len(parts) == 2:
+    return (fromHex(parts[0]), fromHex(parts[1]))
+  elif len(parts) == 1:
+    return fromHex(parts[0])
+  else:
+    raise ValueError(token)
+
+def unicode_file_parse(input, map, default_value = None):
+  '''Parse a file like object, @input where the first column is a code-point
+     range and the second column is mapped via the given dict, @map.'''
+  ranges = []
+  tokens = [line_split(x) for x in lines_get(input)]
+  for line in tokens:
+    if len(line) == 2:
+      codepoints = codepoints_parse(line[0])
+      value = map[line[1]]
+      if value == default_value:
+        continue
+
+      if type(codepoints) == int:
+        codepoints = (codepoints, codepoints)
+
+      ranges.append((codepoints[0], codepoints[1], value))
+    else:
+      raise ValueError(line)
+
+  return ranges
+
+def sort_and_merge(ranges):
+  '''Given a list of (start, end, value), merge elements where the ranges are
+     continuous and the values are the same.'''
+  output = []
+  ranges.sort()
+  current = None
+  for v in ranges:
+    if current is None:
+      current = v
+      continue
+    if current[1] + 1 == v[0] and current[2] == v[2]:
+      current = (current[0], v[1], v[2])
+    else:
+      output.append(current)
+      current = v
+  if current is not None:
+    output.append(current)
+
+  return output
diff --git a/third_party/harfbuzz/harfbuzz.gyp b/third_party/harfbuzz/harfbuzz.gyp
new file mode 100644
index 0000000..c2ed2f6
--- /dev/null
+++ b/third_party/harfbuzz/harfbuzz.gyp
@@ -0,0 +1,69 @@
+# Copyright (c) 2009 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.
+
+{
+  'targets': [
+    {
+      'target_name': 'harfbuzz',
+      'type': '<(library)',
+      'sources': [
+        'src/harfbuzz-buffer.c',
+        'src/harfbuzz-stream.c',
+        'src/harfbuzz-dump.c',
+        'src/harfbuzz-gdef.c',
+        'src/harfbuzz-gpos.c',
+        'src/harfbuzz-gsub.c',
+        'src/harfbuzz-impl.c',
+        'src/harfbuzz-open.c',
+        'src/harfbuzz-shaper.cpp',
+        'src/harfbuzz-tibetan.c',
+        'src/harfbuzz-khmer.c',
+        'src/harfbuzz-indic.cpp',
+        'src/harfbuzz-hebrew.c',
+        'src/harfbuzz-arabic.c',
+        'src/harfbuzz-hangul.c',
+        'src/harfbuzz-myanmar.c',
+        'src/harfbuzz-thai.c',
+      ],
+      'include_dirs': [
+        'src',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          'src',
+        ],
+      },
+      'dependencies': [
+        '../../build/linux/system.gyp:freetype2',
+      ],
+    },
+    {
+      'target_name': 'harfbuzz_interface',
+      'type': '<(library)',
+      'sources': [
+        'contrib/harfbuzz-freetype.c',
+        'contrib/harfbuzz-unicode.c',
+        'contrib/harfbuzz-unicode-tables.c',
+      ],
+      'include_dirs': [
+        'src',
+        'contrib',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          'contrib',
+        ],
+      },
+      'dependencies': [
+        '../../build/linux/system.gyp:freetype2',
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/third_party/harfbuzz/src/.gitignore b/third_party/harfbuzz/src/.gitignore
new file mode 100644
index 0000000..74de98b
--- /dev/null
+++ b/third_party/harfbuzz/src/.gitignore
@@ -0,0 +1,7 @@
+*.o
+*.lo
+Makefile
+*.la
+.deps
+.libs
+*~
diff --git a/third_party/harfbuzz/src/Makefile.am b/third_party/harfbuzz/src/Makefile.am
new file mode 100644
index 0000000..2b0fb1d
--- /dev/null
+++ b/third_party/harfbuzz/src/Makefile.am
@@ -0,0 +1,68 @@
+## Process this file with automake to produce Makefile.in
+
+noinst_LTLIBRARIES = libharfbuzz-1.la
+
+MAINSOURCES =  \
+	harfbuzz-buffer.c \
+	harfbuzz-stream.c \
+	harfbuzz-dump.c \
+	harfbuzz-gdef.c \
+	harfbuzz-gpos.c \
+	harfbuzz-gsub.c \
+	harfbuzz-impl.c \
+	harfbuzz-open.c \
+	harfbuzz-shaper.cpp \
+	harfbuzz-tibetan.c \
+	harfbuzz-khmer.c \
+	harfbuzz-indic.cpp \
+	harfbuzz-hebrew.c \
+	harfbuzz-arabic.c \
+	harfbuzz-hangul.c \
+	harfbuzz-myanmar.c \
+	harfbuzz-thai.c
+
+EXTRA_SOURCES = harfbuzz.c
+
+PUBLICHEADERS = \
+	harfbuzz.h \
+	harfbuzz-buffer.h \
+	harfbuzz-dump.h \
+	harfbuzz-gdef.h \
+	harfbuzz-gpos.h \
+	harfbuzz-gsub.h \
+	harfbuzz-open.h \
+	harfbuzz-global.h \
+	harfbuzz-external.h \
+	harfbuzz-shaper.h \
+	harfbuzz-stream.h
+
+PRIVATEHEADERS = \
+	harfbuzz-impl.h \
+	harfbuzz-buffer-private.h \
+	harfbuzz-stream-private.h \
+	harfbuzz-gdef-private.h \
+	harfbuzz-gpos-private.h \
+	harfbuzz-gsub-private.h \
+	harfbuzz-open-private.h \
+	harfbuzz-shaper-private.h
+
+libharfbuzz_1_la_SOURCES = \
+	$(MAINSOURCES) \
+	$(PUBLICHEADERS) \
+	$(PRIVATEHEADERS)
+
+#noinst_PROGRAMS = harfbuzz-dump
+#
+#harfbuzz_dump_SOURCES = 	\
+#	harfbuzz-dump-main.c
+#
+#harfbuzz_dump_LDADD = 		\
+#	libharfbuzz-1.la
+
+EXTRA_DIST = 		\
+	README		\
+	COPYING.FTL	\
+	COPYING.GPL	\
+	COPYING		\
+	$(EXTRA_SOURCES)
+
diff --git a/third_party/harfbuzz/src/harfbuzz-arabic.c b/third_party/harfbuzz/src/harfbuzz-arabic.c
new file mode 100644
index 0000000..ce2ca6c
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-arabic.c
@@ -0,0 +1,1145 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-shaper-private.h"
+
+#include <assert.h>
+
+static const HB_UChar16 ReplacementCharacter = 0xfffd;
+
+typedef struct {
+    unsigned char shape;
+    unsigned char justification;
+} HB_ArabicProperties;
+
+typedef enum {
+    XIsolated,
+    XFinal,
+    XInitial,
+    XMedial,
+    /* intermediate state */
+    XCausing
+} ArabicShape;
+
+/*
+// these groups correspond to the groups defined in the Unicode standard.
+// Some of these groups are equal with regards to both joining and line breaking behaviour,
+// and thus have the same enum value
+//
+// I'm not sure the mapping of syriac to arabic enums is correct with regards to justification, but as
+// I couldn't find any better document I'll hope for the best.
+*/
+typedef enum {
+    /* NonJoining */
+    ArabicNone,
+    ArabicSpace,
+    /* Transparent */
+    Transparent,
+    /* Causing */
+    Center,
+    Kashida,
+
+    /* Arabic */
+    /* Dual */
+    Beh,
+    Noon,
+    Meem = Noon,
+    Heh = Noon,
+    KnottedHeh = Noon,
+    HehGoal = Noon,
+    SwashKaf = Noon,
+    Yeh,
+    Hah,
+    Seen,
+    Sad = Seen,
+    Tah,
+    Kaf = Tah,
+    Gaf = Tah,
+    Lam = Tah,
+    Ain,
+    Feh = Ain,
+    Qaf = Ain,
+    /* Right */
+    Alef,
+    Waw,
+    Dal,
+    TehMarbuta = Dal,
+    Reh,
+    HamzaOnHehGoal,
+    YehWithTail = HamzaOnHehGoal,
+    YehBarre = HamzaOnHehGoal,
+
+    /* Syriac */
+    /* Dual */
+    Beth = Beh,
+    Gamal = Ain,
+    Heth = Noon,
+    Teth = Hah,
+    Yudh = Noon,
+    Kaph = Noon,
+    Lamadh = Lam,
+    Mim = Noon,
+    Nun = Noon,
+    Semakh = Noon,
+    FinalSemakh = Noon,
+    SyriacE = Ain,
+    Pe = Ain,
+    ReversedPe = Hah,
+    Qaph = Noon,
+    Shin = Noon,
+    Fe = Ain,
+
+    /* Right */
+    Alaph = Alef,
+    Dalath = Dal,
+    He = Dal,
+    SyriacWaw = Waw,
+    Zain = Alef,
+    YudhHe = Waw,
+    Sadhe = HamzaOnHehGoal,
+    Taw = Dal,
+
+    /* Compiler bug? Otherwise ArabicGroupsEnd would be equal to Dal + 1. */
+    Dummy = HamzaOnHehGoal,
+    ArabicGroupsEnd
+} ArabicGroup;
+
+static const unsigned char arabic_group[0x150] = {
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+    Transparent, Transparent, Transparent, Transparent,
+    Transparent, Transparent, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+    ArabicNone, ArabicNone, Alef, Alef,
+    Waw, Alef, Yeh, Alef,
+    Beh, TehMarbuta, Beh, Beh,
+    Hah, Hah, Hah, Dal,
+
+    Dal, Reh, Reh, Seen,
+    Seen, Sad, Sad, Tah,
+    Tah, Ain, Ain, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+    /* 0x640 */
+    Kashida, Feh, Qaf, Kaf,
+    Lam, Meem, Noon, Heh,
+    Waw, Yeh, Yeh, Transparent,
+    Transparent, Transparent, Transparent, Transparent,
+
+    Transparent, Transparent, Transparent, Transparent,
+    Transparent, Transparent, Transparent, Transparent,
+    Transparent, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, Beh, Qaf,
+
+    Transparent, Alef, Alef, Alef,
+    ArabicNone, Alef, Waw, Waw,
+    Yeh, Beh, Beh, Beh,
+    Beh, Beh, Beh, Beh,
+
+    /* 0x680 */
+    Beh, Hah, Hah, Hah,
+    Hah, Hah, Hah, Hah,
+    Dal, Dal, Dal, Dal,
+    Dal, Dal, Dal, Dal,
+
+    Dal, Reh, Reh, Reh,
+    Reh, Reh, Reh, Reh,
+    Reh, Reh, Seen, Seen,
+    Seen, Sad, Sad, Tah,
+
+    Ain, Feh, Feh, Feh,
+    Feh, Feh, Feh, Qaf,
+    Qaf, Gaf, SwashKaf, Gaf,
+    Kaf, Kaf, Kaf, Gaf,
+
+    Gaf, Gaf, Gaf, Gaf,
+    Gaf, Lam, Lam, Lam,
+    Lam, Noon, Noon, Noon,
+    Noon, Noon, KnottedHeh, Hah,
+
+    /* 0x6c0 */
+    TehMarbuta, HehGoal, HamzaOnHehGoal, HamzaOnHehGoal,
+    Waw, Waw, Waw, Waw,
+    Waw, Waw, Waw, Waw,
+    Yeh, YehWithTail, Yeh, Waw,
+
+    Yeh, Yeh, YehBarre, YehBarre,
+    ArabicNone, TehMarbuta, Transparent, Transparent,
+    Transparent, Transparent, Transparent, Transparent,
+    Transparent, ArabicNone, ArabicNone, Transparent,
+
+    Transparent, Transparent, Transparent, Transparent,
+    Transparent, ArabicNone, ArabicNone, Transparent,
+    Transparent, ArabicNone, Transparent, Transparent,
+    Transparent, Transparent, Dal, Reh,
+
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, Seen, Sad,
+    Ain, ArabicNone, ArabicNone, KnottedHeh,
+
+    /* 0x700 */
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
+
+    Alaph, Transparent, Beth, Gamal,
+    Gamal, Dalath, Dalath, He,
+    SyriacWaw, Zain, Heth, Teth,
+    Teth, Yudh, YudhHe, Kaph,
+
+    Lamadh, Mim, Nun, Semakh,
+    FinalSemakh, SyriacE, Pe, ReversedPe,
+    Sadhe, Qaph, Dalath, Shin,
+    Taw, Beth, Gamal, Dalath,
+
+    Transparent, Transparent, Transparent, Transparent,
+    Transparent, Transparent, Transparent, Transparent,
+    Transparent, Transparent, Transparent, Transparent,
+    Transparent, Transparent, Transparent, Transparent,
+
+    Transparent, Transparent, Transparent, Transparent,
+    Transparent, Transparent, Transparent, Transparent,
+    Transparent, Transparent, Transparent, ArabicNone,
+    ArabicNone, Zain, Kaph, Fe,
+};
+
+static ArabicGroup arabicGroup(unsigned short uc)
+{
+    if (uc >= 0x0600 && uc < 0x750)
+        return (ArabicGroup) arabic_group[uc-0x600];
+    else if (uc == 0x200d)
+        return Center;
+    else if (HB_GetUnicodeCharCategory(uc) == HB_Separator_Space)
+        return ArabicSpace;
+    else
+        return ArabicNone;
+}
+
+
+/*
+   Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
+   arabic).
+
+   Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
+   transparent joining is not encoded in HB_UChar16::joining(), but applies to all combining marks and format marks.
+
+   Right join-causing: dual + center
+   Left join-causing: dual + right + center
+
+   Rules are as follows (for a string already in visual order, as we have it here):
+
+   R1 Transparent characters do not affect joining behaviour.
+   R2 A right joining character, that has a right join-causing char on the right will get form XRight
+   (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
+   Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
+   R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
+             the right will get form XMedial
+   R5  A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
+         will get form XRight
+   R6 A dual joining character, that has a  left join causing char on the left, and no right join causing char on the right
+         will get form XLeft
+   R7 Otherwise the character will get form XIsolated
+
+   Additionally we have to do the minimal ligature support for lam-alef ligatures:
+
+   L1 Transparent characters do not affect ligature behaviour.
+   L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
+   L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
+
+   The state table below handles rules R1-R7.
+*/
+
+typedef enum {
+    JNone,
+    JCausing,
+    JDual,
+    JRight,
+    JTransparent
+} Joining;
+
+static const Joining joining_for_group[ArabicGroupsEnd] = {
+    /* NonJoining */
+    JNone, /* ArabicNone */
+    JNone, /* ArabicSpace */
+    /* Transparent */
+    JTransparent, /* Transparent */
+    /* Causing */
+    JCausing, /* Center */
+    JCausing, /* Kashida */
+    /* Dual */
+    JDual, /* Beh */
+    JDual, /* Noon */
+    JDual, /* Yeh */
+    JDual, /* Hah */
+    JDual, /* Seen */
+    JDual, /* Tah */
+    JDual, /* Ain */
+    /* Right */
+    JRight, /* Alef */
+    JRight, /* Waw */
+    JRight, /* Dal */
+    JRight, /* Reh */
+    JRight  /* HamzaOnHehGoal */
+};
+
+
+typedef struct {
+    ArabicShape form1;
+    ArabicShape form2;
+} JoiningPair;
+
+static const JoiningPair joining_table[5][4] =
+/* None, Causing, Dual, Right */
+{
+    { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XInitial }, { XIsolated, XIsolated } }, /* XIsolated */
+    { { XFinal, XIsolated }, { XFinal, XCausing }, { XFinal, XInitial }, { XFinal, XIsolated } }, /* XFinal */
+    { { XIsolated, XIsolated }, { XInitial, XCausing }, { XInitial, XMedial }, { XInitial, XFinal } }, /* XInitial */
+    { { XFinal, XIsolated }, { XMedial, XCausing }, { XMedial, XMedial }, { XMedial, XFinal } }, /* XMedial */
+    { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XMedial }, { XIsolated, XFinal } }, /* XCausing */
+};
+
+
+/*
+According to http://www.microsoft.com/middleeast/Arabicdev/IE6/KBase.asp
+
+1. Find the priority of the connecting opportunities in each word
+2. Add expansion at the highest priority connection opportunity
+3. If more than one connection opportunity have the same highest value,
+   use the opportunity closest to the end of the word.
+
+Following is a chart that provides the priority for connection
+opportunities and where expansion occurs. The character group names
+are those in table 6.6 of the UNICODE 2.0 book.
+
+
+PrioritY        Glyph                   Condition                                       Kashida Location
+
+Arabic_Kashida        User inserted Kashida   The user entered a Kashida in a position.       After the user
+                (Shift+j or Shift+[E with hat])    Thus, it is the highest priority to insert an   inserted kashida
+                                        automatic kashida.
+
+Arabic_Seen        Seen, Sad               Connecting to the next character.               After the character.
+                                        (Initial or medial form).
+
+Arabic_HaaDal        Teh Marbutah, Haa, Dal  Connecting to previous character.               Before the final form
+                                                                                        of these characters.
+
+Arabic_Alef     Alef, Tah, Lam,         Connecting to previous character.               Before the final form
+                Kaf and Gaf                                                             of these characters.
+
+Arabic_BaRa     Reh, Yeh                Connected to medial Beh                         Before preceding medial Baa
+
+Arabic_Waw        Waw, Ain, Qaf, Feh      Connecting to previous character.               Before the final form of
+                                                                                        these characters.
+
+Arabic_Normal   Other connecting        Connecting to previous character.               Before the final form
+                characters                                                              of these characters.
+
+
+
+This seems to imply that we have at most one kashida point per arabic word.
+
+*/
+
+static void getArabicProperties(const unsigned short *chars, int len, HB_ArabicProperties *properties)
+{
+/*     qDebug("arabicSyriacOpenTypeShape: properties:"); */
+    int lastPos = 0;
+    int lastGroup = ArabicNone;
+    int i = 0;
+
+    ArabicGroup group = arabicGroup(chars[0]);
+    Joining j = joining_for_group[group];
+    ArabicShape shape = joining_table[XIsolated][j].form2;
+    properties[0].justification = HB_NoJustification;
+
+    for (i = 1; i < len; ++i) {
+        /* #### fix handling for spaces and punktuation */
+        properties[i].justification = HB_NoJustification;
+
+        group = arabicGroup(chars[i]);
+        j = joining_for_group[group];
+
+        if (j == JTransparent) {
+            properties[i].shape = XIsolated;
+            continue;
+        }
+
+        properties[lastPos].shape = joining_table[shape][j].form1;
+        shape = joining_table[shape][j].form2;
+
+        switch(lastGroup) {
+        case Seen:
+            if (properties[lastPos].shape == XInitial || properties[lastPos].shape == XMedial)
+                properties[i-1].justification = HB_Arabic_Seen;
+            break;
+        case Hah:
+            if (properties[lastPos].shape == XFinal)
+                properties[lastPos-1].justification = HB_Arabic_HaaDal;
+            break;
+        case Alef:
+            if (properties[lastPos].shape == XFinal)
+                properties[lastPos-1].justification = HB_Arabic_Alef;
+            break;
+        case Ain:
+            if (properties[lastPos].shape == XFinal)
+                properties[lastPos-1].justification = HB_Arabic_Waw;
+            break;
+        case Noon:
+            if (properties[lastPos].shape == XFinal)
+                properties[lastPos-1].justification = HB_Arabic_Normal;
+            break;
+        case ArabicNone:
+            break;
+
+        default:
+            assert(FALSE);
+        }
+
+        lastGroup = ArabicNone;
+
+        switch(group) {
+        case ArabicNone:
+        case Transparent:
+        /* ### Center should probably be treated as transparent when it comes to justification. */
+        case Center:
+            break;
+        case ArabicSpace:
+            properties[i].justification = HB_Arabic_Space;
+            break;
+        case Kashida:
+            properties[i].justification = HB_Arabic_Kashida;
+            break;
+        case Seen:
+            lastGroup = Seen;
+            break;
+
+        case Hah:
+        case Dal:
+            lastGroup = Hah;
+            break;
+
+        case Alef:
+        case Tah:
+            lastGroup = Alef;
+            break;
+
+        case Yeh:
+        case Reh:
+            if (properties[lastPos].shape == XMedial && arabicGroup(chars[lastPos]) == Beh)
+                properties[lastPos-1].justification = HB_Arabic_BaRa;
+            break;
+
+        case Ain:
+        case Waw:
+            lastGroup = Ain;
+            break;
+
+        case Noon:
+        case Beh:
+        case HamzaOnHehGoal:
+            lastGroup = Noon;
+            break;
+        case ArabicGroupsEnd:
+            assert(FALSE);
+        }
+
+        lastPos = i;
+    }
+    properties[lastPos].shape = joining_table[shape][JNone].form1;
+
+
+    /*
+     for (int i = 0; i < len; ++i)
+         qDebug("arabic properties(%d): uc=%x shape=%d, justification=%d", i, chars[i], properties[i].shape, properties[i].justification);
+    */
+}
+
+static Joining getNkoJoining(unsigned short uc)
+{
+    if (uc < 0x7ca)
+        return JNone;
+    if (uc <= 0x7ea)
+        return JDual;
+    if (uc <= 0x7f3)
+        return JTransparent;
+    if (uc <= 0x7f9)
+        return JNone;
+    if (uc == 0x7fa)
+        return JCausing;
+    return JNone;
+}
+
+static void getNkoProperties(const unsigned short *chars, int len, HB_ArabicProperties *properties)
+{
+    int lastPos = 0;
+    int i = 0;
+
+    Joining j = getNkoJoining(chars[0]);
+    ArabicShape shape = joining_table[XIsolated][j].form2;
+    properties[0].justification = HB_NoJustification;
+
+    for (i = 1; i < len; ++i) {
+        properties[i].justification = (HB_GetUnicodeCharCategory(chars[i]) == HB_Separator_Space) ?
+                                      ArabicSpace : ArabicNone;
+
+        j = getNkoJoining(chars[i]);
+
+        if (j == JTransparent) {
+            properties[i].shape = XIsolated;
+            continue;
+        }
+
+        properties[lastPos].shape = joining_table[shape][j].form1;
+        shape = joining_table[shape][j].form2;
+
+
+        lastPos = i;
+    }
+    properties[lastPos].shape = joining_table[shape][JNone].form1;
+
+
+    /*
+     for (int i = 0; i < len; ++i)
+         qDebug("nko properties(%d): uc=%x shape=%d, justification=%d", i, chars[i], properties[i].shape, properties[i].justification);
+    */
+}
+
+/*
+// The unicode to unicode shaping codec.
+// does only presentation forms B at the moment, but that should be enough for
+// simple display
+*/
+static const hb_uint16 arabicUnicodeMapping[256][2] = {
+    /* base of shaped forms, and number-1 of them (0 for non shaping,
+       1 for right binding and 3 for dual binding */
+
+    /* These are just the glyphs available in Unicode,
+       some characters are in R class, but have no glyphs in Unicode. */
+
+    { 0x0600, 0 }, /* 0x0600 */
+    { 0x0601, 0 }, /* 0x0601 */
+    { 0x0602, 0 }, /* 0x0602 */
+    { 0x0603, 0 }, /* 0x0603 */
+    { 0x0604, 0 }, /* 0x0604 */
+    { 0x0605, 0 }, /* 0x0605 */
+    { 0x0606, 0 }, /* 0x0606 */
+    { 0x0607, 0 }, /* 0x0607 */
+    { 0x0608, 0 }, /* 0x0608 */
+    { 0x0609, 0 }, /* 0x0609 */
+    { 0x060A, 0 }, /* 0x060A */
+    { 0x060B, 0 }, /* 0x060B */
+    { 0x060C, 0 }, /* 0x060C */
+    { 0x060D, 0 }, /* 0x060D */
+    { 0x060E, 0 }, /* 0x060E */
+    { 0x060F, 0 }, /* 0x060F */
+
+    { 0x0610, 0 }, /* 0x0610 */
+    { 0x0611, 0 }, /* 0x0611 */
+    { 0x0612, 0 }, /* 0x0612 */
+    { 0x0613, 0 }, /* 0x0613 */
+    { 0x0614, 0 }, /* 0x0614 */
+    { 0x0615, 0 }, /* 0x0615 */
+    { 0x0616, 0 }, /* 0x0616 */
+    { 0x0617, 0 }, /* 0x0617 */
+    { 0x0618, 0 }, /* 0x0618 */
+    { 0x0619, 0 }, /* 0x0619 */
+    { 0x061A, 0 }, /* 0x061A */
+    { 0x061B, 0 }, /* 0x061B */
+    { 0x061C, 0 }, /* 0x061C */
+    { 0x061D, 0 }, /* 0x061D */
+    { 0x061E, 0 }, /* 0x061E */
+    { 0x061F, 0 }, /* 0x061F */
+
+    { 0x0620, 0 }, /* 0x0620 */
+    { 0xFE80, 0 }, /* 0x0621            HAMZA */
+    { 0xFE81, 1 }, /* 0x0622    R       ALEF WITH MADDA ABOVE */
+    { 0xFE83, 1 }, /* 0x0623    R       ALEF WITH HAMZA ABOVE */
+    { 0xFE85, 1 }, /* 0x0624    R       WAW WITH HAMZA ABOVE */
+    { 0xFE87, 1 }, /* 0x0625    R       ALEF WITH HAMZA BELOW */
+    { 0xFE89, 3 }, /* 0x0626    D       YEH WITH HAMZA ABOVE */
+    { 0xFE8D, 1 }, /* 0x0627    R       ALEF */
+    { 0xFE8F, 3 }, /* 0x0628    D       BEH */
+    { 0xFE93, 1 }, /* 0x0629    R       TEH MARBUTA */
+    { 0xFE95, 3 }, /* 0x062A    D       TEH */
+    { 0xFE99, 3 }, /* 0x062B    D       THEH */
+    { 0xFE9D, 3 }, /* 0x062C    D       JEEM */
+    { 0xFEA1, 3 }, /* 0x062D    D       HAH */
+    { 0xFEA5, 3 }, /* 0x062E    D       KHAH */
+    { 0xFEA9, 1 }, /* 0x062F    R       DAL */
+
+    { 0xFEAB, 1 }, /* 0x0630    R       THAL */
+    { 0xFEAD, 1 }, /* 0x0631    R       REH */
+    { 0xFEAF, 1 }, /* 0x0632    R       ZAIN */
+    { 0xFEB1, 3 }, /* 0x0633    D       SEEN */
+    { 0xFEB5, 3 }, /* 0x0634    D       SHEEN */
+    { 0xFEB9, 3 }, /* 0x0635    D       SAD */
+    { 0xFEBD, 3 }, /* 0x0636    D       DAD */
+    { 0xFEC1, 3 }, /* 0x0637    D       TAH */
+    { 0xFEC5, 3 }, /* 0x0638    D       ZAH */
+    { 0xFEC9, 3 }, /* 0x0639    D       AIN */
+    { 0xFECD, 3 }, /* 0x063A    D       GHAIN */
+    { 0x063B, 0 }, /* 0x063B */
+    { 0x063C, 0 }, /* 0x063C */
+    { 0x063D, 0 }, /* 0x063D */
+    { 0x063E, 0 }, /* 0x063E */
+    { 0x063F, 0 }, /* 0x063F */
+
+    { 0x0640, 0 }, /* 0x0640    C       TATWEEL // ### Join Causing, only one glyph */
+    { 0xFED1, 3 }, /* 0x0641    D       FEH */
+    { 0xFED5, 3 }, /* 0x0642    D       QAF */
+    { 0xFED9, 3 }, /* 0x0643    D       KAF */
+    { 0xFEDD, 3 }, /* 0x0644    D       LAM */
+    { 0xFEE1, 3 }, /* 0x0645    D       MEEM */
+    { 0xFEE5, 3 }, /* 0x0646    D       NOON */
+    { 0xFEE9, 3 }, /* 0x0647    D       HEH */
+    { 0xFEED, 1 }, /* 0x0648    R       WAW */
+    { 0x0649, 3 }, /* 0x0649            ALEF MAKSURA // ### Dual, glyphs not consecutive, handle in code. */
+    { 0xFEF1, 3 }, /* 0x064A    D       YEH */
+    { 0x064B, 0 }, /* 0x064B */
+    { 0x064C, 0 }, /* 0x064C */
+    { 0x064D, 0 }, /* 0x064D */
+    { 0x064E, 0 }, /* 0x064E */
+    { 0x064F, 0 }, /* 0x064F */
+
+    { 0x0650, 0 }, /* 0x0650 */
+    { 0x0651, 0 }, /* 0x0651 */
+    { 0x0652, 0 }, /* 0x0652 */
+    { 0x0653, 0 }, /* 0x0653 */
+    { 0x0654, 0 }, /* 0x0654 */
+    { 0x0655, 0 }, /* 0x0655 */
+    { 0x0656, 0 }, /* 0x0656 */
+    { 0x0657, 0 }, /* 0x0657 */
+    { 0x0658, 0 }, /* 0x0658 */
+    { 0x0659, 0 }, /* 0x0659 */
+    { 0x065A, 0 }, /* 0x065A */
+    { 0x065B, 0 }, /* 0x065B */
+    { 0x065C, 0 }, /* 0x065C */
+    { 0x065D, 0 }, /* 0x065D */
+    { 0x065E, 0 }, /* 0x065E */
+    { 0x065F, 0 }, /* 0x065F */
+
+    { 0x0660, 0 }, /* 0x0660 */
+    { 0x0661, 0 }, /* 0x0661 */
+    { 0x0662, 0 }, /* 0x0662 */
+    { 0x0663, 0 }, /* 0x0663 */
+    { 0x0664, 0 }, /* 0x0664 */
+    { 0x0665, 0 }, /* 0x0665 */
+    { 0x0666, 0 }, /* 0x0666 */
+    { 0x0667, 0 }, /* 0x0667 */
+    { 0x0668, 0 }, /* 0x0668 */
+    { 0x0669, 0 }, /* 0x0669 */
+    { 0x066A, 0 }, /* 0x066A */
+    { 0x066B, 0 }, /* 0x066B */
+    { 0x066C, 0 }, /* 0x066C */
+    { 0x066D, 0 }, /* 0x066D */
+    { 0x066E, 0 }, /* 0x066E */
+    { 0x066F, 0 }, /* 0x066F */
+
+    { 0x0670, 0 }, /* 0x0670 */
+    { 0xFB50, 1 }, /* 0x0671    R       ALEF WASLA */
+    { 0x0672, 0 }, /* 0x0672 */
+    { 0x0673, 0 }, /* 0x0673 */
+    { 0x0674, 0 }, /* 0x0674 */
+    { 0x0675, 0 }, /* 0x0675 */
+    { 0x0676, 0 }, /* 0x0676 */
+    { 0x0677, 0 }, /* 0x0677 */
+    { 0x0678, 0 }, /* 0x0678 */
+    { 0xFB66, 3 }, /* 0x0679    D       TTEH */
+    { 0xFB5E, 3 }, /* 0x067A    D       TTEHEH */
+    { 0xFB52, 3 }, /* 0x067B    D       BEEH */
+    { 0x067C, 0 }, /* 0x067C */
+    { 0x067D, 0 }, /* 0x067D */
+    { 0xFB56, 3 }, /* 0x067E    D       PEH */
+    { 0xFB62, 3 }, /* 0x067F    D       TEHEH */
+
+    { 0xFB5A, 3 }, /* 0x0680    D       BEHEH */
+    { 0x0681, 0 }, /* 0x0681 */
+    { 0x0682, 0 }, /* 0x0682 */
+    { 0xFB76, 3 }, /* 0x0683    D       NYEH */
+    { 0xFB72, 3 }, /* 0x0684    D       DYEH */
+    { 0x0685, 0 }, /* 0x0685 */
+    { 0xFB7A, 3 }, /* 0x0686    D       TCHEH */
+    { 0xFB7E, 3 }, /* 0x0687    D       TCHEHEH */
+    { 0xFB88, 1 }, /* 0x0688    R       DDAL */
+    { 0x0689, 0 }, /* 0x0689 */
+    { 0x068A, 0 }, /* 0x068A */
+    { 0x068B, 0 }, /* 0x068B */
+    { 0xFB84, 1 }, /* 0x068C    R       DAHAL */
+    { 0xFB82, 1 }, /* 0x068D    R       DDAHAL */
+    { 0xFB86, 1 }, /* 0x068E    R       DUL */
+    { 0x068F, 0 }, /* 0x068F */
+
+    { 0x0690, 0 }, /* 0x0690 */
+    { 0xFB8C, 1 }, /* 0x0691    R       RREH */
+    { 0x0692, 0 }, /* 0x0692 */
+    { 0x0693, 0 }, /* 0x0693 */
+    { 0x0694, 0 }, /* 0x0694 */
+    { 0x0695, 0 }, /* 0x0695 */
+    { 0x0696, 0 }, /* 0x0696 */
+    { 0x0697, 0 }, /* 0x0697 */
+    { 0xFB8A, 1 }, /* 0x0698    R       JEH */
+    { 0x0699, 0 }, /* 0x0699 */
+    { 0x069A, 0 }, /* 0x069A */
+    { 0x069B, 0 }, /* 0x069B */
+    { 0x069C, 0 }, /* 0x069C */
+    { 0x069D, 0 }, /* 0x069D */
+    { 0x069E, 0 }, /* 0x069E */
+    { 0x069F, 0 }, /* 0x069F */
+
+    { 0x06A0, 0 }, /* 0x06A0 */
+    { 0x06A1, 0 }, /* 0x06A1 */
+    { 0x06A2, 0 }, /* 0x06A2 */
+    { 0x06A3, 0 }, /* 0x06A3 */
+    { 0xFB6A, 3 }, /* 0x06A4    D       VEH */
+    { 0x06A5, 0 }, /* 0x06A5 */
+    { 0xFB6E, 3 }, /* 0x06A6    D       PEHEH */
+    { 0x06A7, 0 }, /* 0x06A7 */
+    { 0x06A8, 0 }, /* 0x06A8 */
+    { 0xFB8E, 3 }, /* 0x06A9    D       KEHEH */
+    { 0x06AA, 0 }, /* 0x06AA */
+    { 0x06AB, 0 }, /* 0x06AB */
+    { 0x06AC, 0 }, /* 0x06AC */
+    { 0xFBD3, 3 }, /* 0x06AD    D       NG */
+    { 0x06AE, 0 }, /* 0x06AE */
+    { 0xFB92, 3 }, /* 0x06AF    D       GAF */
+
+    { 0x06B0, 0 }, /* 0x06B0 */
+    { 0xFB9A, 3 }, /* 0x06B1    D       NGOEH */
+    { 0x06B2, 0 }, /* 0x06B2 */
+    { 0xFB96, 3 }, /* 0x06B3    D       GUEH */
+    { 0x06B4, 0 }, /* 0x06B4 */
+    { 0x06B5, 0 }, /* 0x06B5 */
+    { 0x06B6, 0 }, /* 0x06B6 */
+    { 0x06B7, 0 }, /* 0x06B7 */
+    { 0x06B8, 0 }, /* 0x06B8 */
+    { 0x06B9, 0 }, /* 0x06B9 */
+    { 0xFB9E, 1 }, /* 0x06BA    R       NOON GHUNNA */
+    { 0xFBA0, 3 }, /* 0x06BB    D       RNOON */
+    { 0x06BC, 0 }, /* 0x06BC */
+    { 0x06BD, 0 }, /* 0x06BD */
+    { 0xFBAA, 3 }, /* 0x06BE    D       HEH DOACHASHMEE */
+    { 0x06BF, 0 }, /* 0x06BF */
+
+    { 0xFBA4, 1 }, /* 0x06C0    R       HEH WITH YEH ABOVE */
+    { 0xFBA6, 3 }, /* 0x06C1    D       HEH GOAL */
+    { 0x06C2, 0 }, /* 0x06C2 */
+    { 0x06C3, 0 }, /* 0x06C3 */
+    { 0x06C4, 0 }, /* 0x06C4 */
+    { 0xFBE0, 1 }, /* 0x06C5    R       KIRGHIZ OE */
+    { 0xFBD9, 1 }, /* 0x06C6    R       OE */
+    { 0xFBD7, 1 }, /* 0x06C7    R       U */
+    { 0xFBDB, 1 }, /* 0x06C8    R       YU */
+    { 0xFBE2, 1 }, /* 0x06C9    R       KIRGHIZ YU */
+    { 0x06CA, 0 }, /* 0x06CA */
+    { 0xFBDE, 1 }, /* 0x06CB    R       VE */
+    { 0xFBFC, 3 }, /* 0x06CC    D       FARSI YEH */
+    { 0x06CD, 0 }, /* 0x06CD */
+    { 0x06CE, 0 }, /* 0x06CE */
+    { 0x06CF, 0 }, /* 0x06CF */
+
+    { 0xFBE4, 3 }, /* 0x06D0    D       E */
+    { 0x06D1, 0 }, /* 0x06D1 */
+    { 0xFBAE, 1 }, /* 0x06D2    R       YEH BARREE */
+    { 0xFBB0, 1 }, /* 0x06D3    R       YEH BARREE WITH HAMZA ABOVE */
+    { 0x06D4, 0 }, /* 0x06D4 */
+    { 0x06D5, 0 }, /* 0x06D5 */
+    { 0x06D6, 0 }, /* 0x06D6 */
+    { 0x06D7, 0 }, /* 0x06D7 */
+    { 0x06D8, 0 }, /* 0x06D8 */
+    { 0x06D9, 0 }, /* 0x06D9 */
+    { 0x06DA, 0 }, /* 0x06DA */
+    { 0x06DB, 0 }, /* 0x06DB */
+    { 0x06DC, 0 }, /* 0x06DC */
+    { 0x06DD, 0 }, /* 0x06DD */
+    { 0x06DE, 0 }, /* 0x06DE */
+    { 0x06DF, 0 }, /* 0x06DF */
+
+    { 0x06E0, 0 }, /* 0x06E0 */
+    { 0x06E1, 0 }, /* 0x06E1 */
+    { 0x06E2, 0 }, /* 0x06E2 */
+    { 0x06E3, 0 }, /* 0x06E3 */
+    { 0x06E4, 0 }, /* 0x06E4 */
+    { 0x06E5, 0 }, /* 0x06E5 */
+    { 0x06E6, 0 }, /* 0x06E6 */
+    { 0x06E7, 0 }, /* 0x06E7 */
+    { 0x06E8, 0 }, /* 0x06E8 */
+    { 0x06E9, 0 }, /* 0x06E9 */
+    { 0x06EA, 0 }, /* 0x06EA */
+    { 0x06EB, 0 }, /* 0x06EB */
+    { 0x06EC, 0 }, /* 0x06EC */
+    { 0x06ED, 0 }, /* 0x06ED */
+    { 0x06EE, 0 }, /* 0x06EE */
+    { 0x06EF, 0 }, /* 0x06EF */
+
+    { 0x06F0, 0 }, /* 0x06F0 */
+    { 0x06F1, 0 }, /* 0x06F1 */
+    { 0x06F2, 0 }, /* 0x06F2 */
+    { 0x06F3, 0 }, /* 0x06F3 */
+    { 0x06F4, 0 }, /* 0x06F4 */
+    { 0x06F5, 0 }, /* 0x06F5 */
+    { 0x06F6, 0 }, /* 0x06F6 */
+    { 0x06F7, 0 }, /* 0x06F7 */
+    { 0x06F8, 0 }, /* 0x06F8 */
+    { 0x06F9, 0 }, /* 0x06F9 */
+    { 0x06FA, 0 }, /* 0x06FA */
+    { 0x06FB, 0 }, /* 0x06FB */
+    { 0x06FC, 0 }, /* 0x06FC */
+    { 0x06FD, 0 }, /* 0x06FD */
+    { 0x06FE, 0 }, /* 0x06FE */
+    { 0x06FF, 0 }  /* 0x06FF */
+};
+
+/* the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, this table does */
+static const hb_uint16 alefMaksura[4] = {0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9};
+
+/*
+// this is a bit tricky. Alef always binds to the right, so the second parameter descibing the shape
+// of the lam can be either initial of medial. So initial maps to the isolated form of the ligature,
+// medial to the final form
+*/
+static const hb_uint16 arabicUnicodeLamAlefMapping[6][4] = {
+    { 0xfffd, 0xfffd, 0xfef5, 0xfef6 }, /* 0x622        R       Alef with Madda above */
+    { 0xfffd, 0xfffd, 0xfef7, 0xfef8 }, /* 0x623        R       Alef with Hamza above */
+    { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, /* 0x624        // Just to fill the table ;-) */
+    { 0xfffd, 0xfffd, 0xfef9, 0xfefa }, /* 0x625        R       Alef with Hamza below */
+    { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, /* 0x626        // Just to fill the table ;-) */
+    { 0xfffd, 0xfffd, 0xfefb, 0xfefc }  /* 0x627        R       Alef */
+};
+
+static int getShape(hb_uint8 cell, int shape)
+{
+    /* the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, handle this here */
+    int ch = (cell != 0x49)
+              ? (shape ? arabicUnicodeMapping[cell][0] + shape : 0x600+cell)
+              : alefMaksura[shape] ;
+    return ch;
+}
+
+
+/*
+  Two small helper functions for arabic shaping.
+*/
+static HB_UChar16 prevChar(const HB_UChar16 *str, int pos)
+{
+    /*qDebug("leftChar: pos=%d", pos); */
+    const HB_UChar16 *ch = str + pos - 1;
+    pos--;
+    while(pos > -1) {
+        if(HB_GetUnicodeCharCategory(*ch) != HB_Mark_NonSpacing)
+            return *ch;
+        pos--;
+        ch--;
+    }
+    return ReplacementCharacter;
+}
+
+static HB_UChar16 nextChar(const HB_UChar16 *str, hb_uint32 len, hb_uint32 pos)
+{
+    const HB_UChar16 *ch = str + pos + 1;
+    pos++;
+    while(pos < len) {
+        /*qDebug("rightChar: %d isLetter=%d, joining=%d", pos, ch.isLetter(), ch.joining()); */
+        if(HB_GetUnicodeCharCategory(*ch) != HB_Mark_NonSpacing)
+            return *ch;
+        /* assume it's a transparent char, this might not be 100% correct */
+        pos++;
+        ch++;
+    }
+    return ReplacementCharacter;
+}
+
+static void shapedString(const HB_UChar16 *uc, hb_uint32 stringLength, hb_uint32 from, hb_uint32 len, HB_UChar16 *shapeBuffer, int *shapedLength,
+                         HB_Bool reverse, HB_GlyphAttributes *attributes, unsigned short *logClusters)
+{
+    HB_ArabicProperties *properties;
+    hb_int32 f = from;
+    hb_uint32 l = len;
+    const HB_UChar16 *ch;
+    HB_UChar16 *data;
+    int clusterStart;
+    hb_uint32 i;
+    HB_STACKARRAY(HB_ArabicProperties, props, len + 2);
+    properties = props;
+
+    assert(stringLength >= from + len);
+
+    if(len == 0) {
+        *shapedLength = 0;
+        return;
+    }
+
+    if (from > 0) {
+        --f;
+        ++l;
+        ++properties;
+    }
+    if (f + l < stringLength)
+        ++l;
+    getArabicProperties(uc+f, l, props);
+
+    ch = uc + from;
+    data = shapeBuffer;
+    clusterStart = 0;
+
+    for (i = 0; i < len; i++) {
+        hb_uint8 r = *ch >> 8;
+        int gpos = data - shapeBuffer;
+
+        if (r != 0x06) {
+            if (r == 0x20) {
+                if (*ch == 0x200c || *ch == 0x200d)
+                    /* remove ZWJ and ZWNJ */
+                    goto skip;
+            }
+            if (reverse)
+                *data = HB_GetMirroredChar(*ch);
+            else
+                *data = *ch;
+        } else {
+            hb_uint8 c = *ch & 0xff;
+            int pos = i + from;
+            int shape = properties[i].shape;
+/*            qDebug("mapping U+%x to shape %d glyph=0x%x", ch->unicode(), shape, getShape(c, shape)); */
+            /* take care of lam-alef ligatures (lam right of alef) */
+            hb_uint16 map;
+            switch (c) {
+                case 0x44: { /* lam */
+                    const HB_UChar16 pch = nextChar(uc, stringLength, pos);
+                    if ((pch >> 8) == 0x06) {
+                        switch (pch & 0xff) {
+                            case 0x22:
+                            case 0x23:
+                            case 0x25:
+                            case 0x27:
+/*                                 qDebug(" lam of lam-alef ligature"); */
+                                map = arabicUnicodeLamAlefMapping[(pch & 0xff) - 0x22][shape];
+                                goto next;
+                            default:
+                                break;
+                        }
+                    }
+                    break;
+                }
+                case 0x22: /* alef with madda */
+                case 0x23: /* alef with hamza above */
+                case 0x25: /* alef with hamza below */
+                case 0x27: /* alef */
+                    if (prevChar(uc, pos) == 0x0644) {
+                        /* have a lam alef ligature */
+                        /*qDebug(" alef of lam-alef ligature"); */
+                        goto skip;
+                    }
+                default:
+                    break;
+            }
+            map = getShape(c, shape);
+        next:
+            *data = map;
+        }
+        /* ##### Fixme */
+        /*glyphs[gpos].attributes.zeroWidth = zeroWidth; */
+        if (HB_GetUnicodeCharCategory(*ch) == HB_Mark_NonSpacing) {
+            attributes[gpos].mark = TRUE;
+/*             qDebug("glyph %d (char %d) is mark!", gpos, i); */
+        } else {
+            attributes[gpos].mark = FALSE;
+            clusterStart = data - shapeBuffer;
+        }
+        attributes[gpos].clusterStart = !attributes[gpos].mark;
+        attributes[gpos].combiningClass = HB_GetUnicodeCharCombiningClass(*ch);
+        attributes[gpos].justification = properties[i].justification;
+/*         qDebug("data[%d] = %x (from %x)", gpos, (uint)data->unicode(), ch->unicode());*/
+        data++;
+    skip:
+        ch++;
+        logClusters[i] = clusterStart;
+    }
+    *shapedLength = data - shapeBuffer;
+
+    HB_FREE_STACKARRAY(props);
+}
+
+#ifndef NO_OPENTYPE
+
+static const HB_OpenTypeFeature arabic_features[] = {
+    { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
+    { HB_MAKE_TAG('i', 's', 'o', 'l'), IsolProperty },
+    { HB_MAKE_TAG('f', 'i', 'n', 'a'), FinaProperty },
+    { HB_MAKE_TAG('m', 'e', 'd', 'i'), MediProperty },
+    { HB_MAKE_TAG('i', 'n', 'i', 't'), InitProperty },
+    { HB_MAKE_TAG('r', 'l', 'i', 'g'), RligProperty },
+    { HB_MAKE_TAG('c', 'a', 'l', 't'), CaltProperty },
+    { HB_MAKE_TAG('l', 'i', 'g', 'a'), LigaProperty },
+    { HB_MAKE_TAG('d', 'l', 'i', 'g'), DligProperty },
+    { HB_MAKE_TAG('c', 's', 'w', 'h'), CswhProperty },
+    /* mset is used in old Win95 fonts that don't have a 'mark' positioning table. */
+    { HB_MAKE_TAG('m', 's', 'e', 't'), MsetProperty },
+    {0, 0}
+};
+
+static const HB_OpenTypeFeature syriac_features[] = {
+    { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
+    { HB_MAKE_TAG('i', 's', 'o', 'l'), IsolProperty },
+    { HB_MAKE_TAG('f', 'i', 'n', 'a'), FinaProperty },
+    { HB_MAKE_TAG('f', 'i', 'n', '2'), FinaProperty },
+    { HB_MAKE_TAG('f', 'i', 'n', '3'), FinaProperty },
+    { HB_MAKE_TAG('m', 'e', 'd', 'i'), MediProperty },
+    { HB_MAKE_TAG('m', 'e', 'd', '2'), MediProperty },
+    { HB_MAKE_TAG('i', 'n', 'i', 't'), InitProperty },
+    { HB_MAKE_TAG('r', 'l', 'i', 'g'), RligProperty },
+    { HB_MAKE_TAG('c', 'a', 'l', 't'), CaltProperty },
+    { HB_MAKE_TAG('l', 'i', 'g', 'a'), LigaProperty },
+    { HB_MAKE_TAG('d', 'l', 'i', 'g'), DligProperty },
+    {0, 0}
+};
+
+static HB_Bool arabicSyriacOpenTypeShape(HB_ShaperItem *item, HB_Bool *ot_ok)
+{
+    const HB_UChar16 *uc;
+    const int nglyphs = item->num_glyphs;
+    hb_int32 f;
+    hb_uint32 l;
+    HB_ArabicProperties *properties;
+    HB_DECLARE_STACKARRAY(HB_ArabicProperties, props)
+    HB_DECLARE_STACKARRAY(hb_uint32, apply)
+    HB_Bool shaped;
+    int i = 0;
+
+    *ot_ok = TRUE;
+
+    if (!HB_ConvertStringToGlyphIndices(item))
+        return FALSE;
+    HB_HeuristicSetGlyphAttributes(item);
+
+    HB_INIT_STACKARRAY(HB_ArabicProperties, props, item->item.length + 2);
+    HB_INIT_STACKARRAY(hb_uint32, apply, item->num_glyphs);
+
+    uc = item->string + item->item.pos;
+
+    properties = props;
+    f = 0;
+    l = item->item.length;
+    if (item->item.pos > 0) {
+        --f;
+        ++l;
+        ++properties;
+    }
+    if (f + l + item->item.pos < item->stringLength) {
+        ++l;
+    }
+    if (item->item.script == HB_Script_Nko)
+        getNkoProperties(uc+f, l, props);
+    else
+        getArabicProperties(uc+f, l, props);
+
+    for (i = 0; i < (int)item->num_glyphs; i++) {
+        apply[i] = 0;
+
+        if (properties[i].shape == XIsolated)
+            apply[i] |= MediProperty|FinaProperty|InitProperty;
+        else if (properties[i].shape == XMedial)
+            apply[i] |= IsolProperty|FinaProperty|InitProperty;
+        else if (properties[i].shape == XFinal)
+            apply[i] |= IsolProperty|MediProperty|InitProperty;
+        else if (properties[i].shape == XInitial)
+            apply[i] |= IsolProperty|MediProperty|FinaProperty;
+
+        item->attributes[i].justification = properties[i].justification;
+    }
+
+    HB_FREE_STACKARRAY(props);
+
+    shaped = HB_OpenTypeShape(item, apply);
+
+    HB_FREE_STACKARRAY(apply);
+
+    if (!shaped) {
+        *ot_ok = FALSE;
+        return FALSE;
+    }
+    return HB_OpenTypePosition(item, nglyphs, /*doLogClusters*/TRUE);
+}
+
+#endif
+
+/* #### stil missing: identify invalid character combinations */
+HB_Bool HB_ArabicShape(HB_ShaperItem *item)
+{
+    int slen;
+    HB_Bool haveGlyphs;
+    HB_STACKARRAY(HB_UChar16, shapedChars, item->item.length);
+
+    assert(item->item.script == HB_Script_Arabic || item->item.script == HB_Script_Syriac
+           || item->item.script == HB_Script_Nko);
+
+    item->shaperFlags |= HB_ShaperFlag_ForceMarksToZeroWidth;
+#ifndef NO_OPENTYPE
+
+    if (HB_SelectScript(item, item->item.script == HB_Script_Arabic ? arabic_features : syriac_features)) {
+        HB_Bool ot_ok;
+        if (arabicSyriacOpenTypeShape(item, &ot_ok))
+            return TRUE;
+        if (ot_ok)
+            return FALSE;
+            /* fall through to the non OT code*/
+    }
+#endif
+
+    if (item->item.script != HB_Script_Arabic)
+        return HB_BasicShape(item);
+
+    shapedString(item->string, item->stringLength, item->item.pos, item->item.length, shapedChars, &slen,
+                  item->item.bidiLevel % 2,
+                  item->attributes, item->log_clusters);
+
+    haveGlyphs = item->font->klass
+        ->convertStringToGlyphIndices(item->font,
+                                      shapedChars, slen,
+                                      item->glyphs, &item->num_glyphs,
+                                      item->item.bidiLevel % 2);
+
+    HB_FREE_STACKARRAY(shapedChars);
+
+    if (!haveGlyphs)
+        return FALSE;
+
+    HB_HeuristicPosition(item);
+    return TRUE;
+}
+
+
diff --git a/third_party/harfbuzz/src/harfbuzz-buffer-private.h b/third_party/harfbuzz/src/harfbuzz-buffer-private.h
new file mode 100644
index 0000000..5065f2e
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-buffer-private.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2004,2007  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#ifndef HARFBUZZ_BUFFER_PRIVATE_H
+#define HARFBUZZ_BUFFER_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-buffer.h"
+
+HB_BEGIN_HEADER
+
+#define HB_GLYPH_PROPERTIES_UNKNOWN 0xFFFF
+
+HB_INTERNAL void
+_hb_buffer_swap( HB_Buffer buffer );
+
+HB_INTERNAL void
+_hb_buffer_clear_output( HB_Buffer buffer );
+
+HB_INTERNAL HB_Error
+_hb_buffer_clear_positions( HB_Buffer buffer );
+
+HB_INTERNAL HB_Error
+_hb_buffer_add_output_glyphs( HB_Buffer  buffer,
+			      HB_UShort  num_in,
+			      HB_UShort  num_out,
+			      HB_UShort *glyph_data,
+			      HB_UShort  component,
+			      HB_UShort  ligID );
+
+HB_INTERNAL HB_Error
+_hb_buffer_add_output_glyph ( HB_Buffer buffer,
+			      HB_UInt   glyph_index,
+			      HB_UShort component,
+			      HB_UShort ligID );
+
+HB_INTERNAL HB_Error
+_hb_buffer_copy_output_glyph ( HB_Buffer buffer );
+
+HB_INTERNAL HB_Error
+_hb_buffer_replace_output_glyph ( HB_Buffer buffer,
+				  HB_UInt   glyph_index,
+				  HB_Bool   inplace );
+
+HB_INTERNAL HB_UShort
+_hb_buffer_allocate_ligid( HB_Buffer buffer );
+
+
+/* convenience macros */
+
+#define IN_GLYPH( pos )        (buffer->in_string[(pos)].gindex)
+#define IN_ITEM( pos )         (&buffer->in_string[(pos)])
+#define IN_CURGLYPH()          (buffer->in_string[buffer->in_pos].gindex)
+#define IN_CURITEM()           (&buffer->in_string[buffer->in_pos])
+#define IN_PROPERTIES( pos )   (buffer->in_string[(pos)].properties)
+#define IN_LIGID( pos )        (buffer->in_string[(pos)].ligID)
+#define IN_COMPONENT( pos )    (buffer->in_string[(pos)].component)
+#define POSITION( pos )        (&buffer->positions[(pos)])
+#define OUT_GLYPH( pos )       (buffer->out_string[(pos)].gindex)
+#define OUT_ITEM( pos )        (&buffer->out_string[(pos)])
+
+#define CHECK_Property( gdef, index, flags, property )					\
+          ( ( error = _HB_GDEF_Check_Property( (gdef), (index), (flags),		\
+                                      (property) ) ) != HB_Err_Ok )
+
+#define ADD_String( buffer, num_in, num_out, glyph_data, component, ligID )             \
+          ( ( error = _hb_buffer_add_output_glyphs( (buffer),                            \
+						    (num_in), (num_out),                \
+                                                    (glyph_data), (component), (ligID)  \
+                                                  ) ) != HB_Err_Ok )
+#define ADD_Glyph( buffer, glyph_index, component, ligID )				\
+          ( ( error = _hb_buffer_add_output_glyph( (buffer),                             \
+                                                    (glyph_index), (component), (ligID) \
+                                                  ) ) != HB_Err_Ok )
+#define REPLACE_Glyph( buffer, glyph_index, nesting_level )				\
+          ( ( error = _hb_buffer_replace_output_glyph( (buffer), (glyph_index),		\
+						      (nesting_level) == 1 ) ) != HB_Err_Ok )
+#define COPY_Glyph( buffer )								\
+	  ( (error = _hb_buffer_copy_output_glyph ( buffer ) ) != HB_Err_Ok )
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_BUFFER_PRIVATE_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-buffer.c b/third_party/harfbuzz/src/harfbuzz-buffer.c
new file mode 100644
index 0000000..4d4c167
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-buffer.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2004,2007  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-buffer-private.h"
+#include "harfbuzz-gsub-private.h"
+#include "harfbuzz-gpos-private.h"
+
+/* Here is how the buffer works internally:
+ *
+ * There are two string pointers: in_string and out_string.  They
+ * always have same allocated size, but different length and positions.
+ *
+ * As an optimization, both in_string and out_string may point to the
+ * same piece of memory, which is owned by in_string.  This remains the
+ * case as long as:
+ *
+ *   - copy_glyph() is called
+ *   - replace_glyph() is called with inplace=TRUE
+ *   - add_output_glyph() and add_output_glyphs() are not called
+ *
+ * In that case swap(), and copy_glyph(), and replace_glyph() are all
+ * mostly no-op.
+ *
+ * As soon an add_output_glyph[s]() or replace_glyph() with inplace=FALSE is
+ * called, out_string is moved over to an alternate buffer (alt_string), and
+ * its current contents (out_length entries) are copied to the alt buffer.
+ * This should all remain transparent to the user.  swap() then switches
+ * in_string and alt_string.  alt_string is not allocated until its needed,
+ * but after that it's grown with in_string unconditionally.
+ *
+ * The buffer->separate_out boolean keeps status of whether out_string points
+ * to in_string (FALSE) or alt_string (TRUE).
+ */
+
+/* Internal API */
+
+static HB_Error
+hb_buffer_ensure( HB_Buffer buffer,
+		   HB_UInt   size )
+{
+  HB_UInt new_allocated = buffer->allocated;
+
+  if (size > new_allocated)
+    {
+      HB_Error error;
+
+      while (size > new_allocated)
+	new_allocated += (new_allocated >> 1) + 8;
+      
+      if ( buffer->positions )
+        {
+	  if ( REALLOC_ARRAY( buffer->positions, new_allocated, HB_PositionRec ) )
+	    return error;
+	}
+
+      if ( REALLOC_ARRAY( buffer->in_string, new_allocated, HB_GlyphItemRec ) )
+	return error;
+
+      if ( buffer->separate_out )
+        {
+	  if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
+	    return error;
+
+	  buffer->out_string = buffer->alt_string;
+	}
+      else
+        {
+	  buffer->out_string = buffer->in_string;
+
+	  if ( buffer->alt_string )
+	    {
+	      if ( REALLOC_ARRAY( buffer->alt_string, new_allocated, HB_GlyphItemRec ) )
+		return error;
+	    }
+	}
+
+      buffer->allocated = new_allocated;
+    }
+
+  return HB_Err_Ok;
+}
+
+static HB_Error
+hb_buffer_duplicate_out_buffer( HB_Buffer buffer )
+{
+  if ( !buffer->alt_string )
+    {
+      HB_Error error;
+
+      if ( ALLOC_ARRAY( buffer->alt_string, buffer->allocated, HB_GlyphItemRec ) )
+	return error;
+    }
+
+  buffer->out_string = buffer->alt_string;
+  memcpy( buffer->out_string, buffer->in_string, buffer->out_length * sizeof (buffer->out_string[0]) );
+  buffer->separate_out = TRUE;
+
+  return HB_Err_Ok;
+}
+
+/* Public API */
+
+HB_Error
+hb_buffer_new( HB_Buffer *pbuffer )
+{
+  HB_Buffer buffer;
+  HB_Error error;
+
+  if ( ALLOC( buffer, sizeof( HB_BufferRec ) ) )
+    return error;
+
+  buffer->allocated = 0;
+  buffer->in_string = NULL;
+  buffer->alt_string = NULL;
+  buffer->positions = NULL;
+
+  hb_buffer_clear( buffer );
+
+  *pbuffer = buffer;
+
+  return HB_Err_Ok;
+}
+
+void
+hb_buffer_free( HB_Buffer buffer )
+{
+  FREE( buffer->in_string );
+  FREE( buffer->alt_string );
+  buffer->out_string = NULL;
+  FREE( buffer->positions );
+  FREE( buffer );
+}
+
+void
+hb_buffer_clear( HB_Buffer buffer )
+{
+  buffer->in_length = 0;
+  buffer->out_length = 0;
+  buffer->in_pos = 0;
+  buffer->out_pos = 0;
+  buffer->out_string = buffer->in_string;
+  buffer->separate_out = FALSE;
+  buffer->max_ligID = 0;
+}
+
+HB_Error
+hb_buffer_add_glyph( HB_Buffer buffer,
+		      HB_UInt   glyph_index,
+		      HB_UInt   properties,
+		      HB_UInt   cluster )
+{
+  HB_Error error;
+  HB_GlyphItem glyph;
+  
+  error = hb_buffer_ensure( buffer, buffer->in_length + 1 );
+  if ( error )
+    return error;
+
+  glyph = &buffer->in_string[buffer->in_length];
+  glyph->gindex = glyph_index;
+  glyph->properties = properties;
+  glyph->cluster = cluster;
+  glyph->component = 0;
+  glyph->ligID = 0;
+  glyph->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
+  
+  buffer->in_length++;
+
+  return HB_Err_Ok;
+}
+
+/* HarfBuzz-Internal API */
+
+HB_INTERNAL void
+_hb_buffer_clear_output( HB_Buffer buffer )
+{
+  buffer->out_length = 0;
+  buffer->out_pos = 0;
+  buffer->out_string = buffer->in_string;
+  buffer->separate_out = FALSE;
+}
+
+HB_INTERNAL HB_Error
+_hb_buffer_clear_positions( HB_Buffer buffer )
+{
+  if ( !buffer->positions )
+    {
+      HB_Error error;
+
+      if ( ALLOC_ARRAY( buffer->positions, buffer->allocated, HB_PositionRec ) )
+	return error;
+    }
+
+  memset (buffer->positions, 0, sizeof (buffer->positions[0]) * buffer->in_length);
+
+  return HB_Err_Ok;
+}
+
+HB_INTERNAL void
+_hb_buffer_swap( HB_Buffer buffer )
+{
+  HB_GlyphItem tmp_string;
+  int tmp_length;
+  int tmp_pos;
+
+  if ( buffer->separate_out )
+    {
+      tmp_string = buffer->in_string;
+      buffer->in_string = buffer->out_string;
+      buffer->out_string = tmp_string;
+      buffer->alt_string = buffer->out_string;
+    }
+
+  tmp_length = buffer->in_length;
+  buffer->in_length = buffer->out_length;
+  buffer->out_length = tmp_length;
+
+  tmp_pos = buffer->in_pos;
+  buffer->in_pos = buffer->out_pos;
+  buffer->out_pos = tmp_pos;
+}
+
+/* The following function copies `num_out' elements from `glyph_data'
+   to `buffer->out_string', advancing the in array pointer in the structure
+   by `num_in' elements, and the out array pointer by `num_out' elements.
+   Finally, it sets the `length' field of `out' equal to
+   `pos' of the `out' structure.
+
+   If `component' is 0xFFFF, the component value from buffer->in_pos
+   will copied `num_out' times, otherwise `component' itself will
+   be used to fill the `component' fields.
+
+   If `ligID' is 0xFFFF, the ligID value from buffer->in_pos
+   will copied `num_out' times, otherwise `ligID' itself will
+   be used to fill the `ligID' fields.
+
+   The properties for all replacement glyphs are taken
+   from the glyph at position `buffer->in_pos'.
+
+   The cluster value for the glyph at position buffer->in_pos is used
+   for all replacement glyphs */
+HB_INTERNAL HB_Error
+_hb_buffer_add_output_glyphs( HB_Buffer  buffer,
+			      HB_UShort  num_in,
+			      HB_UShort  num_out,
+			      HB_UShort *glyph_data,
+			      HB_UShort  component,
+			      HB_UShort  ligID )
+{
+  HB_Error  error;
+  HB_UShort i;
+  HB_UInt properties;
+  HB_UInt cluster;
+
+  error = hb_buffer_ensure( buffer, buffer->out_pos + num_out );
+  if ( error )
+    return error;
+
+  if ( !buffer->separate_out )
+    {
+      error = hb_buffer_duplicate_out_buffer( buffer );
+      if ( error )
+	return error;
+    }
+
+  properties = buffer->in_string[buffer->in_pos].properties;
+  cluster = buffer->in_string[buffer->in_pos].cluster;
+  if ( component == 0xFFFF )
+    component = buffer->in_string[buffer->in_pos].component;
+  if ( ligID == 0xFFFF )
+    ligID = buffer->in_string[buffer->in_pos].ligID;
+
+  for ( i = 0; i < num_out; i++ )
+  {
+    HB_GlyphItem item = &buffer->out_string[buffer->out_pos + i];
+
+    item->gindex = glyph_data[i];
+    item->properties = properties;
+    item->cluster = cluster;
+    item->component = component;
+    item->ligID = ligID;
+    item->gproperties = HB_GLYPH_PROPERTIES_UNKNOWN;
+  }
+
+  buffer->in_pos  += num_in;
+  buffer->out_pos += num_out;
+
+  buffer->out_length = buffer->out_pos;
+
+  return HB_Err_Ok;
+}
+
+HB_INTERNAL HB_Error
+_hb_buffer_add_output_glyph( HB_Buffer buffer,
+			     HB_UInt   glyph_index,
+			     HB_UShort component,
+			     HB_UShort ligID )
+{
+  HB_UShort glyph_data =  glyph_index;
+
+  return _hb_buffer_add_output_glyphs ( buffer, 1, 1,
+					&glyph_data, component, ligID );
+}
+
+HB_INTERNAL HB_Error
+_hb_buffer_copy_output_glyph ( HB_Buffer buffer )
+{  
+  HB_Error  error;
+
+  error = hb_buffer_ensure( buffer, buffer->out_pos + 1 );
+  if ( error )
+    return error;
+  
+  if ( buffer->separate_out )
+    {
+      buffer->out_string[buffer->out_pos] = buffer->in_string[buffer->in_pos];
+    }
+
+  buffer->in_pos++;
+  buffer->out_pos++;
+  buffer->out_length = buffer->out_pos;
+
+  return HB_Err_Ok;
+}
+
+HB_INTERNAL HB_Error
+_hb_buffer_replace_output_glyph( HB_Buffer buffer,
+				 HB_UInt   glyph_index,
+				 HB_Bool   inplace )
+{
+
+  HB_Error error;
+
+  if ( inplace )
+    {
+      error = _hb_buffer_copy_output_glyph ( buffer );
+      if ( error )
+	return error;
+
+      buffer->out_string[buffer->out_pos-1].gindex = glyph_index;
+    }
+  else
+    {
+      return _hb_buffer_add_output_glyph( buffer, glyph_index, 0xFFFF, 0xFFFF );
+    }
+
+  return HB_Err_Ok;
+}
+
+HB_INTERNAL HB_UShort
+_hb_buffer_allocate_ligid( HB_Buffer buffer )
+{
+  buffer->max_ligID++;
+  if (HB_UNLIKELY (buffer->max_ligID == 0))
+    buffer->max_ligID++;
+
+  return buffer->max_ligID;
+}
diff --git a/third_party/harfbuzz/src/harfbuzz-buffer.h b/third_party/harfbuzz/src/harfbuzz-buffer.h
new file mode 100644
index 0000000..b134407
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-buffer.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2004,2007  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#ifndef HARFBUZZ_BUFFER_H
+#define HARFBUZZ_BUFFER_H
+
+#include "harfbuzz-global.h"
+
+HB_BEGIN_HEADER
+
+typedef struct HB_GlyphItemRec_ {
+  HB_UInt     gindex;
+  HB_UInt     properties;
+  HB_UInt     cluster;
+  HB_UShort   component;
+  HB_UShort   ligID;
+  HB_UShort   gproperties;
+} HB_GlyphItemRec, *HB_GlyphItem;
+
+typedef struct HB_PositionRec_ {
+  HB_Fixed   x_pos;
+  HB_Fixed   y_pos;
+  HB_Fixed   x_advance;
+  HB_Fixed   y_advance;
+  HB_UShort  back;            /* number of glyphs to go back
+				 for drawing current glyph   */
+  HB_Bool    new_advance;     /* if set, the advance width values are
+				 absolute, i.e., they won't be
+				 added to the original glyph's value
+				 but rather replace them.            */
+  HB_Short  cursive_chain;   /* character to which this connects,
+				 may be positive or negative; used
+				 only internally                     */
+} HB_PositionRec, *HB_Position;
+
+
+typedef struct HB_BufferRec_{ 
+  HB_UInt    allocated;
+
+  HB_UInt    in_length;
+  HB_UInt    out_length;
+  HB_UInt    in_pos;
+  HB_UInt    out_pos;
+  
+  HB_Bool       separate_out;
+  HB_GlyphItem  in_string;
+  HB_GlyphItem  out_string;
+  HB_GlyphItem  alt_string;
+  HB_Position   positions;
+  HB_UShort      max_ligID;
+} HB_BufferRec, *HB_Buffer;
+
+HB_Error
+hb_buffer_new( HB_Buffer *buffer );
+
+void
+hb_buffer_free( HB_Buffer buffer );
+
+void
+hb_buffer_clear( HB_Buffer buffer );
+
+HB_Error
+hb_buffer_add_glyph( HB_Buffer buffer,
+		      HB_UInt    glyph_index,
+		      HB_UInt    properties,
+		      HB_UInt    cluster );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_BUFFER_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-dump-main.c b/third_party/harfbuzz/src/harfbuzz-dump-main.c
new file mode 100644
index 0000000..dfb35fb
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-dump-main.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2000  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "harfbuzz.h"
+#include "harfbuzz-dump.h"
+
+#define N_ELEMENTS(arr) (sizeof(arr)/ sizeof((arr)[0]))
+
+static int
+croak (const char *situation, HB_Error error)
+{
+  fprintf (stderr, "%s: Error %d\n", situation, error);
+
+  exit (1);
+}
+
+int 
+main (int argc, char **argv)
+{
+  HB_Error error;
+  FT_Library library;
+  HB_Font font;
+  HB_GSUB gsub;
+  HB_GPOS gpos;
+
+  if (argc != 2)
+    {
+      fprintf (stderr, "Usage: harfbuzz-dump MYFONT.TTF\n");
+      exit(1);
+    }
+
+  if ((error = FT_Init_FreeType (&library)))
+    croak ("FT_Init_FreeType", error);
+
+  if ((error = FT_New_Face (library, argv[1], 0, &font)))
+    croak ("FT_New_Face", error);
+
+  printf ("<?xml version=\"1.0\"?>\n");
+  printf ("<OpenType>\n");
+
+  if (!(error = HB_Load_GSUB_Table (font, &gsub, NULL)))
+    {
+      HB_Dump_GSUB_Table (gsub, stdout);
+      
+      if ((error = HB_Done_GSUB_Table (gsub)))
+	croak ("HB_Done_GSUB_Table", error);
+    }
+  else if (error != HB_Err_Not_Covered)
+    fprintf (stderr, "HB_Load_GSUB_Table: error 0x%x\n", error);
+
+  if (!(error = HB_Load_GPOS_Table (font, &gpos, NULL)))
+    {
+      HB_Dump_GPOS_Table (gpos, stdout);
+      
+      if ((error = HB_Done_GPOS_Table (gpos)))
+	croak ("HB_Done_GPOS_Table", error);
+    }
+  else if (error != HB_Err_Not_Covered)
+    fprintf (stderr, "HB_Load_GPOS_Table: error 0x%x\n", error);
+
+  printf ("</OpenType>\n");
+
+  if ((error = FT_Done_Face (font)))
+    croak ("FT_Done_Face", error);
+
+  if ((error = FT_Done_FreeType (library)))
+    croak ("FT_Done_FreeType", error);
+  
+  return 0;
+}
+
diff --git a/third_party/harfbuzz/src/harfbuzz-dump.c b/third_party/harfbuzz/src/harfbuzz-dump.c
new file mode 100644
index 0000000..8c81da1
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-dump.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 2000, 2007  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-dump.h"
+#include "harfbuzz-gdef-private.h"
+#include "harfbuzz-gsub-private.h"
+#include "harfbuzz-gpos-private.h"
+#include "harfbuzz-open-private.h"
+#include <stdarg.h>
+
+#define DUMP(format) dump (stream, indent, format)
+#define DUMP1(format, arg1) dump (stream, indent, format, arg1)
+#define DUMP2(format, arg1, arg2) dump (stream, indent, format, arg1, arg2)
+#define DUMP3(format, arg1, arg2, arg3) dump (stream, indent, format, arg1, arg2, arg3)
+
+#define DUMP_FINT(strct,fld) dump (stream, indent, "<" #fld ">%d</" #fld ">\n", (strct)->fld)
+#define DUMP_FUINT(strct,fld) dump (stream, indent, "<" #fld ">%u</" #fld ">\n", (strct)->fld)
+#define DUMP_FGLYPH(strct,fld) dump (stream, indent, "<" #fld ">%#06x</" #fld ">\n", (strct)->fld)
+#define DUMP_FGLYPH(strct,fld) dump (stream, indent, "<" #fld ">%#06x</" #fld ">\n", (strct)->fld)
+#define DUMP_USHORT_ARRAY(strct,fld,cnt) Dump_UShort_Array ((strct)->fld, cnt, #fld, stream, indent);
+
+#define DEF_DUMP(type) static void Dump_ ## type (HB_ ## type *type, FILE *stream, int indent, HB_Type hb_type)
+#define RECURSE(name, type, val) do {  DUMP ("<" #name ">\n"); Dump_ ## type (val, stream, indent + 1, hb_type); DUMP ("</" #name ">\n"); } while (0)
+#define RECURSE_NUM(name, i, type, val) do {  DUMP1 ("<" #name "> <!-- %d -->\n", i); Dump_ ## type (val, stream, indent + 1, hb_type); DUMP ("</" #name ">\n"); } while (0)
+#define DUMP_VALUE_RECORD(val, frmt) do {  DUMP ("<ValueRecord>\n"); Dump_ValueRecord (val, stream, indent + 1, hb_type, frmt); DUMP ("</ValueRecord>\n"); } while (0)
+
+static void
+do_indent (FILE *stream, int indent)
+{
+  fprintf (stream, "%*s", indent * 3, "");
+}
+
+static void
+dump (FILE *stream, int indent, const char *format, ...)
+{
+  va_list list;
+
+  do_indent (stream, indent);
+
+  va_start (list, format);
+  vfprintf (stream, format, list);
+  va_end (list);
+}
+
+static void
+Dump_UShort_Array (HB_UShort *array, int count, const char *name, FILE *stream, int indent)
+{
+  int i;
+
+  do_indent (stream, indent);
+
+  fprintf (stream, "<%s>", name);
+  for (i = 0; i < count; i++)
+    fprintf (stream, "%d%s", array[i], i == 0 ? "" : " ");
+  fprintf (stream, "</%s>\n", name);
+}
+
+static void
+Print_Tag (HB_UInt tag, FILE *stream)
+{
+  fprintf (stream, "%c%c%c%c",
+	   (unsigned char)(tag >> 24),
+	   (unsigned char)((tag >> 16) & 0xff),
+	   (unsigned char)((tag >> 8) & 0xff),
+	   (unsigned char)(tag & 0xff));
+}
+
+DEF_DUMP (LangSys)
+{
+  int i;
+
+  HB_UNUSED(hb_type);
+
+  DUMP_FUINT (LangSys, LookupOrderOffset);
+  DUMP_FUINT (LangSys, ReqFeatureIndex);
+  DUMP_FUINT (LangSys, FeatureCount);
+
+  for (i=0; i < LangSys->FeatureCount; i++)
+    DUMP1("<FeatureIndex>%d</FeatureIndex>\n", LangSys->FeatureIndex[i]);
+}
+
+DEF_DUMP (ScriptTable)
+{
+  int i;
+
+  RECURSE (DefaultLangSys, LangSys, &ScriptTable->DefaultLangSys);
+
+  DUMP_FUINT (ScriptTable, LangSysCount);
+
+  for (i=0; i < ScriptTable->LangSysCount; i++)
+    {
+      do_indent (stream, indent);
+      fprintf (stream, "<LangSysTag>");
+      Print_Tag (ScriptTable->LangSysRecord[i].LangSysTag, stream);
+      fprintf (stream, "</LangSysTag>\n");
+      RECURSE_NUM (LangSys, i, LangSys, &ScriptTable->LangSysRecord[i].LangSys);
+    }
+}
+
+DEF_DUMP (ScriptList)
+{
+  int i;
+
+  DUMP_FUINT (ScriptList, ScriptCount);
+
+  for (i=0; i < ScriptList->ScriptCount; i++)
+    {
+      do_indent (stream, indent);
+      fprintf (stream, "<ScriptTag>");
+      Print_Tag (ScriptList->ScriptRecord[i].ScriptTag, stream);
+      fprintf (stream, "</ScriptTag>\n");
+      RECURSE_NUM (Script, i, ScriptTable, &ScriptList->ScriptRecord[i].Script);
+    }
+}
+
+DEF_DUMP (Feature)
+{
+  int i;
+
+  HB_UNUSED(hb_type);
+
+  DUMP_FUINT (Feature, FeatureParams);
+  DUMP_FUINT (Feature, LookupListCount);
+
+  for (i=0; i < Feature->LookupListCount; i++)
+    DUMP1("<LookupIndex>%d</LookupIndex>\n", Feature->LookupListIndex[i]);
+}
+
+DEF_DUMP (MarkRecord)
+{
+  HB_UNUSED(hb_type);
+
+  DUMP_FUINT (MarkRecord, Class);
+  DUMP1("<Anchor>%d</Anchor>\n", MarkRecord->MarkAnchor.PosFormat );
+}
+
+DEF_DUMP (MarkArray)
+{
+  int i;
+
+  DUMP_FUINT (MarkArray, MarkCount);
+
+  for (i=0; i < MarkArray->MarkCount; i++)
+    RECURSE_NUM (MarkRecord, i, MarkRecord, &MarkArray->MarkRecord[i]);
+}
+
+DEF_DUMP (FeatureList)
+{
+  int i;
+
+  DUMP_FUINT (FeatureList, FeatureCount);
+
+  for (i=0; i < FeatureList->FeatureCount; i++)
+    {
+      do_indent (stream, indent);
+      fprintf (stream, "<FeatureTag>");
+      Print_Tag (FeatureList->FeatureRecord[i].FeatureTag, stream);
+      fprintf (stream, "</FeatureTag> <!-- %d -->\n", i);
+      RECURSE_NUM (Feature, i, Feature, &FeatureList->FeatureRecord[i].Feature);
+    }
+}
+
+DEF_DUMP (Coverage)
+{
+  HB_UNUSED(hb_type);
+
+  DUMP_FUINT (Coverage, CoverageFormat);
+
+  if (Coverage->CoverageFormat == 1)
+    {
+      int i;
+      DUMP_FUINT (&Coverage->cf.cf1, GlyphCount);
+
+      for (i = 0; i < Coverage->cf.cf1.GlyphCount; i++)
+	DUMP2("<Glyph>%#06x</Glyph> <!-- %d -->\n",
+	      Coverage->cf.cf1.GlyphArray[i], i);
+    }
+  else
+    {
+      int i;
+      DUMP_FUINT (&Coverage->cf.cf2, RangeCount);
+
+      for ( i = 0; i < Coverage->cf.cf2.RangeCount; i++ )
+	  DUMP3("<Glyph>%#06x - %#06x</Glyph> <!-- %d -->\n",
+	        Coverage->cf.cf2.RangeRecord[i].Start,
+	        Coverage->cf.cf2.RangeRecord[i].End, i);
+    }
+}
+
+DEF_DUMP (ClassRangeRecord)
+{
+  HB_UNUSED(hb_type);
+
+  DUMP_FGLYPH (ClassRangeRecord, Start);
+  DUMP_FGLYPH (ClassRangeRecord, End);
+  DUMP_FUINT (ClassRangeRecord, Class);
+}
+
+DEF_DUMP (ClassDefinition)
+{
+  HB_UNUSED(hb_type);
+
+  DUMP_FUINT( ClassDefinition, ClassFormat);
+  DUMP_FUINT( ClassDefinition, loaded);
+
+  if (ClassDefinition->ClassFormat == 1)
+    {
+      int i;
+      HB_ClassDefFormat1 *ClassDefFormat1 = &ClassDefinition->cd.cd1;
+      DUMP("<ClassDefinition>\n");
+      DUMP_FUINT (ClassDefFormat1, StartGlyph );
+      DUMP_FUINT (ClassDefFormat1, GlyphCount );
+      for (i = 0; i < ClassDefFormat1->GlyphCount; i++)
+	DUMP2(" <Class>%d</Class> <!-- %#06x -->", ClassDefFormat1->ClassValueArray[i],
+	      ClassDefFormat1->StartGlyph+i );
+    }
+  else if (ClassDefinition->ClassFormat == 2)
+    {
+      int i;
+      HB_ClassDefFormat2 *ClassDefFormat2 = &ClassDefinition->cd.cd2;
+      DUMP_FUINT (ClassDefFormat2, ClassRangeCount);
+
+      for (i = 0; i < ClassDefFormat2->ClassRangeCount; i++)
+	RECURSE_NUM (ClassRangeRecord, i, ClassRangeRecord, &ClassDefFormat2->ClassRangeRecord[i]);
+    }
+  else
+    fprintf(stderr, "invalid class def table!!!\n");
+}
+
+DEF_DUMP (SubstLookupRecord)
+{
+  HB_UNUSED(hb_type);
+
+  DUMP_FUINT (SubstLookupRecord, SequenceIndex);
+  DUMP_FUINT (SubstLookupRecord, LookupListIndex);
+}
+
+DEF_DUMP (ChainSubClassRule)
+{
+  int i;
+
+  DUMP_USHORT_ARRAY (ChainSubClassRule, Backtrack, ChainSubClassRule->BacktrackGlyphCount);
+  DUMP_USHORT_ARRAY (ChainSubClassRule, Input, ChainSubClassRule->InputGlyphCount - 1);
+  DUMP_USHORT_ARRAY (ChainSubClassRule, Lookahead, ChainSubClassRule->LookaheadGlyphCount);
+
+  for (i = 0; i < ChainSubClassRule->SubstCount; i++)
+    RECURSE_NUM (SubstLookupRecord, i, SubstLookupRecord, &ChainSubClassRule->SubstLookupRecord[i]);
+
+  indent--;
+}
+
+DEF_DUMP (ChainSubClassSet)
+{
+  int i;
+
+  DUMP_FUINT( ChainSubClassSet, ChainSubClassRuleCount );
+  for (i = 0; i < ChainSubClassSet->ChainSubClassRuleCount; i++)
+    RECURSE_NUM (ChainSubClassRule, i, ChainSubClassRule, &ChainSubClassSet->ChainSubClassRule[i]);
+}
+
+static void
+Dump_GSUB_Lookup_Single (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+  HB_SingleSubst *SingleSubst = &subtable->st.gsub.single;
+
+  DUMP_FUINT (SingleSubst, SubstFormat);
+  RECURSE (Coverage, Coverage, &SingleSubst->Coverage);
+
+  if (SingleSubst->SubstFormat == 1)
+    {
+      DUMP_FINT (&SingleSubst->ssf.ssf1, DeltaGlyphID);
+    }
+  else
+    {
+      int i;
+
+      DUMP_FINT (&SingleSubst->ssf.ssf2, GlyphCount);
+      for (i=0; i < SingleSubst->ssf.ssf2.GlyphCount; i++)
+	DUMP2("<Substitute>%#06x</Substitute> <!-- %d -->\n", SingleSubst->ssf.ssf2.Substitute[i], i);
+    }
+}
+
+DEF_DUMP (Ligature)
+{
+  int i;
+
+  HB_UNUSED(hb_type);
+
+  DUMP_FGLYPH (Ligature, LigGlyph);
+  DUMP_FUINT (Ligature, ComponentCount);
+
+  for (i=0; i < Ligature->ComponentCount - 1; i++)
+    DUMP1("<Component>%#06x</Component>\n", Ligature->Component[i]);
+}
+
+DEF_DUMP (LigatureSet)
+{
+  int i;
+
+  DUMP_FUINT (LigatureSet, LigatureCount);
+
+  for (i=0; i < LigatureSet->LigatureCount; i++)
+    RECURSE_NUM (Ligature, i, Ligature, &LigatureSet->Ligature[i]);
+}
+
+static void
+Dump_GSUB_Lookup_Ligature (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+  int i;
+  HB_LigatureSubst *LigatureSubst = &subtable->st.gsub.ligature;
+
+  DUMP_FUINT (LigatureSubst, SubstFormat);
+  RECURSE (Coverage, Coverage, &LigatureSubst->Coverage);
+
+  DUMP_FUINT (LigatureSubst, LigatureSetCount);
+
+  for (i=0; i < LigatureSubst->LigatureSetCount; i++)
+    RECURSE_NUM (LigatureSet, i, LigatureSet, &LigatureSubst->LigatureSet[i]);
+}
+
+DEF_DUMP (ContextSubstFormat1)
+{
+  HB_UNUSED(hb_type);
+  HB_UNUSED(ContextSubstFormat1);
+
+
+  DUMP("<!-- Not implemented!!! -->\n");
+}
+
+DEF_DUMP (ContextSubstFormat2)
+{
+  DUMP_FUINT (ContextSubstFormat2, MaxContextLength);
+  RECURSE (Coverage, Coverage, &ContextSubstFormat2->Coverage);
+  RECURSE (ClassDefinition, ClassDefinition, &ContextSubstFormat2->ClassDef);
+}
+
+DEF_DUMP (ContextSubstFormat3)
+{
+  HB_UNUSED(hb_type);
+  HB_UNUSED(ContextSubstFormat3);
+
+  DUMP("<!-- Not implemented!!! -->\n");
+}
+
+static void
+Dump_GSUB_Lookup_Context (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+  HB_ContextSubst *ContextSubst = &subtable->st.gsub.context;
+
+  DUMP_FUINT (ContextSubst, SubstFormat);
+  switch( ContextSubst->SubstFormat )
+    {
+    case 1:
+      Dump_ContextSubstFormat1 (&ContextSubst->csf.csf1, stream, indent+2, hb_type);
+      break;
+    case 2:
+      Dump_ContextSubstFormat2 (&ContextSubst->csf.csf2, stream, indent+2, hb_type);
+      break;
+    case 3:
+      Dump_ContextSubstFormat3 (&ContextSubst->csf.csf3, stream, indent+2, hb_type);
+      break;
+    default:
+      fprintf(stderr, "invalid subformat!!!!!\n");
+    }
+}
+
+DEF_DUMP (ChainContextSubstFormat1)
+{
+  HB_UNUSED(hb_type);
+  HB_UNUSED(ChainContextSubstFormat1);
+
+  DUMP("<!-- Not implemented!!! -->\n");
+}
+
+DEF_DUMP (ChainContextSubstFormat2)
+{
+  int i;
+
+  RECURSE (Coverage, Coverage, &ChainContextSubstFormat2->Coverage);
+  DUMP_FUINT (ChainContextSubstFormat2, MaxBacktrackLength);
+  RECURSE (ClassDefinition, ClassDefinition, &ChainContextSubstFormat2->BacktrackClassDef);
+  DUMP_FUINT (ChainContextSubstFormat2, MaxInputLength);
+  RECURSE (ClassDefinition, ClassDefinition, &ChainContextSubstFormat2->InputClassDef);
+  DUMP_FUINT (ChainContextSubstFormat2, MaxLookaheadLength);
+  RECURSE (ClassDefinition, ClassDefinition, &ChainContextSubstFormat2->LookaheadClassDef);
+
+  DUMP_FUINT (ChainContextSubstFormat2, ChainSubClassSetCount);
+  for (i = 0; i < ChainContextSubstFormat2->ChainSubClassSetCount; i++)
+    RECURSE (ChainSubClassSet, ChainSubClassSet, &ChainContextSubstFormat2->ChainSubClassSet[i]);
+}
+
+DEF_DUMP (ChainContextSubstFormat3)
+{
+  int i;
+
+  DUMP_FUINT (ChainContextSubstFormat3, BacktrackGlyphCount);
+  for (i = 0; i < ChainContextSubstFormat3->BacktrackGlyphCount; i++)
+    RECURSE (BacktrackCoverage, Coverage, &ChainContextSubstFormat3->BacktrackCoverage[i]);
+  DUMP_FUINT (ChainContextSubstFormat3, InputGlyphCount);
+  for (i = 0; i < ChainContextSubstFormat3->InputGlyphCount; i++)
+    RECURSE (InputCoverage, Coverage, &ChainContextSubstFormat3->InputCoverage[i]);
+  DUMP_FUINT (ChainContextSubstFormat3, LookaheadGlyphCount);
+  for (i = 0; i < ChainContextSubstFormat3->LookaheadGlyphCount; i++)
+    RECURSE (LookaheadCoverage, Coverage, &ChainContextSubstFormat3->LookaheadCoverage[i]);
+
+  for (i = 0; i < ChainContextSubstFormat3->SubstCount; i++)
+    RECURSE_NUM (SubstLookupRecord, i, SubstLookupRecord, &ChainContextSubstFormat3->SubstLookupRecord[i]);
+
+}
+
+static void
+Dump_GSUB_Lookup_Chain (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+  HB_ChainContextSubst *chain = &subtable->st.gsub.chain;
+
+  DUMP_FUINT (chain, SubstFormat);
+  switch (chain->SubstFormat)
+    {
+    case 1:
+      Dump_ChainContextSubstFormat1 (&chain->ccsf.ccsf1, stream, indent+2, hb_type);
+      break;
+    case 2:
+      Dump_ChainContextSubstFormat2 (&chain->ccsf.ccsf2, stream, indent+2, hb_type);
+      break;
+    case 3:
+      Dump_ChainContextSubstFormat3 (&chain->ccsf.ccsf3, stream, indent+2, hb_type);
+      break;
+    default:
+      fprintf(stderr, "invalid subformat!!!!!\n");
+    }
+}
+
+static void
+Dump_Device (HB_Device *Device, FILE *stream, int indent, HB_Type hb_type)
+{
+  int i;
+  int bits;
+  int n_per;
+  unsigned int mask;
+
+  HB_UNUSED(hb_type);
+
+  DUMP_FUINT (Device, StartSize);
+  DUMP_FUINT (Device, EndSize);
+  DUMP_FUINT (Device, DeltaFormat);
+  switch (Device->DeltaFormat)
+    {
+    case 1:
+      bits = 2;
+      break;
+    case 2:
+      bits = 4;
+      break;
+    case 3:
+      bits = 8;
+      break;
+    default:
+      bits = 0;
+      break;
+    }
+
+  DUMP ("<DeltaValue>");
+  if (!bits)
+    {
+
+      fprintf(stderr, "invalid DeltaFormat!!!!!\n");
+    }
+  else
+    {
+      n_per = 16 / bits;
+      mask = (1 << bits) - 1;
+      mask = mask << (16 - bits);
+
+      for (i = Device->StartSize; i <= Device->EndSize ; i++)
+	{
+	  HB_UShort val = Device->DeltaValue[i / n_per];
+	  HB_Short signed_val = ((val << ((i % n_per) * bits)) & mask);
+	  dump (stream, indent, "%d", signed_val >> (16 - bits));
+	  if (i != Device->EndSize)
+	    DUMP (", ");
+	}
+    }
+  DUMP ("</DeltaValue>\n");
+}
+
+static void
+Dump_ValueRecord (HB_ValueRecord *ValueRecord, FILE *stream, int indent, HB_Type hb_type, HB_UShort value_format)
+{
+  if (value_format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT)
+    DUMP_FINT (ValueRecord, XPlacement);
+  if (value_format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT)
+    DUMP_FINT (ValueRecord, YPlacement);
+  if (value_format & HB_GPOS_FORMAT_HAVE_X_ADVANCE)
+    DUMP_FINT (ValueRecord, XAdvance);
+  if (value_format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE)
+    DUMP_FINT (ValueRecord, XAdvance);
+  if (value_format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE)
+    RECURSE (Device, Device, &ValueRecord->XPlacementDevice);
+  if (value_format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE)
+    RECURSE (Device, Device, &ValueRecord->YPlacementDevice);
+  if (value_format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE)
+    RECURSE (Device, Device, &ValueRecord->XAdvanceDevice);
+  if (value_format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE)
+    RECURSE (Device, Device, &ValueRecord->YAdvanceDevice);
+  if (value_format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT)
+    DUMP_FUINT (ValueRecord, XIdPlacement);
+  if (value_format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT)
+    DUMP_FUINT (ValueRecord, YIdPlacement);
+  if (value_format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE)
+    DUMP_FUINT (ValueRecord, XIdAdvance);
+  if (value_format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE)
+    DUMP_FUINT (ValueRecord, XIdAdvance);
+}
+
+static void
+Dump_GPOS_Lookup_Single (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+  HB_SinglePos *SinglePos = &subtable->st.gpos.single;
+
+  DUMP_FUINT (SinglePos, PosFormat);
+  RECURSE (Coverage, Coverage, &SinglePos->Coverage);
+
+  DUMP_FUINT (SinglePos, ValueFormat);
+
+  if (SinglePos->PosFormat == 1)
+    {
+      DUMP_VALUE_RECORD (&SinglePos->spf.spf1.Value, SinglePos->ValueFormat);
+    }
+  else
+    {
+      int i;
+
+      DUMP_FUINT (&SinglePos->spf.spf2, ValueCount);
+      for (i = 0; i < SinglePos->spf.spf2.ValueCount; i++)
+	DUMP_VALUE_RECORD (&SinglePos->spf.spf2.Value[i], SinglePos->ValueFormat);
+    }
+}
+
+static void
+Dump_PairValueRecord (HB_PairValueRecord *PairValueRecord, FILE *stream, int indent, HB_Type hb_type, HB_UShort ValueFormat1, HB_UShort ValueFormat2)
+{
+  DUMP_FUINT (PairValueRecord, SecondGlyph);
+  DUMP_VALUE_RECORD (&PairValueRecord->Value1, ValueFormat1);
+  DUMP_VALUE_RECORD (&PairValueRecord->Value2, ValueFormat2);
+}
+
+static void
+Dump_PairSet (HB_PairSet *PairSet, FILE *stream, int indent, HB_Type hb_type, HB_UShort ValueFormat1, HB_UShort ValueFormat2)
+{
+  int i;
+  DUMP_FUINT (PairSet, PairValueCount);
+
+  for (i = 0; i < PairSet->PairValueCount; i++)
+    {
+      DUMP ("<PairValueRecord>\n");
+      Dump_PairValueRecord (&PairSet->PairValueRecord[i], stream, indent + 1, hb_type, ValueFormat1, ValueFormat2);
+      DUMP ("</PairValueRecord>\n");
+    }
+}
+
+static void
+Dump_GPOS_Lookup_Pair (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+  HB_PairPos *PairPos = &subtable->st.gpos.pair;
+
+  DUMP_FUINT (PairPos, PosFormat);
+  RECURSE (Coverage, Coverage, &PairPos->Coverage);
+
+  DUMP_FUINT (PairPos, ValueFormat1);
+  DUMP_FUINT (PairPos, ValueFormat2);
+
+  if (PairPos->PosFormat == 1)
+    {
+      int i;
+
+      DUMP_FUINT (&PairPos->ppf.ppf1, PairSetCount);
+      for (i = 0; i < PairPos->ppf.ppf1.PairSetCount; i++)
+	{
+	  DUMP ("<PairSet>\n");
+	  Dump_PairSet (&PairPos->ppf.ppf1.PairSet[i], stream, indent + 1, hb_type, PairPos->ValueFormat1, PairPos->ValueFormat2);
+	  DUMP ("</PairSet>\n");
+	}
+    }
+  else
+    {
+    }
+}
+
+static void
+Dump_GPOS_Lookup_Markbase (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type)
+{
+  int i;
+  HB_MarkBasePos *markbase = &subtable->st.gpos.markbase;
+
+  DUMP_FUINT (markbase, PosFormat);
+  RECURSE (Coverage, Coverage, &markbase->MarkCoverage);
+  RECURSE (Coverage, Coverage, &markbase->BaseCoverage);
+  DUMP_FUINT (markbase, ClassCount);
+  RECURSE (MarkArray, MarkArray, &markbase->MarkArray);
+
+  DUMP ("<BaseArray>\n");
+  indent++;
+
+  DUMP_FUINT (&markbase->BaseArray, BaseCount);
+  for (i = 0; i < markbase->BaseArray.BaseCount; i++)
+    {
+      int j;
+      HB_BaseRecord *r = &markbase->BaseArray.BaseRecord[i];
+      DUMP1 ("<BaseRecord> <!-- %d -->\n",  i);
+      for (j = 0; j < markbase->ClassCount; j++)
+	DUMP1 ("  <Anchor>%d</Anchor>\n", r->BaseAnchor->PosFormat);
+      DUMP ("<BaseRecord>\n");
+    }
+
+  indent--;
+  DUMP ("</BaseArray>\n");
+}
+
+DEF_DUMP (Lookup)
+{
+  int i;
+  const char *lookup_name;
+  void (*lookup_func) (HB_SubTable *subtable, FILE *stream, int indent, HB_Type hb_type) = NULL;
+
+  if (hb_type == HB_Type_GSUB)
+    {
+      switch (Lookup->LookupType)
+	{
+	case  HB_GSUB_LOOKUP_SINGLE:
+	  lookup_name = "SINGLE";
+	  lookup_func = Dump_GSUB_Lookup_Single;
+	  break;
+	case  HB_GSUB_LOOKUP_MULTIPLE:
+	  lookup_name = "MULTIPLE";
+	  break;
+	case  HB_GSUB_LOOKUP_ALTERNATE:
+	  lookup_name = "ALTERNATE";
+	  break;
+	case  HB_GSUB_LOOKUP_LIGATURE:
+	  lookup_name = "LIGATURE";
+	  lookup_func = Dump_GSUB_Lookup_Ligature;
+	  break;
+	case  HB_GSUB_LOOKUP_CONTEXT:
+	  lookup_name = "CONTEXT";
+	  lookup_func = Dump_GSUB_Lookup_Context;
+	  break;
+	case  HB_GSUB_LOOKUP_CHAIN:
+	  lookup_name = "CHAIN";
+	  lookup_func = Dump_GSUB_Lookup_Chain;
+	  break;
+	default:
+	  lookup_name = "(unknown)";
+	  lookup_func = NULL;
+	  break;
+	}
+    }
+  else
+    {
+      switch (Lookup->LookupType)
+	{
+	case HB_GPOS_LOOKUP_SINGLE:
+	  lookup_name = "SINGLE";
+	  lookup_func = Dump_GPOS_Lookup_Single;
+	  break;
+	case HB_GPOS_LOOKUP_PAIR:
+	  lookup_name = "PAIR";
+	  lookup_func = Dump_GPOS_Lookup_Pair;
+	  break;
+	case HB_GPOS_LOOKUP_CURSIVE:
+	  lookup_name = "CURSIVE";
+	  break;
+	case HB_GPOS_LOOKUP_MARKBASE:
+	  lookup_name = "MARKBASE";
+	  lookup_func = Dump_GPOS_Lookup_Markbase;
+	  break;
+	case HB_GPOS_LOOKUP_MARKLIG:
+	  lookup_name = "MARKLIG";
+	  break;
+	case HB_GPOS_LOOKUP_MARKMARK:
+	  lookup_name = "MARKMARK";
+	  break;
+	case HB_GPOS_LOOKUP_CONTEXT:
+	  lookup_name = "CONTEXT";
+	  break;
+	case HB_GPOS_LOOKUP_CHAIN:
+	  lookup_name = "CHAIN";
+	  break;
+	default:
+	  lookup_name = "(unknown)";
+	  lookup_func = NULL;
+	  break;
+	}
+    }
+
+  DUMP2("<LookupType>%s</LookupType> <!-- %d -->\n", lookup_name, Lookup->LookupType);
+  DUMP1("<LookupFlag>%#06x</LookupFlag>\n", Lookup->LookupFlag);
+
+  for (i=0; i < Lookup->SubTableCount; i++)
+    {
+      DUMP ("<Subtable>\n");
+      if (lookup_func)
+	(*lookup_func) (&Lookup->SubTable[i], stream, indent + 1, hb_type);
+      DUMP ("</Subtable>\n");
+    }
+}
+
+DEF_DUMP (LookupList)
+{
+  int i;
+
+  DUMP_FUINT (LookupList, LookupCount);
+
+  for (i=0; i < LookupList->LookupCount; i++)
+    RECURSE_NUM (Lookup, i, Lookup, &LookupList->Lookup[i]);
+}
+
+void
+HB_Dump_GSUB_Table (HB_GSUB gsub, FILE *stream)
+{
+  int indent = 1;
+  HB_Type hb_type = HB_Type_GSUB;
+
+  do_indent (stream, indent);
+  fprintf(stream, "<!-- GSUB -->\n");
+  RECURSE (ScriptList, ScriptList, &gsub->ScriptList);
+  RECURSE (FeatureList, FeatureList, &gsub->FeatureList);
+  RECURSE (LookupList, LookupList, &gsub->LookupList);
+}
+
+void
+HB_Dump_GPOS_Table (HB_GPOS gpos, FILE *stream)
+{
+  int indent = 1;
+  HB_Type hb_type = HB_Type_GPOS;
+
+  do_indent (stream, indent);
+  fprintf(stream, "<!-- GPOS -->\n");
+  RECURSE (ScriptList, ScriptList, &gpos->ScriptList);
+  RECURSE (FeatureList, FeatureList, &gpos->FeatureList);
+  RECURSE (LookupList, LookupList, &gpos->LookupList);
+}
diff --git a/third_party/harfbuzz/src/harfbuzz-dump.h b/third_party/harfbuzz/src/harfbuzz-dump.h
new file mode 100644
index 0000000..ea4a62b
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-dump.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2000, 2007  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
+ */
+
+#ifndef HARFBUZZ_DUMP_H
+#define HARFBUZZ_DUMP_H
+
+#include <stdio.h>
+#include "harfbuzz-gsub.h"
+#include "harfbuzz-gpos.h"
+
+HB_BEGIN_HEADER
+
+void HB_Dump_GSUB_Table (HB_GSUB gsub, FILE *stream);
+void HB_Dump_GPOS_Table (HB_GPOS gpos, FILE *stream);
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_DUMP_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-external.h b/third_party/harfbuzz/src/harfbuzz-external.h
new file mode 100644
index 0000000..760749b
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-external.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_EXTERNAL_H
+#define HARFBUZZ_EXTERNAL_H
+
+#include "harfbuzz-global.h"
+
+HB_BEGIN_HEADER
+
+/* This header contains some methods that are not part of
+   Harfbuzz itself, but referenced by it.
+   They need to be provided by the application/library
+*/
+
+
+/*
+ see http://www.unicode.org/reports/tr14/tr14-19.html
+ we don't use the XX, AI and CB properties and map them to AL instead.
+ as we don't support any EBDIC based OS'es, NL is ignored and mapped to AL as well.
+*/
+typedef enum {
+    HB_LineBreak_OP, HB_LineBreak_CL, HB_LineBreak_QU, HB_LineBreak_GL, HB_LineBreak_NS,
+    HB_LineBreak_EX, HB_LineBreak_SY, HB_LineBreak_IS, HB_LineBreak_PR, HB_LineBreak_PO,
+    HB_LineBreak_NU, HB_LineBreak_AL, HB_LineBreak_ID, HB_LineBreak_IN, HB_LineBreak_HY,
+    HB_LineBreak_BA, HB_LineBreak_BB, HB_LineBreak_B2, HB_LineBreak_ZW, HB_LineBreak_CM,
+    HB_LineBreak_WJ, HB_LineBreak_H2, HB_LineBreak_H3, HB_LineBreak_JL, HB_LineBreak_JV,
+    HB_LineBreak_JT, HB_LineBreak_SA, HB_LineBreak_SG,
+    HB_LineBreak_SP, HB_LineBreak_CR, HB_LineBreak_LF, HB_LineBreak_BK
+} HB_LineBreakClass;
+
+typedef enum 
+{
+    HB_NoCategory,
+
+    HB_Mark_NonSpacing,          /*   Mn */
+    HB_Mark_SpacingCombining,    /*   Mc */
+    HB_Mark_Enclosing,           /*   Me */
+
+    HB_Number_DecimalDigit,      /*   Nd */
+    HB_Number_Letter,            /*   Nl */
+    HB_Number_Other,             /*   No */
+
+    HB_Separator_Space,          /*   Zs */
+    HB_Separator_Line,           /*   Zl */
+    HB_Separator_Paragraph,      /*   Zp */
+
+    HB_Other_Control,            /*   Cc */
+    HB_Other_Format,             /*   Cf */
+    HB_Other_Surrogate,          /*   Cs */
+    HB_Other_PrivateUse,         /*   Co */
+    HB_Other_NotAssigned,        /*   Cn */
+
+    HB_Letter_Uppercase,         /*   Lu */
+    HB_Letter_Lowercase,         /*   Ll */
+    HB_Letter_Titlecase,         /*   Lt */
+    HB_Letter_Modifier,          /*   Lm */
+    HB_Letter_Other,             /*   Lo */
+
+    HB_Punctuation_Connector,    /*   Pc */
+    HB_Punctuation_Dash,         /*   Pd */
+    HB_Punctuation_Open,         /*   Ps */
+    HB_Punctuation_Close,        /*   Pe */
+    HB_Punctuation_InitialQuote, /*   Pi */
+    HB_Punctuation_FinalQuote,   /*   Pf */
+    HB_Punctuation_Other,        /*   Po */
+
+    HB_Symbol_Math,              /*   Sm */
+    HB_Symbol_Currency,          /*   Sc */
+    HB_Symbol_Modifier,          /*   Sk */
+    HB_Symbol_Other              /*   So */
+} HB_CharCategory;
+
+typedef enum
+{
+    HB_Grapheme_Other, 
+    HB_Grapheme_CR,
+    HB_Grapheme_LF,
+    HB_Grapheme_Control,
+    HB_Grapheme_Extend,
+    HB_Grapheme_L, 
+    HB_Grapheme_V, 
+    HB_Grapheme_T, 
+    HB_Grapheme_LV, 
+    HB_Grapheme_LVT
+} HB_GraphemeClass;
+
+
+typedef enum
+{
+    HB_Word_Other,
+    HB_Word_Format,
+    HB_Word_Katakana,
+    HB_Word_ALetter,
+    HB_Word_MidLetter,
+    HB_Word_MidNum,
+    HB_Word_Numeric,
+    HB_Word_ExtendNumLet
+} HB_WordClass;
+
+
+typedef enum
+{
+    HB_Sentence_Other,
+    HB_Sentence_Sep,
+    HB_Sentence_Format,
+    HB_Sentence_Sp,
+    HB_Sentence_Lower,
+    HB_Sentence_Upper,
+    HB_Sentence_OLetter,
+    HB_Sentence_Numeric,
+    HB_Sentence_ATerm,
+    HB_Sentence_STerm,
+    HB_Sentence_Close
+} HB_SentenceClass;
+
+HB_GraphemeClass HB_GetGraphemeClass(HB_UChar32 ch);
+HB_WordClass HB_GetWordClass(HB_UChar32 ch);
+HB_SentenceClass HB_GetSentenceClass(HB_UChar32 ch);
+HB_LineBreakClass HB_GetLineBreakClass(HB_UChar32 ch);
+
+void HB_GetGraphemeAndLineBreakClass(HB_UChar32 ch, HB_GraphemeClass *grapheme, HB_LineBreakClass *lineBreak);
+void HB_GetUnicodeCharProperties(HB_UChar32 ch, HB_CharCategory *category, int *combiningClass);
+HB_CharCategory HB_GetUnicodeCharCategory(HB_UChar32 ch);
+int HB_GetUnicodeCharCombiningClass(HB_UChar32 ch);
+HB_UChar16 HB_GetMirroredChar(HB_UChar16 ch);
+
+void *HB_Library_Resolve(const char *library, const char *symbol);
+
+void *HB_TextCodecForMib(int mib);
+char *HB_TextCodec_ConvertFromUnicode(void *codec, const HB_UChar16 *unicode, hb_uint32 length, hb_uint32 *outputLength);
+void HB_TextCodec_FreeResult(char *);
+
+HB_END_HEADER
+
+#endif
diff --git a/third_party/harfbuzz/src/harfbuzz-gdef-private.h b/third_party/harfbuzz/src/harfbuzz-gdef-private.h
new file mode 100644
index 0000000..da06b6f
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-gdef-private.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_GDEF_PRIVATE_H
+#define HARFBUZZ_GDEF_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-stream-private.h"
+#include "harfbuzz-buffer-private.h"
+#include "harfbuzz-gdef.h"
+
+HB_BEGIN_HEADER
+
+
+/* Attachment related structures */
+
+struct  HB_AttachPoint_
+{
+  HB_UShort   PointCount;             /* size of the PointIndex array */
+  HB_UShort*  PointIndex;             /* array of contour points      */
+};
+
+/* Ligature Caret related structures */
+
+struct  HB_CaretValueFormat1_
+{
+  HB_Short  Coordinate;               /* x or y value (in design units) */
+};
+
+typedef struct HB_CaretValueFormat1_  HB_CaretValueFormat1;
+
+
+struct  HB_CaretValueFormat2_
+{
+  HB_UShort  CaretValuePoint;         /* contour point index on glyph */
+};
+
+typedef struct HB_CaretValueFormat2_  HB_CaretValueFormat2;
+
+
+struct  HB_CaretValueFormat3_
+{
+  HB_Short    Coordinate;             /* x or y value (in design units) */
+  HB_Device  Device;                 /* Device table for x or y value  */
+};
+
+typedef struct HB_CaretValueFormat3_  HB_CaretValueFormat3;
+
+
+struct  HB_CaretValueFormat4_
+{
+  HB_UShort  IdCaretValue;            /* metric ID */
+};
+
+typedef struct HB_CaretValueFormat4_  HB_CaretValueFormat4;
+
+
+struct  HB_CaretValue_
+{
+  HB_UShort  CaretValueFormat;        /* 1, 2, 3, or 4 */
+
+  union
+  {
+    HB_CaretValueFormat1  cvf1;
+    HB_CaretValueFormat2  cvf2;
+    HB_CaretValueFormat3  cvf3;
+    HB_CaretValueFormat4  cvf4;
+  } cvf;
+};
+
+typedef struct HB_CaretValue_  HB_CaretValue;
+
+
+struct  HB_LigGlyph_
+{
+  HB_Bool          loaded;
+
+  HB_UShort        CaretCount;        /* number of caret values */
+  HB_CaretValue*  CaretValue;        /* array of caret values  */
+};
+
+
+HB_INTERNAL HB_Error
+_HB_GDEF_Add_Glyph_Property( HB_GDEFHeader* gdef,
+				       HB_UShort        glyphID,
+				       HB_UShort        property );
+
+HB_INTERNAL HB_Error
+_HB_GDEF_Check_Property( HB_GDEFHeader* gdef,
+				   HB_GlyphItem    item,
+				   HB_UShort        flags,
+				   HB_UShort*       property );
+
+HB_INTERNAL HB_Error
+_HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( HB_GDEFHeader* gdef,
+						  HB_Stream      input,
+						  HB_Lookup*     lo,
+						  HB_UShort      num_lookups );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GDEF_PRIVATE_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-gdef.c b/third_party/harfbuzz/src/harfbuzz-gdef.c
new file mode 100644
index 0000000..ff3a1f4
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-gdef.c
@@ -0,0 +1,1159 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-gdef-private.h"
+#include "harfbuzz-open-private.h"
+
+static HB_Error  Load_AttachList( HB_AttachList*  al,
+				  HB_Stream        stream );
+static HB_Error  Load_LigCaretList( HB_LigCaretList*  lcl,
+				    HB_Stream          stream );
+
+static void  Free_AttachList( HB_AttachList*  al);
+static void  Free_LigCaretList( HB_LigCaretList*  lcl);
+
+static void  Free_NewGlyphClasses( HB_GDEFHeader*  gdef);
+
+
+
+/* GDEF glyph classes */
+
+#define UNCLASSIFIED_GLYPH  0
+#define SIMPLE_GLYPH        1
+#define LIGATURE_GLYPH      2
+#define MARK_GLYPH          3
+#define COMPONENT_GLYPH     4
+
+
+
+
+
+
+HB_Error  HB_New_GDEF_Table( HB_GDEFHeader** retptr )
+{
+  HB_Error         error;
+
+  HB_GDEFHeader*  gdef;
+
+  if ( !retptr )
+    return ERR(HB_Err_Invalid_Argument);
+
+  if ( ALLOC( gdef, sizeof( *gdef ) ) )
+    return error;
+
+  gdef->GlyphClassDef.loaded = FALSE;
+  gdef->AttachList.loaded = FALSE;
+  gdef->LigCaretList.loaded = FALSE;
+  gdef->MarkAttachClassDef_offset = 0;
+  gdef->MarkAttachClassDef.loaded = FALSE;
+
+  gdef->LastGlyph = 0;
+  gdef->NewGlyphClasses = NULL;
+
+  *retptr = gdef;
+
+  return HB_Err_Ok;
+}
+
+
+HB_Error  HB_Load_GDEF_Table( HB_Stream stream, 
+			      HB_GDEFHeader** retptr )
+{
+  HB_Error         error;
+  HB_UInt         cur_offset, new_offset, base_offset;
+
+  HB_GDEFHeader*  gdef;
+
+
+  if ( !retptr )
+    return ERR(HB_Err_Invalid_Argument);
+
+  if ( GOTO_Table( TTAG_GDEF ) )
+    return error;
+
+  if (( error = HB_New_GDEF_Table ( &gdef ) ))
+    return error;
+
+  base_offset = FILE_Pos();
+
+  /* skip version */
+
+  if ( FILE_Seek( base_offset + 4L ) ||
+       ACCESS_Frame( 2L ) )
+    goto Fail0;
+
+  new_offset = GET_UShort();
+
+  FORGET_Frame();
+
+  /* all GDEF subtables are optional */
+
+  if ( new_offset )
+  {
+    new_offset += base_offset;
+
+    /* only classes 1-4 are allowed here */
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_ClassDefinition( &gdef->GlyphClassDef, 5,
+					 stream ) ) != HB_Err_Ok )
+      goto Fail0;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail1;
+
+  new_offset = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( new_offset )
+  {
+    new_offset += base_offset;
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_AttachList( &gdef->AttachList,
+				    stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  new_offset = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( new_offset )
+  {
+    new_offset += base_offset;
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_LigCaretList( &gdef->LigCaretList,
+				      stream ) ) != HB_Err_Ok )
+      goto Fail2;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  /* OpenType 1.2 has introduced the `MarkAttachClassDef' field.  We
+     first have to scan the LookupFlag values to find out whether we
+     must load it or not.  Here we only store the offset of the table. */
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  new_offset = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( new_offset )
+    gdef->MarkAttachClassDef_offset = new_offset + base_offset;
+  else
+    gdef->MarkAttachClassDef_offset = 0;
+
+  *retptr = gdef;
+
+  return HB_Err_Ok;
+
+Fail3:
+  Free_LigCaretList( &gdef->LigCaretList );
+  
+Fail2:
+  Free_AttachList( &gdef->AttachList );
+
+Fail1:
+  _HB_OPEN_Free_ClassDefinition( &gdef->GlyphClassDef );
+
+Fail0:
+  FREE( gdef );
+
+  return error;
+}
+
+
+HB_Error  HB_Done_GDEF_Table ( HB_GDEFHeader* gdef ) 
+{  
+  Free_LigCaretList( &gdef->LigCaretList );
+  Free_AttachList( &gdef->AttachList );
+  _HB_OPEN_Free_ClassDefinition( &gdef->GlyphClassDef );
+  _HB_OPEN_Free_ClassDefinition( &gdef->MarkAttachClassDef );
+  
+  Free_NewGlyphClasses( gdef );
+
+  FREE( gdef );
+
+  return HB_Err_Ok;
+}
+
+
+
+
+/*******************************
+ * AttachList related functions
+ *******************************/
+
+
+/* AttachPoint */
+
+static HB_Error  Load_AttachPoint( HB_AttachPoint*  ap,
+				   HB_Stream         stream )
+{
+  HB_Error  error;
+
+  HB_UShort   n, count;
+  HB_UShort*  pi;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = ap->PointCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ap->PointIndex = NULL;
+
+  if ( count )
+  {
+    if ( ALLOC_ARRAY( ap->PointIndex, count, HB_UShort ) )
+      return error;
+
+    pi = ap->PointIndex;
+
+    if ( ACCESS_Frame( count * 2L ) )
+    {
+      FREE( pi );
+      return error;
+    }
+
+    for ( n = 0; n < count; n++ )
+      pi[n] = GET_UShort();
+
+    FORGET_Frame();
+  }
+
+  return HB_Err_Ok;
+}
+
+
+static void  Free_AttachPoint( HB_AttachPoint*  ap )
+{
+  FREE( ap->PointIndex );
+}
+
+
+/* AttachList */
+
+static HB_Error  Load_AttachList( HB_AttachList*  al,
+				  HB_Stream        stream )
+{
+  HB_Error  error;
+
+  HB_UShort         n, m, count;
+  HB_UInt          cur_offset, new_offset, base_offset;
+
+  HB_AttachPoint*  ap;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &al->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = al->GlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  al->AttachPoint = NULL;
+
+  if ( ALLOC_ARRAY( al->AttachPoint, count, HB_AttachPoint ) )
+    goto Fail2;
+
+  ap = al->AttachPoint;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_AttachPoint( &ap[n], stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  al->loaded = TRUE;
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_AttachPoint( &ap[m] );
+
+  FREE( ap );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &al->Coverage );
+  return error;
+}
+
+
+static void  Free_AttachList( HB_AttachList*  al)
+{
+  HB_UShort         n, count;
+
+  HB_AttachPoint*  ap;
+
+
+  if ( !al->loaded )
+    return;
+
+  if ( al->AttachPoint )
+  {
+    count = al->GlyphCount;
+    ap    = al->AttachPoint;
+
+    for ( n = 0; n < count; n++ )
+      Free_AttachPoint( &ap[n] );
+
+    FREE( ap );
+  }
+
+  _HB_OPEN_Free_Coverage( &al->Coverage );
+}
+
+
+
+/*********************************
+ * LigCaretList related functions
+ *********************************/
+
+
+/* CaretValueFormat1 */
+/* CaretValueFormat2 */
+/* CaretValueFormat3 */
+/* CaretValueFormat4 */
+
+static HB_Error  Load_CaretValue( HB_CaretValue*  cv,
+				  HB_Stream        stream )
+{
+  HB_Error  error;
+
+  HB_UInt cur_offset, new_offset, base_offset;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  cv->CaretValueFormat = GET_UShort();
+
+  FORGET_Frame();
+
+  switch ( cv->CaretValueFormat )
+  {
+  case 1:
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+
+    cv->cvf.cvf1.Coordinate = GET_Short();
+
+    FORGET_Frame();
+
+    break;
+
+  case 2:
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+
+    cv->cvf.cvf2.CaretValuePoint = GET_UShort();
+
+    FORGET_Frame();
+
+    break;
+
+  case 3:
+    if ( ACCESS_Frame( 4L ) )
+      return error;
+
+    cv->cvf.cvf3.Coordinate = GET_Short();
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Device( &cv->cvf.cvf3.Device,
+				stream ) ) != HB_Err_Ok )
+      return error;
+    (void)FILE_Seek( cur_offset );
+
+    break;
+
+  case 4:
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+
+    cv->cvf.cvf4.IdCaretValue = GET_UShort();
+
+    FORGET_Frame();
+    break;
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;
+}
+
+
+static void  Free_CaretValue( HB_CaretValue*  cv)
+{
+  if ( cv->CaretValueFormat == 3 )
+    _HB_OPEN_Free_Device( &cv->cvf.cvf3.Device );
+}
+
+
+/* LigGlyph */
+
+static HB_Error  Load_LigGlyph( HB_LigGlyph*  lg,
+				HB_Stream      stream )
+{
+  HB_Error  error;
+
+  HB_UShort        n, m, count;
+  HB_UInt         cur_offset, new_offset, base_offset;
+
+  HB_CaretValue*  cv;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = lg->CaretCount = GET_UShort();
+
+  FORGET_Frame();
+
+  lg->CaretValue = NULL;
+
+  if ( ALLOC_ARRAY( lg->CaretValue, count, HB_CaretValue ) )
+    return error;
+
+  cv = lg->CaretValue;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_CaretValue( &cv[n], stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_CaretValue( &cv[m] );
+
+  FREE( cv );
+  return error;
+}
+
+
+static void  Free_LigGlyph( HB_LigGlyph*  lg)
+{
+  HB_UShort        n, count;
+
+  HB_CaretValue*  cv;
+
+
+  if ( lg->CaretValue )
+  {
+    count = lg->CaretCount;
+    cv    = lg->CaretValue;
+
+    for ( n = 0; n < count; n++ )
+      Free_CaretValue( &cv[n] );
+
+    FREE( cv );
+  }
+}
+
+
+/* LigCaretList */
+
+static HB_Error  Load_LigCaretList( HB_LigCaretList*  lcl,
+				    HB_Stream          stream )
+{
+  HB_Error  error;
+
+  HB_UShort      m, n, count;
+  HB_UInt       cur_offset, new_offset, base_offset;
+
+  HB_LigGlyph*  lg;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &lcl->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = lcl->LigGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  lcl->LigGlyph = NULL;
+
+  if ( ALLOC_ARRAY( lcl->LigGlyph, count, HB_LigGlyph ) )
+    goto Fail2;
+
+  lg = lcl->LigGlyph;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_LigGlyph( &lg[n], stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  lcl->loaded = TRUE;
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_LigGlyph( &lg[m] );
+
+  FREE( lg );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &lcl->Coverage );
+  return error;
+}
+
+
+static void  Free_LigCaretList( HB_LigCaretList*  lcl )
+{
+  HB_UShort      n, count;
+
+  HB_LigGlyph*  lg;
+
+
+  if ( !lcl->loaded )
+    return;
+
+  if ( lcl->LigGlyph )
+  {
+    count = lcl->LigGlyphCount;
+    lg    = lcl->LigGlyph;
+
+    for ( n = 0; n < count; n++ )
+      Free_LigGlyph( &lg[n] );
+
+    FREE( lg );
+  }
+
+  _HB_OPEN_Free_Coverage( &lcl->Coverage );
+}
+
+
+
+/***********
+ * GDEF API
+ ***********/
+
+
+static HB_UShort  Get_New_Class( HB_GDEFHeader*  gdef,
+				 HB_UShort        glyphID,
+				 HB_UShort        index )
+{
+  HB_UShort              glyph_index, array_index, count;
+  HB_UShort              byte, bits;
+  
+  HB_ClassRangeRecord*  gcrr;
+  HB_UShort**            ngc;
+
+
+  if ( glyphID >= gdef->LastGlyph )
+    return 0;
+
+  count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount;
+  gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
+  ngc  = gdef->NewGlyphClasses;
+
+  if ( index < count && glyphID < gcrr[index].Start )
+  {
+    array_index = index;
+    if ( index == 0 )
+      glyph_index = glyphID;
+    else
+      glyph_index = glyphID - gcrr[index - 1].End - 1;
+  }
+  else
+  {
+    array_index = index + 1;
+    glyph_index = glyphID - gcrr[index].End - 1;
+  }
+
+  byte = ngc[array_index][glyph_index / 4];
+  bits = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 );
+
+  return bits & 0x000F;
+}
+
+
+
+HB_Error  HB_GDEF_Get_Glyph_Property( HB_GDEFHeader*  gdef,
+				      HB_UShort        glyphID,
+				      HB_UShort*       property )
+{
+  HB_UShort class = 0, index = 0; /* shut compiler up */
+
+  HB_Error  error;
+
+
+  if ( !gdef || !property )
+    return ERR(HB_Err_Invalid_Argument);
+
+  /* first, we check for mark attach classes */
+
+  if ( gdef->MarkAttachClassDef.loaded )
+  {
+    error = _HB_OPEN_Get_Class( &gdef->MarkAttachClassDef, glyphID, &class, &index );
+    if ( error && error != HB_Err_Not_Covered )
+      return error;
+    if ( !error )
+    {
+      *property = class << 8;
+      return HB_Err_Ok;
+    }
+  }
+
+  error = _HB_OPEN_Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
+  if ( error && error != HB_Err_Not_Covered )
+    return error;
+
+  /* if we have a constructed class table, check whether additional
+     values have been assigned                                      */
+
+  if ( error == HB_Err_Not_Covered && gdef->NewGlyphClasses )
+    class = Get_New_Class( gdef, glyphID, index );
+
+  switch ( class )
+  {
+  default:
+  case UNCLASSIFIED_GLYPH:
+    *property = 0;
+    break;
+
+  case SIMPLE_GLYPH:
+    *property = HB_GDEF_BASE_GLYPH;
+    break;
+
+  case LIGATURE_GLYPH:
+    *property = HB_GDEF_LIGATURE;
+    break;
+
+  case MARK_GLYPH:
+    *property = HB_GDEF_MARK;
+    break;
+
+  case COMPONENT_GLYPH:
+    *property = HB_GDEF_COMPONENT;
+    break;
+  }
+
+  return HB_Err_Ok;
+}
+
+
+static HB_Error  Make_ClassRange( HB_ClassDefinition*  cd,
+				  HB_UShort             start,
+				  HB_UShort             end,
+				  HB_UShort             class )
+{
+  HB_Error               error;
+  HB_UShort              index;
+
+  HB_ClassDefFormat2*   cdf2;
+  HB_ClassRangeRecord*  crr;
+
+
+  cdf2 = &cd->cd.cd2;
+
+  if ( REALLOC_ARRAY( cdf2->ClassRangeRecord,
+		      cdf2->ClassRangeCount + 1 ,
+		      HB_ClassRangeRecord ) )
+    return error;
+
+  cdf2->ClassRangeCount++;
+
+  crr   = cdf2->ClassRangeRecord;
+  index = cdf2->ClassRangeCount - 1;
+
+  crr[index].Start = start;
+  crr[index].End   = end;
+  crr[index].Class = class;
+
+  return HB_Err_Ok;
+}
+
+
+
+HB_Error  HB_GDEF_Build_ClassDefinition( HB_GDEFHeader*  gdef,
+					 HB_UShort        num_glyphs,
+					 HB_UShort        glyph_count,
+					 HB_UShort*       glyph_array,
+					 HB_UShort*       class_array )
+{
+  HB_UShort              start, curr_glyph, curr_class;
+  HB_UShort              n, m, count;
+  HB_Error               error;
+
+  HB_ClassDefinition*   gcd;
+  HB_ClassRangeRecord*  gcrr;
+  HB_UShort**            ngc;
+
+
+  if ( !gdef || !glyph_array || !class_array )
+    return ERR(HB_Err_Invalid_Argument);
+
+  gcd = &gdef->GlyphClassDef;
+
+  /* We build a format 2 table */
+
+  gcd->ClassFormat = 2;
+
+  gcd->cd.cd2.ClassRangeCount  = 0;
+  gcd->cd.cd2.ClassRangeRecord = NULL;
+
+  start      = glyph_array[0];
+  curr_class = class_array[0];
+  curr_glyph = start;
+
+  if ( curr_class >= 5 )
+  {
+    error = ERR(HB_Err_Invalid_Argument);
+    goto Fail4;
+  }
+
+  glyph_count--;
+
+  for ( n = 0; n < glyph_count + 1; n++ )
+  {
+    if ( curr_glyph == glyph_array[n] && curr_class == class_array[n] )
+    {
+      if ( n == glyph_count )
+      {
+	if ( ( error = Make_ClassRange( gcd, start,
+					curr_glyph,
+					curr_class) ) != HB_Err_Ok )
+	  goto Fail3;
+      }
+      else
+      {
+	if ( curr_glyph == 0xFFFF )
+	{
+	  error = ERR(HB_Err_Invalid_Argument);
+	  goto Fail3;
+	}
+	else
+	  curr_glyph++;
+      }
+    }
+    else
+    {
+      if ( ( error = Make_ClassRange( gcd, start,
+				      curr_glyph - 1,
+				      curr_class) ) != HB_Err_Ok )
+	goto Fail3;
+
+      if ( curr_glyph > glyph_array[n] )
+      {
+	error = ERR(HB_Err_Invalid_Argument);
+	goto Fail3;
+      }
+
+      start      = glyph_array[n];
+      curr_class = class_array[n];
+      curr_glyph = start;
+
+      if ( curr_class >= 5 )
+      {
+	error = ERR(HB_Err_Invalid_Argument);
+	goto Fail3;
+      }
+
+      if ( n == glyph_count )
+      {
+	if ( ( error = Make_ClassRange( gcd, start,
+					curr_glyph,
+					curr_class) ) != HB_Err_Ok )
+	  goto Fail3;
+      }
+      else
+      {
+	if ( curr_glyph == 0xFFFF )
+	{
+	  error = ERR(HB_Err_Invalid_Argument);
+	  goto Fail3;
+	}
+	else
+	  curr_glyph++;
+      }
+    }
+  }
+
+  /* now prepare the arrays for class values assigned during the lookup
+     process                                                            */
+
+  if ( ALLOC_ARRAY( gdef->NewGlyphClasses,
+		    gcd->cd.cd2.ClassRangeCount + 1, HB_UShort* ) )
+    goto Fail3;
+
+  count = gcd->cd.cd2.ClassRangeCount;
+  gcrr  = gcd->cd.cd2.ClassRangeRecord;
+  ngc   = gdef->NewGlyphClasses;
+
+  /* We allocate arrays for all glyphs not covered by the class range
+     records.  Each element holds four class values.                  */
+
+  if ( count > 0 )
+  {
+      if ( gcrr[0].Start )
+      {
+	if ( ALLOC_ARRAY( ngc[0], ( gcrr[0].Start + 3 ) / 4, HB_UShort ) )
+	  goto Fail2;
+      }
+
+      for ( n = 1; n < count; n++ )
+      {
+	if ( gcrr[n].Start - gcrr[n - 1].End > 1 )
+	  if ( ALLOC_ARRAY( ngc[n],
+			    ( gcrr[n].Start - gcrr[n - 1].End + 2 ) / 4,
+			    HB_UShort ) )
+	    goto Fail1;
+      }
+
+      if ( gcrr[count - 1].End != num_glyphs - 1 )
+      {
+	if ( ALLOC_ARRAY( ngc[count],
+			  ( num_glyphs - gcrr[count - 1].End + 2 ) / 4,
+			  HB_UShort ) )
+	    goto Fail1;
+      }
+  }
+  else if ( num_glyphs > 0 )
+  {
+      if ( ALLOC_ARRAY( ngc[count],
+			( num_glyphs + 3 ) / 4,
+			HB_UShort ) )
+	  goto Fail2;
+  }
+      
+  gdef->LastGlyph = num_glyphs - 1;
+
+  gdef->MarkAttachClassDef_offset = 0L;
+  gdef->MarkAttachClassDef.loaded = FALSE;
+
+  gcd->loaded = TRUE;
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    FREE( ngc[m] );
+
+Fail2:
+  FREE( gdef->NewGlyphClasses );
+
+Fail3:
+  FREE( gcd->cd.cd2.ClassRangeRecord );
+
+Fail4:
+  return error;
+}
+
+
+static void  Free_NewGlyphClasses( HB_GDEFHeader*  gdef )
+{
+  HB_UShort**  ngc;
+  HB_UShort    n, count;
+
+
+  if ( gdef->NewGlyphClasses )
+  {
+    count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount + 1;
+    ngc   = gdef->NewGlyphClasses;
+
+    for ( n = 0; n < count; n++ )
+      FREE( ngc[n] );
+
+    FREE( ngc );
+  }
+}
+
+
+HB_INTERNAL HB_Error
+_HB_GDEF_Add_Glyph_Property( HB_GDEFHeader* gdef,
+			      HB_UShort        glyphID,
+			      HB_UShort        property )
+{
+  HB_Error               error;
+  HB_UShort              class, new_class, index = 0; /* shut compiler up */
+  HB_UShort              byte, bits, mask;
+  HB_UShort              array_index, glyph_index, count;
+
+  HB_ClassRangeRecord*  gcrr;
+  HB_UShort**            ngc;
+
+
+  error = _HB_OPEN_Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
+  if ( error && error != HB_Err_Not_Covered )
+    return error;
+
+  /* we don't accept glyphs covered in `GlyphClassDef' */
+
+  if ( !error )
+    return HB_Err_Not_Covered;
+
+  switch ( property )
+  {
+  case 0:
+    new_class = UNCLASSIFIED_GLYPH;
+    break;
+
+  case HB_GDEF_BASE_GLYPH:
+    new_class = SIMPLE_GLYPH;
+    break;
+
+  case HB_GDEF_LIGATURE:
+    new_class = LIGATURE_GLYPH;
+    break;
+
+  case HB_GDEF_MARK:
+    new_class = MARK_GLYPH;
+    break;
+
+  case HB_GDEF_COMPONENT:
+    new_class = COMPONENT_GLYPH;
+    break;
+
+  default:
+    return ERR(HB_Err_Invalid_Argument);
+  }
+
+  count = gdef->GlyphClassDef.cd.cd2.ClassRangeCount;
+  gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
+  ngc  = gdef->NewGlyphClasses;
+
+  if ( index < count && glyphID < gcrr[index].Start )
+  {
+    array_index = index;
+    if ( index == 0 )
+      glyph_index = glyphID;
+    else
+      glyph_index = glyphID - gcrr[index - 1].End - 1;
+  }
+  else
+  {
+    array_index = index + 1;
+    glyph_index = glyphID - gcrr[index].End - 1;
+  }
+
+  byte  = ngc[array_index][glyph_index / 4];
+  bits  = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 );
+  class = bits & 0x000F;
+
+  /* we don't overwrite existing entries */
+
+  if ( !class )
+  {
+    bits = new_class << ( 16 - ( glyph_index % 4 + 1 ) * 4 );
+    mask = ~( 0x000F << ( 16 - ( glyph_index % 4 + 1 ) * 4 ) );
+
+    ngc[array_index][glyph_index / 4] &= mask;
+    ngc[array_index][glyph_index / 4] |= bits;
+  }
+
+  return HB_Err_Ok;
+}
+
+
+HB_INTERNAL HB_Error
+_HB_GDEF_Check_Property( HB_GDEFHeader* gdef,
+			  HB_GlyphItem    gitem,
+			  HB_UShort        flags,
+			  HB_UShort*       property )
+{
+  HB_Error  error;
+
+  if ( gdef )
+  {
+    HB_UShort basic_glyph_class;
+    HB_UShort desired_attachment_class;
+
+    if ( gitem->gproperties == HB_GLYPH_PROPERTIES_UNKNOWN )
+    {
+      error = HB_GDEF_Get_Glyph_Property( gdef, gitem->gindex, &gitem->gproperties );
+      if ( error )
+	return error;
+    }
+
+    *property = gitem->gproperties;
+
+    /* If the glyph was found in the MarkAttachmentClass table,
+     * then that class value is the high byte of the result,
+     * otherwise the low byte contains the basic type of the glyph
+     * as defined by the GlyphClassDef table.
+     */
+    if ( *property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS  )
+      basic_glyph_class = HB_GDEF_MARK;
+    else
+      basic_glyph_class = *property;
+
+    /* Return Not_Covered, if, for example, basic_glyph_class
+     * is HB_GDEF_LIGATURE and LookFlags includes HB_LOOKUP_FLAG_IGNORE_LIGATURES
+     */
+    if ( flags & basic_glyph_class )
+      return HB_Err_Not_Covered;
+    
+    /* The high byte of LookupFlags has the meaning
+     * "ignore marks of attachment type different than
+     * the attachment type specified."
+     */
+    desired_attachment_class = flags & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS;
+    if ( desired_attachment_class )
+    {
+      if ( basic_glyph_class == HB_GDEF_MARK &&
+	   *property != desired_attachment_class )
+	return HB_Err_Not_Covered;
+    }
+  } else {
+      *property = 0;
+  }
+
+  return HB_Err_Ok;
+}
+
+HB_INTERNAL HB_Error
+_HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( HB_GDEFHeader* gdef,
+						  HB_Stream      stream,
+						  HB_Lookup*     lo,
+						  HB_UShort      num_lookups)
+{
+  HB_Error   error = HB_Err_Ok;
+  HB_UShort  i;
+
+  /* We now check the LookupFlags for values larger than 0xFF to find
+     out whether we need to load the `MarkAttachClassDef' field of the
+     GDEF table -- this hack is necessary for OpenType 1.2 tables since
+     the version field of the GDEF table hasn't been incremented.
+
+     For constructed GDEF tables, we only load it if
+     `MarkAttachClassDef_offset' is not zero (nevertheless, a build of
+     a constructed mark attach table is not supported currently).       */
+
+  if ( gdef &&
+       gdef->MarkAttachClassDef_offset && !gdef->MarkAttachClassDef.loaded )
+  {
+    for ( i = 0; i < num_lookups; i++ )
+    {
+
+      if ( lo[i].LookupFlag & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
+      {
+	if ( FILE_Seek( gdef->MarkAttachClassDef_offset ) ||
+	     ( error = _HB_OPEN_Load_ClassDefinition( &gdef->MarkAttachClassDef,
+					     256, stream ) ) != HB_Err_Ok )
+	  goto Done;
+
+	break;
+      }
+    }
+  }
+
+Done:
+  return error;
+}
+
+/* END */
diff --git a/third_party/harfbuzz/src/harfbuzz-gdef.h b/third_party/harfbuzz/src/harfbuzz-gdef.h
new file mode 100644
index 0000000..b6dcadc
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-gdef.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_GDEF_H
+#define HARFBUZZ_GDEF_H
+
+#include "harfbuzz-open.h"
+#include "harfbuzz-stream.h"
+
+HB_BEGIN_HEADER
+
+/* GDEF glyph properties.  Note that HB_GDEF_COMPONENT has no corresponding
+ * flag in the LookupFlag field.     */
+#define HB_GDEF_BASE_GLYPH  0x0002
+#define HB_GDEF_LIGATURE    0x0004
+#define HB_GDEF_MARK        0x0008
+#define HB_GDEF_COMPONENT   0x0010
+
+
+typedef struct HB_AttachPoint_  HB_AttachPoint;
+
+
+struct  HB_AttachList_
+{
+  HB_Bool           loaded;
+
+  HB_Coverage       Coverage;         /* Coverage table              */
+  HB_UShort         GlyphCount;       /* number of glyphs with
+					 attachments                 */
+  HB_AttachPoint*   AttachPoint;      /* array of AttachPoint tables */
+};
+
+typedef struct HB_AttachList_  HB_AttachList;
+
+typedef struct HB_LigGlyph_  HB_LigGlyph;
+
+struct  HB_LigCaretList_
+{
+  HB_Bool        loaded;
+
+  HB_Coverage    Coverage;            /* Coverage table            */
+  HB_UShort      LigGlyphCount;       /* number of ligature glyphs */
+  HB_LigGlyph*   LigGlyph;            /* array of LigGlyph tables  */
+};
+
+typedef struct HB_LigCaretList_  HB_LigCaretList;
+
+
+
+/* The `NewGlyphClasses' field is not defined in the TTO specification.
+   We use it for fonts with a constructed `GlyphClassDef' structure
+   (i.e., which don't have a GDEF table) to collect glyph classes
+   assigned during the lookup process.  The number of arrays in this
+   pointer array is GlyphClassDef->cd.cd2.ClassRangeCount+1; the nth
+   array then contains the glyph class values of the glyphs not covered
+   by the ClassRangeRecords structures with index n-1 and n.  We store
+   glyph class values for four glyphs in a single array element.
+
+   `LastGlyph' is identical to the number of glyphs minus one in the
+   font; we need it only if `NewGlyphClasses' is not NULL (to have an
+   upper bound for the last array).
+
+   Note that we first store the file offset to the `MarkAttachClassDef'
+   field (which has been introduced in OpenType 1.2) -- since the
+   `Version' field value hasn't been increased to indicate that we have
+   one more field for some obscure reason, we must parse the GSUB table
+   to find out whether class values refer to this table.  Only then we
+   can finally load the MarkAttachClassDef structure if necessary.      */
+
+struct  HB_GDEFHeader_
+{
+  HB_UInt             offset;
+
+  HB_16Dot16             Version;
+
+  HB_ClassDefinition   GlyphClassDef;
+  HB_AttachList        AttachList;
+  HB_LigCaretList      LigCaretList;
+  HB_UInt             MarkAttachClassDef_offset;
+  HB_ClassDefinition   MarkAttachClassDef;        /* new in OT 1.2 */
+
+  HB_UShort            LastGlyph;
+  HB_UShort**          NewGlyphClasses;
+};
+
+typedef struct HB_GDEFHeader_   HB_GDEFHeader;
+typedef struct HB_GDEFHeader_*  HB_GDEF;
+
+
+HB_Error  HB_New_GDEF_Table( HB_GDEFHeader** retptr );
+      
+
+HB_Error  HB_Load_GDEF_Table( HB_Stream       stream,
+			      HB_GDEFHeader** gdef );
+
+
+HB_Error  HB_Done_GDEF_Table ( HB_GDEFHeader* gdef );
+
+
+HB_Error  HB_GDEF_Get_Glyph_Property( HB_GDEFHeader*  gdef,
+				      HB_UShort        glyphID,
+				      HB_UShort*       property );
+
+HB_Error  HB_GDEF_Build_ClassDefinition( HB_GDEFHeader*  gdef,
+					 HB_UShort        num_glyphs,
+					 HB_UShort        glyph_count,
+					 HB_UShort*       glyph_array,
+					 HB_UShort*       class_array );
+
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GDEF_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-global.h b/third_party/harfbuzz/src/harfbuzz-global.h
new file mode 100644
index 0000000..d4e6b46
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-global.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2007  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HARFBUZZ_GLOBAL_H
+#define HARFBUZZ_GLOBAL_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+#define HB_BEGIN_HEADER  extern "C" {
+#define HB_END_HEADER  }
+#else
+#define HB_BEGIN_HEADER  /* nothing */
+#define HB_END_HEADER  /* nothing */
+#endif
+
+HB_BEGIN_HEADER
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+#define HB_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
+          ( ( (HB_UInt)_x1 << 24 ) |     \
+            ( (HB_UInt)_x2 << 16 ) |     \
+            ( (HB_UInt)_x3 <<  8 ) |     \
+              (HB_UInt)_x4         )
+
+typedef char hb_int8;
+typedef unsigned char hb_uint8;
+typedef short hb_int16;
+typedef unsigned short hb_uint16;
+typedef int hb_int32;
+typedef unsigned int hb_uint32;
+
+typedef hb_uint8 HB_Bool;
+
+typedef hb_uint8 HB_Byte;
+typedef hb_uint16 HB_UShort;
+typedef hb_uint32 HB_UInt;
+typedef hb_int8 HB_Char;
+typedef hb_int16 HB_Short;
+typedef hb_int32 HB_Int;
+
+typedef hb_uint16 HB_UChar16;
+typedef hb_uint32 HB_UChar32;
+typedef hb_uint32 HB_Glyph;
+typedef hb_int32 HB_Fixed; /* 26.6 */
+
+#define HB_FIXED_CONSTANT(v) ((v) * 64)
+#define HB_FIXED_ROUND(v) (((v)+32) & -64)
+
+typedef hb_int32 HB_16Dot16; /* 16.16 */
+
+typedef void * HB_Pointer;
+typedef hb_uint32 HB_Tag;
+
+typedef enum {
+  /* no error */
+  HB_Err_Ok                           = 0x0000,
+  HB_Err_Not_Covered                  = 0xFFFF,
+
+  /* _hb_err() is called whenever returning the following errors,
+   * and in a couple places for HB_Err_Not_Covered too. */
+
+  /* programmer error */
+  HB_Err_Invalid_Argument             = 0x1A66,
+
+  /* font error */
+  HB_Err_Invalid_SubTable_Format      = 0x157F,
+  HB_Err_Invalid_SubTable             = 0x1570,
+  HB_Err_Read_Error                   = 0x6EAD,
+
+  /* system error */
+  HB_Err_Out_Of_Memory                = 0xDEAD
+} HB_Error;
+
+typedef struct {
+    HB_Fixed x;
+    HB_Fixed y;
+} HB_FixedPoint;
+
+typedef struct HB_Font_ *HB_Font;
+typedef struct HB_StreamRec_ *HB_Stream;
+typedef struct HB_FaceRec_ *HB_Face;
+
+HB_END_HEADER
+
+#endif
diff --git a/third_party/harfbuzz/src/harfbuzz-gpos-private.h b/third_party/harfbuzz/src/harfbuzz-gpos-private.h
new file mode 100644
index 0000000..4110700
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-gpos-private.h
@@ -0,0 +1,712 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_GPOS_PRIVATE_H
+#define HARFBUZZ_GPOS_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-stream-private.h"
+#include "harfbuzz-gpos.h"
+
+HB_BEGIN_HEADER
+
+
+/* shared tables */
+
+struct  HB_ValueRecord_
+{
+  HB_Short    XPlacement;             /* horizontal adjustment for
+					 placement                      */
+  HB_Short    YPlacement;             /* vertical adjustment for
+					 placement                      */
+  HB_Short    XAdvance;               /* horizontal adjustment for
+					 advance                        */
+  HB_Short    YAdvance;               /* vertical adjustment for
+					 advance                        */
+  HB_Device  XPlacementDevice;       /* device table for horizontal
+					 placement                      */
+  HB_Device  YPlacementDevice;       /* device table for vertical
+					 placement                      */
+  HB_Device  XAdvanceDevice;         /* device table for horizontal
+					 advance                        */
+  HB_Device  YAdvanceDevice;         /* device table for vertical
+					 advance                        */
+  HB_UShort   XIdPlacement;           /* horizontal placement metric ID */
+  HB_UShort   YIdPlacement;           /* vertical placement metric ID   */
+  HB_UShort   XIdAdvance;             /* horizontal advance metric ID   */
+  HB_UShort   YIdAdvance;             /* vertical advance metric ID     */
+};
+
+typedef struct HB_ValueRecord_  HB_ValueRecord;
+
+
+/* Mask values to scan the value format of the ValueRecord structure.
+ We always expand compressed ValueRecords of the font.              */
+
+#define HB_GPOS_FORMAT_HAVE_X_PLACEMENT         0x0001
+#define HB_GPOS_FORMAT_HAVE_Y_PLACEMENT         0x0002
+#define HB_GPOS_FORMAT_HAVE_X_ADVANCE           0x0004
+#define HB_GPOS_FORMAT_HAVE_Y_ADVANCE           0x0008
+#define HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE  0x0010
+#define HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE  0x0020
+#define HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE    0x0040
+#define HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE    0x0080
+#define HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT      0x0100
+#define HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT      0x0200
+#define HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE        0x0400
+#define HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE        0x0800
+
+
+struct  HB_AnchorFormat1_
+{
+  HB_Short   XCoordinate;             /* horizontal value */
+  HB_Short   YCoordinate;             /* vertical value   */
+};
+
+typedef struct HB_AnchorFormat1_  HB_AnchorFormat1;
+
+
+struct  HB_AnchorFormat2_
+{
+  HB_Short   XCoordinate;             /* horizontal value             */
+  HB_Short   YCoordinate;             /* vertical value               */
+  HB_UShort  AnchorPoint;             /* index to glyph contour point */
+};
+
+typedef struct HB_AnchorFormat2_  HB_AnchorFormat2;
+
+
+struct  HB_AnchorFormat3_
+{
+  HB_Short    XCoordinate;            /* horizontal value              */
+  HB_Short    YCoordinate;            /* vertical value                */
+  HB_Device  XDeviceTable;           /* device table for X coordinate */
+  HB_Device  YDeviceTable;           /* device table for Y coordinate */
+};
+
+typedef struct HB_AnchorFormat3_  HB_AnchorFormat3;
+
+
+struct  HB_AnchorFormat4_
+{
+  HB_UShort  XIdAnchor;               /* horizontal metric ID */
+  HB_UShort  YIdAnchor;               /* vertical metric ID   */
+};
+
+typedef struct HB_AnchorFormat4_  HB_AnchorFormat4;
+
+
+struct  HB_Anchor_
+{
+  HB_UShort  PosFormat;               /* 1, 2, 3, or 4 -- 0 indicates
+					 that there is no Anchor table */
+
+  union
+  {
+    HB_AnchorFormat1  af1;
+    HB_AnchorFormat2  af2;
+    HB_AnchorFormat3  af3;
+    HB_AnchorFormat4  af4;
+  } af;
+};
+
+typedef struct HB_Anchor_  HB_Anchor;
+
+
+struct  HB_MarkRecord_
+{
+  HB_UShort   Class;                  /* mark class   */
+  HB_Anchor  MarkAnchor;             /* anchor table */
+};
+
+typedef struct HB_MarkRecord_  HB_MarkRecord;
+
+
+struct  HB_MarkArray_
+{
+  HB_UShort        MarkCount;         /* number of MarkRecord tables */
+  HB_MarkRecord*  MarkRecord;        /* array of MarkRecord tables  */
+};
+
+typedef struct HB_MarkArray_  HB_MarkArray;
+
+
+/* LookupType 1 */
+
+struct  HB_SinglePosFormat1_
+{
+  HB_ValueRecord  Value;             /* ValueRecord for all covered
+					 glyphs                      */
+};
+
+typedef struct HB_SinglePosFormat1_  HB_SinglePosFormat1;
+
+
+struct  HB_SinglePosFormat2_
+{
+  HB_UShort         ValueCount;       /* number of ValueRecord tables */
+  HB_ValueRecord*  Value;            /* array of ValueRecord tables  */
+};
+
+typedef struct HB_SinglePosFormat2_  HB_SinglePosFormat2;
+
+
+struct  HB_SinglePos_
+{
+  HB_UShort     PosFormat;            /* 1 or 2         */
+  HB_Coverage  Coverage;             /* Coverage table */
+
+  HB_UShort     ValueFormat;          /* format of ValueRecord table */
+
+  union
+  {
+    HB_SinglePosFormat1  spf1;
+    HB_SinglePosFormat2  spf2;
+  } spf;
+};
+
+typedef struct HB_SinglePos_  HB_SinglePos;
+
+
+/* LookupType 2 */
+
+struct  HB_PairValueRecord_
+{
+  HB_UShort        SecondGlyph;       /* glyph ID for second glyph  */
+  HB_ValueRecord  Value1;            /* pos. data for first glyph  */
+  HB_ValueRecord  Value2;            /* pos. data for second glyph */
+};
+
+typedef struct HB_PairValueRecord_  HB_PairValueRecord;
+
+
+struct  HB_PairSet_
+{
+  HB_UShort             PairValueCount;
+				      /* number of PairValueRecord tables */
+  HB_PairValueRecord*  PairValueRecord;
+				      /* array of PairValueRecord tables  */
+};
+
+typedef struct HB_PairSet_  HB_PairSet;
+
+
+struct  HB_PairPosFormat1_
+{
+  HB_UShort     PairSetCount;         /* number of PairSet tables    */
+  HB_PairSet*  PairSet;              /* array of PairSet tables     */
+};
+
+typedef struct HB_PairPosFormat1_  HB_PairPosFormat1;
+
+
+struct  HB_Class2Record_
+{
+  HB_ValueRecord  Value1;            /* pos. data for first glyph  */
+  HB_ValueRecord  Value2;            /* pos. data for second glyph */
+};
+
+typedef struct HB_Class2Record_  HB_Class2Record;
+
+
+struct  HB_Class1Record_
+{
+  HB_Class2Record*  Class2Record;    /* array of Class2Record tables */
+};
+
+typedef struct HB_Class1Record_  HB_Class1Record;
+
+
+struct  HB_PairPosFormat2_
+{
+  HB_ClassDefinition  ClassDef1;     /* class def. for first glyph     */
+  HB_ClassDefinition  ClassDef2;     /* class def. for second glyph    */
+  HB_UShort            Class1Count;   /* number of classes in ClassDef1
+					 table                          */
+  HB_UShort            Class2Count;   /* number of classes in ClassDef2
+					 table                          */
+  HB_Class1Record*    Class1Record;  /* array of Class1Record tables   */
+};
+
+typedef struct HB_PairPosFormat2_  HB_PairPosFormat2;
+
+
+struct  HB_PairPos_
+{
+  HB_UShort     PosFormat;            /* 1 or 2         */
+  HB_Coverage  Coverage;             /* Coverage table */
+  HB_UShort     ValueFormat1;         /* format of ValueRecord table
+					 for first glyph             */
+  HB_UShort     ValueFormat2;         /* format of ValueRecord table
+					 for second glyph            */
+
+  union
+  {
+    HB_PairPosFormat1  ppf1;
+    HB_PairPosFormat2  ppf2;
+  } ppf;
+};
+
+typedef struct HB_PairPos_  HB_PairPos;
+
+
+/* LookupType 3 */
+
+struct  HB_EntryExitRecord_
+{
+  HB_Anchor  EntryAnchor;            /* entry Anchor table */
+  HB_Anchor  ExitAnchor;             /* exit Anchor table  */
+};
+
+
+typedef struct HB_EntryExitRecord_  HB_EntryExitRecord;
+
+struct  HB_CursivePos_
+{
+  HB_UShort             PosFormat;    /* always 1                         */
+  HB_Coverage          Coverage;     /* Coverage table                   */
+  HB_UShort             EntryExitCount;
+				      /* number of EntryExitRecord tables */
+  HB_EntryExitRecord*  EntryExitRecord;
+				      /* array of EntryExitRecord tables  */
+};
+
+typedef struct HB_CursivePos_  HB_CursivePos;
+
+
+/* LookupType 4 */
+
+struct  HB_BaseRecord_
+{
+  HB_Anchor*  BaseAnchor;            /* array of base glyph anchor
+					 tables                     */
+};
+
+typedef struct HB_BaseRecord_  HB_BaseRecord;
+
+
+struct  HB_BaseArray_
+{
+  HB_UShort        BaseCount;         /* number of BaseRecord tables */
+  HB_BaseRecord*  BaseRecord;        /* array of BaseRecord tables  */
+};
+
+typedef struct HB_BaseArray_  HB_BaseArray;
+
+
+struct  HB_MarkBasePos_
+{
+  HB_UShort      PosFormat;           /* always 1                  */
+  HB_Coverage   MarkCoverage;        /* mark glyph coverage table */
+  HB_Coverage   BaseCoverage;        /* base glyph coverage table */
+  HB_UShort      ClassCount;          /* number of mark classes    */
+  HB_MarkArray  MarkArray;           /* mark array table          */
+  HB_BaseArray  BaseArray;           /* base array table          */
+};
+
+typedef struct HB_MarkBasePos_  HB_MarkBasePos;
+
+
+/* LookupType 5 */
+
+struct  HB_ComponentRecord_
+{
+  HB_Anchor*  LigatureAnchor;        /* array of ligature glyph anchor
+					 tables                         */
+};
+
+typedef struct HB_ComponentRecord_  HB_ComponentRecord;
+
+
+struct  HB_LigatureAttach_
+{
+  HB_UShort             ComponentCount;
+				      /* number of ComponentRecord tables */
+  HB_ComponentRecord*  ComponentRecord;
+				      /* array of ComponentRecord tables  */
+};
+
+typedef struct HB_LigatureAttach_  HB_LigatureAttach;
+
+
+struct  HB_LigatureArray_
+{
+  HB_UShort            LigatureCount; /* number of LigatureAttach tables */
+  HB_LigatureAttach*  LigatureAttach;
+				      /* array of LigatureAttach tables  */
+};
+
+typedef struct HB_LigatureArray_  HB_LigatureArray;
+
+
+struct  HB_MarkLigPos_
+{
+  HB_UShort          PosFormat;       /* always 1                      */
+  HB_Coverage       MarkCoverage;    /* mark glyph coverage table     */
+  HB_Coverage       LigatureCoverage;
+				      /* ligature glyph coverage table */
+  HB_UShort          ClassCount;      /* number of mark classes        */
+  HB_MarkArray      MarkArray;       /* mark array table              */
+  HB_LigatureArray  LigatureArray;   /* ligature array table          */
+};
+
+typedef struct HB_MarkLigPos_  HB_MarkLigPos;
+
+
+/* LookupType 6 */
+
+struct  HB_Mark2Record_
+{
+  HB_Anchor*  Mark2Anchor;           /* array of mark glyph anchor
+					 tables                     */
+};
+
+typedef struct HB_Mark2Record_  HB_Mark2Record;
+
+
+struct  HB_Mark2Array_
+{
+  HB_UShort         Mark2Count;       /* number of Mark2Record tables */
+  HB_Mark2Record*  Mark2Record;      /* array of Mark2Record tables  */
+};
+
+typedef struct HB_Mark2Array_  HB_Mark2Array;
+
+
+struct  HB_MarkMarkPos_
+{
+  HB_UShort       PosFormat;          /* always 1                         */
+  HB_Coverage    Mark1Coverage;      /* first mark glyph coverage table  */
+  HB_Coverage    Mark2Coverage;      /* second mark glyph coverave table */
+  HB_UShort       ClassCount;         /* number of combining mark classes */
+  HB_MarkArray   Mark1Array;         /* MarkArray table for first mark   */
+  HB_Mark2Array  Mark2Array;         /* MarkArray table for second mark  */
+};
+
+typedef struct HB_MarkMarkPos_  HB_MarkMarkPos;
+
+
+/* needed by both lookup type 7 and 8 */
+
+struct  HB_PosLookupRecord_
+{
+  HB_UShort  SequenceIndex;           /* index into current
+					 glyph sequence               */
+  HB_UShort  LookupListIndex;         /* Lookup to apply to that pos. */
+};
+
+typedef struct HB_PosLookupRecord_  HB_PosLookupRecord;
+
+
+/* LookupType 7 */
+
+struct  HB_PosRule_
+{
+  HB_UShort             GlyphCount;   /* total number of input glyphs     */
+  HB_UShort             PosCount;     /* number of PosLookupRecord tables */
+  HB_UShort*            Input;        /* array of input glyph IDs         */
+  HB_PosLookupRecord*  PosLookupRecord;
+				      /* array of PosLookupRecord tables  */
+};
+
+typedef struct HB_PosRule_  HB_PosRule;
+
+
+struct  HB_PosRuleSet_
+{
+  HB_UShort     PosRuleCount;         /* number of PosRule tables */
+  HB_PosRule*  PosRule;              /* array of PosRule tables  */
+};
+
+typedef struct HB_PosRuleSet_  HB_PosRuleSet;
+
+
+struct  HB_ContextPosFormat1_
+{
+  HB_Coverage     Coverage;          /* Coverage table              */
+  HB_UShort        PosRuleSetCount;   /* number of PosRuleSet tables */
+  HB_PosRuleSet*  PosRuleSet;        /* array of PosRuleSet tables  */
+};
+
+typedef struct HB_ContextPosFormat1_  HB_ContextPosFormat1;
+
+
+struct  HB_PosClassRule_
+{
+  HB_UShort             GlyphCount;   /* total number of context classes  */
+  HB_UShort             PosCount;     /* number of PosLookupRecord tables */
+  HB_UShort*            Class;        /* array of classes                 */
+  HB_PosLookupRecord*  PosLookupRecord;
+				      /* array of PosLookupRecord tables  */
+};
+
+typedef struct HB_PosClassRule_  HB_PosClassRule;
+
+
+struct  HB_PosClassSet_
+{
+  HB_UShort          PosClassRuleCount;
+				      /* number of PosClassRule tables */
+  HB_PosClassRule*  PosClassRule;    /* array of PosClassRule tables  */
+};
+
+typedef struct HB_PosClassSet_  HB_PosClassSet;
+
+
+/* The `MaxContextLength' field is not defined in the TTO specification
+   but simplifies the implementation of this format.  It holds the
+   maximal context length used in the context rules.                    */
+
+struct  HB_ContextPosFormat2_
+{
+  HB_UShort            MaxContextLength;
+				      /* maximal context length       */
+  HB_Coverage         Coverage;      /* Coverage table               */
+  HB_ClassDefinition  ClassDef;      /* ClassDef table               */
+  HB_UShort            PosClassSetCount;
+				      /* number of PosClassSet tables */
+  HB_PosClassSet*     PosClassSet;   /* array of PosClassSet tables  */
+};
+
+typedef struct HB_ContextPosFormat2_  HB_ContextPosFormat2;
+
+
+struct  HB_ContextPosFormat3_
+{
+  HB_UShort             GlyphCount;   /* number of input glyphs           */
+  HB_UShort             PosCount;     /* number of PosLookupRecord tables */
+  HB_Coverage*         Coverage;     /* array of Coverage tables         */
+  HB_PosLookupRecord*  PosLookupRecord;
+				      /* array of PosLookupRecord tables  */
+};
+
+typedef struct HB_ContextPosFormat3_  HB_ContextPosFormat3;
+
+
+struct  HB_ContextPos_
+{
+  HB_UShort  PosFormat;               /* 1, 2, or 3     */
+
+  union
+  {
+    HB_ContextPosFormat1  cpf1;
+    HB_ContextPosFormat2  cpf2;
+    HB_ContextPosFormat3  cpf3;
+  } cpf;
+};
+
+typedef struct HB_ContextPos_  HB_ContextPos;
+
+
+/* LookupType 8 */
+
+struct  HB_ChainPosRule_
+{
+  HB_UShort             BacktrackGlyphCount;
+				      /* total number of backtrack glyphs */
+  HB_UShort*            Backtrack;    /* array of backtrack glyph IDs     */
+  HB_UShort             InputGlyphCount;
+				      /* total number of input glyphs     */
+  HB_UShort*            Input;        /* array of input glyph IDs         */
+  HB_UShort             LookaheadGlyphCount;
+				      /* total number of lookahead glyphs */
+  HB_UShort*            Lookahead;    /* array of lookahead glyph IDs     */
+  HB_UShort             PosCount;     /* number of PosLookupRecords       */
+  HB_PosLookupRecord*  PosLookupRecord;
+				      /* array of PosLookupRecords       */
+};
+
+typedef struct HB_ChainPosRule_  HB_ChainPosRule;
+
+
+struct  HB_ChainPosRuleSet_
+{
+  HB_UShort          ChainPosRuleCount;
+				      /* number of ChainPosRule tables */
+  HB_ChainPosRule*  ChainPosRule;    /* array of ChainPosRule tables  */
+};
+
+typedef struct HB_ChainPosRuleSet_  HB_ChainPosRuleSet;
+
+
+struct  HB_ChainContextPosFormat1_
+{
+  HB_Coverage          Coverage;     /* Coverage table                   */
+  HB_UShort             ChainPosRuleSetCount;
+				      /* number of ChainPosRuleSet tables */
+  HB_ChainPosRuleSet*  ChainPosRuleSet;
+				      /* array of ChainPosRuleSet tables  */
+};
+
+typedef struct HB_ChainContextPosFormat1_  HB_ChainContextPosFormat1;
+
+
+struct  HB_ChainPosClassRule_
+{
+  HB_UShort             BacktrackGlyphCount;
+				      /* total number of backtrack
+					 classes                         */
+  HB_UShort*            Backtrack;    /* array of backtrack classes      */
+  HB_UShort             InputGlyphCount;
+				      /* total number of context classes */
+  HB_UShort*            Input;        /* array of context classes        */
+  HB_UShort             LookaheadGlyphCount;
+				      /* total number of lookahead
+					 classes                         */
+  HB_UShort*            Lookahead;    /* array of lookahead classes      */
+  HB_UShort             PosCount;     /* number of PosLookupRecords      */
+  HB_PosLookupRecord*  PosLookupRecord;
+				      /* array of substitution lookups   */
+};
+
+typedef struct HB_ChainPosClassRule_  HB_ChainPosClassRule;
+
+
+struct  HB_ChainPosClassSet_
+{
+  HB_UShort               ChainPosClassRuleCount;
+				      /* number of ChainPosClassRule
+					 tables                      */
+  HB_ChainPosClassRule*  ChainPosClassRule;
+				      /* array of ChainPosClassRule
+					 tables                      */
+};
+
+typedef struct HB_ChainPosClassSet_  HB_ChainPosClassSet;
+
+
+/* The `MaxXXXLength' fields are not defined in the TTO specification
+   but simplifies the implementation of this format.  It holds the
+   maximal context length used in the specific context rules.         */
+
+struct  HB_ChainContextPosFormat2_
+{
+  HB_Coverage           Coverage;    /* Coverage table             */
+
+  HB_UShort              MaxBacktrackLength;
+				      /* maximal backtrack length   */
+  HB_ClassDefinition    BacktrackClassDef;
+				      /* BacktrackClassDef table    */
+  HB_UShort              MaxInputLength;
+				      /* maximal input length       */
+  HB_ClassDefinition    InputClassDef;
+				      /* InputClassDef table        */
+  HB_UShort              MaxLookaheadLength;
+				      /* maximal lookahead length   */
+  HB_ClassDefinition    LookaheadClassDef;
+				      /* LookaheadClassDef table    */
+
+  HB_UShort              ChainPosClassSetCount;
+				      /* number of ChainPosClassSet
+					 tables                     */
+  HB_ChainPosClassSet*  ChainPosClassSet;
+				      /* array of ChainPosClassSet
+					 tables                     */
+};
+
+typedef struct HB_ChainContextPosFormat2_  HB_ChainContextPosFormat2;
+
+
+struct  HB_ChainContextPosFormat3_
+{
+  HB_UShort             BacktrackGlyphCount;
+				      /* number of backtrack glyphs    */
+  HB_Coverage*         BacktrackCoverage;
+				      /* array of backtrack Coverage
+					 tables                        */
+  HB_UShort             InputGlyphCount;
+				      /* number of input glyphs        */
+  HB_Coverage*         InputCoverage;
+				      /* array of input coverage
+					 tables                        */
+  HB_UShort             LookaheadGlyphCount;
+				      /* number of lookahead glyphs    */
+  HB_Coverage*         LookaheadCoverage;
+				      /* array of lookahead coverage
+					 tables                        */
+  HB_UShort             PosCount;     /* number of PosLookupRecords    */
+  HB_PosLookupRecord*  PosLookupRecord;
+				      /* array of substitution lookups */
+};
+
+typedef struct HB_ChainContextPosFormat3_  HB_ChainContextPosFormat3;
+
+
+struct  HB_ChainContextPos_
+{
+  HB_UShort  PosFormat;             /* 1, 2, or 3 */
+
+  union
+  {
+    HB_ChainContextPosFormat1  ccpf1;
+    HB_ChainContextPosFormat2  ccpf2;
+    HB_ChainContextPosFormat3  ccpf3;
+  } ccpf;
+};
+
+typedef struct HB_ChainContextPos_  HB_ChainContextPos;
+
+
+#if 0
+/* LookupType 10 */
+struct HB_ExtensionPos_
+{
+  HB_UShort      PosFormat;           /* always 1 */
+  HB_UShort      LookuptType;         /* lookup-type of referenced subtable */
+  HB_GPOS_SubTable *subtable;         /* referenced subtable */
+};
+
+typedef struct HB_ExtensionPos_  HB_ExtensionPos;
+#endif
+
+
+union  HB_GPOS_SubTable_
+{
+  HB_SinglePos        single;
+  HB_PairPos          pair;
+  HB_CursivePos       cursive;
+  HB_MarkBasePos      markbase;
+  HB_MarkLigPos       marklig;
+  HB_MarkMarkPos      markmark;
+  HB_ContextPos       context;
+  HB_ChainContextPos  chain;
+};
+
+typedef union HB_GPOS_SubTable_  HB_GPOS_SubTable;
+
+
+
+HB_INTERNAL HB_Error
+_HB_GPOS_Load_SubTable( HB_GPOS_SubTable* st,
+				  HB_Stream     stream,
+				  HB_UShort     lookup_type );
+
+HB_INTERNAL void
+_HB_GPOS_Free_SubTable( HB_GPOS_SubTable* st,
+			      HB_UShort     lookup_type );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GPOS_PRIVATE_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-gpos.c b/third_party/harfbuzz/src/harfbuzz-gpos.c
new file mode 100644
index 0000000..356dc01
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-gpos.c
@@ -0,0 +1,6055 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ * Copyright (C) 2007  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-gpos-private.h"
+#include "harfbuzz-open-private.h"
+#include "harfbuzz-gdef-private.h"
+#include "harfbuzz-shaper.h"
+
+struct  GPOS_Instance_
+{
+  HB_GPOSHeader*  gpos;
+  HB_Font          font;
+  HB_Bool          dvi;
+  HB_UShort        load_flags;  /* how the glyph should be loaded */
+  HB_Bool          r2l;
+
+  HB_UShort        last;        /* the last valid glyph -- used
+				   with cursive positioning     */
+  HB_Fixed           anchor_x;    /* the coordinates of the anchor point */
+  HB_Fixed           anchor_y;    /* of the last valid glyph             */
+};
+
+typedef struct GPOS_Instance_  GPOS_Instance;
+
+
+static HB_Error  GPOS_Do_Glyph_Lookup( GPOS_Instance*    gpi,
+				       HB_UShort         lookup_index,
+				       HB_Buffer        buffer,
+				       HB_UShort         context_length,
+				       int               nesting_level );
+
+
+
+/* the client application must replace this with something more
+   meaningful if multiple master fonts are to be supported.     */
+
+static HB_Error  default_mmfunc( HB_Font      font,
+				 HB_UShort    metric_id,
+				 HB_Fixed*      metric_value,
+				 void*        data )
+{
+  HB_UNUSED(font);
+  HB_UNUSED(metric_id);
+  HB_UNUSED(metric_value);
+  HB_UNUSED(data);
+  return ERR(HB_Err_Not_Covered); /* ERR() call intended */
+}
+
+
+
+HB_Error  HB_Load_GPOS_Table( HB_Stream stream, 
+			      HB_GPOSHeader** retptr,
+			      HB_GDEFHeader*  gdef,
+			      HB_Stream       gdefStream )
+{
+  HB_UInt         cur_offset, new_offset, base_offset;
+
+  HB_GPOSHeader*  gpos;
+
+  HB_Error   error;
+
+
+  if ( !retptr )
+    return ERR(HB_Err_Invalid_Argument);
+
+  if ( GOTO_Table( TTAG_GPOS ) )
+    return error;
+
+  base_offset = FILE_Pos();
+
+  if ( ALLOC ( gpos, sizeof( *gpos ) ) )
+    return error;
+
+  gpos->mmfunc = default_mmfunc;
+
+  /* skip version */
+
+  if ( FILE_Seek( base_offset + 4L ) ||
+       ACCESS_Frame( 2L ) )
+    goto Fail4;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_ScriptList( &gpos->ScriptList,
+				  stream ) ) != HB_Err_Ok )
+    goto Fail4;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_FeatureList( &gpos->FeatureList,
+				   stream ) ) != HB_Err_Ok )
+    goto Fail3;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_LookupList( &gpos->LookupList,
+				  stream, HB_Type_GPOS ) ) != HB_Err_Ok )
+    goto Fail2;
+
+  gpos->gdef = gdef;      /* can be NULL */
+
+  if ( ( error =  _HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( gdef, gdefStream,
+								     gpos->LookupList.Lookup,
+								     gpos->LookupList.LookupCount ) ) )
+    goto Fail1;
+
+  *retptr = gpos;
+
+  return HB_Err_Ok;
+
+Fail1:
+  _HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS );
+
+Fail2:
+  _HB_OPEN_Free_FeatureList( &gpos->FeatureList );
+
+Fail3:
+  _HB_OPEN_Free_ScriptList( &gpos->ScriptList );
+
+Fail4:
+  FREE( gpos );
+
+  return error;
+}
+
+
+HB_Error  HB_Done_GPOS_Table( HB_GPOSHeader* gpos )
+{
+  _HB_OPEN_Free_LookupList( &gpos->LookupList, HB_Type_GPOS );
+  _HB_OPEN_Free_FeatureList( &gpos->FeatureList );
+  _HB_OPEN_Free_ScriptList( &gpos->ScriptList );
+
+  FREE( gpos );
+
+  return HB_Err_Ok;
+}
+
+
+/*****************************
+ * SubTable related functions
+ *****************************/
+
+/* shared tables */
+
+/* ValueRecord */
+
+/* There is a subtle difference in the specs between a `table' and a
+   `record' -- offsets for device tables in ValueRecords are taken from
+   the parent table and not the parent record.                          */
+
+static HB_Error  Load_ValueRecord( HB_ValueRecord*  vr,
+				   HB_UShort         format,
+				   HB_UInt          base_offset,
+				   HB_Stream         stream )
+{
+  HB_Error  error;
+
+  HB_UInt cur_offset, new_offset;
+
+
+  if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+
+    vr->XPlacement = GET_Short();
+
+    FORGET_Frame();
+  }
+  else
+    vr->XPlacement = 0;
+
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+
+    vr->YPlacement = GET_Short();
+
+    FORGET_Frame();
+  }
+  else
+    vr->YPlacement = 0;
+
+  if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+
+    vr->XAdvance = GET_Short();
+
+    FORGET_Frame();
+  }
+  else
+    vr->XAdvance = 0;
+
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+
+    vr->YAdvance = GET_Short();
+
+    FORGET_Frame();
+  }
+  else
+    vr->YAdvance = 0;
+
+  if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+
+    new_offset = GET_UShort();
+
+    FORGET_Frame();
+
+    if ( new_offset )
+    {
+      new_offset += base_offset;
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = _HB_OPEN_Load_Device( &vr->XPlacementDevice,
+				  stream ) ) != HB_Err_Ok )
+	return error;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+      goto empty1;
+  }
+  else
+  {
+  empty1:
+    vr->XPlacementDevice.StartSize  = 0;
+    vr->XPlacementDevice.EndSize    = 0;
+    vr->XPlacementDevice.DeltaValue = NULL;
+  }
+
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail3;
+
+    new_offset = GET_UShort();
+
+    FORGET_Frame();
+
+    if ( new_offset )
+    {
+      new_offset += base_offset;
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = _HB_OPEN_Load_Device( &vr->YPlacementDevice,
+				  stream ) ) != HB_Err_Ok )
+	goto Fail3;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+      goto empty2;
+  }
+  else
+  {
+  empty2:
+    vr->YPlacementDevice.StartSize  = 0;
+    vr->YPlacementDevice.EndSize    = 0;
+    vr->YPlacementDevice.DeltaValue = NULL;
+  }
+
+  if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail2;
+
+    new_offset = GET_UShort();
+
+    FORGET_Frame();
+
+    if ( new_offset )
+    {
+      new_offset += base_offset;
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = _HB_OPEN_Load_Device( &vr->XAdvanceDevice,
+				  stream ) ) != HB_Err_Ok )
+	goto Fail2;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+      goto empty3;
+  }
+  else
+  {
+  empty3:
+    vr->XAdvanceDevice.StartSize  = 0;
+    vr->XAdvanceDevice.EndSize    = 0;
+    vr->XAdvanceDevice.DeltaValue = NULL;
+  }
+
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort();
+
+    FORGET_Frame();
+
+    if ( new_offset )
+    {
+      new_offset += base_offset;
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = _HB_OPEN_Load_Device( &vr->YAdvanceDevice,
+				  stream ) ) != HB_Err_Ok )
+	goto Fail1;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+      goto empty4;
+  }
+  else
+  {
+  empty4:
+    vr->YAdvanceDevice.StartSize  = 0;
+    vr->YAdvanceDevice.EndSize    = 0;
+    vr->YAdvanceDevice.DeltaValue = NULL;
+  }
+
+  if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    vr->XIdPlacement = GET_UShort();
+
+    FORGET_Frame();
+  }
+  else
+    vr->XIdPlacement = 0;
+
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    vr->YIdPlacement = GET_UShort();
+
+    FORGET_Frame();
+  }
+  else
+    vr->YIdPlacement = 0;
+
+  if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    vr->XIdAdvance = GET_UShort();
+
+    FORGET_Frame();
+  }
+  else
+    vr->XIdAdvance = 0;
+
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    vr->YIdAdvance = GET_UShort();
+
+    FORGET_Frame();
+  }
+  else
+    vr->YIdAdvance = 0;
+
+  return HB_Err_Ok;
+
+Fail1:
+  _HB_OPEN_Free_Device( &vr->YAdvanceDevice );
+
+Fail2:
+  _HB_OPEN_Free_Device( &vr->XAdvanceDevice );
+
+Fail3:
+  _HB_OPEN_Free_Device( &vr->YPlacementDevice );
+  return error;
+}
+
+
+static void  Free_ValueRecord( HB_ValueRecord*  vr,
+			       HB_UShort         format )
+{
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
+    _HB_OPEN_Free_Device( &vr->YAdvanceDevice );
+  if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
+    _HB_OPEN_Free_Device( &vr->XAdvanceDevice );
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
+    _HB_OPEN_Free_Device( &vr->YPlacementDevice );
+  if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
+    _HB_OPEN_Free_Device( &vr->XPlacementDevice );
+}
+
+
+static HB_Error  Get_ValueRecord( GPOS_Instance*    gpi,
+				  HB_ValueRecord*  vr,
+				  HB_UShort         format,
+				  HB_Position      gd )
+{
+  HB_Fixed           value;
+  HB_Short         pixel_value;
+  HB_Error         error = HB_Err_Ok;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+
+  HB_UShort  x_ppem, y_ppem;
+  HB_16Dot16   x_scale, y_scale;
+
+
+  if ( !format )
+    return HB_Err_Ok;
+
+  x_ppem  = gpi->font->x_ppem;
+  y_ppem  = gpi->font->y_ppem;
+  x_scale = gpi->font->x_scale;
+  y_scale = gpi->font->y_scale;
+
+  /* design units -> fractional pixel */
+
+  if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT )
+    gd->x_pos += x_scale * vr->XPlacement / 0x10000;
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT )
+    gd->y_pos += y_scale * vr->YPlacement / 0x10000;
+  if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE )
+    gd->x_advance += x_scale * vr->XAdvance / 0x10000;
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE )
+    gd->y_advance += y_scale * vr->YAdvance / 0x10000;
+
+  if ( !gpi->dvi )
+  {
+    /* pixel -> fractional pixel */
+
+    if ( format & HB_GPOS_FORMAT_HAVE_X_PLACEMENT_DEVICE )
+    {
+      _HB_OPEN_Get_Device( &vr->XPlacementDevice, x_ppem, &pixel_value );
+      gd->x_pos += pixel_value << 6;
+    }
+    if ( format & HB_GPOS_FORMAT_HAVE_Y_PLACEMENT_DEVICE )
+    {
+      _HB_OPEN_Get_Device( &vr->YPlacementDevice, y_ppem, &pixel_value );
+      gd->y_pos += pixel_value << 6;
+    }
+    if ( format & HB_GPOS_FORMAT_HAVE_X_ADVANCE_DEVICE )
+    {
+      _HB_OPEN_Get_Device( &vr->XAdvanceDevice, x_ppem, &pixel_value );
+      gd->x_advance += pixel_value << 6;
+    }
+    if ( format & HB_GPOS_FORMAT_HAVE_Y_ADVANCE_DEVICE )
+    {
+      _HB_OPEN_Get_Device( &vr->YAdvanceDevice, y_ppem, &pixel_value );
+      gd->y_advance += pixel_value << 6;
+    }
+  }
+
+  /* values returned from mmfunc() are already in fractional pixels */
+
+  if ( format & HB_GPOS_FORMAT_HAVE_X_ID_PLACEMENT )
+  {
+    error = (gpos->mmfunc)( gpi->font, vr->XIdPlacement,
+			    &value, gpos->data );
+    if ( error )
+      return error;
+    gd->x_pos += value;
+  }
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_PLACEMENT )
+  {
+    error = (gpos->mmfunc)( gpi->font, vr->YIdPlacement,
+			    &value, gpos->data );
+    if ( error )
+      return error;
+    gd->y_pos += value;
+  }
+  if ( format & HB_GPOS_FORMAT_HAVE_X_ID_ADVANCE )
+  {
+    error = (gpos->mmfunc)( gpi->font, vr->XIdAdvance,
+			    &value, gpos->data );
+    if ( error )
+      return error;
+    gd->x_advance += value;
+  }
+  if ( format & HB_GPOS_FORMAT_HAVE_Y_ID_ADVANCE )
+  {
+    error = (gpos->mmfunc)( gpi->font, vr->YIdAdvance,
+			    &value, gpos->data );
+    if ( error )
+      return error;
+    gd->y_advance += value;
+  }
+
+  return error;
+}
+
+
+/* AnchorFormat1 */
+/* AnchorFormat2 */
+/* AnchorFormat3 */
+/* AnchorFormat4 */
+
+static HB_Error  Load_Anchor( HB_Anchor*  an,
+			      HB_Stream    stream )
+{
+  HB_Error  error;
+
+  HB_UInt cur_offset, new_offset, base_offset;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  an->PosFormat = GET_UShort();
+
+  FORGET_Frame();
+
+  switch ( an->PosFormat )
+  {
+  case 1:
+    if ( ACCESS_Frame( 4L ) )
+      return error;
+
+    an->af.af1.XCoordinate = GET_Short();
+    an->af.af1.YCoordinate = GET_Short();
+
+    FORGET_Frame();
+    break;
+
+  case 2:
+    if ( ACCESS_Frame( 6L ) )
+      return error;
+
+    an->af.af2.XCoordinate = GET_Short();
+    an->af.af2.YCoordinate = GET_Short();
+    an->af.af2.AnchorPoint = GET_UShort();
+
+    FORGET_Frame();
+    break;
+
+  case 3:
+    if ( ACCESS_Frame( 6L ) )
+      return error;
+
+    an->af.af3.XCoordinate = GET_Short();
+    an->af.af3.YCoordinate = GET_Short();
+
+    new_offset = GET_UShort();
+
+    FORGET_Frame();
+
+    if ( new_offset )
+    {
+      new_offset += base_offset;
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = _HB_OPEN_Load_Device( &an->af.af3.XDeviceTable,
+				  stream ) ) != HB_Err_Ok )
+	return error;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+    {
+      an->af.af3.XDeviceTable.StartSize  = 0;
+      an->af.af3.XDeviceTable.EndSize    = 0;
+      an->af.af3.XDeviceTable.DeltaValue = NULL;
+    }
+
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort();
+
+    FORGET_Frame();
+
+    if ( new_offset )
+    {
+      new_offset += base_offset;
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = _HB_OPEN_Load_Device( &an->af.af3.YDeviceTable,
+				  stream ) ) != HB_Err_Ok )
+	goto Fail;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+    {
+      an->af.af3.YDeviceTable.StartSize  = 0;
+      an->af.af3.YDeviceTable.EndSize    = 0;
+      an->af.af3.YDeviceTable.DeltaValue = NULL;
+    }
+    break;
+
+  case 4:
+    if ( ACCESS_Frame( 4L ) )
+      return error;
+
+    an->af.af4.XIdAnchor = GET_UShort();
+    an->af.af4.YIdAnchor = GET_UShort();
+
+    FORGET_Frame();
+    break;
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  _HB_OPEN_Free_Device( &an->af.af3.XDeviceTable );
+  return error;
+}
+
+
+static void  Free_Anchor( HB_Anchor*  an)
+{
+  if ( an->PosFormat == 3 )
+  {
+    _HB_OPEN_Free_Device( &an->af.af3.YDeviceTable );
+    _HB_OPEN_Free_Device( &an->af.af3.XDeviceTable );
+  }
+}
+
+
+static HB_Error  Get_Anchor( GPOS_Instance*   gpi,
+			     HB_Anchor*      an,
+			     HB_UShort        glyph_index,
+			     HB_Fixed*          x_value,
+			     HB_Fixed*          y_value )
+{
+  HB_Error  error = HB_Err_Ok;
+
+  HB_GPOSHeader*  gpos = gpi->gpos;
+  HB_UShort        ap;
+
+  HB_Short         pixel_value;
+
+  HB_UShort        x_ppem, y_ppem;
+  HB_16Dot16         x_scale, y_scale;
+
+
+  x_ppem  = gpi->font->x_ppem;
+  y_ppem  = gpi->font->y_ppem;
+  x_scale = gpi->font->x_scale;
+  y_scale = gpi->font->y_scale;
+
+  switch ( an->PosFormat )
+  {
+  case 0:
+    /* The special case of an empty AnchorTable */
+  default:
+
+    return HB_Err_Not_Covered;
+
+  case 1:
+    *x_value = x_scale * an->af.af1.XCoordinate / 0x10000;
+    *y_value = y_scale * an->af.af1.YCoordinate / 0x10000;
+    break;
+
+  case 2:
+    if ( !gpi->dvi )
+    {
+      hb_uint32 n_points = 0;
+      ap = an->af.af2.AnchorPoint;
+      if (!gpi->font->klass->getPointInOutline)
+          goto no_contour_point;
+      error = gpi->font->klass->getPointInOutline(gpi->font, glyph_index, gpi->load_flags, ap, x_value, y_value, &n_points);
+      if (error)
+          return error;
+      /* if n_points is set to zero, we use the design coordinate value pair.
+       * This can happen e.g. for sbit glyphs. */
+      if (!n_points)
+          goto no_contour_point;
+    }
+    else
+    {
+    no_contour_point:
+      *x_value = x_scale * an->af.af3.XCoordinate / 0x10000;
+      *y_value = y_scale * an->af.af3.YCoordinate / 0x10000;
+    }
+    break;
+
+  case 3:
+    if ( !gpi->dvi )
+    {
+      _HB_OPEN_Get_Device( &an->af.af3.XDeviceTable, x_ppem, &pixel_value );
+      *x_value = pixel_value << 6;
+      _HB_OPEN_Get_Device( &an->af.af3.YDeviceTable, y_ppem, &pixel_value );
+      *y_value = pixel_value << 6;
+    }
+    else
+      *x_value = *y_value = 0;
+
+    *x_value += x_scale * an->af.af3.XCoordinate / 0x10000;
+    *y_value += y_scale * an->af.af3.YCoordinate / 0x10000;
+    break;
+
+  case 4:
+    error = (gpos->mmfunc)( gpi->font, an->af.af4.XIdAnchor,
+			    x_value, gpos->data );
+    if ( error )
+      return error;
+
+    error = (gpos->mmfunc)( gpi->font, an->af.af4.YIdAnchor,
+			    y_value, gpos->data );
+    if ( error )
+      return error;
+    break;
+  }
+
+  return error;
+}
+
+
+/* MarkArray */
+
+static HB_Error  Load_MarkArray ( HB_MarkArray*  ma,
+				  HB_Stream       stream )
+{
+  HB_Error  error;
+
+  HB_UShort        n, m, count;
+  HB_UInt         cur_offset, new_offset, base_offset;
+
+  HB_MarkRecord*  mr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = ma->MarkCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ma->MarkRecord = NULL;
+
+  if ( ALLOC_ARRAY( ma->MarkRecord, count, HB_MarkRecord ) )
+    return error;
+
+  mr = ma->MarkRecord;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 4L ) )
+      goto Fail;
+
+    mr[n].Class = GET_UShort();
+    new_offset  = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_Anchor( &mr[n].MarkAnchor, stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_Anchor( &mr[m].MarkAnchor );
+
+  FREE( mr );
+  return error;
+}
+
+
+static void  Free_MarkArray( HB_MarkArray*  ma )
+{
+  HB_UShort        n, count;
+
+  HB_MarkRecord*  mr;
+
+
+  if ( ma->MarkRecord )
+  {
+    count = ma->MarkCount;
+    mr    = ma->MarkRecord;
+
+    for ( n = 0; n < count; n++ )
+      Free_Anchor( &mr[n].MarkAnchor );
+
+    FREE( mr );
+  }
+}
+
+
+/* LookupType 1 */
+
+/* SinglePosFormat1 */
+/* SinglePosFormat2 */
+
+static HB_Error  Load_SinglePos( HB_GPOS_SubTable* st,
+				 HB_Stream       stream )
+{
+  HB_Error  error;
+  HB_SinglePos*   sp = &st->single;
+
+  HB_UShort         n, m, count, format;
+  HB_UInt          cur_offset, new_offset, base_offset;
+
+  HB_ValueRecord*  vr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 6L ) )
+    return error;
+
+  sp->PosFormat = GET_UShort();
+  new_offset    = GET_UShort() + base_offset;
+
+  format = sp->ValueFormat = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( !format )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &sp->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  switch ( sp->PosFormat )
+  {
+  case 1:
+    error = Load_ValueRecord( &sp->spf.spf1.Value, format,
+			      base_offset, stream );
+    if ( error )
+      goto Fail2;
+    break;
+
+  case 2:
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail2;
+
+    count = sp->spf.spf2.ValueCount = GET_UShort();
+
+    FORGET_Frame();
+
+    sp->spf.spf2.Value = NULL;
+
+    if ( ALLOC_ARRAY( sp->spf.spf2.Value, count, HB_ValueRecord ) )
+      goto Fail2;
+
+    vr = sp->spf.spf2.Value;
+
+    for ( n = 0; n < count; n++ )
+    {
+      error = Load_ValueRecord( &vr[n], format, base_offset, stream );
+      if ( error )
+	goto Fail1;
+    }
+    break;
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_ValueRecord( &vr[m], format );
+
+  FREE( vr );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &sp->Coverage );
+  return error;
+}
+
+
+static void  Free_SinglePos( HB_GPOS_SubTable* st )
+{
+  HB_UShort         n, count, format;
+  HB_SinglePos*   sp = &st->single;
+
+  HB_ValueRecord*  v;
+
+
+  format = sp->ValueFormat;
+
+  switch ( sp->PosFormat )
+  {
+  case 1:
+    Free_ValueRecord( &sp->spf.spf1.Value, format );
+    break;
+
+  case 2:
+    if ( sp->spf.spf2.Value )
+    {
+      count = sp->spf.spf2.ValueCount;
+      v     = sp->spf.spf2.Value;
+
+      for ( n = 0; n < count; n++ )
+	Free_ValueRecord( &v[n], format );
+
+      FREE( v );
+    }
+    break;
+  default:
+    break;
+  }
+
+  _HB_OPEN_Free_Coverage( &sp->Coverage );
+}
+
+static HB_Error  Lookup_SinglePos( GPOS_Instance*    gpi,
+				   HB_GPOS_SubTable* st,
+				   HB_Buffer        buffer,
+				   HB_UShort         flags,
+				   HB_UShort         context_length,
+				   int               nesting_level )
+{
+  HB_UShort        index, property;
+  HB_Error         error;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+  HB_SinglePos*   sp = &st->single;
+
+  HB_UNUSED(nesting_level);
+
+  if ( context_length != 0xFFFF && context_length < 1 )
+    return HB_Err_Not_Covered;
+
+  if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &sp->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  switch ( sp->PosFormat )
+  {
+  case 1:
+    error = Get_ValueRecord( gpi, &sp->spf.spf1.Value,
+			     sp->ValueFormat, POSITION( buffer->in_pos ) );
+    if ( error )
+      return error;
+    break;
+
+  case 2:
+    if ( index >= sp->spf.spf2.ValueCount )
+      return ERR(HB_Err_Invalid_SubTable);
+    error = Get_ValueRecord( gpi, &sp->spf.spf2.Value[index],
+			     sp->ValueFormat, POSITION( buffer->in_pos ) );
+    if ( error )
+      return error;
+    break;
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable);
+  }
+
+  (buffer->in_pos)++;
+
+  return HB_Err_Ok;
+}
+
+
+/* LookupType 2 */
+
+/* PairSet */
+
+static HB_Error  Load_PairSet ( HB_PairSet*  ps,
+				HB_UShort     format1,
+				HB_UShort     format2,
+				HB_Stream     stream )
+{
+  HB_Error  error;
+
+  HB_UShort             n, m, count;
+  HB_UInt              base_offset;
+
+  HB_PairValueRecord*  pvr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = ps->PairValueCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ps->PairValueRecord = NULL;
+
+  if ( ALLOC_ARRAY( ps->PairValueRecord, count, HB_PairValueRecord ) )
+    return error;
+
+  pvr = ps->PairValueRecord;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    pvr[n].SecondGlyph = GET_UShort();
+
+    FORGET_Frame();
+
+    if ( format1 )
+    {
+      error = Load_ValueRecord( &pvr[n].Value1, format1,
+				base_offset, stream );
+      if ( error )
+	goto Fail;
+    }
+    if ( format2 )
+    {
+      error = Load_ValueRecord( &pvr[n].Value2, format2,
+				base_offset, stream );
+      if ( error )
+      {
+	if ( format1 )
+	  Free_ValueRecord( &pvr[n].Value1, format1 );
+	goto Fail;
+      }
+    }
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+  {
+    if ( format1 )
+      Free_ValueRecord( &pvr[m].Value1, format1 );
+    if ( format2 )
+      Free_ValueRecord( &pvr[m].Value2, format2 );
+  }
+
+  FREE( pvr );
+  return error;
+}
+
+
+static void  Free_PairSet( HB_PairSet*  ps,
+			   HB_UShort     format1,
+			   HB_UShort     format2 )
+{
+  HB_UShort             n, count;
+
+  HB_PairValueRecord*  pvr;
+
+
+  if ( ps->PairValueRecord )
+  {
+    count = ps->PairValueCount;
+    pvr   = ps->PairValueRecord;
+
+    for ( n = 0; n < count; n++ )
+    {
+      if ( format1 )
+	Free_ValueRecord( &pvr[n].Value1, format1 );
+      if ( format2 )
+	Free_ValueRecord( &pvr[n].Value2, format2 );
+    }
+
+    FREE( pvr );
+  }
+}
+
+
+/* PairPosFormat1 */
+
+static HB_Error  Load_PairPos1( HB_PairPosFormat1*  ppf1,
+				HB_UShort            format1,
+				HB_UShort            format2,
+				HB_Stream            stream )
+{
+  HB_Error  error;
+
+  HB_UShort     n, m, count;
+  HB_UInt      cur_offset, new_offset, base_offset;
+
+  HB_PairSet*  ps;
+
+
+  base_offset = FILE_Pos() - 8L;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = ppf1->PairSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ppf1->PairSet = NULL;
+
+  if ( ALLOC_ARRAY( ppf1->PairSet, count, HB_PairSet ) )
+    return error;
+
+  ps = ppf1->PairSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_PairSet( &ps[n], format1,
+				 format2, stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_PairSet( &ps[m], format1, format2 );
+
+  FREE( ps );
+  return error;
+}
+
+
+static void  Free_PairPos1( HB_PairPosFormat1*  ppf1,
+			    HB_UShort            format1,
+			    HB_UShort            format2 )
+{
+  HB_UShort     n, count;
+
+  HB_PairSet*  ps;
+
+
+  if ( ppf1->PairSet )
+  {
+    count = ppf1->PairSetCount;
+    ps    = ppf1->PairSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_PairSet( &ps[n], format1, format2 );
+
+    FREE( ps );
+  }
+}
+
+
+/* PairPosFormat2 */
+
+static HB_Error  Load_PairPos2( HB_PairPosFormat2*  ppf2,
+				HB_UShort            format1,
+				HB_UShort            format2,
+				HB_Stream            stream )
+{
+  HB_Error  error;
+
+  HB_UShort          m, n, k, count1, count2;
+  HB_UInt           cur_offset, new_offset1, new_offset2, base_offset;
+
+  HB_Class1Record*  c1r;
+  HB_Class2Record*  c2r;
+
+
+  base_offset = FILE_Pos() - 8L;
+
+  if ( ACCESS_Frame( 8L ) )
+    return error;
+
+  new_offset1 = GET_UShort() + base_offset;
+  new_offset2 = GET_UShort() + base_offset;
+
+  /* `Class1Count' and `Class2Count' are the upper limits for class
+     values, thus we read it now to make additional safety checks.  */
+
+  count1 = ppf2->Class1Count = GET_UShort();
+  count2 = ppf2->Class2Count = GET_UShort();
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset1 ) ||
+       ( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef1, count1,
+				       stream ) ) != HB_Err_Ok )
+    return error;
+  if ( FILE_Seek( new_offset2 ) ||
+       ( error = _HB_OPEN_Load_ClassDefinition( &ppf2->ClassDef2, count2,
+				       stream ) ) != HB_Err_Ok )
+    goto Fail3;
+  (void)FILE_Seek( cur_offset );
+
+  ppf2->Class1Record = NULL;
+
+  if ( ALLOC_ARRAY( ppf2->Class1Record, count1, HB_Class1Record ) )
+    goto Fail2;
+
+  c1r = ppf2->Class1Record;
+
+  for ( m = 0; m < count1; m++ )
+  {
+    c1r[m].Class2Record = NULL;
+
+    if ( ALLOC_ARRAY( c1r[m].Class2Record, count2, HB_Class2Record ) )
+      goto Fail1;
+
+    c2r = c1r[m].Class2Record;
+
+    for ( n = 0; n < count2; n++ )
+    {
+      if ( format1 )
+      {
+	error = Load_ValueRecord( &c2r[n].Value1, format1,
+				  base_offset, stream );
+	if ( error )
+	  goto Fail0;
+      }
+      if ( format2 )
+      {
+	error = Load_ValueRecord( &c2r[n].Value2, format2,
+				  base_offset, stream );
+	if ( error )
+	{
+	  if ( format1 )
+	    Free_ValueRecord( &c2r[n].Value1, format1 );
+	  goto Fail0;
+	}
+      }
+    }
+
+    continue;
+
+  Fail0:
+    for ( k = 0; k < n; k++ )
+    {
+      if ( format1 )
+	Free_ValueRecord( &c2r[k].Value1, format1 );
+      if ( format2 )
+	Free_ValueRecord( &c2r[k].Value2, format2 );
+    }
+    goto Fail1;
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( k = 0; k < m; k++ )
+  {
+    c2r = c1r[k].Class2Record;
+
+    for ( n = 0; n < count2; n++ )
+    {
+      if ( format1 )
+	Free_ValueRecord( &c2r[n].Value1, format1 );
+      if ( format2 )
+	Free_ValueRecord( &c2r[n].Value2, format2 );
+    }
+
+    FREE( c2r );
+  }
+
+  FREE( c1r );
+Fail2:
+
+  _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2 );
+
+Fail3:
+  _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1 );
+  return error;
+}
+
+
+static void  Free_PairPos2( HB_PairPosFormat2*  ppf2,
+			    HB_UShort            format1,
+			    HB_UShort            format2)
+{
+  HB_UShort          m, n, count1, count2;
+
+  HB_Class1Record*  c1r;
+  HB_Class2Record*  c2r;
+
+
+  if ( ppf2->Class1Record )
+  {
+    c1r    = ppf2->Class1Record;
+    count1 = ppf2->Class1Count;
+    count2 = ppf2->Class2Count;
+
+    for ( m = 0; m < count1; m++ )
+    {
+      c2r = c1r[m].Class2Record;
+
+      for ( n = 0; n < count2; n++ )
+      {
+	if ( format1 )
+	  Free_ValueRecord( &c2r[n].Value1, format1 );
+	if ( format2 )
+	  Free_ValueRecord( &c2r[n].Value2, format2 );
+      }
+
+      FREE( c2r );
+    }
+
+    FREE( c1r );
+
+    _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef2 );
+    _HB_OPEN_Free_ClassDefinition( &ppf2->ClassDef1 );
+  }
+}
+
+
+static HB_Error  Load_PairPos( HB_GPOS_SubTable* st,
+			       HB_Stream     stream )
+{
+  HB_Error  error;
+  HB_PairPos*     pp = &st->pair;
+
+  HB_UShort         format1, format2;
+  HB_UInt          cur_offset, new_offset, base_offset;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 8L ) )
+    return error;
+
+  pp->PosFormat = GET_UShort();
+  new_offset    = GET_UShort() + base_offset;
+
+  format1 = pp->ValueFormat1 = GET_UShort();
+  format2 = pp->ValueFormat2 = GET_UShort();
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &pp->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  switch ( pp->PosFormat )
+  {
+  case 1:
+    error = Load_PairPos1( &pp->ppf.ppf1, format1, format2, stream );
+    if ( error )
+      goto Fail;
+    break;
+
+  case 2:
+    error = Load_PairPos2( &pp->ppf.ppf2, format1, format2, stream );
+    if ( error )
+      goto Fail;
+    break;
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  _HB_OPEN_Free_Coverage( &pp->Coverage );
+  return error;
+}
+
+
+static void  Free_PairPos( HB_GPOS_SubTable* st )
+{
+  HB_UShort  format1, format2;
+  HB_PairPos*     pp = &st->pair;
+
+
+  format1 = pp->ValueFormat1;
+  format2 = pp->ValueFormat2;
+
+  switch ( pp->PosFormat )
+  {
+  case 1:
+    Free_PairPos1( &pp->ppf.ppf1, format1, format2 );
+    break;
+
+  case 2:
+    Free_PairPos2( &pp->ppf.ppf2, format1, format2 );
+    break;
+
+  default:
+    break;
+  }
+
+  _HB_OPEN_Free_Coverage( &pp->Coverage );
+}
+
+
+static HB_Error  Lookup_PairPos1( GPOS_Instance*       gpi,
+				  HB_PairPosFormat1*  ppf1,
+				  HB_Buffer           buffer,
+				  HB_UInt              first_pos,
+				  HB_UShort            index,
+				  HB_UShort            format1,
+				  HB_UShort            format2 )
+{
+  HB_Error              error;
+  HB_UShort             numpvr, glyph2;
+
+  HB_PairValueRecord*  pvr;
+
+
+  if ( index >= ppf1->PairSetCount )
+     return ERR(HB_Err_Invalid_SubTable);
+
+  pvr = ppf1->PairSet[index].PairValueRecord;
+  if ( !pvr )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  glyph2 = IN_CURGLYPH();
+
+  for ( numpvr = ppf1->PairSet[index].PairValueCount;
+	numpvr;
+	numpvr--, pvr++ )
+  {
+    if ( glyph2 == pvr->SecondGlyph )
+    {
+      error = Get_ValueRecord( gpi, &pvr->Value1, format1,
+			       POSITION( first_pos ) );
+      if ( error )
+	return error;
+      return Get_ValueRecord( gpi, &pvr->Value2, format2,
+			      POSITION( buffer->in_pos ) );
+    }
+  }
+
+  return HB_Err_Not_Covered;
+}
+
+
+static HB_Error  Lookup_PairPos2( GPOS_Instance*       gpi,
+				  HB_PairPosFormat2*  ppf2,
+				  HB_Buffer           buffer,
+				  HB_UInt              first_pos,
+				  HB_UShort            format1,
+				  HB_UShort            format2 )
+{
+  HB_Error           error;
+  HB_UShort          cl1 = 0, cl2 = 0; /* shut compiler up */
+
+  HB_Class1Record*  c1r;
+  HB_Class2Record*  c2r;
+
+
+  error = _HB_OPEN_Get_Class( &ppf2->ClassDef1, IN_GLYPH( first_pos ),
+		     &cl1, NULL );
+  if ( error && error != HB_Err_Not_Covered )
+    return error;
+  error = _HB_OPEN_Get_Class( &ppf2->ClassDef2, IN_CURGLYPH(),
+		     &cl2, NULL );
+  if ( error && error != HB_Err_Not_Covered )
+    return error;
+
+  c1r = &ppf2->Class1Record[cl1];
+  if ( !c1r )
+    return ERR(HB_Err_Invalid_SubTable);
+  c2r = &c1r->Class2Record[cl2];
+
+  error = Get_ValueRecord( gpi, &c2r->Value1, format1, POSITION( first_pos ) );
+  if ( error )
+    return error;
+  return Get_ValueRecord( gpi, &c2r->Value2, format2, POSITION( buffer->in_pos ) );
+}
+
+
+static HB_Error  Lookup_PairPos( GPOS_Instance*    gpi,
+				 HB_GPOS_SubTable* st,
+				 HB_Buffer        buffer,
+				 HB_UShort         flags,
+				 HB_UShort         context_length,
+				 int               nesting_level )
+{
+  HB_Error         error;
+  HB_UShort        index, property;
+  HB_UInt          first_pos;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+  HB_PairPos*     pp = &st->pair;
+
+  HB_UNUSED(nesting_level);
+
+  if ( buffer->in_pos >= buffer->in_length - 1 )
+    return HB_Err_Not_Covered;           /* Not enough glyphs in stream */
+
+  if ( context_length != 0xFFFF && context_length < 2 )
+    return HB_Err_Not_Covered;
+
+  if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &pp->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  /* second glyph */
+
+  first_pos = buffer->in_pos;
+  (buffer->in_pos)++;
+
+  while ( CHECK_Property( gpos->gdef, IN_CURITEM(),
+			  flags, &property ) )
+  {
+    if ( error && error != HB_Err_Not_Covered )
+      return error;
+
+    if ( buffer->in_pos == buffer->in_length )
+      {
+	buffer->in_pos = first_pos;
+        return HB_Err_Not_Covered;
+      }
+    (buffer->in_pos)++;
+
+  }
+
+  switch ( pp->PosFormat )
+  {
+  case 1:
+    error = Lookup_PairPos1( gpi, &pp->ppf.ppf1, buffer,
+			     first_pos, index,
+			     pp->ValueFormat1, pp->ValueFormat2 );
+    break;
+
+  case 2:
+    error = Lookup_PairPos2( gpi, &pp->ppf.ppf2, buffer, first_pos,
+			     pp->ValueFormat1, pp->ValueFormat2 );
+    break;
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  /* if we don't have coverage for the second glyph don't skip it for
+     further lookups but reset in_pos back to the first_glyph and let
+     the caller in Do_String_Lookup increment in_pos */
+  if ( error == HB_Err_Not_Covered )
+      buffer->in_pos = first_pos;
+
+  /* adjusting the `next' glyph */
+
+  if ( pp->ValueFormat2 )
+    (buffer->in_pos)++;
+
+  return error;
+}
+
+
+/* LookupType 3 */
+
+/* CursivePosFormat1 */
+
+static HB_Error  Load_CursivePos( HB_GPOS_SubTable* st,
+				  HB_Stream        stream )
+{
+  HB_Error  error;
+  HB_CursivePos*  cp = &st->cursive;
+
+  HB_UShort             n, m, count;
+  HB_UInt              cur_offset, new_offset, base_offset;
+
+  HB_EntryExitRecord*  eer;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  cp->PosFormat = GET_UShort();
+  new_offset    = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &cp->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = cp->EntryExitCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cp->EntryExitRecord = NULL;
+
+  if ( ALLOC_ARRAY( cp->EntryExitRecord, count, HB_EntryExitRecord ) )
+    goto Fail2;
+
+  eer = cp->EntryExitRecord;
+
+  for ( n = 0; n < count; n++ )
+  {
+    HB_UInt entry_offset;
+
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+
+    entry_offset = new_offset = GET_UShort();
+
+    FORGET_Frame();
+
+    if ( new_offset )
+    {
+      new_offset += base_offset;
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = Load_Anchor( &eer[n].EntryAnchor,
+				  stream ) ) != HB_Err_Ok )
+	goto Fail1;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+      eer[n].EntryAnchor.PosFormat   = 0;
+
+    if ( ACCESS_Frame( 2L ) )
+      return error;
+
+    new_offset = GET_UShort();
+
+    FORGET_Frame();
+
+    if ( new_offset )
+    {
+      new_offset += base_offset;
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = Load_Anchor( &eer[n].ExitAnchor,
+				  stream ) ) != HB_Err_Ok )
+      {
+	if ( entry_offset )
+	  Free_Anchor( &eer[n].EntryAnchor );
+	goto Fail1;
+      }
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+      eer[n].ExitAnchor.PosFormat   = 0;
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+  {
+    Free_Anchor( &eer[m].EntryAnchor );
+    Free_Anchor( &eer[m].ExitAnchor );
+  }
+
+  FREE( eer );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &cp->Coverage );
+  return error;
+}
+
+
+static void  Free_CursivePos( HB_GPOS_SubTable* st )
+{
+  HB_UShort             n, count;
+  HB_CursivePos*  cp = &st->cursive;
+
+  HB_EntryExitRecord*  eer;
+
+
+  if ( cp->EntryExitRecord )
+  {
+    count = cp->EntryExitCount;
+    eer   = cp->EntryExitRecord;
+
+    for ( n = 0; n < count; n++ )
+    {
+      Free_Anchor( &eer[n].EntryAnchor );
+      Free_Anchor( &eer[n].ExitAnchor );
+    }
+
+    FREE( eer );
+  }
+
+  _HB_OPEN_Free_Coverage( &cp->Coverage );
+}
+
+
+static HB_Error  Lookup_CursivePos( GPOS_Instance*    gpi,
+				    HB_GPOS_SubTable* st,
+				    HB_Buffer        buffer,
+				    HB_UShort         flags,
+				    HB_UShort         context_length,
+				    int               nesting_level )
+{
+  HB_UShort        index, property;
+  HB_Error         error;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+  HB_CursivePos*  cp = &st->cursive;
+
+  HB_EntryExitRecord*  eer;
+  HB_Fixed                entry_x, entry_y;
+  HB_Fixed                exit_x, exit_y;
+
+  HB_UNUSED(nesting_level);
+
+  if ( context_length != 0xFFFF && context_length < 1 )
+  {
+    gpi->last = 0xFFFF;
+    return HB_Err_Not_Covered;
+  }
+
+  /* Glyphs not having the right GDEF properties will be ignored, i.e.,
+     gpi->last won't be reset (contrary to user defined properties). */
+
+  if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  /* We don't handle mark glyphs here.  According to Andrei, this isn't
+     possible, but who knows...                                         */
+
+  if ( property == HB_GDEF_MARK )
+  {
+    gpi->last = 0xFFFF;
+    return HB_Err_Not_Covered;
+  }
+
+  error = _HB_OPEN_Coverage_Index( &cp->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+  {
+    gpi->last = 0xFFFF;
+    return error;
+  }
+
+  if ( index >= cp->EntryExitCount )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  eer = &cp->EntryExitRecord[index];
+
+  /* Now comes the messiest part of the whole OpenType
+     specification.  At first glance, cursive connections seem easy
+     to understand, but there are pitfalls!  The reason is that
+     the specs don't mention how to compute the advance values
+     resp. glyph offsets.  I was told it would be an omission, to
+     be fixed in the next OpenType version...  Again many thanks to
+     Andrei Burago <andreib@microsoft.com> for clarifications.
+
+     Consider the following example:
+
+		      |  xadv1    |
+		       +---------+
+		       |         |
+		 +-----+--+ 1    |
+		 |     | .|      |
+		 |    0+--+------+
+		 |   2    |
+		 |        |
+		0+--------+
+		|  xadv2   |
+
+       glyph1: advance width = 12
+	       anchor point = (3,1)
+
+       glyph2: advance width = 11
+	       anchor point = (9,4)
+
+       LSB is 1 for both glyphs (so the boxes drawn above are glyph
+       bboxes).  Writing direction is R2L; `0' denotes the glyph's
+       coordinate origin.
+
+     Now the surprising part: The advance width of the *left* glyph
+     (resp. of the *bottom* glyph) will be modified, no matter
+     whether the writing direction is L2R or R2L (resp. T2B or
+     B2T)!  This assymetry is caused by the fact that the glyph's
+     coordinate origin is always the lower left corner for all
+     writing directions.
+
+     Continuing the above example, we can compute the new
+     (horizontal) advance width of glyph2 as
+
+       9 - 3 = 6  ,
+
+     and the new vertical offset of glyph2 as
+
+       1 - 4 = -3  .
+
+
+     Vertical writing direction is far more complicated:
+
+     a) Assuming that we recompute the advance height of the lower glyph:
+
+				  --
+		       +---------+
+	      --       |         |
+		 +-----+--+ 1    | yadv1
+		 |     | .|      |
+	   yadv2 |    0+--+------+        -- BSB1  --
+		 |   2    |       --      --        y_offset
+		 |        |
+   BSB2 --      0+--------+                        --
+	--    --
+
+       glyph1: advance height = 6
+	       anchor point = (3,1)
+
+       glyph2: advance height = 7
+	       anchor point = (9,4)
+
+       TSB is 1 for both glyphs; writing direction is T2B.
+
+
+	 BSB1     = yadv1 - (TSB1 + ymax1)
+	 BSB2     = yadv2 - (TSB2 + ymax2)
+	 y_offset = y2 - y1
+
+       vertical advance width of glyph2
+	 = y_offset + BSB2 - BSB1
+	 = (y2 - y1) + (yadv2 - (TSB2 + ymax2)) - (yadv1 - (TSB1 + ymax1))
+	 = y2 - y1 + yadv2 - TSB2 - ymax2 - (yadv1 - TSB1 - ymax1)
+	 = y2 - y1 + yadv2 - TSB2 - ymax2 - yadv1 + TSB1 + ymax1
+
+
+     b) Assuming that we recompute the advance height of the upper glyph:
+
+				  --      --
+		       +---------+        -- TSB1
+	--    --       |         |
+   TSB2 --       +-----+--+ 1    | yadv1   ymax1
+		 |     | .|      |
+	   yadv2 |    0+--+------+        --       --
+    ymax2        |   2    |       --                y_offset
+		 |        |
+	--      0+--------+                        --
+	      --
+
+       glyph1: advance height = 6
+	       anchor point = (3,1)
+
+       glyph2: advance height = 7
+	       anchor point = (9,4)
+
+       TSB is 1 for both glyphs; writing direction is T2B.
+
+       y_offset = y2 - y1
+
+       vertical advance width of glyph2
+	 = TSB1 + ymax1 + y_offset - (TSB2 + ymax2)
+	 = TSB1 + ymax1 + y2 - y1 - TSB2 - ymax2
+
+
+     Comparing a) with b) shows that b) is easier to compute.  I'll wait
+     for a reply from Andrei to see what should really be implemented...
+
+     Since horizontal advance widths or vertical advance heights
+     can be used alone but not together, no ambiguity occurs.        */
+
+  if ( gpi->last == 0xFFFF )
+    goto end;
+
+  /* Get_Anchor() returns HB_Err_Not_Covered if there is no anchor
+     table.                                                         */
+
+  error = Get_Anchor( gpi, &eer->EntryAnchor, IN_CURGLYPH(),
+		      &entry_x, &entry_y );
+  if ( error == HB_Err_Not_Covered )
+    goto end;
+  if ( error )
+    return error;
+
+  if ( gpi->r2l )
+  {
+    POSITION( buffer->in_pos )->x_advance   = entry_x - gpi->anchor_x;
+    POSITION( buffer->in_pos )->new_advance = TRUE;
+  }
+  else
+  {
+    POSITION( gpi->last )->x_advance   = gpi->anchor_x - entry_x;
+    POSITION( gpi->last )->new_advance = TRUE;
+  }
+
+  if ( flags & HB_LOOKUP_FLAG_RIGHT_TO_LEFT )
+  {
+    POSITION( gpi->last )->cursive_chain = gpi->last - buffer->in_pos;
+    POSITION( gpi->last )->y_pos = entry_y - gpi->anchor_y;
+  }
+  else
+  {
+    POSITION( buffer->in_pos )->cursive_chain = buffer->in_pos - gpi->last;
+    POSITION( buffer->in_pos )->y_pos = gpi->anchor_y - entry_y;
+  }
+
+end:
+  error = Get_Anchor( gpi, &eer->ExitAnchor, IN_CURGLYPH(),
+		      &exit_x, &exit_y );
+  if ( error == HB_Err_Not_Covered )
+    gpi->last = 0xFFFF;
+  else
+  {
+    gpi->last     = buffer->in_pos;
+    gpi->anchor_x = exit_x;
+    gpi->anchor_y = exit_y;
+  }
+  if ( error )
+    return error;
+
+  (buffer->in_pos)++;
+
+  return HB_Err_Ok;
+}
+
+
+/* LookupType 4 */
+
+/* BaseArray */
+
+static HB_Error  Load_BaseArray( HB_BaseArray*  ba,
+				 HB_UShort       num_classes,
+				 HB_Stream       stream )
+{
+  HB_Error  error;
+
+  HB_UShort       m, n, count;
+  HB_UInt         cur_offset, new_offset, base_offset;
+
+  HB_BaseRecord  *br;
+  HB_Anchor      *ban, *bans;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = ba->BaseCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ba->BaseRecord = NULL;
+
+  if ( ALLOC_ARRAY( ba->BaseRecord, count, HB_BaseRecord ) )
+    return error;
+
+  br = ba->BaseRecord;
+
+  bans = NULL;
+
+  if ( ALLOC_ARRAY( bans, count * num_classes, HB_Anchor ) )
+    goto Fail;
+
+  for ( m = 0; m < count; m++ )
+  {
+    br[m].BaseAnchor = NULL;
+
+    ban = br[m].BaseAnchor = bans + m * num_classes;
+
+    for ( n = 0; n < num_classes; n++ )
+    {
+      if ( ACCESS_Frame( 2L ) )
+	goto Fail;
+
+      new_offset = GET_UShort() + base_offset;
+
+      FORGET_Frame();
+
+      if (new_offset == base_offset) {
+	/* XXX
+	 * Doulos SIL Regular is buggy and has zero offsets here.
+	 * Skip it
+	 */
+	ban[n].PosFormat = 0;
+	continue;
+      }
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = Load_Anchor( &ban[n], stream ) ) != HB_Err_Ok )
+	goto Fail;
+      (void)FILE_Seek( cur_offset );
+    }
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  FREE( bans );
+  FREE( br );
+  return error;
+}
+
+
+static void  Free_BaseArray( HB_BaseArray*  ba,
+			     HB_UShort       num_classes )
+{
+  HB_BaseRecord  *br;
+  HB_Anchor      *bans;
+
+  if ( ba->BaseRecord )
+  {
+    br    = ba->BaseRecord;
+
+    if ( ba->BaseCount )
+    {
+      HB_UShort i, count;
+      count = num_classes * ba->BaseCount;
+      bans = br[0].BaseAnchor;
+      for (i = 0; i < count; i++)
+        Free_Anchor (&bans[i]);
+      FREE( bans );
+    }
+
+    FREE( br );
+  }
+}
+
+
+/* MarkBasePosFormat1 */
+
+static HB_Error  Load_MarkBasePos( HB_GPOS_SubTable* st,
+				   HB_Stream         stream )
+{
+  HB_Error  error;
+  HB_MarkBasePos* mbp = &st->markbase;
+
+  HB_UInt  cur_offset, new_offset, base_offset;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  mbp->PosFormat = GET_UShort();
+  new_offset     = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  if (mbp->PosFormat != 1)
+    return ERR(HB_Err_Invalid_SubTable_Format);
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &mbp->MarkCoverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &mbp->BaseCoverage, stream ) ) != HB_Err_Ok )
+    goto Fail3;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 4L ) )
+    goto Fail2;
+
+  mbp->ClassCount = GET_UShort();
+  new_offset      = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = Load_MarkArray( &mbp->MarkArray, stream ) ) != HB_Err_Ok )
+    goto Fail2;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail1;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = Load_BaseArray( &mbp->BaseArray, mbp->ClassCount,
+				 stream ) ) != HB_Err_Ok )
+    goto Fail1;
+
+  return HB_Err_Ok;
+
+Fail1:
+  Free_MarkArray( &mbp->MarkArray );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &mbp->BaseCoverage );
+
+Fail3:
+  _HB_OPEN_Free_Coverage( &mbp->MarkCoverage );
+  return error;
+}
+
+
+static void  Free_MarkBasePos( HB_GPOS_SubTable* st )
+{
+  HB_MarkBasePos* mbp = &st->markbase;
+
+  Free_BaseArray( &mbp->BaseArray, mbp->ClassCount );
+  Free_MarkArray( &mbp->MarkArray );
+  _HB_OPEN_Free_Coverage( &mbp->BaseCoverage );
+  _HB_OPEN_Free_Coverage( &mbp->MarkCoverage );
+}
+
+
+static HB_Error  Lookup_MarkBasePos( GPOS_Instance*    gpi,
+				     HB_GPOS_SubTable* st,
+				     HB_Buffer        buffer,
+				     HB_UShort         flags,
+				     HB_UShort         context_length,
+				     int               nesting_level )
+{
+  HB_UShort        i, j, mark_index, base_index, property, class;
+  HB_Fixed           x_mark_value, y_mark_value, x_base_value, y_base_value;
+  HB_Error         error;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+  HB_MarkBasePos* mbp = &st->markbase;
+
+  HB_MarkArray*   ma;
+  HB_BaseArray*   ba;
+  HB_BaseRecord*  br;
+  HB_Anchor*      mark_anchor;
+  HB_Anchor*      base_anchor;
+
+  HB_Position     o;
+
+  HB_UNUSED(nesting_level);
+
+  if ( context_length != 0xFFFF && context_length < 1 )
+    return HB_Err_Not_Covered;
+
+  if ( flags & HB_LOOKUP_FLAG_IGNORE_BASE_GLYPHS )
+    return HB_Err_Not_Covered;
+
+  if ( CHECK_Property( gpos->gdef, IN_CURITEM(),
+		       flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &mbp->MarkCoverage, IN_CURGLYPH(),
+			  &mark_index );
+  if ( error )
+    return error;
+
+  /* now we search backwards for a non-mark glyph */
+
+  i = 1;
+  j = buffer->in_pos - 1;
+
+  while ( i <= buffer->in_pos )
+  {
+    error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
+					&property );
+    if ( error )
+      return error;
+
+    if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
+      break;
+
+    i++;
+    j--;
+  }
+
+  /* The following assertion is too strong -- at least for mangal.ttf. */
+#if 0
+  if ( property != HB_GDEF_BASE_GLYPH )
+    return HB_Err_Not_Covered;
+#endif
+
+  if ( i > buffer->in_pos )
+    return HB_Err_Not_Covered;
+
+  error = _HB_OPEN_Coverage_Index( &mbp->BaseCoverage, IN_GLYPH( j ),
+			  &base_index );
+  if ( error )
+    return error;
+
+  ma = &mbp->MarkArray;
+
+  if ( mark_index >= ma->MarkCount )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  class       = ma->MarkRecord[mark_index].Class;
+  mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
+
+  if ( class >= mbp->ClassCount )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  ba = &mbp->BaseArray;
+
+  if ( base_index >= ba->BaseCount )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  br          = &ba->BaseRecord[base_index];
+  base_anchor = &br->BaseAnchor[class];
+
+  error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH(),
+		      &x_mark_value, &y_mark_value );
+  if ( error )
+    return error;
+
+  error = Get_Anchor( gpi, base_anchor, IN_GLYPH( j ),
+		      &x_base_value, &y_base_value );
+  if ( error )
+    return error;
+
+  /* anchor points are not cumulative */
+
+  o = POSITION( buffer->in_pos );
+
+  o->x_pos     = x_base_value - x_mark_value;
+  o->y_pos     = y_base_value - y_mark_value;
+  o->x_advance = 0;
+  o->y_advance = 0;
+  o->back      = i;
+
+  (buffer->in_pos)++;
+
+  return HB_Err_Ok;
+}
+
+
+/* LookupType 5 */
+
+/* LigatureAttach */
+
+static HB_Error  Load_LigatureAttach( HB_LigatureAttach*  lat,
+				      HB_UShort            num_classes,
+				      HB_Stream            stream )
+{
+  HB_Error  error;
+
+  HB_UShort             m, n, k, count;
+  HB_UInt              cur_offset, new_offset, base_offset;
+
+  HB_ComponentRecord*  cr;
+  HB_Anchor*           lan;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = lat->ComponentCount = GET_UShort();
+
+  FORGET_Frame();
+
+  lat->ComponentRecord = NULL;
+
+  if ( ALLOC_ARRAY( lat->ComponentRecord, count, HB_ComponentRecord ) )
+    return error;
+
+  cr = lat->ComponentRecord;
+
+  for ( m = 0; m < count; m++ )
+  {
+    cr[m].LigatureAnchor = NULL;
+
+    if ( ALLOC_ARRAY( cr[m].LigatureAnchor, num_classes, HB_Anchor ) )
+      goto Fail;
+
+    lan = cr[m].LigatureAnchor;
+
+    for ( n = 0; n < num_classes; n++ )
+    {
+      if ( ACCESS_Frame( 2L ) )
+	goto Fail0;
+
+      new_offset = GET_UShort();
+
+      FORGET_Frame();
+
+      if ( new_offset )
+      {
+	new_offset += base_offset;
+
+	cur_offset = FILE_Pos();
+	if ( FILE_Seek( new_offset ) ||
+	     ( error = Load_Anchor( &lan[n], stream ) ) != HB_Err_Ok )
+	  goto Fail0;
+	(void)FILE_Seek( cur_offset );
+      }
+      else
+	lan[n].PosFormat = 0;
+    }
+
+    continue;
+  Fail0:
+    for ( k = 0; k < n; k++ )
+      Free_Anchor( &lan[k] );
+    goto Fail;
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( k = 0; k < m; k++ )
+  {
+    lan = cr[k].LigatureAnchor;
+
+    for ( n = 0; n < num_classes; n++ )
+      Free_Anchor( &lan[n] );
+
+    FREE( lan );
+  }
+
+  FREE( cr );
+  return error;
+}
+
+
+static void  Free_LigatureAttach( HB_LigatureAttach*  lat,
+				  HB_UShort            num_classes )
+{
+  HB_UShort        m, n, count;
+
+  HB_ComponentRecord*  cr;
+  HB_Anchor*           lan;
+
+
+  if ( lat->ComponentRecord )
+  {
+    count = lat->ComponentCount;
+    cr    = lat->ComponentRecord;
+
+    for ( m = 0; m < count; m++ )
+    {
+      lan = cr[m].LigatureAnchor;
+
+      for ( n = 0; n < num_classes; n++ )
+	Free_Anchor( &lan[n] );
+
+      FREE( lan );
+    }
+
+    FREE( cr );
+  }
+}
+
+
+/* LigatureArray */
+
+static HB_Error  Load_LigatureArray( HB_LigatureArray*  la,
+				     HB_UShort           num_classes,
+				     HB_Stream           stream )
+{
+  HB_Error  error;
+
+  HB_UShort            n, m, count;
+  HB_UInt             cur_offset, new_offset, base_offset;
+
+  HB_LigatureAttach*  lat;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = la->LigatureCount = GET_UShort();
+
+  FORGET_Frame();
+
+  la->LigatureAttach = NULL;
+
+  if ( ALLOC_ARRAY( la->LigatureAttach, count, HB_LigatureAttach ) )
+    return error;
+
+  lat = la->LigatureAttach;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_LigatureAttach( &lat[n], num_classes,
+					stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_LigatureAttach( &lat[m], num_classes );
+
+  FREE( lat );
+  return error;
+}
+
+
+static void  Free_LigatureArray( HB_LigatureArray*  la,
+				 HB_UShort           num_classes )
+{
+  HB_UShort            n, count;
+
+  HB_LigatureAttach*  lat;
+
+
+  if ( la->LigatureAttach )
+  {
+    count = la->LigatureCount;
+    lat   = la->LigatureAttach;
+
+    for ( n = 0; n < count; n++ )
+      Free_LigatureAttach( &lat[n], num_classes );
+
+    FREE( lat );
+  }
+}
+
+
+/* MarkLigPosFormat1 */
+
+static HB_Error  Load_MarkLigPos( HB_GPOS_SubTable* st,
+				  HB_Stream        stream )
+{
+  HB_Error  error;
+  HB_MarkLigPos*  mlp = &st->marklig;
+
+  HB_UInt  cur_offset, new_offset, base_offset;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  mlp->PosFormat = GET_UShort();
+  new_offset     = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &mlp->MarkCoverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &mlp->LigatureCoverage,
+				stream ) ) != HB_Err_Ok )
+    goto Fail3;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 4L ) )
+    goto Fail2;
+
+  mlp->ClassCount = GET_UShort();
+  new_offset      = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = Load_MarkArray( &mlp->MarkArray, stream ) ) != HB_Err_Ok )
+    goto Fail2;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail1;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = Load_LigatureArray( &mlp->LigatureArray, mlp->ClassCount,
+				     stream ) ) != HB_Err_Ok )
+    goto Fail1;
+
+  return HB_Err_Ok;
+
+Fail1:
+  Free_MarkArray( &mlp->MarkArray );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &mlp->LigatureCoverage );
+
+Fail3:
+  _HB_OPEN_Free_Coverage( &mlp->MarkCoverage );
+  return error;
+}
+
+
+static void  Free_MarkLigPos( HB_GPOS_SubTable* st)
+{
+  HB_MarkLigPos*  mlp = &st->marklig;
+
+  Free_LigatureArray( &mlp->LigatureArray, mlp->ClassCount );
+  Free_MarkArray( &mlp->MarkArray );
+  _HB_OPEN_Free_Coverage( &mlp->LigatureCoverage );
+  _HB_OPEN_Free_Coverage( &mlp->MarkCoverage );
+}
+
+
+static HB_Error  Lookup_MarkLigPos( GPOS_Instance*    gpi,
+				    HB_GPOS_SubTable* st,
+				    HB_Buffer        buffer,
+				    HB_UShort         flags,
+				    HB_UShort         context_length,
+				    int               nesting_level )
+{
+  HB_UShort        i, j, mark_index, lig_index, property, class;
+  HB_UShort        mark_glyph;
+  HB_Fixed           x_mark_value, y_mark_value, x_lig_value, y_lig_value;
+  HB_Error         error;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+  HB_MarkLigPos*  mlp = &st->marklig;
+
+  HB_MarkArray*        ma;
+  HB_LigatureArray*    la;
+  HB_LigatureAttach*   lat;
+  HB_ComponentRecord*  cr;
+  HB_UShort             comp_index;
+  HB_Anchor*           mark_anchor;
+  HB_Anchor*           lig_anchor;
+
+  HB_Position    o;
+
+  HB_UNUSED(nesting_level);
+
+  if ( context_length != 0xFFFF && context_length < 1 )
+    return HB_Err_Not_Covered;
+
+  if ( flags & HB_LOOKUP_FLAG_IGNORE_LIGATURES )
+    return HB_Err_Not_Covered;
+
+  mark_glyph = IN_CURGLYPH();
+
+  if ( CHECK_Property( gpos->gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &mlp->MarkCoverage, mark_glyph, &mark_index );
+  if ( error )
+    return error;
+
+  /* now we search backwards for a non-mark glyph */
+
+  i = 1;
+  j = buffer->in_pos - 1;
+
+  while ( i <= buffer->in_pos )
+  {
+    error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
+					&property );
+    if ( error )
+      return error;
+
+    if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
+      break;
+
+    i++;
+    j--;
+  }
+
+  /* Similar to Lookup_MarkBasePos(), I suspect that this assertion is
+     too strong, thus it is commented out.                             */
+#if 0
+  if ( property != HB_GDEF_LIGATURE )
+    return HB_Err_Not_Covered;
+#endif
+
+  if ( i > buffer->in_pos )
+    return HB_Err_Not_Covered;
+
+  error = _HB_OPEN_Coverage_Index( &mlp->LigatureCoverage, IN_GLYPH( j ),
+			  &lig_index );
+  if ( error )
+    return error;
+
+  ma = &mlp->MarkArray;
+
+  if ( mark_index >= ma->MarkCount )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  class       = ma->MarkRecord[mark_index].Class;
+  mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
+
+  if ( class >= mlp->ClassCount )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  la = &mlp->LigatureArray;
+
+  if ( lig_index >= la->LigatureCount )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  lat = &la->LigatureAttach[lig_index];
+
+  /* We must now check whether the ligature ID of the current mark glyph
+     is identical to the ligature ID of the found ligature.  If yes, we
+     can directly use the component index.  If not, we attach the mark
+     glyph to the last component of the ligature.                        */
+
+  if ( IN_LIGID( j ) == IN_LIGID( buffer->in_pos) )
+  {
+    comp_index = IN_COMPONENT( buffer->in_pos );
+    if ( comp_index >= lat->ComponentCount )
+      return HB_Err_Not_Covered;
+  }
+  else
+    comp_index = lat->ComponentCount - 1;
+
+  cr         = &lat->ComponentRecord[comp_index];
+  lig_anchor = &cr->LigatureAnchor[class];
+
+  error = Get_Anchor( gpi, mark_anchor, IN_CURGLYPH(),
+		      &x_mark_value, &y_mark_value );
+  if ( error )
+    return error;
+  error = Get_Anchor( gpi, lig_anchor, IN_GLYPH( j ),
+		      &x_lig_value, &y_lig_value );
+  if ( error )
+    return error;
+
+  /* anchor points are not cumulative */
+
+  o = POSITION( buffer->in_pos );
+
+  o->x_pos     = x_lig_value - x_mark_value;
+  o->y_pos     = y_lig_value - y_mark_value;
+  o->x_advance = 0;
+  o->y_advance = 0;
+  o->back      = i;
+
+  (buffer->in_pos)++;
+
+  return HB_Err_Ok;
+}
+
+
+/* LookupType 6 */
+
+/* Mark2Array */
+
+static HB_Error  Load_Mark2Array( HB_Mark2Array*  m2a,
+				  HB_UShort        num_classes,
+				  HB_Stream        stream )
+{
+  HB_Error  error;
+
+  HB_UShort        m, n, count;
+  HB_UInt          cur_offset, new_offset, base_offset;
+
+  HB_Mark2Record  *m2r;
+  HB_Anchor       *m2an, *m2ans;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = m2a->Mark2Count = GET_UShort();
+
+  FORGET_Frame();
+
+  m2a->Mark2Record = NULL;
+
+  if ( ALLOC_ARRAY( m2a->Mark2Record, count, HB_Mark2Record ) )
+    return error;
+
+  m2r = m2a->Mark2Record;
+
+  m2ans = NULL;
+
+  if ( ALLOC_ARRAY( m2ans, count * num_classes, HB_Anchor ) )
+    goto Fail;
+
+  for ( m = 0; m < count; m++ )
+  {
+    m2an = m2r[m].Mark2Anchor = m2ans + m * num_classes;
+
+    for ( n = 0; n < num_classes; n++ )
+    {
+      if ( ACCESS_Frame( 2L ) )
+	goto Fail;
+
+      new_offset = GET_UShort() + base_offset;
+
+      FORGET_Frame();
+
+      if (new_offset == base_offset) {
+        /* Anchor table not provided.  Skip loading.
+	 * Some versions of FreeSans hit this. */
+        m2an[n].PosFormat = 0;
+	continue;
+      }
+
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = Load_Anchor( &m2an[n], stream ) ) != HB_Err_Ok )
+	goto Fail;
+      (void)FILE_Seek( cur_offset );
+    }
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  FREE( m2ans );
+  FREE( m2r );
+  return error;
+}
+
+
+static void  Free_Mark2Array( HB_Mark2Array*  m2a,
+			      HB_UShort        num_classes )
+{
+  HB_Mark2Record  *m2r;
+  HB_Anchor       *m2ans;
+
+  HB_UNUSED(num_classes);
+
+  if ( m2a->Mark2Record )
+  {
+    m2r   = m2a->Mark2Record;
+
+    if ( m2a->Mark2Count )
+    {
+      m2ans = m2r[0].Mark2Anchor;
+      FREE( m2ans );
+    }
+
+    FREE( m2r );
+  }
+}
+
+
+/* MarkMarkPosFormat1 */
+
+static HB_Error  Load_MarkMarkPos( HB_GPOS_SubTable* st,
+				   HB_Stream         stream )
+{
+  HB_Error  error;
+  HB_MarkMarkPos* mmp = &st->markmark;
+
+  HB_UInt  cur_offset, new_offset, base_offset;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  mmp->PosFormat = GET_UShort();
+  new_offset     = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &mmp->Mark1Coverage,
+				stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &mmp->Mark2Coverage,
+				stream ) ) != HB_Err_Ok )
+    goto Fail3;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 4L ) )
+    goto Fail2;
+
+  mmp->ClassCount = GET_UShort();
+  new_offset      = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = Load_MarkArray( &mmp->Mark1Array, stream ) ) != HB_Err_Ok )
+    goto Fail2;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail1;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = Load_Mark2Array( &mmp->Mark2Array, mmp->ClassCount,
+				  stream ) ) != HB_Err_Ok )
+    goto Fail1;
+
+  return HB_Err_Ok;
+
+Fail1:
+  Free_MarkArray( &mmp->Mark1Array );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &mmp->Mark2Coverage );
+
+Fail3:
+  _HB_OPEN_Free_Coverage( &mmp->Mark1Coverage );
+  return error;
+}
+
+
+static void  Free_MarkMarkPos( HB_GPOS_SubTable* st)
+{
+  HB_MarkMarkPos* mmp = &st->markmark;
+
+  Free_Mark2Array( &mmp->Mark2Array, mmp->ClassCount );
+  Free_MarkArray( &mmp->Mark1Array );
+  _HB_OPEN_Free_Coverage( &mmp->Mark2Coverage );
+  _HB_OPEN_Free_Coverage( &mmp->Mark1Coverage );
+}
+
+
+static HB_Error  Lookup_MarkMarkPos( GPOS_Instance*    gpi,
+				     HB_GPOS_SubTable* st,
+				     HB_Buffer        buffer,
+				     HB_UShort         flags,
+				     HB_UShort         context_length,
+				     int               nesting_level )
+{
+  HB_UShort        i, j, mark1_index, mark2_index, property, class;
+  HB_Fixed           x_mark1_value, y_mark1_value,
+		   x_mark2_value, y_mark2_value;
+  HB_Error         error;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+  HB_MarkMarkPos* mmp = &st->markmark;
+
+  HB_MarkArray*    ma1;
+  HB_Mark2Array*   ma2;
+  HB_Mark2Record*  m2r;
+  HB_Anchor*       mark1_anchor;
+  HB_Anchor*       mark2_anchor;
+
+  HB_Position    o;
+
+  HB_UNUSED(nesting_level);
+
+  if ( context_length != 0xFFFF && context_length < 1 )
+    return HB_Err_Not_Covered;
+
+  if ( flags & HB_LOOKUP_FLAG_IGNORE_MARKS )
+    return HB_Err_Not_Covered;
+
+  if ( CHECK_Property( gpos->gdef, IN_CURITEM(),
+		       flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &mmp->Mark1Coverage, IN_CURGLYPH(),
+			  &mark1_index );
+  if ( error )
+    return error;
+
+  /* now we search backwards for a suitable mark glyph until a non-mark
+     glyph                                                */
+
+  if ( buffer->in_pos == 0 )
+    return HB_Err_Not_Covered;
+
+  i = 1;
+  j = buffer->in_pos - 1;
+  while ( i <= buffer->in_pos )
+  {
+    error = HB_GDEF_Get_Glyph_Property( gpos->gdef, IN_GLYPH( j ),
+					&property );
+    if ( error )
+      return error;
+
+    if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
+      return HB_Err_Not_Covered;
+
+    if ( flags & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
+    {
+      if ( property == (flags & 0xFF00) )
+        break;
+    }
+    else
+      break;
+
+    i++;
+    j--;
+  }
+
+  error = _HB_OPEN_Coverage_Index( &mmp->Mark2Coverage, IN_GLYPH( j ),
+			  &mark2_index );
+  if ( error )
+    return error;
+
+  ma1 = &mmp->Mark1Array;
+
+  if ( mark1_index >= ma1->MarkCount )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  class        = ma1->MarkRecord[mark1_index].Class;
+  mark1_anchor = &ma1->MarkRecord[mark1_index].MarkAnchor;
+
+  if ( class >= mmp->ClassCount )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  ma2 = &mmp->Mark2Array;
+
+  if ( mark2_index >= ma2->Mark2Count )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  m2r          = &ma2->Mark2Record[mark2_index];
+  mark2_anchor = &m2r->Mark2Anchor[class];
+
+  error = Get_Anchor( gpi, mark1_anchor, IN_CURGLYPH(),
+		      &x_mark1_value, &y_mark1_value );
+  if ( error )
+    return error;
+  error = Get_Anchor( gpi, mark2_anchor, IN_GLYPH( j ),
+		      &x_mark2_value, &y_mark2_value );
+  if ( error )
+    return error;
+
+  /* anchor points are not cumulative */
+
+  o = POSITION( buffer->in_pos );
+
+  o->x_pos     = x_mark2_value - x_mark1_value;
+  o->y_pos     = y_mark2_value - y_mark1_value;
+  o->x_advance = 0;
+  o->y_advance = 0;
+  o->back      = 1;
+
+  (buffer->in_pos)++;
+
+  return HB_Err_Ok;
+}
+
+
+/* Do the actual positioning for a context positioning (either format
+   7 or 8).  This is only called after we've determined that the stream
+   matches the subrule.                                                 */
+
+static HB_Error  Do_ContextPos( GPOS_Instance*        gpi,
+				HB_UShort             GlyphCount,
+				HB_UShort             PosCount,
+				HB_PosLookupRecord*  pos,
+				HB_Buffer            buffer,
+				int                   nesting_level )
+{
+  HB_Error  error;
+  HB_UInt   i, old_pos;
+
+
+  i = 0;
+
+  while ( i < GlyphCount )
+  {
+    if ( PosCount && i == pos->SequenceIndex )
+    {
+      old_pos = buffer->in_pos;
+
+      /* Do a positioning */
+
+      error = GPOS_Do_Glyph_Lookup( gpi, pos->LookupListIndex, buffer,
+				    GlyphCount, nesting_level );
+
+      if ( error )
+	return error;
+
+      pos++;
+      PosCount--;
+      i += buffer->in_pos - old_pos;
+    }
+    else
+    {
+      i++;
+      (buffer->in_pos)++;
+    }
+  }
+
+  return HB_Err_Ok;
+}
+
+
+/* LookupType 7 */
+
+/* PosRule */
+
+static HB_Error  Load_PosRule( HB_PosRule*  pr,
+			       HB_Stream     stream )
+{
+  HB_Error  error;
+
+  HB_UShort             n, count;
+  HB_UShort*            i;
+
+  HB_PosLookupRecord*  plr;
+
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  pr->GlyphCount = GET_UShort();
+  pr->PosCount   = GET_UShort();
+
+  FORGET_Frame();
+
+  pr->Input = NULL;
+
+  count = pr->GlyphCount - 1;         /* only GlyphCount - 1 elements */
+
+  if ( ALLOC_ARRAY( pr->Input, count, HB_UShort ) )
+    return error;
+
+  i = pr->Input;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail2;
+
+  for ( n = 0; n < count; n++ )
+    i[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  pr->PosLookupRecord = NULL;
+
+  count = pr->PosCount;
+
+  if ( ALLOC_ARRAY( pr->PosLookupRecord, count, HB_PosLookupRecord ) )
+    goto Fail2;
+
+  plr = pr->PosLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    plr[n].SequenceIndex   = GET_UShort();
+    plr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( plr );
+
+Fail2:
+  FREE( i );
+  return error;
+}
+
+
+static void  Free_PosRule( HB_PosRule*  pr )
+{
+  FREE( pr->PosLookupRecord );
+  FREE( pr->Input );
+}
+
+
+/* PosRuleSet */
+
+static HB_Error  Load_PosRuleSet( HB_PosRuleSet*  prs,
+				  HB_Stream        stream )
+{
+  HB_Error  error;
+
+  HB_UShort     n, m, count;
+  HB_UInt      cur_offset, new_offset, base_offset;
+
+  HB_PosRule*  pr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = prs->PosRuleCount = GET_UShort();
+
+  FORGET_Frame();
+
+  prs->PosRule = NULL;
+
+  if ( ALLOC_ARRAY( prs->PosRule, count, HB_PosRule ) )
+    return error;
+
+  pr = prs->PosRule;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_PosRule( &pr[n], stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_PosRule( &pr[m] );
+
+  FREE( pr );
+  return error;
+}
+
+
+static void  Free_PosRuleSet( HB_PosRuleSet*  prs )
+{
+  HB_UShort     n, count;
+
+  HB_PosRule*  pr;
+
+
+  if ( prs->PosRule )
+  {
+    count = prs->PosRuleCount;
+    pr    = prs->PosRule;
+
+    for ( n = 0; n < count; n++ )
+      Free_PosRule( &pr[n] );
+
+    FREE( pr );
+  }
+}
+
+
+/* ContextPosFormat1 */
+
+static HB_Error  Load_ContextPos1( HB_ContextPosFormat1*  cpf1,
+				   HB_Stream               stream )
+{
+  HB_Error  error;
+
+  HB_UShort        n, m, count;
+  HB_UInt         cur_offset, new_offset, base_offset;
+
+  HB_PosRuleSet*  prs;
+
+
+  base_offset = FILE_Pos() - 2L;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &cpf1->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = cpf1->PosRuleSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cpf1->PosRuleSet = NULL;
+
+  if ( ALLOC_ARRAY( cpf1->PosRuleSet, count, HB_PosRuleSet ) )
+    goto Fail2;
+
+  prs = cpf1->PosRuleSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_PosRuleSet( &prs[n], stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_PosRuleSet( &prs[m] );
+
+  FREE( prs );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &cpf1->Coverage );
+  return error;
+}
+
+
+static void  Free_ContextPos1( HB_ContextPosFormat1*  cpf1 )
+{
+  HB_UShort        n, count;
+
+  HB_PosRuleSet*  prs;
+
+
+  if ( cpf1->PosRuleSet )
+  {
+    count = cpf1->PosRuleSetCount;
+    prs   = cpf1->PosRuleSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_PosRuleSet( &prs[n] );
+
+    FREE( prs );
+  }
+
+  _HB_OPEN_Free_Coverage( &cpf1->Coverage );
+}
+
+
+/* PosClassRule */
+
+static HB_Error  Load_PosClassRule( HB_ContextPosFormat2*  cpf2,
+				    HB_PosClassRule*       pcr,
+				    HB_Stream               stream )
+{
+  HB_Error  error;
+
+  HB_UShort             n, count;
+
+  HB_UShort*            c;
+  HB_PosLookupRecord*  plr;
+
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  pcr->GlyphCount = GET_UShort();
+  pcr->PosCount   = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( pcr->GlyphCount > cpf2->MaxContextLength )
+    cpf2->MaxContextLength = pcr->GlyphCount;
+
+  pcr->Class = NULL;
+
+  count = pcr->GlyphCount - 1;        /* only GlyphCount - 1 elements */
+
+  if ( ALLOC_ARRAY( pcr->Class, count, HB_UShort ) )
+    return error;
+
+  c = pcr->Class;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail2;
+
+  for ( n = 0; n < count; n++ )
+    c[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  pcr->PosLookupRecord = NULL;
+
+  count = pcr->PosCount;
+
+  if ( ALLOC_ARRAY( pcr->PosLookupRecord, count, HB_PosLookupRecord ) )
+    goto Fail2;
+
+  plr = pcr->PosLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    plr[n].SequenceIndex   = GET_UShort();
+    plr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( plr );
+
+Fail2:
+  FREE( c );
+  return error;
+}
+
+
+static void  Free_PosClassRule( HB_PosClassRule*  pcr )
+{
+  FREE( pcr->PosLookupRecord );
+  FREE( pcr->Class );
+}
+
+
+/* PosClassSet */
+
+static HB_Error  Load_PosClassSet( HB_ContextPosFormat2*  cpf2,
+				   HB_PosClassSet*        pcs,
+				   HB_Stream               stream )
+{
+  HB_Error  error;
+
+  HB_UShort          n, m, count;
+  HB_UInt           cur_offset, new_offset, base_offset;
+
+  HB_PosClassRule*  pcr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = pcs->PosClassRuleCount = GET_UShort();
+
+  FORGET_Frame();
+
+  pcs->PosClassRule = NULL;
+
+  if ( ALLOC_ARRAY( pcs->PosClassRule, count, HB_PosClassRule ) )
+    return error;
+
+  pcr = pcs->PosClassRule;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_PosClassRule( cpf2, &pcr[n],
+				      stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_PosClassRule( &pcr[m] );
+
+  FREE( pcr );
+  return error;
+}
+
+
+static void  Free_PosClassSet( HB_PosClassSet*  pcs )
+{
+  HB_UShort          n, count;
+
+  HB_PosClassRule*  pcr;
+
+
+  if ( pcs->PosClassRule )
+  {
+    count = pcs->PosClassRuleCount;
+    pcr   = pcs->PosClassRule;
+
+    for ( n = 0; n < count; n++ )
+      Free_PosClassRule( &pcr[n] );
+
+    FREE( pcr );
+  }
+}
+
+
+/* ContextPosFormat2 */
+
+static HB_Error  Load_ContextPos2( HB_ContextPosFormat2*  cpf2,
+				   HB_Stream               stream )
+{
+  HB_Error  error;
+
+  HB_UShort         n, m, count;
+  HB_UInt          cur_offset, new_offset, base_offset;
+
+  HB_PosClassSet*  pcs;
+
+
+  base_offset = FILE_Pos() - 2;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &cpf2->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 4L ) )
+    goto Fail3;
+
+  new_offset = GET_UShort() + base_offset;
+
+  /* `PosClassSetCount' is the upper limit for class values, thus we
+     read it now to make an additional safety check.                 */
+
+  count = cpf2->PosClassSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_ClassDefinition( &cpf2->ClassDef, count,
+				       stream ) ) != HB_Err_Ok )
+    goto Fail3;
+  (void)FILE_Seek( cur_offset );
+
+  cpf2->PosClassSet      = NULL;
+  cpf2->MaxContextLength = 0;
+
+  if ( ALLOC_ARRAY( cpf2->PosClassSet, count, HB_PosClassSet ) )
+    goto Fail2;
+
+  pcs = cpf2->PosClassSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    if ( new_offset != base_offset )      /* not a NULL offset */
+    {
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = Load_PosClassSet( cpf2, &pcs[n],
+				       stream ) ) != HB_Err_Ok )
+	goto Fail1;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+    {
+      /* we create a PosClassSet table with no entries */
+
+      cpf2->PosClassSet[n].PosClassRuleCount = 0;
+      cpf2->PosClassSet[n].PosClassRule      = NULL;
+    }
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; n++ )
+    Free_PosClassSet( &pcs[m] );
+
+  FREE( pcs );
+
+Fail2:
+  _HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef );
+
+Fail3:
+  _HB_OPEN_Free_Coverage( &cpf2->Coverage );
+  return error;
+}
+
+
+static void  Free_ContextPos2( HB_ContextPosFormat2*  cpf2 )
+{
+  HB_UShort         n, count;
+
+  HB_PosClassSet*  pcs;
+
+
+  if ( cpf2->PosClassSet )
+  {
+    count = cpf2->PosClassSetCount;
+    pcs   = cpf2->PosClassSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_PosClassSet( &pcs[n] );
+
+    FREE( pcs );
+  }
+
+  _HB_OPEN_Free_ClassDefinition( &cpf2->ClassDef );
+  _HB_OPEN_Free_Coverage( &cpf2->Coverage );
+}
+
+
+/* ContextPosFormat3 */
+
+static HB_Error  Load_ContextPos3( HB_ContextPosFormat3*  cpf3,
+				   HB_Stream               stream )
+{
+  HB_Error  error;
+
+  HB_UShort             n, count;
+  HB_UInt              cur_offset, new_offset, base_offset;
+
+  HB_Coverage*         c;
+  HB_PosLookupRecord*  plr;
+
+
+  base_offset = FILE_Pos() - 2L;
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  cpf3->GlyphCount = GET_UShort();
+  cpf3->PosCount   = GET_UShort();
+
+  FORGET_Frame();
+
+  cpf3->Coverage = NULL;
+
+  count = cpf3->GlyphCount;
+
+  if ( ALLOC_ARRAY( cpf3->Coverage, count, HB_Coverage ) )
+    return error;
+
+  c = cpf3->Coverage;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail2;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Coverage( &c[n], stream ) ) != HB_Err_Ok )
+      goto Fail2;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  cpf3->PosLookupRecord = NULL;
+
+  count = cpf3->PosCount;
+
+  if ( ALLOC_ARRAY( cpf3->PosLookupRecord, count, HB_PosLookupRecord ) )
+    goto Fail2;
+
+  plr = cpf3->PosLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    plr[n].SequenceIndex   = GET_UShort();
+    plr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( plr );
+
+Fail2:
+  for ( n = 0; n < count; n++ )
+    _HB_OPEN_Free_Coverage( &c[n] );
+
+  FREE( c );
+  return error;
+}
+
+
+static void  Free_ContextPos3( HB_ContextPosFormat3*  cpf3 )
+{
+  HB_UShort      n, count;
+
+  HB_Coverage*  c;
+
+
+  FREE( cpf3->PosLookupRecord );
+
+  if ( cpf3->Coverage )
+  {
+    count = cpf3->GlyphCount;
+    c     = cpf3->Coverage;
+
+    for ( n = 0; n < count; n++ )
+      _HB_OPEN_Free_Coverage( &c[n] );
+
+    FREE( c );
+  }
+}
+
+
+/* ContextPos */
+
+static HB_Error  Load_ContextPos( HB_GPOS_SubTable* st,
+				  HB_Stream        stream )
+{
+  HB_Error  error;
+  HB_ContextPos*   cp = &st->context;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  cp->PosFormat = GET_UShort();
+
+  FORGET_Frame();
+
+  switch ( cp->PosFormat )
+  {
+  case 1:
+    return Load_ContextPos1( &cp->cpf.cpf1, stream );
+
+  case 2:
+    return Load_ContextPos2( &cp->cpf.cpf2, stream );
+
+  case 3:
+    return Load_ContextPos3( &cp->cpf.cpf3, stream );
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;               /* never reached */
+}
+
+
+static void  Free_ContextPos( HB_GPOS_SubTable* st )
+{
+  HB_ContextPos*   cp = &st->context;
+
+  switch ( cp->PosFormat )
+  {
+  case 1:  Free_ContextPos1( &cp->cpf.cpf1 ); break;
+  case 2:  Free_ContextPos2( &cp->cpf.cpf2 ); break;
+  case 3:  Free_ContextPos3( &cp->cpf.cpf3 ); break;
+  default:					      break;
+  }
+}
+
+
+static HB_Error  Lookup_ContextPos1( GPOS_Instance*          gpi,
+				     HB_ContextPosFormat1*  cpf1,
+				     HB_Buffer              buffer,
+				     HB_UShort               flags,
+				     HB_UShort               context_length,
+				     int                     nesting_level )
+{
+  HB_UShort        index, property;
+  HB_UShort        i, j, k, numpr;
+  HB_Error         error;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+
+  HB_PosRule*     pr;
+  HB_GDEFHeader*  gdef;
+
+
+  gdef = gpos->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &cpf1->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  pr    = cpf1->PosRuleSet[index].PosRule;
+  numpr = cpf1->PosRuleSet[index].PosRuleCount;
+
+  for ( k = 0; k < numpr; k++ )
+  {
+    if ( context_length != 0xFFFF && context_length < pr[k].GlyphCount )
+      goto next_posrule;
+
+    if ( buffer->in_pos + pr[k].GlyphCount > buffer->in_length )
+      goto next_posrule;                       /* context is too long */
+
+    for ( i = 1, j = buffer->in_pos + 1; i < pr[k].GlyphCount; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  return error;
+
+	if ( j + pr[k].GlyphCount - i == (HB_Int)buffer->in_length )
+	  goto next_posrule;
+	j++;
+      }
+
+      if ( IN_GLYPH( j ) != pr[k].Input[i - 1] )
+	goto next_posrule;
+    }
+
+    return Do_ContextPos( gpi, pr[k].GlyphCount,
+			  pr[k].PosCount, pr[k].PosLookupRecord,
+			  buffer,
+			  nesting_level );
+
+    next_posrule:
+      ;
+  }
+
+  return HB_Err_Not_Covered;
+}
+
+
+static HB_Error  Lookup_ContextPos2( GPOS_Instance*          gpi,
+				     HB_ContextPosFormat2*  cpf2,
+				     HB_Buffer              buffer,
+				     HB_UShort               flags,
+				     HB_UShort               context_length,
+				     int                     nesting_level )
+{
+  HB_UShort          index, property;
+  HB_Error           error;
+  HB_UShort          i, j, k, known_classes;
+
+  HB_UShort*         classes;
+  HB_UShort*         cl;
+  HB_GPOSHeader*    gpos = gpi->gpos;
+
+  HB_PosClassSet*   pcs;
+  HB_PosClassRule*  pr;
+  HB_GDEFHeader*    gdef;
+
+
+  gdef = gpos->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  /* Note: The coverage table in format 2 doesn't give an index into
+	   anything.  It just lets us know whether or not we need to
+	   do any lookup at all.                                     */
+
+  error = _HB_OPEN_Coverage_Index( &cpf2->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  if (cpf2->MaxContextLength < 1)
+    return HB_Err_Not_Covered;
+
+  if ( ALLOC_ARRAY( classes, cpf2->MaxContextLength, HB_UShort ) )
+    return error;
+
+  error = _HB_OPEN_Get_Class( &cpf2->ClassDef, IN_CURGLYPH(),
+		     &classes[0], NULL );
+  if ( error && error != HB_Err_Not_Covered )
+    goto End;
+  known_classes = 0;
+
+  pcs = &cpf2->PosClassSet[classes[0]];
+  if ( !pcs )
+  {
+    error = ERR(HB_Err_Invalid_SubTable);
+    goto End;
+  }
+
+  for ( k = 0; k < pcs->PosClassRuleCount; k++ )
+  {
+    pr = &pcs->PosClassRule[k];
+
+    if ( context_length != 0xFFFF && context_length < pr->GlyphCount )
+      goto next_posclassrule;
+
+    if ( buffer->in_pos + pr->GlyphCount > buffer->in_length )
+      goto next_posclassrule;                /* context is too long */
+
+    cl   = pr->Class;
+
+    /* Start at 1 because [0] is implied */
+
+    for ( i = 1, j = buffer->in_pos + 1; i < pr->GlyphCount; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End;
+
+	if ( j + pr->GlyphCount - i == (HB_Int)buffer->in_length )
+	  goto next_posclassrule;
+	j++;
+      }
+
+      if ( i > known_classes )
+      {
+	/* Keeps us from having to do this for each rule */
+
+	error = _HB_OPEN_Get_Class( &cpf2->ClassDef, IN_GLYPH( j ), &classes[i], NULL );
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End;
+	known_classes = i;
+      }
+
+      if ( cl[i - 1] != classes[i] )
+	goto next_posclassrule;
+    }
+
+    error = Do_ContextPos( gpi, pr->GlyphCount,
+			   pr->PosCount, pr->PosLookupRecord,
+			   buffer,
+			   nesting_level );
+    goto End;
+
+  next_posclassrule:
+    ;
+  }
+
+  error = HB_Err_Not_Covered;
+
+End:
+  FREE( classes );
+  return error;
+}
+
+
+static HB_Error  Lookup_ContextPos3( GPOS_Instance*          gpi,
+				     HB_ContextPosFormat3*  cpf3,
+				     HB_Buffer              buffer,
+				     HB_UShort               flags,
+				     HB_UShort               context_length,
+				     int                     nesting_level )
+{
+  HB_Error         error;
+  HB_UShort        index, i, j, property;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+
+  HB_Coverage*    c;
+  HB_GDEFHeader*  gdef;
+
+
+  gdef = gpos->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  if ( context_length != 0xFFFF && context_length < cpf3->GlyphCount )
+    return HB_Err_Not_Covered;
+
+  if ( buffer->in_pos + cpf3->GlyphCount > buffer->in_length )
+    return HB_Err_Not_Covered;         /* context is too long */
+
+  c    = cpf3->Coverage;
+
+  for ( i = 1, j = 1; i < cpf3->GlyphCount; i++, j++ )
+  {
+    while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+    {
+      if ( error && error != HB_Err_Not_Covered )
+	return error;
+
+      if ( j + cpf3->GlyphCount - i == (HB_Int)buffer->in_length )
+	return HB_Err_Not_Covered;
+      j++;
+    }
+
+    error = _HB_OPEN_Coverage_Index( &c[i], IN_GLYPH( j ), &index );
+    if ( error )
+      return error;
+  }
+
+  return Do_ContextPos( gpi, cpf3->GlyphCount,
+			cpf3->PosCount, cpf3->PosLookupRecord,
+			buffer,
+			nesting_level );
+}
+
+
+static HB_Error  Lookup_ContextPos( GPOS_Instance*    gpi,
+				    HB_GPOS_SubTable* st,
+				    HB_Buffer        buffer,
+				    HB_UShort         flags,
+				    HB_UShort         context_length,
+				    int               nesting_level )
+{
+  HB_ContextPos*   cp = &st->context;
+
+  switch ( cp->PosFormat )
+  {
+  case 1:
+    return Lookup_ContextPos1( gpi, &cp->cpf.cpf1, buffer,
+			       flags, context_length, nesting_level );
+
+  case 2:
+    return Lookup_ContextPos2( gpi, &cp->cpf.cpf2, buffer,
+			       flags, context_length, nesting_level );
+
+  case 3:
+    return Lookup_ContextPos3( gpi, &cp->cpf.cpf3, buffer,
+			       flags, context_length, nesting_level );
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;               /* never reached */
+}
+
+
+/* LookupType 8 */
+
+/* ChainPosRule */
+
+static HB_Error  Load_ChainPosRule( HB_ChainPosRule*  cpr,
+				    HB_Stream          stream )
+{
+  HB_Error  error;
+
+  HB_UShort             n, count;
+  HB_UShort*            b;
+  HB_UShort*            i;
+  HB_UShort*            l;
+
+  HB_PosLookupRecord*  plr;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  cpr->BacktrackGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cpr->Backtrack = NULL;
+
+  count = cpr->BacktrackGlyphCount;
+
+  if ( ALLOC_ARRAY( cpr->Backtrack, count, HB_UShort ) )
+    return error;
+
+  b = cpr->Backtrack;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail4;
+
+  for ( n = 0; n < count; n++ )
+    b[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail4;
+
+  cpr->InputGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cpr->Input = NULL;
+
+  count = cpr->InputGlyphCount - 1;  /* only InputGlyphCount - 1 elements */
+
+  if ( ALLOC_ARRAY( cpr->Input, count, HB_UShort ) )
+    goto Fail4;
+
+  i = cpr->Input;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail3;
+
+  for ( n = 0; n < count; n++ )
+    i[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  cpr->LookaheadGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cpr->Lookahead = NULL;
+
+  count = cpr->LookaheadGlyphCount;
+
+  if ( ALLOC_ARRAY( cpr->Lookahead, count, HB_UShort ) )
+    goto Fail3;
+
+  l = cpr->Lookahead;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail2;
+
+  for ( n = 0; n < count; n++ )
+    l[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  cpr->PosCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cpr->PosLookupRecord = NULL;
+
+  count = cpr->PosCount;
+
+  if ( ALLOC_ARRAY( cpr->PosLookupRecord, count, HB_PosLookupRecord ) )
+    goto Fail2;
+
+  plr = cpr->PosLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    plr[n].SequenceIndex   = GET_UShort();
+    plr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( plr );
+
+Fail2:
+  FREE( l );
+
+Fail3:
+  FREE( i );
+
+Fail4:
+  FREE( b );
+  return error;
+}
+
+
+static void  Free_ChainPosRule( HB_ChainPosRule*  cpr )
+{
+  FREE( cpr->PosLookupRecord );
+  FREE( cpr->Lookahead );
+  FREE( cpr->Input );
+  FREE( cpr->Backtrack );
+}
+
+
+/* ChainPosRuleSet */
+
+static HB_Error  Load_ChainPosRuleSet( HB_ChainPosRuleSet*  cprs,
+				       HB_Stream             stream )
+{
+  HB_Error  error;
+
+  HB_UShort          n, m, count;
+  HB_UInt           cur_offset, new_offset, base_offset;
+
+  HB_ChainPosRule*  cpr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = cprs->ChainPosRuleCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cprs->ChainPosRule = NULL;
+
+  if ( ALLOC_ARRAY( cprs->ChainPosRule, count, HB_ChainPosRule ) )
+    return error;
+
+  cpr = cprs->ChainPosRule;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_ChainPosRule( &cpr[n], stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_ChainPosRule( &cpr[m] );
+
+  FREE( cpr );
+  return error;
+}
+
+
+static void  Free_ChainPosRuleSet( HB_ChainPosRuleSet*  cprs )
+{
+  HB_UShort          n, count;
+
+  HB_ChainPosRule*  cpr;
+
+
+  if ( cprs->ChainPosRule )
+  {
+    count = cprs->ChainPosRuleCount;
+    cpr   = cprs->ChainPosRule;
+
+    for ( n = 0; n < count; n++ )
+      Free_ChainPosRule( &cpr[n] );
+
+    FREE( cpr );
+  }
+}
+
+
+/* ChainContextPosFormat1 */
+
+static HB_Error  Load_ChainContextPos1( HB_ChainContextPosFormat1*  ccpf1,
+					HB_Stream                    stream )
+{
+  HB_Error  error;
+
+  HB_UShort             n, m, count;
+  HB_UInt              cur_offset, new_offset, base_offset;
+
+  HB_ChainPosRuleSet*  cprs;
+
+
+  base_offset = FILE_Pos() - 2L;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &ccpf1->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = ccpf1->ChainPosRuleSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ccpf1->ChainPosRuleSet = NULL;
+
+  if ( ALLOC_ARRAY( ccpf1->ChainPosRuleSet, count, HB_ChainPosRuleSet ) )
+    goto Fail2;
+
+  cprs = ccpf1->ChainPosRuleSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_ChainPosRuleSet( &cprs[n], stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_ChainPosRuleSet( &cprs[m] );
+
+  FREE( cprs );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &ccpf1->Coverage );
+  return error;
+}
+
+
+static void  Free_ChainContextPos1( HB_ChainContextPosFormat1*  ccpf1 )
+{
+  HB_UShort             n, count;
+
+  HB_ChainPosRuleSet*  cprs;
+
+
+  if ( ccpf1->ChainPosRuleSet )
+  {
+    count = ccpf1->ChainPosRuleSetCount;
+    cprs  = ccpf1->ChainPosRuleSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_ChainPosRuleSet( &cprs[n] );
+
+    FREE( cprs );
+  }
+
+  _HB_OPEN_Free_Coverage( &ccpf1->Coverage );
+}
+
+
+/* ChainPosClassRule */
+
+static HB_Error  Load_ChainPosClassRule(
+		   HB_ChainContextPosFormat2*  ccpf2,
+		   HB_ChainPosClassRule*       cpcr,
+		   HB_Stream                    stream )
+{
+  HB_Error  error;
+
+  HB_UShort             n, count;
+
+  HB_UShort*            b;
+  HB_UShort*            i;
+  HB_UShort*            l;
+  HB_PosLookupRecord*  plr;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  cpcr->BacktrackGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( cpcr->BacktrackGlyphCount > ccpf2->MaxBacktrackLength )
+    ccpf2->MaxBacktrackLength = cpcr->BacktrackGlyphCount;
+
+  cpcr->Backtrack = NULL;
+
+  count = cpcr->BacktrackGlyphCount;
+
+  if ( ALLOC_ARRAY( cpcr->Backtrack, count, HB_UShort ) )
+    return error;
+
+  b = cpcr->Backtrack;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail4;
+
+  for ( n = 0; n < count; n++ )
+    b[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail4;
+
+  cpcr->InputGlyphCount = GET_UShort();
+
+  if ( cpcr->InputGlyphCount > ccpf2->MaxInputLength )
+    ccpf2->MaxInputLength = cpcr->InputGlyphCount;
+
+  FORGET_Frame();
+
+  cpcr->Input = NULL;
+
+  count = cpcr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */
+
+  if ( ALLOC_ARRAY( cpcr->Input, count, HB_UShort ) )
+    goto Fail4;
+
+  i = cpcr->Input;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail3;
+
+  for ( n = 0; n < count; n++ )
+    i[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  cpcr->LookaheadGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( cpcr->LookaheadGlyphCount > ccpf2->MaxLookaheadLength )
+    ccpf2->MaxLookaheadLength = cpcr->LookaheadGlyphCount;
+
+  cpcr->Lookahead = NULL;
+
+  count = cpcr->LookaheadGlyphCount;
+
+  if ( ALLOC_ARRAY( cpcr->Lookahead, count, HB_UShort ) )
+    goto Fail3;
+
+  l = cpcr->Lookahead;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail2;
+
+  for ( n = 0; n < count; n++ )
+    l[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  cpcr->PosCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cpcr->PosLookupRecord = NULL;
+
+  count = cpcr->PosCount;
+
+  if ( ALLOC_ARRAY( cpcr->PosLookupRecord, count, HB_PosLookupRecord ) )
+    goto Fail2;
+
+  plr = cpcr->PosLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    plr[n].SequenceIndex   = GET_UShort();
+    plr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( plr );
+
+Fail2:
+  FREE( l );
+
+Fail3:
+  FREE( i );
+
+Fail4:
+  FREE( b );
+  return error;
+}
+
+
+static void  Free_ChainPosClassRule( HB_ChainPosClassRule*  cpcr )
+{
+  FREE( cpcr->PosLookupRecord );
+  FREE( cpcr->Lookahead );
+  FREE( cpcr->Input );
+  FREE( cpcr->Backtrack );
+}
+
+
+/* PosClassSet */
+
+static HB_Error  Load_ChainPosClassSet(
+		   HB_ChainContextPosFormat2*  ccpf2,
+		   HB_ChainPosClassSet*        cpcs,
+		   HB_Stream                    stream )
+{
+  HB_Error  error;
+
+  HB_UShort               n, m, count;
+  HB_UInt                cur_offset, new_offset, base_offset;
+
+  HB_ChainPosClassRule*  cpcr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = cpcs->ChainPosClassRuleCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cpcs->ChainPosClassRule = NULL;
+
+  if ( ALLOC_ARRAY( cpcs->ChainPosClassRule, count,
+		    HB_ChainPosClassRule ) )
+    return error;
+
+  cpcr = cpcs->ChainPosClassRule;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_ChainPosClassRule( ccpf2, &cpcr[n],
+					   stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_ChainPosClassRule( &cpcr[m] );
+
+  FREE( cpcr );
+  return error;
+}
+
+
+static void  Free_ChainPosClassSet( HB_ChainPosClassSet*  cpcs )
+{
+  HB_UShort               n, count;
+
+  HB_ChainPosClassRule*  cpcr;
+
+
+  if ( cpcs->ChainPosClassRule )
+  {
+    count = cpcs->ChainPosClassRuleCount;
+    cpcr  = cpcs->ChainPosClassRule;
+
+    for ( n = 0; n < count; n++ )
+      Free_ChainPosClassRule( &cpcr[n] );
+
+    FREE( cpcr );
+  }
+}
+
+
+/* ChainContextPosFormat2 */
+
+static HB_Error  Load_ChainContextPos2( HB_ChainContextPosFormat2*  ccpf2,
+					HB_Stream                    stream )
+{
+  HB_Error  error;
+
+  HB_UShort              n, m, count;
+  HB_UInt               cur_offset, new_offset, base_offset;
+  HB_UInt               backtrack_offset, input_offset, lookahead_offset;
+
+  HB_ChainPosClassSet*  cpcs;
+
+
+  base_offset = FILE_Pos() - 2;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &ccpf2->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 8L ) )
+    goto Fail5;
+
+  backtrack_offset = GET_UShort();
+  input_offset     = GET_UShort();
+  lookahead_offset = GET_UShort();
+
+  /* `ChainPosClassSetCount' is the upper limit for input class values,
+     thus we read it now to make an additional safety check. No limit
+     is known or needed for the other two class definitions          */
+
+  count = ccpf2->ChainPosClassSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->BacktrackClassDef, 65535,
+						       backtrack_offset, base_offset,
+						       stream ) ) != HB_Err_Ok )
+    goto Fail5;
+  if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->InputClassDef, count,
+						       input_offset, base_offset,
+						       stream ) ) != HB_Err_Ok )
+    goto Fail4;
+  if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccpf2->LookaheadClassDef, 65535,
+						       lookahead_offset, base_offset,
+						       stream ) ) != HB_Err_Ok )
+    goto Fail3;
+
+  ccpf2->ChainPosClassSet   = NULL;
+  ccpf2->MaxBacktrackLength = 0;
+  ccpf2->MaxInputLength     = 0;
+  ccpf2->MaxLookaheadLength = 0;
+
+  if ( ALLOC_ARRAY( ccpf2->ChainPosClassSet, count, HB_ChainPosClassSet ) )
+    goto Fail2;
+
+  cpcs = ccpf2->ChainPosClassSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    if ( new_offset != base_offset )      /* not a NULL offset */
+    {
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = Load_ChainPosClassSet( ccpf2, &cpcs[n],
+					    stream ) ) != HB_Err_Ok )
+	goto Fail1;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+    {
+      /* we create a ChainPosClassSet table with no entries */
+
+      ccpf2->ChainPosClassSet[n].ChainPosClassRuleCount = 0;
+      ccpf2->ChainPosClassSet[n].ChainPosClassRule      = NULL;
+    }
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_ChainPosClassSet( &cpcs[m] );
+
+  FREE( cpcs );
+
+Fail2:
+  _HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef );
+
+Fail3:
+  _HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef );
+
+Fail4:
+  _HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef );
+
+Fail5:
+  _HB_OPEN_Free_Coverage( &ccpf2->Coverage );
+  return error;
+}
+
+
+static void  Free_ChainContextPos2( HB_ChainContextPosFormat2*  ccpf2 )
+{
+  HB_UShort              n, count;
+
+  HB_ChainPosClassSet*  cpcs;
+
+
+  if ( ccpf2->ChainPosClassSet )
+  {
+    count = ccpf2->ChainPosClassSetCount;
+    cpcs  = ccpf2->ChainPosClassSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_ChainPosClassSet( &cpcs[n] );
+
+    FREE( cpcs );
+  }
+
+  _HB_OPEN_Free_ClassDefinition( &ccpf2->LookaheadClassDef );
+  _HB_OPEN_Free_ClassDefinition( &ccpf2->InputClassDef );
+  _HB_OPEN_Free_ClassDefinition( &ccpf2->BacktrackClassDef );
+
+  _HB_OPEN_Free_Coverage( &ccpf2->Coverage );
+}
+
+
+/* ChainContextPosFormat3 */
+
+static HB_Error  Load_ChainContextPos3( HB_ChainContextPosFormat3*  ccpf3,
+					HB_Stream                    stream )
+{
+  HB_Error  error;
+
+  HB_UShort             n, nb, ni, nl, m, count;
+  HB_UShort             backtrack_count, input_count, lookahead_count;
+  HB_UInt              cur_offset, new_offset, base_offset;
+
+  HB_Coverage*         b;
+  HB_Coverage*         i;
+  HB_Coverage*         l;
+  HB_PosLookupRecord*  plr;
+
+
+  base_offset = FILE_Pos() - 2L;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  ccpf3->BacktrackGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ccpf3->BacktrackCoverage = NULL;
+
+  backtrack_count = ccpf3->BacktrackGlyphCount;
+
+  if ( ALLOC_ARRAY( ccpf3->BacktrackCoverage, backtrack_count,
+		    HB_Coverage ) )
+    return error;
+
+  b = ccpf3->BacktrackCoverage;
+
+  for ( nb = 0; nb < backtrack_count; nb++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail4;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Coverage( &b[nb], stream ) ) != HB_Err_Ok )
+      goto Fail4;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail4;
+
+  ccpf3->InputGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ccpf3->InputCoverage = NULL;
+
+  input_count = ccpf3->InputGlyphCount;
+
+  if ( ALLOC_ARRAY( ccpf3->InputCoverage, input_count, HB_Coverage ) )
+    goto Fail4;
+
+  i = ccpf3->InputCoverage;
+
+  for ( ni = 0; ni < input_count; ni++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail3;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Coverage( &i[ni], stream ) ) != HB_Err_Ok )
+      goto Fail3;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  ccpf3->LookaheadGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ccpf3->LookaheadCoverage = NULL;
+
+  lookahead_count = ccpf3->LookaheadGlyphCount;
+
+  if ( ALLOC_ARRAY( ccpf3->LookaheadCoverage, lookahead_count,
+		    HB_Coverage ) )
+    goto Fail3;
+
+  l = ccpf3->LookaheadCoverage;
+
+  for ( nl = 0; nl < lookahead_count; nl++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail2;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Coverage( &l[nl], stream ) ) != HB_Err_Ok )
+      goto Fail2;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  ccpf3->PosCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ccpf3->PosLookupRecord = NULL;
+
+  count = ccpf3->PosCount;
+
+  if ( ALLOC_ARRAY( ccpf3->PosLookupRecord, count, HB_PosLookupRecord ) )
+    goto Fail2;
+
+  plr = ccpf3->PosLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    plr[n].SequenceIndex   = GET_UShort();
+    plr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( plr );
+
+Fail2:
+  for ( m = 0; m < nl; m++ )
+    _HB_OPEN_Free_Coverage( &l[m] );
+
+  FREE( l );
+
+Fail3:
+  for ( m = 0; m < ni; m++ )
+    _HB_OPEN_Free_Coverage( &i[m] );
+
+  FREE( i );
+
+Fail4:
+  for ( m = 0; m < nb; m++ )
+    _HB_OPEN_Free_Coverage( &b[m] );
+
+  FREE( b );
+  return error;
+}
+
+
+static void  Free_ChainContextPos3( HB_ChainContextPosFormat3*  ccpf3 )
+{
+  HB_UShort      n, count;
+
+  HB_Coverage*  c;
+
+
+  FREE( ccpf3->PosLookupRecord );
+
+  if ( ccpf3->LookaheadCoverage )
+  {
+    count = ccpf3->LookaheadGlyphCount;
+    c     = ccpf3->LookaheadCoverage;
+
+    for ( n = 0; n < count; n++ )
+      _HB_OPEN_Free_Coverage( &c[n] );
+
+    FREE( c );
+  }
+
+  if ( ccpf3->InputCoverage )
+  {
+    count = ccpf3->InputGlyphCount;
+    c     = ccpf3->InputCoverage;
+
+    for ( n = 0; n < count; n++ )
+      _HB_OPEN_Free_Coverage( &c[n] );
+
+    FREE( c );
+  }
+
+  if ( ccpf3->BacktrackCoverage )
+  {
+    count = ccpf3->BacktrackGlyphCount;
+    c     = ccpf3->BacktrackCoverage;
+
+    for ( n = 0; n < count; n++ )
+      _HB_OPEN_Free_Coverage( &c[n] );
+
+    FREE( c );
+  }
+}
+
+
+/* ChainContextPos */
+
+static HB_Error  Load_ChainContextPos( HB_GPOS_SubTable* st,
+				       HB_Stream             stream )
+{
+  HB_Error  error;
+  HB_ChainContextPos*  ccp = &st->chain;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  ccp->PosFormat = GET_UShort();
+
+  FORGET_Frame();
+
+  switch ( ccp->PosFormat )
+  {
+  case 1:
+    return Load_ChainContextPos1( &ccp->ccpf.ccpf1, stream );
+
+  case 2:
+    return Load_ChainContextPos2( &ccp->ccpf.ccpf2, stream );
+
+  case 3:
+    return Load_ChainContextPos3( &ccp->ccpf.ccpf3, stream );
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;               /* never reached */
+}
+
+
+static void  Free_ChainContextPos( HB_GPOS_SubTable* st )
+{
+  HB_ChainContextPos*  ccp = &st->chain;
+
+  switch ( ccp->PosFormat )
+  {
+  case 1:  Free_ChainContextPos1( &ccp->ccpf.ccpf1 ); break;
+  case 2:  Free_ChainContextPos2( &ccp->ccpf.ccpf2 ); break;
+  case 3:  Free_ChainContextPos3( &ccp->ccpf.ccpf3 ); break;
+  default:						      break;
+  }
+}
+
+
+static HB_Error  Lookup_ChainContextPos1(
+		   GPOS_Instance*               gpi,
+		   HB_ChainContextPosFormat1*  ccpf1,
+		   HB_Buffer                   buffer,
+		   HB_UShort                    flags,
+		   HB_UShort                    context_length,
+		   int                          nesting_level )
+{
+  HB_UShort          index, property;
+  HB_UShort          i, j, k, num_cpr;
+  HB_UShort          bgc, igc, lgc;
+  HB_Error           error;
+  HB_GPOSHeader*    gpos = gpi->gpos;
+
+  HB_ChainPosRule*  cpr;
+  HB_ChainPosRule   curr_cpr;
+  HB_GDEFHeader*    gdef;
+
+
+  gdef = gpos->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &ccpf1->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  cpr     = ccpf1->ChainPosRuleSet[index].ChainPosRule;
+  num_cpr = ccpf1->ChainPosRuleSet[index].ChainPosRuleCount;
+
+  for ( k = 0; k < num_cpr; k++ )
+  {
+    curr_cpr = cpr[k];
+    bgc      = curr_cpr.BacktrackGlyphCount;
+    igc      = curr_cpr.InputGlyphCount;
+    lgc      = curr_cpr.LookaheadGlyphCount;
+
+    if ( context_length != 0xFFFF && context_length < igc )
+      goto next_chainposrule;
+
+    /* check whether context is too long; it is a first guess only */
+
+    if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+      goto next_chainposrule;
+
+    if ( bgc )
+    {
+      /* Since we don't know in advance the number of glyphs to inspect,
+	 we search backwards for matches in the backtrack glyph array    */
+
+      for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
+      {
+	while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+	{
+	  if ( error && error != HB_Err_Not_Covered )
+	    return error;
+
+	  if ( j + 1 == bgc - i )
+	    goto next_chainposrule;
+	  j--;
+	}
+
+	/* In OpenType 1.3, it is undefined whether the offsets of
+	   backtrack glyphs is in logical order or not.  Version 1.4
+	   will clarify this:
+
+	     Logical order -      a  b  c  d  e  f  g  h  i  j
+					      i
+	     Input offsets -                  0  1
+	     Backtrack offsets -  3  2  1  0
+	     Lookahead offsets -                    0  1  2  3           */
+
+	if ( IN_GLYPH( j ) != curr_cpr.Backtrack[i] )
+	  goto next_chainposrule;
+      }
+    }
+
+    /* Start at 1 because [0] is implied */
+
+    for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  return error;
+
+	if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+	  goto next_chainposrule;
+	j++;
+      }
+
+      if ( IN_GLYPH( j ) != curr_cpr.Input[i - 1] )
+	goto next_chainposrule;
+    }
+
+    /* we are starting to check for lookahead glyphs right after the
+       last context glyph                                            */
+
+    for ( i = 0; i < lgc; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  return error;
+
+	if ( j + lgc - i == (HB_Int)buffer->in_length )
+	  goto next_chainposrule;
+	j++;
+      }
+
+      if ( IN_GLYPH( j ) != curr_cpr.Lookahead[i] )
+	goto next_chainposrule;
+    }
+
+    return Do_ContextPos( gpi, igc,
+			  curr_cpr.PosCount,
+			  curr_cpr.PosLookupRecord,
+			  buffer,
+			  nesting_level );
+
+  next_chainposrule:
+    ;
+  }
+
+  return HB_Err_Not_Covered;
+}
+
+
+static HB_Error  Lookup_ChainContextPos2(
+		   GPOS_Instance*               gpi,
+		   HB_ChainContextPosFormat2*  ccpf2,
+		   HB_Buffer                   buffer,
+		   HB_UShort                    flags,
+		   HB_UShort                    context_length,
+		   int                          nesting_level )
+{
+  HB_UShort              index, property;
+  HB_Error               error;
+  HB_UShort              i, j, k;
+  HB_UShort              bgc, igc, lgc;
+  HB_UShort              known_backtrack_classes,
+			 known_input_classes,
+			 known_lookahead_classes;
+
+  HB_UShort*             backtrack_classes;
+  HB_UShort*             input_classes;
+  HB_UShort*             lookahead_classes;
+
+  HB_UShort*             bc;
+  HB_UShort*             ic;
+  HB_UShort*             lc;
+  HB_GPOSHeader*        gpos = gpi->gpos;
+
+  HB_ChainPosClassSet*  cpcs;
+  HB_ChainPosClassRule  cpcr;
+  HB_GDEFHeader*        gdef;
+
+
+  gdef = gpos->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  /* Note: The coverage table in format 2 doesn't give an index into
+	   anything.  It just lets us know whether or not we need to
+	   do any lookup at all.                                     */
+
+  error = _HB_OPEN_Coverage_Index( &ccpf2->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  if ( ALLOC_ARRAY( backtrack_classes, ccpf2->MaxBacktrackLength, HB_UShort ) )
+    return error;
+  known_backtrack_classes = 0;
+
+  if (ccpf2->MaxInputLength < 1)
+    return HB_Err_Not_Covered;
+
+  if ( ALLOC_ARRAY( input_classes, ccpf2->MaxInputLength, HB_UShort ) )
+    goto End3;
+  known_input_classes = 1;
+
+  if ( ALLOC_ARRAY( lookahead_classes, ccpf2->MaxLookaheadLength, HB_UShort ) )
+    goto End2;
+  known_lookahead_classes = 0;
+
+  error = _HB_OPEN_Get_Class( &ccpf2->InputClassDef, IN_CURGLYPH(),
+		     &input_classes[0], NULL );
+  if ( error && error != HB_Err_Not_Covered )
+    goto End1;
+
+  cpcs = &ccpf2->ChainPosClassSet[input_classes[0]];
+  if ( !cpcs )
+  {
+    error = ERR(HB_Err_Invalid_SubTable);
+    goto End1;
+  }
+
+  for ( k = 0; k < cpcs->ChainPosClassRuleCount; k++ )
+  {
+    cpcr = cpcs->ChainPosClassRule[k];
+    bgc  = cpcr.BacktrackGlyphCount;
+    igc  = cpcr.InputGlyphCount;
+    lgc  = cpcr.LookaheadGlyphCount;
+
+    if ( context_length != 0xFFFF && context_length < igc )
+      goto next_chainposclassrule;
+
+    /* check whether context is too long; it is a first guess only */
+
+    if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+      goto next_chainposclassrule;
+
+    if ( bgc )
+    {
+      /* Since we don't know in advance the number of glyphs to inspect,
+	 we search backwards for matches in the backtrack glyph array.
+	 Note that `known_backtrack_classes' starts at index 0.         */
+
+      bc       = cpcr.Backtrack;
+
+      for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
+      {
+	while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+	{
+	  if ( error && error != HB_Err_Not_Covered )
+	    goto End1;
+
+	  if ( j + 1 == bgc - i )
+	    goto next_chainposclassrule;
+	  j++;
+	}
+
+	if ( i >= known_backtrack_classes )
+	{
+	  /* Keeps us from having to do this for each rule */
+
+	  error = _HB_OPEN_Get_Class( &ccpf2->BacktrackClassDef, IN_GLYPH( j ),
+			     &backtrack_classes[i], NULL );
+	  if ( error && error != HB_Err_Not_Covered )
+	    goto End1;
+	  known_backtrack_classes = i;
+	}
+
+	if ( bc[i] != backtrack_classes[i] )
+	  goto next_chainposclassrule;
+      }
+    }
+
+    ic       = cpcr.Input;
+
+    /* Start at 1 because [0] is implied */
+
+    for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End1;
+
+	if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+	  goto next_chainposclassrule;
+	j++;
+      }
+
+      if ( i >= known_input_classes )
+      {
+	error = _HB_OPEN_Get_Class( &ccpf2->InputClassDef, IN_GLYPH( j ),
+			   &input_classes[i], NULL );
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End1;
+	known_input_classes = i;
+      }
+
+      if ( ic[i - 1] != input_classes[i] )
+	goto next_chainposclassrule;
+    }
+
+    /* we are starting to check for lookahead glyphs right after the
+       last context glyph                                            */
+
+    lc       = cpcr.Lookahead;
+
+    for ( i = 0; i < lgc; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End1;
+
+	if ( j + lgc - i == (HB_Int)buffer->in_length )
+	  goto next_chainposclassrule;
+	j++;
+      }
+
+      if ( i >= known_lookahead_classes )
+      {
+	error = _HB_OPEN_Get_Class( &ccpf2->LookaheadClassDef, IN_GLYPH( j ),
+			   &lookahead_classes[i], NULL );
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End1;
+	known_lookahead_classes = i;
+      }
+
+      if ( lc[i] != lookahead_classes[i] )
+	goto next_chainposclassrule;
+    }
+
+    error = Do_ContextPos( gpi, igc,
+			   cpcr.PosCount,
+			   cpcr.PosLookupRecord,
+			   buffer,
+			   nesting_level );
+    goto End1;
+
+  next_chainposclassrule:
+    ;
+  }
+
+  error = HB_Err_Not_Covered;
+
+End1:
+  FREE( lookahead_classes );
+
+End2:
+  FREE( input_classes );
+
+End3:
+  FREE( backtrack_classes );
+  return error;
+}
+
+
+static HB_Error  Lookup_ChainContextPos3(
+		   GPOS_Instance*               gpi,
+		   HB_ChainContextPosFormat3*  ccpf3,
+		   HB_Buffer                   buffer,
+		   HB_UShort                    flags,
+		   HB_UShort                    context_length,
+		   int                          nesting_level )
+{
+  HB_UShort        index, i, j, property;
+  HB_UShort        bgc, igc, lgc;
+  HB_Error         error;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+
+  HB_Coverage*    bc;
+  HB_Coverage*    ic;
+  HB_Coverage*    lc;
+  HB_GDEFHeader*  gdef;
+
+
+  gdef = gpos->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  bgc = ccpf3->BacktrackGlyphCount;
+  igc = ccpf3->InputGlyphCount;
+  lgc = ccpf3->LookaheadGlyphCount;
+
+  if ( context_length != 0xFFFF && context_length < igc )
+    return HB_Err_Not_Covered;
+
+  /* check whether context is too long; it is a first guess only */
+
+  if ( bgc > buffer->in_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+    return HB_Err_Not_Covered;
+
+  if ( bgc )
+  {
+    /* Since we don't know in advance the number of glyphs to inspect,
+       we search backwards for matches in the backtrack glyph array    */
+
+    bc       = ccpf3->BacktrackCoverage;
+
+    for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  return error;
+
+	if ( j + 1 == bgc - i )
+	  return HB_Err_Not_Covered;
+	j--;
+      }
+
+      error = _HB_OPEN_Coverage_Index( &bc[i], IN_GLYPH( j ), &index );
+      if ( error )
+	return error;
+    }
+  }
+
+  ic       = ccpf3->InputCoverage;
+
+  for ( i = 0, j = buffer->in_pos; i < igc; i++, j++ )
+  {
+    /* We already called CHECK_Property for IN_GLYPH ( buffer->in_pos ) */
+    while ( j > buffer->in_pos && CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+    {
+      if ( error && error != HB_Err_Not_Covered )
+	return error;
+
+      if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+	return HB_Err_Not_Covered;
+      j++;
+    }
+
+    error = _HB_OPEN_Coverage_Index( &ic[i], IN_GLYPH( j ), &index );
+    if ( error )
+      return error;
+  }
+
+  /* we are starting to check for lookahead glyphs right after the
+     last context glyph                                            */
+
+  lc       = ccpf3->LookaheadCoverage;
+
+  for ( i = 0; i < lgc; i++, j++ )
+  {
+    while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+    {
+      if ( error && error != HB_Err_Not_Covered )
+	return error;
+
+      if ( j + lgc - i == (HB_Int)buffer->in_length )
+	return HB_Err_Not_Covered;
+      j++;
+    }
+
+    error = _HB_OPEN_Coverage_Index( &lc[i], IN_GLYPH( j ), &index );
+    if ( error )
+      return error;
+  }
+
+  return Do_ContextPos( gpi, igc,
+			ccpf3->PosCount,
+			ccpf3->PosLookupRecord,
+			buffer,
+			nesting_level );
+}
+
+
+static HB_Error  Lookup_ChainContextPos(
+		   GPOS_Instance*        gpi,
+		   HB_GPOS_SubTable* st,
+		   HB_Buffer            buffer,
+		   HB_UShort             flags,
+		   HB_UShort             context_length,
+		   int                   nesting_level )
+{
+  HB_ChainContextPos*  ccp = &st->chain;
+
+  switch ( ccp->PosFormat )
+  {
+  case 1:
+    return Lookup_ChainContextPos1( gpi, &ccp->ccpf.ccpf1, buffer,
+				    flags, context_length,
+				    nesting_level );
+
+  case 2:
+    return Lookup_ChainContextPos2( gpi, &ccp->ccpf.ccpf2, buffer,
+				    flags, context_length,
+				    nesting_level );
+
+  case 3:
+    return Lookup_ChainContextPos3( gpi, &ccp->ccpf.ccpf3, buffer,
+				    flags, context_length,
+				    nesting_level );
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;               /* never reached */
+}
+
+
+
+/***********
+ * GPOS API
+ ***********/
+
+
+
+HB_Error  HB_GPOS_Select_Script( HB_GPOSHeader*  gpos,
+				 HB_UInt         script_tag,
+				 HB_UShort*       script_index )
+{
+  HB_UShort          n;
+
+  HB_ScriptList*    sl;
+  HB_ScriptRecord*  sr;
+
+
+  if ( !gpos || !script_index )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gpos->ScriptList;
+  sr = sl->ScriptRecord;
+
+  for ( n = 0; n < sl->ScriptCount; n++ )
+    if ( script_tag == sr[n].ScriptTag )
+    {
+      *script_index = n;
+
+      return HB_Err_Ok;
+    }
+
+  return HB_Err_Not_Covered;
+}
+
+
+
+HB_Error  HB_GPOS_Select_Language( HB_GPOSHeader*  gpos,
+				   HB_UInt         language_tag,
+				   HB_UShort        script_index,
+				   HB_UShort*       language_index,
+				   HB_UShort*       req_feature_index )
+{
+  HB_UShort           n;
+
+  HB_ScriptList*     sl;
+  HB_ScriptRecord*   sr;
+  HB_ScriptTable*         s;
+  HB_LangSysRecord*  lsr;
+
+
+  if ( !gpos || !language_index || !req_feature_index )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gpos->ScriptList;
+  sr = sl->ScriptRecord;
+
+  if ( script_index >= sl->ScriptCount )
+    return ERR(HB_Err_Invalid_Argument);
+
+  s   = &sr[script_index].Script;
+  lsr = s->LangSysRecord;
+
+  for ( n = 0; n < s->LangSysCount; n++ )
+    if ( language_tag == lsr[n].LangSysTag )
+    {
+      *language_index = n;
+      *req_feature_index = lsr[n].LangSys.ReqFeatureIndex;
+
+      return HB_Err_Ok;
+    }
+
+  return HB_Err_Not_Covered;
+}
+
+
+/* selecting 0xFFFF for language_index asks for the values of the
+   default language (DefaultLangSys)                              */
+
+
+HB_Error  HB_GPOS_Select_Feature( HB_GPOSHeader*  gpos,
+				  HB_UInt         feature_tag,
+				  HB_UShort        script_index,
+				  HB_UShort        language_index,
+				  HB_UShort*       feature_index )
+{
+  HB_UShort           n;
+
+  HB_ScriptList*     sl;
+  HB_ScriptRecord*   sr;
+  HB_ScriptTable*         s;
+  HB_LangSysRecord*  lsr;
+  HB_LangSys*        ls;
+  HB_UShort*          fi;
+
+  HB_FeatureList*    fl;
+  HB_FeatureRecord*  fr;
+
+
+  if ( !gpos || !feature_index )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gpos->ScriptList;
+  sr = sl->ScriptRecord;
+
+  fl = &gpos->FeatureList;
+  fr = fl->FeatureRecord;
+
+  if ( script_index >= sl->ScriptCount )
+    return ERR(HB_Err_Invalid_Argument);
+
+  s   = &sr[script_index].Script;
+  lsr = s->LangSysRecord;
+
+  if ( language_index == 0xFFFF )
+    ls = &s->DefaultLangSys;
+  else
+  {
+    if ( language_index >= s->LangSysCount )
+      return ERR(HB_Err_Invalid_Argument);
+
+    ls = &lsr[language_index].LangSys;
+  }
+
+  fi = ls->FeatureIndex;
+
+  for ( n = 0; n < ls->FeatureCount; n++ )
+  {
+    if ( fi[n] >= fl->FeatureCount )
+      return ERR(HB_Err_Invalid_SubTable_Format);
+
+    if ( feature_tag == fr[fi[n]].FeatureTag )
+    {
+      *feature_index = fi[n];
+
+      return HB_Err_Ok;
+    }
+  }
+
+  return HB_Err_Not_Covered;
+}
+
+
+/* The next three functions return a null-terminated list */
+
+
+HB_Error  HB_GPOS_Query_Scripts( HB_GPOSHeader*  gpos,
+				 HB_UInt**       script_tag_list )
+{
+  HB_Error           error;
+  HB_UShort          n;
+  HB_UInt*          stl;
+
+  HB_ScriptList*    sl;
+  HB_ScriptRecord*  sr;
+
+
+  if ( !gpos || !script_tag_list )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gpos->ScriptList;
+  sr = sl->ScriptRecord;
+
+  if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, HB_UInt ) )
+    return error;
+
+  for ( n = 0; n < sl->ScriptCount; n++ )
+    stl[n] = sr[n].ScriptTag;
+  stl[n] = 0;
+
+  *script_tag_list = stl;
+
+  return HB_Err_Ok;
+}
+
+
+
+HB_Error  HB_GPOS_Query_Languages( HB_GPOSHeader*  gpos,
+				   HB_UShort        script_index,
+				   HB_UInt**       language_tag_list )
+{
+  HB_Error            error;
+  HB_UShort           n;
+  HB_UInt*           ltl;
+
+  HB_ScriptList*     sl;
+  HB_ScriptRecord*   sr;
+  HB_ScriptTable*    s;
+  HB_LangSysRecord*  lsr;
+
+
+  if ( !gpos || !language_tag_list )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gpos->ScriptList;
+  sr = sl->ScriptRecord;
+
+  if ( script_index >= sl->ScriptCount )
+    return ERR(HB_Err_Invalid_Argument);
+
+  s   = &sr[script_index].Script;
+  lsr = s->LangSysRecord;
+
+  if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, HB_UInt ) )
+    return error;
+
+  for ( n = 0; n < s->LangSysCount; n++ )
+    ltl[n] = lsr[n].LangSysTag;
+  ltl[n] = 0;
+
+  *language_tag_list = ltl;
+
+  return HB_Err_Ok;
+}
+
+
+/* selecting 0xFFFF for language_index asks for the values of the
+   default language (DefaultLangSys)                              */
+
+
+HB_Error  HB_GPOS_Query_Features( HB_GPOSHeader*  gpos,
+				  HB_UShort        script_index,
+				  HB_UShort        language_index,
+				  HB_UInt**       feature_tag_list )
+{
+  HB_UShort           n;
+  HB_Error            error;
+  HB_UInt*           ftl;
+
+  HB_ScriptList*     sl;
+  HB_ScriptRecord*   sr;
+  HB_ScriptTable*    s;
+  HB_LangSysRecord*  lsr;
+  HB_LangSys*        ls;
+  HB_UShort*          fi;
+
+  HB_FeatureList*    fl;
+  HB_FeatureRecord*  fr;
+
+
+  if ( !gpos || !feature_tag_list )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gpos->ScriptList;
+  sr = sl->ScriptRecord;
+
+  fl = &gpos->FeatureList;
+  fr = fl->FeatureRecord;
+
+  if ( script_index >= sl->ScriptCount )
+    return ERR(HB_Err_Invalid_Argument);
+
+  s   = &sr[script_index].Script;
+  lsr = s->LangSysRecord;
+
+  if ( language_index == 0xFFFF )
+    ls = &s->DefaultLangSys;
+  else
+  {
+    if ( language_index >= s->LangSysCount )
+      return ERR(HB_Err_Invalid_Argument);
+
+    ls = &lsr[language_index].LangSys;
+  }
+
+  fi = ls->FeatureIndex;
+
+  if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, HB_UInt ) )
+    return error;
+
+  for ( n = 0; n < ls->FeatureCount; n++ )
+  {
+    if ( fi[n] >= fl->FeatureCount )
+    {
+      FREE( ftl );
+      return ERR(HB_Err_Invalid_SubTable_Format);
+    }
+    ftl[n] = fr[fi[n]].FeatureTag;
+  }
+  ftl[n] = 0;
+
+  *feature_tag_list = ftl;
+
+  return HB_Err_Ok;
+}
+
+
+/* Do an individual subtable lookup.  Returns HB_Err_Ok if positioning
+   has been done, or HB_Err_Not_Covered if not.                        */
+static HB_Error  GPOS_Do_Glyph_Lookup( GPOS_Instance*    gpi,
+				       HB_UShort         lookup_index,
+				       HB_Buffer        buffer,
+				       HB_UShort         context_length,
+				       int               nesting_level )
+{
+  HB_Error             error = HB_Err_Not_Covered;
+  HB_UShort            i, flags, lookup_count;
+  HB_GPOSHeader*       gpos = gpi->gpos;
+  HB_Lookup*           lo;
+  int		       lookup_type;
+
+
+  nesting_level++;
+
+  if ( nesting_level > HB_MAX_NESTING_LEVEL )
+    return ERR(HB_Err_Not_Covered); /* ERR() call intended */
+
+  lookup_count = gpos->LookupList.LookupCount;
+  if (lookup_index >= lookup_count)
+    return error;
+
+  lo    = &gpos->LookupList.Lookup[lookup_index];
+  flags = lo->LookupFlag;
+  lookup_type = lo->LookupType;
+
+  for ( i = 0; i < lo->SubTableCount; i++ )
+  {
+    HB_GPOS_SubTable *st = &lo->SubTable[i].st.gpos;
+
+    switch (lookup_type) {
+      case HB_GPOS_LOOKUP_SINGLE:
+        error = Lookup_SinglePos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GPOS_LOOKUP_PAIR:
+	error = Lookup_PairPos		( gpi, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GPOS_LOOKUP_CURSIVE:
+	error = Lookup_CursivePos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GPOS_LOOKUP_MARKBASE:
+	error = Lookup_MarkBasePos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GPOS_LOOKUP_MARKLIG:
+	error = Lookup_MarkLigPos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GPOS_LOOKUP_MARKMARK:
+	error = Lookup_MarkMarkPos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GPOS_LOOKUP_CONTEXT:
+	error = Lookup_ContextPos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GPOS_LOOKUP_CHAIN:
+	error = Lookup_ChainContextPos	( gpi, st, buffer, flags, context_length, nesting_level ); break;
+    /*case HB_GPOS_LOOKUP_EXTENSION:
+	error = Lookup_ExtensionPos	( gpi, st, buffer, flags, context_length, nesting_level ); break;*/
+      default:
+	error = HB_Err_Not_Covered;
+    }
+
+    /* Check whether we have a successful positioning or an error other
+       than HB_Err_Not_Covered                                         */
+    if ( error != HB_Err_Not_Covered )
+      return error;
+  }
+
+  return HB_Err_Not_Covered;
+}
+
+
+HB_INTERNAL HB_Error
+_HB_GPOS_Load_SubTable( HB_GPOS_SubTable* st,
+			HB_Stream         stream,
+			HB_UShort         lookup_type )
+{
+  switch ( lookup_type ) {
+    case HB_GPOS_LOOKUP_SINGLE:		return Load_SinglePos		( st, stream );
+    case HB_GPOS_LOOKUP_PAIR:		return Load_PairPos		( st, stream );
+    case HB_GPOS_LOOKUP_CURSIVE:	return Load_CursivePos		( st, stream );
+    case HB_GPOS_LOOKUP_MARKBASE:	return Load_MarkBasePos		( st, stream );
+    case HB_GPOS_LOOKUP_MARKLIG:	return Load_MarkLigPos		( st, stream );
+    case HB_GPOS_LOOKUP_MARKMARK:	return Load_MarkMarkPos		( st, stream );
+    case HB_GPOS_LOOKUP_CONTEXT:	return Load_ContextPos		( st, stream );
+    case HB_GPOS_LOOKUP_CHAIN:		return Load_ChainContextPos	( st, stream );
+  /*case HB_GPOS_LOOKUP_EXTENSION:	return Load_ExtensionPos	( st, stream );*/
+    default:				return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+}
+
+
+HB_INTERNAL void
+_HB_GPOS_Free_SubTable( HB_GPOS_SubTable* st,
+			HB_UShort         lookup_type )
+{
+  switch ( lookup_type ) {
+    case HB_GPOS_LOOKUP_SINGLE:		Free_SinglePos		( st ); return;
+    case HB_GPOS_LOOKUP_PAIR:		Free_PairPos		( st ); return;
+    case HB_GPOS_LOOKUP_CURSIVE:	Free_CursivePos		( st ); return;
+    case HB_GPOS_LOOKUP_MARKBASE:	Free_MarkBasePos	( st ); return;
+    case HB_GPOS_LOOKUP_MARKLIG:	Free_MarkLigPos		( st ); return;
+    case HB_GPOS_LOOKUP_MARKMARK:	Free_MarkMarkPos	( st ); return;
+    case HB_GPOS_LOOKUP_CONTEXT:	Free_ContextPos		( st ); return;
+    case HB_GPOS_LOOKUP_CHAIN:		Free_ChainContextPos	( st ); return;
+  /*case HB_GPOS_LOOKUP_EXTENSION:	Free_ExtensionPos	( st ); return;*/
+    default:									return;
+  }
+}
+
+
+/* apply one lookup to the input string object */
+
+static HB_Error  GPOS_Do_String_Lookup( GPOS_Instance*    gpi,
+				   HB_UShort         lookup_index,
+				   HB_Buffer        buffer )
+{
+  HB_Error         error, retError = HB_Err_Not_Covered;
+  HB_GPOSHeader*  gpos = gpi->gpos;
+
+  HB_UInt*  properties = gpos->LookupList.Properties;
+
+  const int       nesting_level = 0;
+  /* 0xFFFF indicates that we don't have a context length yet */
+  const HB_UShort context_length = 0xFFFF;
+
+
+  gpi->last  = 0xFFFF;     /* no last valid glyph for cursive pos. */
+
+  buffer->in_pos = 0;
+  while ( buffer->in_pos < buffer->in_length )
+  {
+    if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
+    {
+      /* Note that the connection between mark and base glyphs hold
+	 exactly one (string) lookup.  For example, it would be possible
+	 that in the first lookup, mark glyph X is attached to base
+	 glyph A, and in the next lookup it is attached to base glyph B.
+	 It is up to the font designer to provide meaningful lookups and
+	 lookup order.                                                   */
+
+      error = GPOS_Do_Glyph_Lookup( gpi, lookup_index, buffer, context_length, nesting_level );
+      if ( error && error != HB_Err_Not_Covered )
+	return error;
+    }
+    else
+    {
+      /* Contrary to properties defined in GDEF, user-defined properties
+	 will always stop a possible cursive positioning.                */
+      gpi->last = 0xFFFF;
+
+      error = HB_Err_Not_Covered;
+    }
+
+    if ( error == HB_Err_Not_Covered )
+      (buffer->in_pos)++;
+    else
+      retError = error;
+  }
+
+  return retError;
+}
+
+
+static HB_Error  Position_CursiveChain ( HB_Buffer     buffer )
+{
+  HB_UInt   i, j;
+  HB_Position positions = buffer->positions;
+
+  /* First handle all left-to-right connections */
+  for (j = 0; j < buffer->in_length; j++)
+  {
+    if (positions[j].cursive_chain > 0)
+      positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
+  }
+
+  /* Then handle all right-to-left connections */
+  for (i = buffer->in_length; i > 0; i--)
+  {
+    j = i - 1;
+
+    if (positions[j].cursive_chain < 0)
+      positions[j].y_pos += positions[j - positions[j].cursive_chain].y_pos;
+  }
+
+  return HB_Err_Ok;
+}
+
+
+HB_Error  HB_GPOS_Add_Feature( HB_GPOSHeader*  gpos,
+			       HB_UShort        feature_index,
+			       HB_UInt          property )
+{
+  HB_UShort    i;
+
+  HB_Feature  feature;
+  HB_UInt*     properties;
+  HB_UShort*   index;
+  HB_UShort    lookup_count;
+
+  /* Each feature can only be added once */
+
+  if ( !gpos ||
+       feature_index >= gpos->FeatureList.FeatureCount ||
+       gpos->FeatureList.ApplyCount == gpos->FeatureList.FeatureCount )
+    return ERR(HB_Err_Invalid_Argument);
+
+  gpos->FeatureList.ApplyOrder[gpos->FeatureList.ApplyCount++] = feature_index;
+
+  properties = gpos->LookupList.Properties;
+
+  feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
+  index   = feature.LookupListIndex;
+  lookup_count = gpos->LookupList.LookupCount;
+
+  for ( i = 0; i < feature.LookupListCount; i++ )
+  {
+    HB_UShort lookup_index = index[i];
+    if (lookup_index < lookup_count)
+      properties[lookup_index] |= property;
+  }
+
+  return HB_Err_Ok;
+}
+
+
+
+HB_Error  HB_GPOS_Clear_Features( HB_GPOSHeader*  gpos )
+{
+  HB_UShort i;
+
+  HB_UInt*  properties;
+
+
+  if ( !gpos )
+    return ERR(HB_Err_Invalid_Argument);
+
+  gpos->FeatureList.ApplyCount = 0;
+
+  properties = gpos->LookupList.Properties;
+
+  for ( i = 0; i < gpos->LookupList.LookupCount; i++ )
+    properties[i] = 0;
+
+  return HB_Err_Ok;
+}
+
+
+
+HB_Error  HB_GPOS_Register_MM_Function( HB_GPOSHeader*  gpos,
+					HB_MMFunction   mmfunc,
+					void*            data )
+{
+  if ( !gpos )
+    return ERR(HB_Err_Invalid_Argument);
+
+  gpos->mmfunc = mmfunc;
+  gpos->data   = data;
+
+  return HB_Err_Ok;
+}
+
+/* If `dvi' is TRUE, glyph contour points for anchor points and device
+   tables are ignored -- you will get device independent values.         */
+
+
+HB_Error  HB_GPOS_Apply_String( HB_Font            font,
+				HB_GPOSHeader*    gpos,
+				HB_UShort          load_flags,
+				HB_Buffer         buffer,
+				HB_Bool            dvi,
+				HB_Bool            r2l )
+{
+  HB_Error       error, retError = HB_Err_Not_Covered;
+  GPOS_Instance  gpi;
+  int            i, j, lookup_count, num_features;
+
+  if ( !font || !gpos || !buffer )
+    return ERR(HB_Err_Invalid_Argument);
+
+  if ( buffer->in_length == 0 )
+    return HB_Err_Not_Covered;
+
+  gpi.font       = font;
+  gpi.gpos       = gpos;
+  gpi.load_flags = load_flags;
+  gpi.r2l        = r2l;
+  gpi.dvi        = dvi;
+
+  lookup_count = gpos->LookupList.LookupCount;
+  num_features = gpos->FeatureList.ApplyCount;
+
+  if ( num_features )
+    {
+      error = _hb_buffer_clear_positions( buffer );
+      if ( error )
+	return error;
+    }
+
+  for ( i = 0; i < num_features; i++ )
+  {
+    HB_UShort  feature_index = gpos->FeatureList.ApplyOrder[i];
+    HB_Feature feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
+
+    for ( j = 0; j < feature.LookupListCount; j++ )
+    {
+      HB_UShort lookup_index = feature.LookupListIndex[j];
+
+      /* Skip nonexistant lookups */
+      if (lookup_index >= lookup_count)
+       continue;
+
+      error = GPOS_Do_String_Lookup( &gpi, lookup_index, buffer );
+      if ( error )
+      {
+	if ( error != HB_Err_Not_Covered )
+	  return error;
+      }
+      else
+	retError = error;
+    }
+  }
+
+  if ( num_features )
+    {
+  error = Position_CursiveChain ( buffer );
+  if ( error )
+    return error;
+    }
+
+  return retError;
+}
+
+/* END */
diff --git a/third_party/harfbuzz/src/harfbuzz-gpos.h b/third_party/harfbuzz/src/harfbuzz-gpos.h
new file mode 100644
index 0000000..2840dae
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-gpos.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_GPOS_H
+#define HARFBUZZ_GPOS_H
+
+#include "harfbuzz-gdef.h"
+#include "harfbuzz-buffer.h"
+
+HB_BEGIN_HEADER
+
+
+/* Lookup types for glyph positioning */
+
+#define HB_GPOS_LOOKUP_SINGLE     1
+#define HB_GPOS_LOOKUP_PAIR       2
+#define HB_GPOS_LOOKUP_CURSIVE    3
+#define HB_GPOS_LOOKUP_MARKBASE   4
+#define HB_GPOS_LOOKUP_MARKLIG    5
+#define HB_GPOS_LOOKUP_MARKMARK   6
+#define HB_GPOS_LOOKUP_CONTEXT    7
+#define HB_GPOS_LOOKUP_CHAIN      8
+#define HB_GPOS_LOOKUP_EXTENSION  9
+
+/* A pointer to a function which accesses the PostScript interpreter.
+   Multiple Master fonts need this interface to convert a metric ID
+   (as stored in an OpenType font version 1.2 or higher) `metric_id'
+   into a metric value (returned in `metric_value').
+
+   `data' points to the user-defined structure specified during a
+   call to HB_GPOS_Register_MM_Function().
+
+   `metric_value' must be returned as a scaled value (but shouldn't
+   be rounded).                                                       */
+
+typedef HB_Error  (*HB_MMFunction)(HB_Font       font,
+				    HB_UShort    metric_id,
+				    HB_Fixed*      metric_value,
+				    void*        data );
+
+
+struct  HB_GPOSHeader_
+{
+  HB_16Dot16           Version;
+
+  HB_ScriptList     ScriptList;
+  HB_FeatureList    FeatureList;
+  HB_LookupList     LookupList;
+
+  HB_GDEFHeader*    gdef;
+
+  /* this is OpenType 1.2 -- Multiple Master fonts need this
+     callback function to get various metric values from the
+     PostScript interpreter.                                 */
+
+  HB_MMFunction     mmfunc;
+  void*              data;
+};
+
+typedef struct HB_GPOSHeader_  HB_GPOSHeader;
+typedef HB_GPOSHeader* HB_GPOS;
+
+
+HB_Error  HB_Load_GPOS_Table( HB_Stream stream, 
+                              HB_GPOSHeader** gpos,
+			      HB_GDEFHeader*  gdef,
+                              HB_Stream       gdefStream );
+
+
+HB_Error  HB_Done_GPOS_Table( HB_GPOSHeader* gpos );
+
+
+HB_Error  HB_GPOS_Select_Script( HB_GPOSHeader*  gpos,
+				 HB_UInt         script_tag,
+				 HB_UShort*       script_index );
+
+HB_Error  HB_GPOS_Select_Language( HB_GPOSHeader*  gpos,
+				   HB_UInt         language_tag,
+				   HB_UShort        script_index,
+				   HB_UShort*       language_index,
+				   HB_UShort*       req_feature_index );
+
+HB_Error  HB_GPOS_Select_Feature( HB_GPOSHeader*  gpos,
+				  HB_UInt         feature_tag,
+				  HB_UShort        script_index,
+				  HB_UShort        language_index,
+				  HB_UShort*       feature_index );
+
+
+HB_Error  HB_GPOS_Query_Scripts( HB_GPOSHeader*  gpos,
+				 HB_UInt**       script_tag_list );
+
+HB_Error  HB_GPOS_Query_Languages( HB_GPOSHeader*  gpos,
+				   HB_UShort        script_index,
+				   HB_UInt**       language_tag_list );
+
+HB_Error  HB_GPOS_Query_Features( HB_GPOSHeader*  gpos,
+				  HB_UShort        script_index,
+				  HB_UShort        language_index,
+				  HB_UInt**       feature_tag_list );
+
+
+HB_Error  HB_GPOS_Add_Feature( HB_GPOSHeader*  gpos,
+			       HB_UShort        feature_index,
+			       HB_UInt          property );
+
+HB_Error  HB_GPOS_Clear_Features( HB_GPOSHeader*  gpos );
+
+
+HB_Error  HB_GPOS_Register_MM_Function( HB_GPOSHeader*  gpos,
+					HB_MMFunction   mmfunc,
+					void*            data );
+
+/* If `dvi' is TRUE, glyph contour points for anchor points and device
+   tables are ignored -- you will get device independent values.         */
+
+
+HB_Error  HB_GPOS_Apply_String( HB_Font           font,
+				HB_GPOSHeader*   gpos,
+				HB_UShort         load_flags,
+				HB_Buffer        buffer,
+				HB_Bool           dvi,
+				HB_Bool           r2l );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GPOS_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-gsub-private.h b/third_party/harfbuzz/src/harfbuzz-gsub-private.h
new file mode 100644
index 0000000..dd5ffdf
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-gsub-private.h
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_GSUB_PRIVATE_H
+#define HARFBUZZ_GSUB_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-stream-private.h"
+#include "harfbuzz-gsub.h"
+
+HB_BEGIN_HEADER
+
+
+typedef union HB_GSUB_SubTable_  HB_GSUB_SubTable;
+
+/* LookupType 1 */
+
+struct  HB_SingleSubstFormat1_
+{
+  HB_Short  DeltaGlyphID;             /* constant added to get
+					 substitution glyph index */
+};
+
+typedef struct HB_SingleSubstFormat1_  HB_SingleSubstFormat1;
+
+
+struct  HB_SingleSubstFormat2_
+{
+  HB_UShort   GlyphCount;             /* number of glyph IDs in
+					 Substitute array              */
+  HB_UShort*  Substitute;             /* array of substitute glyph IDs */
+};
+
+typedef struct HB_SingleSubstFormat2_  HB_SingleSubstFormat2;
+
+
+struct  HB_SingleSubst_
+{
+  HB_UShort     SubstFormat;          /* 1 or 2         */
+  HB_Coverage  Coverage;             /* Coverage table */
+
+  union
+  {
+    HB_SingleSubstFormat1  ssf1;
+    HB_SingleSubstFormat2  ssf2;
+  } ssf;
+};
+
+typedef struct HB_SingleSubst_  HB_SingleSubst;
+
+
+/* LookupType 2 */
+
+struct  HB_Sequence_
+{
+  HB_UShort   GlyphCount;             /* number of glyph IDs in the
+					 Substitute array           */
+  HB_UShort*  Substitute;             /* string of glyph IDs to
+					 substitute                 */
+};
+
+typedef struct HB_Sequence_  HB_Sequence;
+
+
+struct  HB_MultipleSubst_
+{
+  HB_UShort      SubstFormat;         /* always 1                  */
+  HB_Coverage   Coverage;            /* Coverage table            */
+  HB_UShort      SequenceCount;       /* number of Sequence tables */
+  HB_Sequence*  Sequence;            /* array of Sequence tables  */
+};
+
+typedef struct HB_MultipleSubst_  HB_MultipleSubst;
+
+
+/* LookupType 3 */
+
+struct  HB_AlternateSet_
+{
+  HB_UShort   GlyphCount;             /* number of glyph IDs in the
+					 Alternate array              */
+  HB_UShort*  Alternate;              /* array of alternate glyph IDs */
+};
+
+typedef struct HB_AlternateSet_  HB_AlternateSet;
+
+
+struct  HB_AlternateSubst_
+{
+  HB_UShort          SubstFormat;     /* always 1                      */
+  HB_Coverage       Coverage;        /* Coverage table                */
+  HB_UShort          AlternateSetCount;
+				      /* number of AlternateSet tables */
+  HB_AlternateSet*  AlternateSet;    /* array of AlternateSet tables  */
+};
+
+typedef struct HB_AlternateSubst_  HB_AlternateSubst;
+
+
+/* LookupType 4 */
+
+struct  HB_Ligature_
+{
+  HB_UShort   LigGlyph;               /* glyphID of ligature
+					 to substitute                    */
+  HB_UShort   ComponentCount;         /* number of components in ligature */
+  HB_UShort*  Component;              /* array of component glyph IDs     */
+};
+
+typedef struct HB_Ligature_  HB_Ligature;
+
+
+struct  HB_LigatureSet_
+{
+  HB_UShort      LigatureCount;       /* number of Ligature tables */
+  HB_Ligature*  Ligature;            /* array of Ligature tables  */
+};
+
+typedef struct HB_LigatureSet_  HB_LigatureSet;
+
+
+struct  HB_LigatureSubst_
+{
+  HB_UShort         SubstFormat;      /* always 1                     */
+  HB_Coverage      Coverage;         /* Coverage table               */
+  HB_UShort         LigatureSetCount; /* number of LigatureSet tables */
+  HB_LigatureSet*  LigatureSet;      /* array of LigatureSet tables  */
+};
+
+typedef struct HB_LigatureSubst_  HB_LigatureSubst;
+
+
+/* needed by both lookup type 5 and 6 */
+
+struct  HB_SubstLookupRecord_
+{
+  HB_UShort  SequenceIndex;           /* index into current
+					 glyph sequence               */
+  HB_UShort  LookupListIndex;         /* Lookup to apply to that pos. */
+};
+
+typedef struct HB_SubstLookupRecord_  HB_SubstLookupRecord;
+
+
+/* LookupType 5 */
+
+struct  HB_SubRule_
+{
+  HB_UShort               GlyphCount; /* total number of input glyphs */
+  HB_UShort               SubstCount; /* number of SubstLookupRecord
+					 tables                       */
+  HB_UShort*              Input;      /* array of input glyph IDs     */
+  HB_SubstLookupRecord*  SubstLookupRecord;
+				      /* array of SubstLookupRecord
+					 tables                       */
+};
+
+typedef struct HB_SubRule_  HB_SubRule;
+
+
+struct  HB_SubRuleSet_
+{
+  HB_UShort     SubRuleCount;         /* number of SubRule tables */
+  HB_SubRule*  SubRule;              /* array of SubRule tables  */
+};
+
+typedef struct HB_SubRuleSet_  HB_SubRuleSet;
+
+
+struct  HB_ContextSubstFormat1_
+{
+  HB_Coverage     Coverage;          /* Coverage table              */
+  HB_UShort        SubRuleSetCount;   /* number of SubRuleSet tables */
+  HB_SubRuleSet*  SubRuleSet;        /* array of SubRuleSet tables  */
+};
+
+typedef struct HB_ContextSubstFormat1_  HB_ContextSubstFormat1;
+
+
+struct  HB_SubClassRule_
+{
+  HB_UShort               GlyphCount; /* total number of context classes */
+  HB_UShort               SubstCount; /* number of SubstLookupRecord
+					 tables                          */
+  HB_UShort*              Class;      /* array of classes                */
+  HB_SubstLookupRecord*  SubstLookupRecord;
+				      /* array of SubstLookupRecord
+					 tables                          */
+};
+
+typedef struct HB_SubClassRule_  HB_SubClassRule;
+
+
+struct  HB_SubClassSet_
+{
+  HB_UShort          SubClassRuleCount;
+				      /* number of SubClassRule tables */
+  HB_SubClassRule*  SubClassRule;    /* array of SubClassRule tables  */
+};
+
+typedef struct HB_SubClassSet_  HB_SubClassSet;
+
+
+/* The `MaxContextLength' field is not defined in the TTO specification
+   but simplifies the implementation of this format.  It holds the
+   maximal context length used in the context rules.                    */
+
+struct  HB_ContextSubstFormat2_
+{
+  HB_UShort            MaxContextLength;
+				      /* maximal context length       */
+  HB_Coverage         Coverage;      /* Coverage table               */
+  HB_ClassDefinition  ClassDef;      /* ClassDef table               */
+  HB_UShort            SubClassSetCount;
+				      /* number of SubClassSet tables */
+  HB_SubClassSet*     SubClassSet;   /* array of SubClassSet tables  */
+};
+
+typedef struct HB_ContextSubstFormat2_  HB_ContextSubstFormat2;
+
+
+struct  HB_ContextSubstFormat3_
+{
+  HB_UShort               GlyphCount; /* number of input glyphs        */
+  HB_UShort               SubstCount; /* number of SubstLookupRecords  */
+  HB_Coverage*           Coverage;   /* array of Coverage tables      */
+  HB_SubstLookupRecord*  SubstLookupRecord;
+				      /* array of substitution lookups */
+};
+
+typedef struct HB_ContextSubstFormat3_  HB_ContextSubstFormat3;
+
+
+struct  HB_ContextSubst_
+{
+  HB_UShort  SubstFormat;             /* 1, 2, or 3 */
+
+  union
+  {
+    HB_ContextSubstFormat1  csf1;
+    HB_ContextSubstFormat2  csf2;
+    HB_ContextSubstFormat3  csf3;
+  } csf;
+};
+
+typedef struct HB_ContextSubst_  HB_ContextSubst;
+
+
+/* LookupType 6 */
+
+struct  HB_ChainSubRule_
+{
+  HB_UShort               BacktrackGlyphCount;
+				      /* total number of backtrack glyphs */
+  HB_UShort*              Backtrack;  /* array of backtrack glyph IDs     */
+  HB_UShort               InputGlyphCount;
+				      /* total number of input glyphs     */
+  HB_UShort*              Input;      /* array of input glyph IDs         */
+  HB_UShort               LookaheadGlyphCount;
+				      /* total number of lookahead glyphs */
+  HB_UShort*              Lookahead;  /* array of lookahead glyph IDs     */
+  HB_UShort               SubstCount; /* number of SubstLookupRecords     */
+  HB_SubstLookupRecord*  SubstLookupRecord;
+				      /* array of SubstLookupRecords      */
+};
+
+typedef struct HB_ChainSubRule_  HB_ChainSubRule;
+
+
+struct  HB_ChainSubRuleSet_
+{
+  HB_UShort          ChainSubRuleCount;
+				      /* number of ChainSubRule tables */
+  HB_ChainSubRule*  ChainSubRule;    /* array of ChainSubRule tables  */
+};
+
+typedef struct HB_ChainSubRuleSet_  HB_ChainSubRuleSet;
+
+
+struct  HB_ChainContextSubstFormat1_
+{
+  HB_Coverage          Coverage;     /* Coverage table                   */
+  HB_UShort             ChainSubRuleSetCount;
+				      /* number of ChainSubRuleSet tables */
+  HB_ChainSubRuleSet*  ChainSubRuleSet;
+				      /* array of ChainSubRuleSet tables  */
+};
+
+typedef struct HB_ChainContextSubstFormat1_  HB_ChainContextSubstFormat1;
+
+
+struct  HB_ChainSubClassRule_
+{
+  HB_UShort               BacktrackGlyphCount;
+				      /* total number of backtrack
+					 classes                         */
+  HB_UShort*              Backtrack;  /* array of backtrack classes      */
+  HB_UShort               InputGlyphCount;
+				      /* total number of context classes */
+  HB_UShort*              Input;      /* array of context classes        */
+  HB_UShort               LookaheadGlyphCount;
+				      /* total number of lookahead
+					 classes                         */
+  HB_UShort*              Lookahead;  /* array of lookahead classes      */
+  HB_UShort               SubstCount; /* number of SubstLookupRecords    */
+  HB_SubstLookupRecord*  SubstLookupRecord;
+				      /* array of substitution lookups   */
+};
+
+typedef struct HB_ChainSubClassRule_  HB_ChainSubClassRule;
+
+
+struct  HB_ChainSubClassSet_
+{
+  HB_UShort               ChainSubClassRuleCount;
+				      /* number of ChainSubClassRule
+					 tables                      */
+  HB_ChainSubClassRule*  ChainSubClassRule;
+				      /* array of ChainSubClassRule
+					 tables                      */
+};
+
+typedef struct HB_ChainSubClassSet_  HB_ChainSubClassSet;
+
+
+/* The `MaxXXXLength' fields are not defined in the TTO specification
+   but simplifies the implementation of this format.  It holds the
+   maximal context length used in the specific context rules.         */
+
+struct  HB_ChainContextSubstFormat2_
+{
+  HB_Coverage           Coverage;    /* Coverage table             */
+
+  HB_UShort              MaxBacktrackLength;
+				      /* maximal backtrack length   */
+  HB_ClassDefinition    BacktrackClassDef;
+				      /* BacktrackClassDef table    */
+  HB_UShort              MaxInputLength;
+				      /* maximal input length       */
+  HB_ClassDefinition    InputClassDef;
+				      /* InputClassDef table        */
+  HB_UShort              MaxLookaheadLength;
+				      /* maximal lookahead length   */
+  HB_ClassDefinition    LookaheadClassDef;
+				      /* LookaheadClassDef table    */
+
+  HB_UShort              ChainSubClassSetCount;
+				      /* number of ChainSubClassSet
+					 tables                     */
+  HB_ChainSubClassSet*  ChainSubClassSet;
+				      /* array of ChainSubClassSet
+					 tables                     */
+};
+
+typedef struct HB_ChainContextSubstFormat2_  HB_ChainContextSubstFormat2;
+
+
+struct  HB_ChainContextSubstFormat3_
+{
+  HB_UShort               BacktrackGlyphCount;
+				      /* number of backtrack glyphs    */
+  HB_Coverage*           BacktrackCoverage;
+				      /* array of backtrack Coverage
+					 tables                        */
+  HB_UShort               InputGlyphCount;
+				      /* number of input glyphs        */
+  HB_Coverage*           InputCoverage;
+				      /* array of input coverage
+					 tables                        */
+  HB_UShort               LookaheadGlyphCount;
+				      /* number of lookahead glyphs    */
+  HB_Coverage*           LookaheadCoverage;
+				      /* array of lookahead coverage
+					 tables                        */
+  HB_UShort               SubstCount; /* number of SubstLookupRecords  */
+  HB_SubstLookupRecord*  SubstLookupRecord;
+				      /* array of substitution lookups */
+};
+
+typedef struct HB_ChainContextSubstFormat3_  HB_ChainContextSubstFormat3;
+
+
+struct  HB_ChainContextSubst_
+{
+  HB_UShort  SubstFormat;             /* 1, 2, or 3 */
+
+  union
+  {
+    HB_ChainContextSubstFormat1  ccsf1;
+    HB_ChainContextSubstFormat2  ccsf2;
+    HB_ChainContextSubstFormat3  ccsf3;
+  } ccsf;
+};
+
+typedef struct HB_ChainContextSubst_  HB_ChainContextSubst;
+
+
+#if 0
+/* LookupType 7 */
+struct HB_ExtensionSubst_
+{
+  HB_UShort      SubstFormat;         /* always 1 */
+  HB_UShort      LookuptType;         /* lookup-type of referenced subtable */
+  HB_GSUB_SubTable *subtable;         /* referenced subtable */
+};
+
+typedef struct HB_ExtensionSubst_  HB_ExtensionSubst;
+#endif
+
+
+/* LookupType 8 */
+struct HB_ReverseChainContextSubst_
+{
+  HB_UShort      SubstFormat;         /* always 1 */
+  HB_Coverage   Coverage;	        /* coverage table for input glyphs */
+  HB_UShort      BacktrackGlyphCount; /* number of backtrack glyphs      */
+  HB_Coverage*  BacktrackCoverage;   /* array of backtrack Coverage
+					 tables                          */
+  HB_UShort      LookaheadGlyphCount; /* number of lookahead glyphs      */
+  HB_Coverage*  LookaheadCoverage;   /* array of lookahead Coverage
+					 tables                          */
+  HB_UShort      GlyphCount;          /* number of Glyph IDs             */
+  HB_UShort*     Substitute;          /* array of substitute Glyph ID    */
+};
+
+typedef struct HB_ReverseChainContextSubst_  HB_ReverseChainContextSubst;
+
+
+union  HB_GSUB_SubTable_
+{
+  HB_SingleSubst              single;
+  HB_MultipleSubst            multiple;
+  HB_AlternateSubst           alternate;
+  HB_LigatureSubst            ligature;
+  HB_ContextSubst             context;
+  HB_ChainContextSubst        chain;
+  HB_ReverseChainContextSubst reverse;
+};
+
+
+
+
+HB_INTERNAL HB_Error
+_HB_GSUB_Load_SubTable( HB_GSUB_SubTable* st,
+				  HB_Stream     stream,
+				  HB_UShort     lookup_type );
+
+HB_INTERNAL void
+_HB_GSUB_Free_SubTable( HB_GSUB_SubTable* st,
+			      HB_UShort     lookup_type );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GSUB_PRIVATE_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-gsub.c b/third_party/harfbuzz/src/harfbuzz-gsub.c
new file mode 100644
index 0000000..21fec51
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-gsub.c
@@ -0,0 +1,4329 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ * Copyright (C) 2007  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-gsub-private.h"
+#include "harfbuzz-open-private.h"
+#include "harfbuzz-gdef-private.h"
+
+static HB_Error  GSUB_Do_Glyph_Lookup( HB_GSUBHeader*   gsub,
+				       HB_UShort         lookup_index,
+				       HB_Buffer        buffer,
+				       HB_UShort         context_length,
+				       int               nesting_level );
+
+
+
+/**********************
+ * Auxiliary functions
+ **********************/
+
+
+
+HB_Error  HB_Load_GSUB_Table( HB_Stream stream,
+			      HB_GSUBHeader** retptr,
+			      HB_GDEFHeader*  gdef,
+                              HB_Stream       gdefStream )
+{
+  HB_Error         error;
+  HB_UInt         cur_offset, new_offset, base_offset;
+
+  HB_GSUBHeader*  gsub;
+
+  if ( !retptr )
+    return ERR(HB_Err_Invalid_Argument);
+
+  if ( GOTO_Table( TTAG_GSUB ) )
+    return error;
+
+  base_offset = FILE_Pos();
+
+  if ( ALLOC ( gsub, sizeof( *gsub ) ) ) 
+      return error;
+  
+
+  /* skip version */
+
+  if ( FILE_Seek( base_offset + 4L ) ||
+       ACCESS_Frame( 2L ) )
+    goto Fail4;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_ScriptList( &gsub->ScriptList,
+				  stream ) ) != HB_Err_Ok )
+    goto Fail4;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_FeatureList( &gsub->FeatureList,
+				   stream ) ) != HB_Err_Ok )
+    goto Fail3;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_LookupList( &gsub->LookupList,
+				  stream, HB_Type_GSUB ) ) != HB_Err_Ok )
+    goto Fail2;
+
+  gsub->gdef = gdef;      /* can be NULL */
+
+  if ( ( error =  _HB_GDEF_LoadMarkAttachClassDef_From_LookupFlags( gdef, gdefStream,
+								     gsub->LookupList.Lookup,
+								     gsub->LookupList.LookupCount ) ) )
+    goto Fail1;
+
+  *retptr = gsub;
+
+  return HB_Err_Ok;
+
+Fail1:
+  _HB_OPEN_Free_LookupList( &gsub->LookupList, HB_Type_GSUB );
+
+Fail2:
+  _HB_OPEN_Free_FeatureList( &gsub->FeatureList );
+
+Fail3:
+  _HB_OPEN_Free_ScriptList( &gsub->ScriptList );
+
+Fail4:
+  FREE ( gsub );
+
+
+  return error;
+}
+
+
+HB_Error   HB_Done_GSUB_Table( HB_GSUBHeader* gsub )
+{
+  _HB_OPEN_Free_LookupList( &gsub->LookupList, HB_Type_GSUB );
+  _HB_OPEN_Free_FeatureList( &gsub->FeatureList );
+  _HB_OPEN_Free_ScriptList( &gsub->ScriptList );
+
+  FREE( gsub );
+
+  return HB_Err_Ok;
+}
+
+/*****************************
+ * SubTable related functions
+ *****************************/
+
+
+/* LookupType 1 */
+
+/* SingleSubstFormat1 */
+/* SingleSubstFormat2 */
+
+static HB_Error  Load_SingleSubst( HB_GSUB_SubTable* st,
+				   HB_Stream         stream )
+{
+  HB_Error error;
+  HB_SingleSubst*  ss = &st->single;
+
+  HB_UShort n, count;
+  HB_UInt cur_offset, new_offset, base_offset;
+
+  HB_UShort*  s;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  ss->SubstFormat = GET_UShort();
+  new_offset      = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &ss->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  switch ( ss->SubstFormat )
+  {
+  case 1:
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail2;
+
+    ss->ssf.ssf1.DeltaGlyphID = GET_UShort();
+
+    FORGET_Frame();
+
+    break;
+
+  case 2:
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail2;
+
+    count = ss->ssf.ssf2.GlyphCount = GET_UShort();
+
+    FORGET_Frame();
+
+    ss->ssf.ssf2.Substitute = NULL;
+
+    if ( ALLOC_ARRAY( ss->ssf.ssf2.Substitute, count, HB_UShort ) )
+      goto Fail2;
+
+    s = ss->ssf.ssf2.Substitute;
+
+    if ( ACCESS_Frame( count * 2L ) )
+      goto Fail1;
+
+    for ( n = 0; n < count; n++ )
+      s[n] = GET_UShort();
+
+    FORGET_Frame();
+
+    break;
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( s );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &ss->Coverage );
+  return error;
+}
+
+
+static void  Free_SingleSubst( HB_GSUB_SubTable* st )
+{
+  HB_SingleSubst*  ss = &st->single;
+
+  switch ( ss->SubstFormat )
+  {
+  case 1:
+    break;
+
+  case 2:
+    FREE( ss->ssf.ssf2.Substitute );
+    break;
+
+  default:
+    break;
+  }
+
+  _HB_OPEN_Free_Coverage( &ss->Coverage );
+}
+
+
+static HB_Error  Lookup_SingleSubst( HB_GSUBHeader*   gsub,
+				     HB_GSUB_SubTable* st,
+				     HB_Buffer        buffer,
+				     HB_UShort         flags,
+				     HB_UShort         context_length,
+				     int               nesting_level )
+{
+  HB_UShort index, value, property;
+  HB_Error  error;
+  HB_SingleSubst*  ss = &st->single;
+  HB_GDEFHeader*   gdef = gsub->gdef;
+
+  HB_UNUSED(nesting_level);
+
+  if ( context_length != 0xFFFF && context_length < 1 )
+    return HB_Err_Not_Covered;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &ss->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  switch ( ss->SubstFormat )
+  {
+  case 1:
+    value = ( IN_CURGLYPH() + ss->ssf.ssf1.DeltaGlyphID ) & 0xFFFF;
+    if ( REPLACE_Glyph( buffer, value, nesting_level ) )
+      return error;
+    break;
+
+  case 2:
+    if ( index >= ss->ssf.ssf2.GlyphCount )
+      return ERR(HB_Err_Invalid_SubTable);
+    value = ss->ssf.ssf2.Substitute[index];
+    if ( REPLACE_Glyph( buffer, value, nesting_level ) )
+      return error;
+    break;
+
+  default:
+    return ERR(HB_Err_Invalid_SubTable);
+  }
+
+  if ( gdef && gdef->NewGlyphClasses )
+  {
+    /* we inherit the old glyph class to the substituted glyph */
+
+    error = _HB_GDEF_Add_Glyph_Property( gdef, value, property );
+    if ( error && error != HB_Err_Not_Covered )
+      return error;
+  }
+
+  return HB_Err_Ok;
+}
+
+
+/* LookupType 2 */
+
+/* Sequence */
+
+static HB_Error  Load_Sequence( HB_Sequence*  s,
+				HB_Stream      stream )
+{
+  HB_Error error;
+
+  HB_UShort n, count;
+  HB_UShort*  sub;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = s->GlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  s->Substitute = NULL;
+
+  if ( count )
+  {
+    if ( ALLOC_ARRAY( s->Substitute, count, HB_UShort ) )
+      return error;
+
+    sub = s->Substitute;
+
+    if ( ACCESS_Frame( count * 2L ) )
+    {
+      FREE( sub );
+      return error;
+    }
+
+    for ( n = 0; n < count; n++ )
+      sub[n] = GET_UShort();
+
+    FORGET_Frame();
+  }
+
+  return HB_Err_Ok;
+}
+
+
+static void  Free_Sequence( HB_Sequence*  s )
+{
+  FREE( s->Substitute );
+}
+
+
+/* MultipleSubstFormat1 */
+
+static HB_Error  Load_MultipleSubst( HB_GSUB_SubTable* st,
+				     HB_Stream         stream )
+{
+  HB_Error error;
+  HB_MultipleSubst*  ms = &st->multiple;
+
+  HB_UShort      n = 0, m, count;
+  HB_UInt       cur_offset, new_offset, base_offset;
+
+  HB_Sequence*  s;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  ms->SubstFormat = GET_UShort();             /* should be 1 */
+  new_offset      = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &ms->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = ms->SequenceCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ms->Sequence = NULL;
+
+  if ( ALLOC_ARRAY( ms->Sequence, count, HB_Sequence ) )
+    goto Fail2;
+
+  s = ms->Sequence;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_Sequence( &s[n], stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_Sequence( &s[m] );
+
+  FREE( s );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &ms->Coverage );
+  return error;
+}
+
+
+static void  Free_MultipleSubst( HB_GSUB_SubTable* st )
+{
+  HB_UShort      n, count;
+  HB_MultipleSubst*  ms = &st->multiple;
+
+  HB_Sequence*  s;
+
+
+  if ( ms->Sequence )
+  {
+    count = ms->SequenceCount;
+    s     = ms->Sequence;
+
+    for ( n = 0; n < count; n++ )
+      Free_Sequence( &s[n] );
+
+    FREE( s );
+  }
+
+  _HB_OPEN_Free_Coverage( &ms->Coverage );
+}
+
+
+static HB_Error  Lookup_MultipleSubst( HB_GSUBHeader*    gsub,
+				       HB_GSUB_SubTable* st,
+				       HB_Buffer         buffer,
+				       HB_UShort          flags,
+				       HB_UShort          context_length,
+				       int                nesting_level )
+{
+  HB_Error  error;
+  HB_UShort index, property, n, count;
+  HB_UShort*s;
+  HB_MultipleSubst*  ms = &st->multiple;
+  HB_GDEFHeader*     gdef = gsub->gdef;
+
+  HB_UNUSED(nesting_level);
+
+  if ( context_length != 0xFFFF && context_length < 1 )
+    return HB_Err_Not_Covered;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &ms->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  if ( index >= ms->SequenceCount )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  count = ms->Sequence[index].GlyphCount;
+  s     = ms->Sequence[index].Substitute;
+
+  if ( ADD_String( buffer, 1, count, s, 0xFFFF, 0xFFFF ) )
+    return error;
+
+  if ( gdef && gdef->NewGlyphClasses )
+  {
+    /* this is a guess only ... */
+
+    if ( property == HB_GDEF_LIGATURE )
+      property = HB_GDEF_BASE_GLYPH;
+
+    for ( n = 0; n < count; n++ )
+    {
+      error = _HB_GDEF_Add_Glyph_Property( gdef, s[n], property );
+      if ( error && error != HB_Err_Not_Covered )
+	return error;
+    }
+  }
+
+  return HB_Err_Ok;
+}
+
+
+/* LookupType 3 */
+
+/* AlternateSet */
+
+static HB_Error  Load_AlternateSet( HB_AlternateSet*  as,
+				    HB_Stream          stream )
+{
+  HB_Error error;
+
+  HB_UShort n, count;
+  HB_UShort*  a;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = as->GlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  as->Alternate = NULL;
+
+  if ( ALLOC_ARRAY( as->Alternate, count, HB_UShort ) )
+    return error;
+
+  a = as->Alternate;
+
+  if ( ACCESS_Frame( count * 2L ) )
+  {
+    FREE( a );
+    return error;
+  }
+
+  for ( n = 0; n < count; n++ )
+    a[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+}
+
+
+static void  Free_AlternateSet( HB_AlternateSet*  as )
+{
+  FREE( as->Alternate );
+}
+
+
+/* AlternateSubstFormat1 */
+
+static HB_Error  Load_AlternateSubst( HB_GSUB_SubTable* st,
+				      HB_Stream         stream )
+{
+  HB_Error error;
+  HB_AlternateSubst* as = &st->alternate;
+
+  HB_UShort          n = 0, m, count;
+  HB_UInt           cur_offset, new_offset, base_offset;
+
+  HB_AlternateSet*  aset;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  as->SubstFormat = GET_UShort();             /* should be 1 */
+  new_offset      = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &as->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = as->AlternateSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  as->AlternateSet = NULL;
+
+  if ( ALLOC_ARRAY( as->AlternateSet, count, HB_AlternateSet ) )
+    goto Fail2;
+
+  aset = as->AlternateSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_AlternateSet( &aset[n], stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_AlternateSet( &aset[m] );
+
+  FREE( aset );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &as->Coverage );
+  return error;
+}
+
+
+static void  Free_AlternateSubst( HB_GSUB_SubTable* st )
+{
+  HB_UShort          n, count;
+  HB_AlternateSubst* as = &st->alternate;
+
+  HB_AlternateSet*  aset;
+
+
+  if ( as->AlternateSet )
+  {
+    count = as->AlternateSetCount;
+    aset  = as->AlternateSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_AlternateSet( &aset[n] );
+
+    FREE( aset );
+  }
+
+  _HB_OPEN_Free_Coverage( &as->Coverage );
+}
+
+
+static HB_Error  Lookup_AlternateSubst( HB_GSUBHeader*    gsub,
+					HB_GSUB_SubTable* st,
+					HB_Buffer         buffer,
+					HB_UShort          flags,
+					HB_UShort          context_length,
+					int                nesting_level )
+{
+  HB_Error          error;
+  HB_UShort         index, value, alt_index, property;
+  HB_AlternateSubst* as = &st->alternate;
+  HB_GDEFHeader*     gdef = gsub->gdef;
+  HB_AlternateSet  aset;
+
+  HB_UNUSED(nesting_level);
+
+  if ( context_length != 0xFFFF && context_length < 1 )
+    return HB_Err_Not_Covered;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &as->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  aset = as->AlternateSet[index];
+
+  /* we use a user-defined callback function to get the alternate index */
+
+  if ( gsub->altfunc )
+    alt_index = (gsub->altfunc)( buffer->out_pos, IN_CURGLYPH(),
+				 aset.GlyphCount, aset.Alternate,
+				 gsub->data );
+  else
+    alt_index = 0;
+
+  value = aset.Alternate[alt_index];
+  if ( REPLACE_Glyph( buffer, value, nesting_level ) )
+    return error;
+
+  if ( gdef && gdef->NewGlyphClasses )
+  {
+    /* we inherit the old glyph class to the substituted glyph */
+
+    error = _HB_GDEF_Add_Glyph_Property( gdef, value, property );
+    if ( error && error != HB_Err_Not_Covered )
+      return error;
+  }
+
+  return HB_Err_Ok;
+}
+
+
+/* LookupType 4 */
+
+/* Ligature */
+
+static HB_Error  Load_Ligature( HB_Ligature*  l,
+				HB_Stream      stream )
+{
+  HB_Error error;
+
+  HB_UShort n, count;
+  HB_UShort*  c;
+
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  l->LigGlyph       = GET_UShort();
+  l->ComponentCount = GET_UShort();
+
+  FORGET_Frame();
+
+  l->Component = NULL;
+
+  count = l->ComponentCount - 1;      /* only ComponentCount - 1 elements */
+
+  if ( ALLOC_ARRAY( l->Component, count, HB_UShort ) )
+    return error;
+
+  c = l->Component;
+
+  if ( ACCESS_Frame( count * 2L ) )
+  {
+    FREE( c );
+    return error;
+  }
+
+  for ( n = 0; n < count; n++ )
+    c[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+}
+
+
+static void  Free_Ligature( HB_Ligature*  l )
+{
+  FREE( l->Component );
+}
+
+
+/* LigatureSet */
+
+static HB_Error  Load_LigatureSet( HB_LigatureSet*  ls,
+				   HB_Stream         stream )
+{
+  HB_Error error;
+
+  HB_UShort      n = 0, m, count;
+  HB_UInt       cur_offset, new_offset, base_offset;
+
+  HB_Ligature*  l;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = ls->LigatureCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ls->Ligature = NULL;
+
+  if ( ALLOC_ARRAY( ls->Ligature, count, HB_Ligature ) )
+    return error;
+
+  l = ls->Ligature;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_Ligature( &l[n], stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_Ligature( &l[m] );
+
+  FREE( l );
+  return error;
+}
+
+
+static void  Free_LigatureSet( HB_LigatureSet*  ls )
+{
+  HB_UShort      n, count;
+
+  HB_Ligature*  l;
+
+
+  if ( ls->Ligature )
+  {
+    count = ls->LigatureCount;
+    l     = ls->Ligature;
+
+    for ( n = 0; n < count; n++ )
+      Free_Ligature( &l[n] );
+
+    FREE( l );
+  }
+}
+
+
+/* LigatureSubstFormat1 */
+
+static HB_Error  Load_LigatureSubst( HB_GSUB_SubTable* st,
+				     HB_Stream         stream )
+{
+  HB_Error error;
+  HB_LigatureSubst*  ls = &st->ligature;
+
+  HB_UShort         n = 0, m, count;
+  HB_UInt          cur_offset, new_offset, base_offset;
+
+  HB_LigatureSet*  lset;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  ls->SubstFormat = GET_UShort();             /* should be 1 */
+  new_offset      = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &ls->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = ls->LigatureSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ls->LigatureSet = NULL;
+
+  if ( ALLOC_ARRAY( ls->LigatureSet, count, HB_LigatureSet ) )
+    goto Fail2;
+
+  lset = ls->LigatureSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_LigatureSet( &lset[n], stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_LigatureSet( &lset[m] );
+
+  FREE( lset );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &ls->Coverage );
+  return error;
+}
+
+
+static void  Free_LigatureSubst( HB_GSUB_SubTable* st )
+{
+  HB_UShort         n, count;
+  HB_LigatureSubst*  ls = &st->ligature;
+
+  HB_LigatureSet*  lset;
+
+
+  if ( ls->LigatureSet )
+  {
+    count = ls->LigatureSetCount;
+    lset  = ls->LigatureSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_LigatureSet( &lset[n] );
+
+    FREE( lset );
+  }
+
+  _HB_OPEN_Free_Coverage( &ls->Coverage );
+}
+
+
+static HB_Error  Lookup_LigatureSubst( HB_GSUBHeader*    gsub,
+				       HB_GSUB_SubTable* st,
+				       HB_Buffer         buffer,
+				       HB_UShort          flags,
+				       HB_UShort          context_length,
+				       int                nesting_level )
+{
+  HB_UShort      index, property;
+  HB_Error       error;
+  HB_UShort      numlig, i, j, is_mark, first_is_mark = FALSE;
+  HB_UShort*     c;
+  HB_LigatureSubst*  ls = &st->ligature;
+  HB_GDEFHeader*     gdef = gsub->gdef;
+
+  HB_Ligature*  lig;
+
+  HB_UNUSED(nesting_level);
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  if ( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS )
+    first_is_mark = TRUE;
+
+  error = _HB_OPEN_Coverage_Index( &ls->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  if ( index >= ls->LigatureSetCount )
+     return ERR(HB_Err_Invalid_SubTable);
+
+  lig = ls->LigatureSet[index].Ligature;
+
+  for ( numlig = ls->LigatureSet[index].LigatureCount;
+	numlig;
+	numlig--, lig++ )
+  {
+    if ( buffer->in_pos + lig->ComponentCount > buffer->in_length )
+      goto next_ligature;               /* Not enough glyphs in input */
+
+    c    = lig->Component;
+
+    is_mark = first_is_mark;
+
+    if ( context_length != 0xFFFF && context_length < lig->ComponentCount )
+      break;
+
+    for ( i = 1, j = buffer->in_pos + 1; i < lig->ComponentCount; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  return error;
+
+	if ( j + lig->ComponentCount - i == (HB_Int)buffer->in_length )
+	  goto next_ligature;
+	j++;
+      }
+
+      if ( !( property == HB_GDEF_MARK || property & HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS ) )
+	is_mark = FALSE;
+
+      if ( IN_GLYPH( j ) != c[i - 1] )
+	goto next_ligature;
+    }
+
+    if ( gdef && gdef->NewGlyphClasses )
+    {
+      /* this is just a guess ... */
+
+      error = _HB_GDEF_Add_Glyph_Property( gdef, lig->LigGlyph,
+				  is_mark ? HB_GDEF_MARK : HB_GDEF_LIGATURE );
+      if ( error && error != HB_Err_Not_Covered )
+	return error;
+    }
+
+    if ( j == buffer->in_pos + i ) /* No input glyphs skipped */
+    {
+      /* We don't use a new ligature ID if there are no skipped
+	 glyphs and the ligature already has an ID.             */
+
+      if ( IN_LIGID( buffer->in_pos ) )
+      {
+	if ( ADD_String( buffer, i, 1, &lig->LigGlyph,
+			0xFFFF, 0xFFFF ) )
+	  return error;
+      }
+      else
+      {
+	HB_UShort ligID = _hb_buffer_allocate_ligid( buffer );
+	if ( ADD_String( buffer, i, 1, &lig->LigGlyph,
+			0xFFFF, ligID ) )
+	  return error;
+      }
+    }
+    else
+    {
+      HB_UShort ligID = _hb_buffer_allocate_ligid( buffer );
+      if ( ADD_Glyph( buffer, lig->LigGlyph, 0xFFFF, ligID ) )
+	return error;
+
+      /* Now we must do a second loop to copy the skipped glyphs to
+	 `out' and assign component values to it.  We start with the
+	 glyph after the first component.  Glyphs between component
+	 i and i+1 belong to component i.  Together with the ligID
+	 value it is later possible to check whether a specific
+	 component value really belongs to a given ligature.         */
+
+      for ( i = 0; i < lig->ComponentCount - 1; i++ )
+      {
+	while ( CHECK_Property( gdef, IN_CURITEM(),
+				flags, &property ) )
+	  if ( ADD_Glyph( buffer, IN_CURGLYPH(), i, ligID ) )
+	    return error;
+
+	(buffer->in_pos)++;
+      }
+    }
+
+    return HB_Err_Ok;
+
+  next_ligature:
+    ;
+  }
+
+  return HB_Err_Not_Covered;
+}
+
+
+/* Do the actual substitution for a context substitution (either format
+   5 or 6).  This is only called after we've determined that the input
+   matches the subrule.                                                 */
+
+static HB_Error  Do_ContextSubst( HB_GSUBHeader*        gsub,
+				  HB_UShort              GlyphCount,
+				  HB_UShort              SubstCount,
+				  HB_SubstLookupRecord* subst,
+				  HB_Buffer             buffer,
+				  int                    nesting_level )
+{
+  HB_Error  error;
+  HB_UInt   i, old_pos;
+
+
+  i = 0;
+
+  while ( i < GlyphCount )
+  {
+    if ( SubstCount && i == subst->SequenceIndex )
+    {
+      old_pos = buffer->in_pos;
+
+      /* Do a substitution */
+
+      error = GSUB_Do_Glyph_Lookup( gsub, subst->LookupListIndex, buffer,
+				    GlyphCount, nesting_level );
+
+      subst++;
+      SubstCount--;
+      i += buffer->in_pos - old_pos;
+
+      if ( error == HB_Err_Not_Covered )
+      {
+	if ( COPY_Glyph( buffer ) )
+	  return error;
+	i++;
+      }
+      else if ( error )
+	return error;
+    }
+    else
+    {
+      /* No substitution for this index */
+
+      if ( COPY_Glyph( buffer ) )
+	return error;
+      i++;
+    }
+  }
+
+  return HB_Err_Ok;
+}
+
+
+/* LookupType 5 */
+
+/* SubRule */
+
+static HB_Error  Load_SubRule( HB_SubRule*  sr,
+			       HB_Stream     stream )
+{
+  HB_Error error;
+
+  HB_UShort               n, count;
+  HB_UShort*              i;
+
+  HB_SubstLookupRecord*  slr;
+
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  sr->GlyphCount = GET_UShort();
+  sr->SubstCount = GET_UShort();
+
+  FORGET_Frame();
+
+  sr->Input = NULL;
+
+  count = sr->GlyphCount - 1;         /* only GlyphCount - 1 elements */
+
+  if ( ALLOC_ARRAY( sr->Input, count, HB_UShort ) )
+    return error;
+
+  i = sr->Input;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail2;
+
+  for ( n = 0; n < count; n++ )
+    i[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  sr->SubstLookupRecord = NULL;
+
+  count = sr->SubstCount;
+
+  if ( ALLOC_ARRAY( sr->SubstLookupRecord, count, HB_SubstLookupRecord ) )
+    goto Fail2;
+
+  slr = sr->SubstLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    slr[n].SequenceIndex   = GET_UShort();
+    slr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( slr );
+
+Fail2:
+  FREE( i );
+  return error;
+}
+
+
+static void  Free_SubRule( HB_SubRule*  sr )
+{
+  FREE( sr->SubstLookupRecord );
+  FREE( sr->Input );
+}
+
+
+/* SubRuleSet */
+
+static HB_Error  Load_SubRuleSet( HB_SubRuleSet*  srs,
+				  HB_Stream        stream )
+{
+  HB_Error error;
+
+  HB_UShort     n = 0, m, count;
+  HB_UInt      cur_offset, new_offset, base_offset;
+
+  HB_SubRule*  sr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = srs->SubRuleCount = GET_UShort();
+
+  FORGET_Frame();
+
+  srs->SubRule = NULL;
+
+  if ( ALLOC_ARRAY( srs->SubRule, count, HB_SubRule ) )
+    return error;
+
+  sr = srs->SubRule;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_SubRule( &sr[n], stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_SubRule( &sr[m] );
+
+  FREE( sr );
+  return error;
+}
+
+
+static void  Free_SubRuleSet( HB_SubRuleSet*  srs )
+{
+  HB_UShort     n, count;
+
+  HB_SubRule*  sr;
+
+
+  if ( srs->SubRule )
+  {
+    count = srs->SubRuleCount;
+    sr    = srs->SubRule;
+
+    for ( n = 0; n < count; n++ )
+      Free_SubRule( &sr[n] );
+
+    FREE( sr );
+  }
+}
+
+
+/* ContextSubstFormat1 */
+
+static HB_Error  Load_ContextSubst1( HB_ContextSubstFormat1*  csf1,
+				     HB_Stream                 stream )
+{
+  HB_Error error;
+
+  HB_UShort        n = 0, m, count;
+  HB_UInt         cur_offset, new_offset, base_offset;
+
+  HB_SubRuleSet*  srs;
+
+
+  base_offset = FILE_Pos() - 2L;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &csf1->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = csf1->SubRuleSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  csf1->SubRuleSet = NULL;
+
+  if ( ALLOC_ARRAY( csf1->SubRuleSet, count, HB_SubRuleSet ) )
+    goto Fail2;
+
+  srs = csf1->SubRuleSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_SubRuleSet( &srs[n], stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_SubRuleSet( &srs[m] );
+
+  FREE( srs );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &csf1->Coverage );
+  return error;
+}
+
+
+static void  Free_ContextSubst1( HB_ContextSubstFormat1* csf1 )
+{
+  HB_UShort        n, count;
+
+  HB_SubRuleSet*  srs;
+
+
+  if ( csf1->SubRuleSet )
+  {
+    count = csf1->SubRuleSetCount;
+    srs   = csf1->SubRuleSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_SubRuleSet( &srs[n] );
+
+    FREE( srs );
+  }
+
+  _HB_OPEN_Free_Coverage( &csf1->Coverage );
+}
+
+
+/* SubClassRule */
+
+static HB_Error  Load_SubClassRule( HB_ContextSubstFormat2*  csf2,
+				    HB_SubClassRule*         scr,
+				    HB_Stream                 stream )
+{
+  HB_Error error;
+
+  HB_UShort               n, count;
+
+  HB_UShort*              c;
+  HB_SubstLookupRecord*  slr;
+
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  scr->GlyphCount = GET_UShort();
+  scr->SubstCount = GET_UShort();
+
+  if ( scr->GlyphCount > csf2->MaxContextLength )
+    csf2->MaxContextLength = scr->GlyphCount;
+
+  FORGET_Frame();
+
+  scr->Class = NULL;
+
+  count = scr->GlyphCount - 1;        /* only GlyphCount - 1 elements */
+
+  if ( ALLOC_ARRAY( scr->Class, count, HB_UShort ) )
+    return error;
+
+  c = scr->Class;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail2;
+
+  for ( n = 0; n < count; n++ )
+    c[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  scr->SubstLookupRecord = NULL;
+
+  count = scr->SubstCount;
+
+  if ( ALLOC_ARRAY( scr->SubstLookupRecord, count, HB_SubstLookupRecord ) )
+    goto Fail2;
+
+  slr = scr->SubstLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    slr[n].SequenceIndex   = GET_UShort();
+    slr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( slr );
+
+Fail2:
+  FREE( c );
+  return error;
+}
+
+
+static void  Free_SubClassRule( HB_SubClassRule*  scr )
+{
+  FREE( scr->SubstLookupRecord );
+  FREE( scr->Class );
+}
+
+
+/* SubClassSet */
+
+static HB_Error  Load_SubClassSet( HB_ContextSubstFormat2*  csf2,
+				   HB_SubClassSet*          scs,
+				   HB_Stream                 stream )
+{
+  HB_Error error;
+
+  HB_UShort          n = 0, m, count;
+  HB_UInt           cur_offset, new_offset, base_offset;
+
+  HB_SubClassRule*  scr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = scs->SubClassRuleCount = GET_UShort();
+
+  FORGET_Frame();
+
+  scs->SubClassRule = NULL;
+
+  if ( ALLOC_ARRAY( scs->SubClassRule, count, HB_SubClassRule ) )
+    return error;
+
+  scr = scs->SubClassRule;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_SubClassRule( csf2, &scr[n],
+				      stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_SubClassRule( &scr[m] );
+
+  FREE( scr );
+  return error;
+}
+
+
+static void  Free_SubClassSet( HB_SubClassSet*  scs )
+{
+  HB_UShort          n, count;
+
+  HB_SubClassRule*  scr;
+
+
+  if ( scs->SubClassRule )
+  {
+    count = scs->SubClassRuleCount;
+    scr   = scs->SubClassRule;
+
+    for ( n = 0; n < count; n++ )
+      Free_SubClassRule( &scr[n] );
+
+    FREE( scr );
+  }
+}
+
+
+/* ContextSubstFormat2 */
+
+static HB_Error  Load_ContextSubst2( HB_ContextSubstFormat2*  csf2,
+				     HB_Stream                 stream )
+{
+  HB_Error error;
+
+  HB_UShort         n = 0, m, count;
+  HB_UInt          cur_offset, new_offset, base_offset;
+
+  HB_SubClassSet*  scs;
+
+
+  base_offset = FILE_Pos() - 2;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &csf2->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 4L ) )
+    goto Fail3;
+
+  new_offset = GET_UShort() + base_offset;
+
+  /* `SubClassSetCount' is the upper limit for class values, thus we
+     read it now to make an additional safety check.                 */
+
+  count = csf2->SubClassSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_ClassDefinition( &csf2->ClassDef, count,
+				       stream ) ) != HB_Err_Ok )
+    goto Fail3;
+  (void)FILE_Seek( cur_offset );
+
+  csf2->SubClassSet      = NULL;
+  csf2->MaxContextLength = 0;
+
+  if ( ALLOC_ARRAY( csf2->SubClassSet, count, HB_SubClassSet ) )
+    goto Fail2;
+
+  scs = csf2->SubClassSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    if ( new_offset != base_offset )      /* not a NULL offset */
+    {
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = Load_SubClassSet( csf2, &scs[n],
+				       stream ) ) != HB_Err_Ok )
+	goto Fail1;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+    {
+      /* we create a SubClassSet table with no entries */
+
+      csf2->SubClassSet[n].SubClassRuleCount = 0;
+      csf2->SubClassSet[n].SubClassRule      = NULL;
+    }
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_SubClassSet( &scs[m] );
+
+  FREE( scs );
+
+Fail2:
+  _HB_OPEN_Free_ClassDefinition( &csf2->ClassDef );
+
+Fail3:
+  _HB_OPEN_Free_Coverage( &csf2->Coverage );
+  return error;
+}
+
+
+static void  Free_ContextSubst2( HB_ContextSubstFormat2*  csf2 )
+{
+  HB_UShort         n, count;
+
+  HB_SubClassSet*  scs;
+
+
+  if ( csf2->SubClassSet )
+  {
+    count = csf2->SubClassSetCount;
+    scs   = csf2->SubClassSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_SubClassSet( &scs[n] );
+
+    FREE( scs );
+  }
+
+  _HB_OPEN_Free_ClassDefinition( &csf2->ClassDef );
+  _HB_OPEN_Free_Coverage( &csf2->Coverage );
+}
+
+
+/* ContextSubstFormat3 */
+
+static HB_Error  Load_ContextSubst3( HB_ContextSubstFormat3*  csf3,
+				     HB_Stream                 stream )
+{
+  HB_Error error;
+
+  HB_UShort               n = 0, m, count;
+  HB_UInt                cur_offset, new_offset, base_offset;
+
+  HB_Coverage*           c;
+  HB_SubstLookupRecord*  slr;
+
+
+  base_offset = FILE_Pos() - 2L;
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  csf3->GlyphCount = GET_UShort();
+  csf3->SubstCount = GET_UShort();
+
+  FORGET_Frame();
+
+  csf3->Coverage = NULL;
+
+  count = csf3->GlyphCount;
+
+  if ( ALLOC_ARRAY( csf3->Coverage, count, HB_Coverage ) )
+    return error;
+
+  c = csf3->Coverage;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail2;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Coverage( &c[n], stream ) ) != HB_Err_Ok )
+      goto Fail2;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  csf3->SubstLookupRecord = NULL;
+
+  count = csf3->SubstCount;
+
+  if ( ALLOC_ARRAY( csf3->SubstLookupRecord, count,
+		    HB_SubstLookupRecord ) )
+    goto Fail2;
+
+  slr = csf3->SubstLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    slr[n].SequenceIndex   = GET_UShort();
+    slr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( slr );
+
+Fail2:
+  for ( m = 0; m < n; m++ )
+    _HB_OPEN_Free_Coverage( &c[m] );
+
+  FREE( c );
+  return error;
+}
+
+
+static void  Free_ContextSubst3( HB_ContextSubstFormat3*  csf3 )
+{
+  HB_UShort      n, count;
+
+  HB_Coverage*  c;
+
+
+  FREE( csf3->SubstLookupRecord );
+
+  if ( csf3->Coverage )
+  {
+    count = csf3->GlyphCount;
+    c     = csf3->Coverage;
+
+    for ( n = 0; n < count; n++ )
+      _HB_OPEN_Free_Coverage( &c[n] );
+
+    FREE( c );
+  }
+}
+
+
+/* ContextSubst */
+
+static HB_Error  Load_ContextSubst( HB_GSUB_SubTable* st,
+				    HB_Stream         stream )
+{
+  HB_Error error;
+  HB_ContextSubst*  cs = &st->context;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  cs->SubstFormat = GET_UShort();
+
+  FORGET_Frame();
+
+  switch ( cs->SubstFormat )
+  {
+  case 1:  return Load_ContextSubst1( &cs->csf.csf1, stream );
+  case 2:  return Load_ContextSubst2( &cs->csf.csf2, stream );
+  case 3:  return Load_ContextSubst3( &cs->csf.csf3, stream );
+  default: return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;               /* never reached */
+}
+
+
+static void  Free_ContextSubst( HB_GSUB_SubTable* st )
+{
+  HB_ContextSubst*  cs = &st->context;
+
+  switch ( cs->SubstFormat )
+  {
+  case 1:  Free_ContextSubst1( &cs->csf.csf1 ); break;
+  case 2:  Free_ContextSubst2( &cs->csf.csf2 ); break;
+  case 3:  Free_ContextSubst3( &cs->csf.csf3 ); break;
+  default:						break;
+  }
+}
+
+
+static HB_Error  Lookup_ContextSubst1( HB_GSUBHeader*          gsub,
+				       HB_ContextSubstFormat1* csf1,
+				       HB_Buffer               buffer,
+				       HB_UShort                flags,
+				       HB_UShort                context_length,
+				       int                      nesting_level )
+{
+  HB_UShort        index, property;
+  HB_UShort        i, j, k, numsr;
+  HB_Error         error;
+
+  HB_SubRule*     sr;
+  HB_GDEFHeader*  gdef;
+
+
+  gdef = gsub->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &csf1->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  sr    = csf1->SubRuleSet[index].SubRule;
+  numsr = csf1->SubRuleSet[index].SubRuleCount;
+
+  for ( k = 0; k < numsr; k++ )
+  {
+    if ( context_length != 0xFFFF && context_length < sr[k].GlyphCount )
+      goto next_subrule;
+
+    if ( buffer->in_pos + sr[k].GlyphCount > buffer->in_length )
+      goto next_subrule;                        /* context is too long */
+
+    for ( i = 1, j = buffer->in_pos + 1; i < sr[k].GlyphCount; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  return error;
+
+	if ( j + sr[k].GlyphCount - i == (HB_Int)buffer->in_length )
+	  goto next_subrule;
+	j++;
+      }
+
+      if ( IN_GLYPH( j ) != sr[k].Input[i - 1] )
+	goto next_subrule;
+    }
+
+    return Do_ContextSubst( gsub, sr[k].GlyphCount,
+			    sr[k].SubstCount, sr[k].SubstLookupRecord,
+			    buffer,
+			    nesting_level );
+  next_subrule:
+    ;
+  }
+
+  return HB_Err_Not_Covered;
+}
+
+
+static HB_Error  Lookup_ContextSubst2( HB_GSUBHeader*          gsub,
+				       HB_ContextSubstFormat2* csf2,
+				       HB_Buffer               buffer,
+				       HB_UShort                flags,
+				       HB_UShort                context_length,
+				       int                      nesting_level )
+{
+  HB_UShort          index, property;
+  HB_Error           error;
+  HB_UShort          i, j, k, known_classes;
+
+  HB_UShort*         classes;
+  HB_UShort*         cl;
+
+  HB_SubClassSet*   scs;
+  HB_SubClassRule*  sr;
+  HB_GDEFHeader*    gdef;
+
+
+  gdef = gsub->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  /* Note: The coverage table in format 2 doesn't give an index into
+	   anything.  It just lets us know whether or not we need to
+	   do any lookup at all.                                     */
+
+  error = _HB_OPEN_Coverage_Index( &csf2->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  if (csf2->MaxContextLength < 1)
+    return HB_Err_Not_Covered;
+
+  if ( ALLOC_ARRAY( classes, csf2->MaxContextLength, HB_UShort ) )
+    return error;
+
+  error = _HB_OPEN_Get_Class( &csf2->ClassDef, IN_CURGLYPH(),
+		     &classes[0], NULL );
+  if ( error && error != HB_Err_Not_Covered )
+    goto End;
+  known_classes = 0;
+
+  scs = &csf2->SubClassSet[classes[0]];
+  if ( !scs )
+  {
+    error = ERR(HB_Err_Invalid_SubTable);
+    goto End;
+  }
+
+  for ( k = 0; k < scs->SubClassRuleCount; k++ )
+  {
+    sr  = &scs->SubClassRule[k];
+
+    if ( context_length != 0xFFFF && context_length < sr->GlyphCount )
+      goto next_subclassrule;
+
+    if ( buffer->in_pos + sr->GlyphCount > buffer->in_length )
+      goto next_subclassrule;                      /* context is too long */
+
+    cl   = sr->Class;
+
+    /* Start at 1 because [0] is implied */
+
+    for ( i = 1, j = buffer->in_pos + 1; i < sr->GlyphCount; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End;
+
+	if ( j + sr->GlyphCount - i < (HB_Int)buffer->in_length )
+	  goto next_subclassrule;
+	j++;
+      }
+
+      if ( i > known_classes )
+      {
+	/* Keeps us from having to do this for each rule */
+
+	error = _HB_OPEN_Get_Class( &csf2->ClassDef, IN_GLYPH( j ), &classes[i], NULL );
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End;
+	known_classes = i;
+      }
+
+      if ( cl[i - 1] != classes[i] )
+	goto next_subclassrule;
+    }
+
+    error = Do_ContextSubst( gsub, sr->GlyphCount,
+			     sr->SubstCount, sr->SubstLookupRecord,
+			     buffer,
+			     nesting_level );
+    goto End;
+
+  next_subclassrule:
+    ;
+  }
+
+  error = HB_Err_Not_Covered;
+
+End:
+  FREE( classes );
+  return error;
+}
+
+
+static HB_Error  Lookup_ContextSubst3( HB_GSUBHeader*          gsub,
+				       HB_ContextSubstFormat3* csf3,
+				       HB_Buffer               buffer,
+				       HB_UShort                flags,
+				       HB_UShort                context_length,
+				       int                      nesting_level )
+{
+  HB_Error         error;
+  HB_UShort        index, i, j, property;
+
+  HB_Coverage*    c;
+  HB_GDEFHeader*  gdef;
+
+
+  gdef = gsub->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  if ( context_length != 0xFFFF && context_length < csf3->GlyphCount )
+    return HB_Err_Not_Covered;
+
+  if ( buffer->in_pos + csf3->GlyphCount > buffer->in_length )
+    return HB_Err_Not_Covered;         /* context is too long */
+
+  c    = csf3->Coverage;
+
+  for ( i = 1, j = buffer->in_pos + 1; i < csf3->GlyphCount; i++, j++ )
+  {
+    while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+    {
+      if ( error && error != HB_Err_Not_Covered )
+	return error;
+
+      if ( j + csf3->GlyphCount - i == (HB_Int)buffer->in_length )
+	return HB_Err_Not_Covered;
+      j++;
+    }
+
+    error = _HB_OPEN_Coverage_Index( &c[i], IN_GLYPH( j ), &index );
+    if ( error )
+      return error;
+  }
+
+  return Do_ContextSubst( gsub, csf3->GlyphCount,
+			  csf3->SubstCount, csf3->SubstLookupRecord,
+			  buffer,
+			  nesting_level );
+}
+
+
+static HB_Error  Lookup_ContextSubst( HB_GSUBHeader*    gsub,
+				      HB_GSUB_SubTable* st,
+				      HB_Buffer         buffer,
+				      HB_UShort          flags,
+				      HB_UShort          context_length,
+				      int                nesting_level )
+{
+  HB_ContextSubst*  cs = &st->context;
+
+  switch ( cs->SubstFormat )
+  {
+  case 1:  return Lookup_ContextSubst1( gsub, &cs->csf.csf1, buffer, flags, context_length, nesting_level );
+  case 2:  return Lookup_ContextSubst2( gsub, &cs->csf.csf2, buffer, flags, context_length, nesting_level );
+  case 3:  return Lookup_ContextSubst3( gsub, &cs->csf.csf3, buffer, flags, context_length, nesting_level );
+  default: return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;               /* never reached */
+}
+
+
+/* LookupType 6 */
+
+/* ChainSubRule */
+
+static HB_Error  Load_ChainSubRule( HB_ChainSubRule*  csr,
+				    HB_Stream          stream )
+{
+  HB_Error error;
+
+  HB_UShort               n, count;
+  HB_UShort*              b;
+  HB_UShort*              i;
+  HB_UShort*              l;
+
+  HB_SubstLookupRecord*  slr;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  csr->BacktrackGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  csr->Backtrack = NULL;
+
+  count = csr->BacktrackGlyphCount;
+
+  if ( ALLOC_ARRAY( csr->Backtrack, count, HB_UShort ) )
+    return error;
+
+  b = csr->Backtrack;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail4;
+
+  for ( n = 0; n < count; n++ )
+    b[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail4;
+
+  csr->InputGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  csr->Input = NULL;
+
+  count = csr->InputGlyphCount - 1;  /* only InputGlyphCount - 1 elements */
+
+  if ( ALLOC_ARRAY( csr->Input, count, HB_UShort ) )
+    goto Fail4;
+
+  i = csr->Input;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail3;
+
+  for ( n = 0; n < count; n++ )
+    i[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  csr->LookaheadGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  csr->Lookahead = NULL;
+
+  count = csr->LookaheadGlyphCount;
+
+  if ( ALLOC_ARRAY( csr->Lookahead, count, HB_UShort ) )
+    goto Fail3;
+
+  l = csr->Lookahead;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail2;
+
+  for ( n = 0; n < count; n++ )
+    l[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  csr->SubstCount = GET_UShort();
+
+  FORGET_Frame();
+
+  csr->SubstLookupRecord = NULL;
+
+  count = csr->SubstCount;
+
+  if ( ALLOC_ARRAY( csr->SubstLookupRecord, count, HB_SubstLookupRecord ) )
+    goto Fail2;
+
+  slr = csr->SubstLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    slr[n].SequenceIndex   = GET_UShort();
+    slr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( slr );
+
+Fail2:
+  FREE( l );
+
+Fail3:
+  FREE( i );
+
+Fail4:
+  FREE( b );
+  return error;
+}
+
+
+static void  Free_ChainSubRule( HB_ChainSubRule*  csr )
+{
+  FREE( csr->SubstLookupRecord );
+  FREE( csr->Lookahead );
+  FREE( csr->Input );
+  FREE( csr->Backtrack );
+}
+
+
+/* ChainSubRuleSet */
+
+static HB_Error  Load_ChainSubRuleSet( HB_ChainSubRuleSet*  csrs,
+				       HB_Stream             stream )
+{
+  HB_Error error;
+
+  HB_UShort          n = 0, m, count;
+  HB_UInt           cur_offset, new_offset, base_offset;
+
+  HB_ChainSubRule*  csr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = csrs->ChainSubRuleCount = GET_UShort();
+
+  FORGET_Frame();
+
+  csrs->ChainSubRule = NULL;
+
+  if ( ALLOC_ARRAY( csrs->ChainSubRule, count, HB_ChainSubRule ) )
+    return error;
+
+  csr = csrs->ChainSubRule;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_ChainSubRule( &csr[n], stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_ChainSubRule( &csr[m] );
+
+  FREE( csr );
+  return error;
+}
+
+
+static void  Free_ChainSubRuleSet( HB_ChainSubRuleSet*  csrs )
+{
+  HB_UShort          n, count;
+
+  HB_ChainSubRule*  csr;
+
+
+  if ( csrs->ChainSubRule )
+  {
+    count = csrs->ChainSubRuleCount;
+    csr   = csrs->ChainSubRule;
+
+    for ( n = 0; n < count; n++ )
+      Free_ChainSubRule( &csr[n] );
+
+    FREE( csr );
+  }
+}
+
+
+/* ChainContextSubstFormat1 */
+
+static HB_Error  Load_ChainContextSubst1(
+		   HB_ChainContextSubstFormat1*  ccsf1,
+		   HB_Stream                      stream )
+{
+  HB_Error error;
+
+  HB_UShort             n = 0, m, count;
+  HB_UInt              cur_offset, new_offset, base_offset;
+
+  HB_ChainSubRuleSet*  csrs;
+
+
+  base_offset = FILE_Pos() - 2L;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &ccsf1->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = ccsf1->ChainSubRuleSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ccsf1->ChainSubRuleSet = NULL;
+
+  if ( ALLOC_ARRAY( ccsf1->ChainSubRuleSet, count, HB_ChainSubRuleSet ) )
+    goto Fail2;
+
+  csrs = ccsf1->ChainSubRuleSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_ChainSubRuleSet( &csrs[n], stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_ChainSubRuleSet( &csrs[m] );
+
+  FREE( csrs );
+
+Fail2:
+  _HB_OPEN_Free_Coverage( &ccsf1->Coverage );
+  return error;
+}
+
+
+static void  Free_ChainContextSubst1( HB_ChainContextSubstFormat1*  ccsf1 )
+{
+  HB_UShort             n, count;
+
+  HB_ChainSubRuleSet*  csrs;
+
+
+  if ( ccsf1->ChainSubRuleSet )
+  {
+    count = ccsf1->ChainSubRuleSetCount;
+    csrs  = ccsf1->ChainSubRuleSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_ChainSubRuleSet( &csrs[n] );
+
+    FREE( csrs );
+  }
+
+  _HB_OPEN_Free_Coverage( &ccsf1->Coverage );
+}
+
+
+/* ChainSubClassRule */
+
+static HB_Error  Load_ChainSubClassRule(
+		   HB_ChainContextSubstFormat2*  ccsf2,
+		   HB_ChainSubClassRule*         cscr,
+		   HB_Stream                      stream )
+{
+  HB_Error error;
+
+  HB_UShort               n, count;
+
+  HB_UShort*              b;
+  HB_UShort*              i;
+  HB_UShort*              l;
+  HB_SubstLookupRecord*  slr;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  cscr->BacktrackGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( cscr->BacktrackGlyphCount > ccsf2->MaxBacktrackLength )
+    ccsf2->MaxBacktrackLength = cscr->BacktrackGlyphCount;
+
+  cscr->Backtrack = NULL;
+
+  count = cscr->BacktrackGlyphCount;
+
+  if ( ALLOC_ARRAY( cscr->Backtrack, count, HB_UShort ) )
+    return error;
+
+  b = cscr->Backtrack;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail4;
+
+  for ( n = 0; n < count; n++ )
+    b[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail4;
+
+  cscr->InputGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( cscr->InputGlyphCount > ccsf2->MaxInputLength )
+    ccsf2->MaxInputLength = cscr->InputGlyphCount;
+
+  cscr->Input = NULL;
+
+  count = cscr->InputGlyphCount - 1; /* only InputGlyphCount - 1 elements */
+
+  if ( ALLOC_ARRAY( cscr->Input, count, HB_UShort ) )
+    goto Fail4;
+
+  i = cscr->Input;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail3;
+
+  for ( n = 0; n < count; n++ )
+    i[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  cscr->LookaheadGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( cscr->LookaheadGlyphCount > ccsf2->MaxLookaheadLength )
+    ccsf2->MaxLookaheadLength = cscr->LookaheadGlyphCount;
+
+  cscr->Lookahead = NULL;
+
+  count = cscr->LookaheadGlyphCount;
+
+  if ( ALLOC_ARRAY( cscr->Lookahead, count, HB_UShort ) )
+    goto Fail3;
+
+  l = cscr->Lookahead;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail2;
+
+  for ( n = 0; n < count; n++ )
+    l[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  cscr->SubstCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cscr->SubstLookupRecord = NULL;
+
+  count = cscr->SubstCount;
+
+  if ( ALLOC_ARRAY( cscr->SubstLookupRecord, count,
+		    HB_SubstLookupRecord ) )
+    goto Fail2;
+
+  slr = cscr->SubstLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    slr[n].SequenceIndex   = GET_UShort();
+    slr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( slr );
+
+Fail2:
+  FREE( l );
+
+Fail3:
+  FREE( i );
+
+Fail4:
+  FREE( b );
+  return error;
+}
+
+
+static void  Free_ChainSubClassRule( HB_ChainSubClassRule*  cscr )
+{
+  FREE( cscr->SubstLookupRecord );
+  FREE( cscr->Lookahead );
+  FREE( cscr->Input );
+  FREE( cscr->Backtrack );
+}
+
+
+/* SubClassSet */
+
+static HB_Error  Load_ChainSubClassSet(
+		   HB_ChainContextSubstFormat2*  ccsf2,
+		   HB_ChainSubClassSet*          cscs,
+		   HB_Stream                      stream )
+{
+  HB_Error error;
+
+  HB_UShort               n = 0, m, count;
+  HB_UInt                cur_offset, new_offset, base_offset;
+
+  HB_ChainSubClassRule*  cscr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = cscs->ChainSubClassRuleCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cscs->ChainSubClassRule = NULL;
+
+  if ( ALLOC_ARRAY( cscs->ChainSubClassRule, count,
+		    HB_ChainSubClassRule ) )
+    return error;
+
+  cscr = cscs->ChainSubClassRule;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_ChainSubClassRule( ccsf2, &cscr[n],
+					   stream ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_ChainSubClassRule( &cscr[m] );
+
+  FREE( cscr );
+  return error;
+}
+
+
+static void  Free_ChainSubClassSet( HB_ChainSubClassSet*  cscs )
+{
+  HB_UShort               n, count;
+
+  HB_ChainSubClassRule*  cscr;
+
+
+  if ( cscs->ChainSubClassRule )
+  {
+    count = cscs->ChainSubClassRuleCount;
+    cscr  = cscs->ChainSubClassRule;
+
+    for ( n = 0; n < count; n++ )
+      Free_ChainSubClassRule( &cscr[n] );
+
+    FREE( cscr );
+  }
+}
+
+
+/* ChainContextSubstFormat2 */
+
+static HB_Error  Load_ChainContextSubst2(
+		   HB_ChainContextSubstFormat2*  ccsf2,
+		   HB_Stream                      stream )
+{
+  HB_Error error;
+
+  HB_UShort              n = 0, m, count;
+  HB_UInt               cur_offset, new_offset, base_offset;
+  HB_UInt               backtrack_offset, input_offset, lookahead_offset;
+
+  HB_ChainSubClassSet*  cscs;
+
+
+  base_offset = FILE_Pos() - 2;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &ccsf2->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+  if ( ACCESS_Frame( 8L ) )
+    goto Fail5;
+
+  backtrack_offset = GET_UShort();
+  input_offset     = GET_UShort();
+  lookahead_offset = GET_UShort();
+
+  /* `ChainSubClassSetCount' is the upper limit for input class values,
+     thus we read it now to make an additional safety check. No limit
+     is known or needed for the other two class definitions          */
+
+  count = ccsf2->ChainSubClassSetCount = GET_UShort();
+
+  FORGET_Frame();
+
+  if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccsf2->BacktrackClassDef, 65535,
+						       backtrack_offset, base_offset,
+						       stream ) ) != HB_Err_Ok )
+      goto Fail5;
+
+  if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccsf2->InputClassDef, count,
+						       input_offset, base_offset,
+						       stream ) ) != HB_Err_Ok )
+      goto Fail4;
+  if ( ( error = _HB_OPEN_Load_EmptyOrClassDefinition( &ccsf2->LookaheadClassDef, 65535,
+						       lookahead_offset, base_offset,
+						       stream ) ) != HB_Err_Ok )
+    goto Fail3;
+
+  ccsf2->ChainSubClassSet   = NULL;
+  ccsf2->MaxBacktrackLength = 0;
+  ccsf2->MaxInputLength     = 0;
+  ccsf2->MaxLookaheadLength = 0;
+
+  if ( ALLOC_ARRAY( ccsf2->ChainSubClassSet, count, HB_ChainSubClassSet ) )
+    goto Fail2;
+
+  cscs = ccsf2->ChainSubClassSet;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    if ( new_offset != base_offset )      /* not a NULL offset */
+    {
+      cur_offset = FILE_Pos();
+      if ( FILE_Seek( new_offset ) ||
+	   ( error = Load_ChainSubClassSet( ccsf2, &cscs[n],
+					    stream ) ) != HB_Err_Ok )
+	goto Fail1;
+      (void)FILE_Seek( cur_offset );
+    }
+    else
+    {
+      /* we create a ChainSubClassSet table with no entries */
+
+      ccsf2->ChainSubClassSet[n].ChainSubClassRuleCount = 0;
+      ccsf2->ChainSubClassSet[n].ChainSubClassRule      = NULL;
+    }
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_ChainSubClassSet( &cscs[m] );
+
+  FREE( cscs );
+
+Fail2:
+  _HB_OPEN_Free_ClassDefinition( &ccsf2->LookaheadClassDef );
+
+Fail3:
+  _HB_OPEN_Free_ClassDefinition( &ccsf2->InputClassDef );
+
+Fail4:
+  _HB_OPEN_Free_ClassDefinition( &ccsf2->BacktrackClassDef );
+
+Fail5:
+  _HB_OPEN_Free_Coverage( &ccsf2->Coverage );
+  return error;
+}
+
+
+static void  Free_ChainContextSubst2( HB_ChainContextSubstFormat2*  ccsf2 )
+{
+  HB_UShort              n, count;
+
+  HB_ChainSubClassSet*  cscs;
+
+
+  if ( ccsf2->ChainSubClassSet )
+  {
+    count = ccsf2->ChainSubClassSetCount;
+    cscs  = ccsf2->ChainSubClassSet;
+
+    for ( n = 0; n < count; n++ )
+      Free_ChainSubClassSet( &cscs[n] );
+
+    FREE( cscs );
+  }
+
+  _HB_OPEN_Free_ClassDefinition( &ccsf2->LookaheadClassDef );
+  _HB_OPEN_Free_ClassDefinition( &ccsf2->InputClassDef );
+  _HB_OPEN_Free_ClassDefinition( &ccsf2->BacktrackClassDef );
+
+  _HB_OPEN_Free_Coverage( &ccsf2->Coverage );
+}
+
+
+/* ChainContextSubstFormat3 */
+
+static HB_Error  Load_ChainContextSubst3(
+		   HB_ChainContextSubstFormat3*  ccsf3,
+		   HB_Stream                      stream )
+{
+  HB_Error error;
+
+  HB_UShort               n, nb = 0, ni =0, nl = 0, m, count;
+  HB_UShort               backtrack_count, input_count, lookahead_count;
+  HB_UInt                cur_offset, new_offset, base_offset;
+
+  HB_Coverage*           b;
+  HB_Coverage*           i;
+  HB_Coverage*           l;
+  HB_SubstLookupRecord*  slr;
+
+
+  base_offset = FILE_Pos() - 2L;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  ccsf3->BacktrackGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ccsf3->BacktrackCoverage = NULL;
+
+  backtrack_count = ccsf3->BacktrackGlyphCount;
+
+  if ( ALLOC_ARRAY( ccsf3->BacktrackCoverage, backtrack_count,
+		    HB_Coverage ) )
+    return error;
+
+  b = ccsf3->BacktrackCoverage;
+
+  for ( nb = 0; nb < backtrack_count; nb++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail4;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Coverage( &b[nb], stream ) ) != HB_Err_Ok )
+      goto Fail4;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail4;
+
+  ccsf3->InputGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ccsf3->InputCoverage = NULL;
+
+  input_count = ccsf3->InputGlyphCount;
+
+  if ( ALLOC_ARRAY( ccsf3->InputCoverage, input_count, HB_Coverage ) )
+    goto Fail4;
+
+  i = ccsf3->InputCoverage;
+
+  for ( ni = 0; ni < input_count; ni++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail3;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Coverage( &i[ni], stream ) ) != HB_Err_Ok )
+      goto Fail3;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  ccsf3->LookaheadGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ccsf3->LookaheadCoverage = NULL;
+
+  lookahead_count = ccsf3->LookaheadGlyphCount;
+
+  if ( ALLOC_ARRAY( ccsf3->LookaheadCoverage, lookahead_count,
+		    HB_Coverage ) )
+    goto Fail3;
+
+  l = ccsf3->LookaheadCoverage;
+
+  for ( nl = 0; nl < lookahead_count; nl++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail2;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Coverage( &l[nl], stream ) ) != HB_Err_Ok )
+      goto Fail2;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  ccsf3->SubstCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ccsf3->SubstLookupRecord = NULL;
+
+  count = ccsf3->SubstCount;
+
+  if ( ALLOC_ARRAY( ccsf3->SubstLookupRecord, count,
+		    HB_SubstLookupRecord ) )
+    goto Fail2;
+
+  slr = ccsf3->SubstLookupRecord;
+
+  if ( ACCESS_Frame( count * 4L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+  {
+    slr[n].SequenceIndex   = GET_UShort();
+    slr[n].LookupListIndex = GET_UShort();
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( slr );
+
+Fail2:
+  for ( m = 0; m < nl; m++ )
+    _HB_OPEN_Free_Coverage( &l[m] );
+
+  FREE( l );
+
+Fail3:
+  for ( m = 0; m < ni; m++ )
+    _HB_OPEN_Free_Coverage( &i[m] );
+
+  FREE( i );
+
+Fail4:
+  for ( m = 0; m < nb; m++ )
+    _HB_OPEN_Free_Coverage( &b[m] );
+
+  FREE( b );
+  return error;
+}
+
+
+static void  Free_ChainContextSubst3( HB_ChainContextSubstFormat3*  ccsf3 )
+{
+  HB_UShort      n, count;
+
+  HB_Coverage*  c;
+
+
+  FREE( ccsf3->SubstLookupRecord );
+
+  if ( ccsf3->LookaheadCoverage )
+  {
+    count = ccsf3->LookaheadGlyphCount;
+    c     = ccsf3->LookaheadCoverage;
+
+    for ( n = 0; n < count; n++ )
+      _HB_OPEN_Free_Coverage( &c[n] );
+
+    FREE( c );
+  }
+
+  if ( ccsf3->InputCoverage )
+  {
+    count = ccsf3->InputGlyphCount;
+    c     = ccsf3->InputCoverage;
+
+    for ( n = 0; n < count; n++ )
+      _HB_OPEN_Free_Coverage( &c[n] );
+
+    FREE( c );
+  }
+
+  if ( ccsf3->BacktrackCoverage )
+  {
+    count = ccsf3->BacktrackGlyphCount;
+    c     = ccsf3->BacktrackCoverage;
+
+    for ( n = 0; n < count; n++ )
+      _HB_OPEN_Free_Coverage( &c[n] );
+
+    FREE( c );
+  }
+}
+
+
+/* ChainContextSubst */
+
+static HB_Error  Load_ChainContextSubst( HB_GSUB_SubTable* st,
+					 HB_Stream         stream )
+{
+  HB_Error error;
+  HB_ChainContextSubst*  ccs = &st->chain;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  ccs->SubstFormat = GET_UShort();
+
+  FORGET_Frame();
+
+  switch ( ccs->SubstFormat ) {
+    case 1:  return Load_ChainContextSubst1( &ccs->ccsf.ccsf1, stream );
+    case 2:  return Load_ChainContextSubst2( &ccs->ccsf.ccsf2, stream );
+    case 3:  return Load_ChainContextSubst3( &ccs->ccsf.ccsf3, stream );
+    default: return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;               /* never reached */
+}
+
+
+static void  Free_ChainContextSubst( HB_GSUB_SubTable* st )
+{
+  HB_ChainContextSubst*  ccs = &st->chain;
+
+  switch ( ccs->SubstFormat ) {
+    case 1:  Free_ChainContextSubst1( &ccs->ccsf.ccsf1 ); break;
+    case 2:  Free_ChainContextSubst2( &ccs->ccsf.ccsf2 ); break;
+    case 3:  Free_ChainContextSubst3( &ccs->ccsf.ccsf3 ); break;
+    default:							  break;
+  }
+}
+
+
+static HB_Error  Lookup_ChainContextSubst1( HB_GSUBHeader*               gsub,
+					    HB_ChainContextSubstFormat1* ccsf1,
+					    HB_Buffer                    buffer,
+					    HB_UShort                     flags,
+					    HB_UShort                     context_length,
+					    int                           nesting_level )
+{
+  HB_UShort          index, property;
+  HB_UShort          i, j, k, num_csr;
+  HB_UShort          bgc, igc, lgc;
+  HB_Error           error;
+
+  HB_ChainSubRule*  csr;
+  HB_ChainSubRule   curr_csr;
+  HB_GDEFHeader*    gdef;
+
+
+  gdef = gsub->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  error = _HB_OPEN_Coverage_Index( &ccsf1->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  csr     = ccsf1->ChainSubRuleSet[index].ChainSubRule;
+  num_csr = ccsf1->ChainSubRuleSet[index].ChainSubRuleCount;
+
+  for ( k = 0; k < num_csr; k++ )
+  {
+    curr_csr = csr[k];
+    bgc      = curr_csr.BacktrackGlyphCount;
+    igc      = curr_csr.InputGlyphCount;
+    lgc      = curr_csr.LookaheadGlyphCount;
+
+    if ( context_length != 0xFFFF && context_length < igc )
+      goto next_chainsubrule;
+
+    /* check whether context is too long; it is a first guess only */
+
+    if ( bgc > buffer->out_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+      goto next_chainsubrule;
+
+    if ( bgc )
+    {
+      /* since we don't know in advance the number of glyphs to inspect,
+	 we search backwards for matches in the backtrack glyph array    */
+
+      for ( i = 0, j = buffer->out_pos - 1; i < bgc; i++, j-- )
+      {
+	while ( CHECK_Property( gdef, OUT_ITEM( j ), flags, &property ) )
+	{
+	  if ( error && error != HB_Err_Not_Covered )
+	    return error;
+
+	  if ( j + 1 == bgc - i )
+	    goto next_chainsubrule;
+	  j--;
+	}
+
+	/* In OpenType 1.3, it is undefined whether the offsets of
+	   backtrack glyphs is in logical order or not.  Version 1.4
+	   will clarify this:
+
+	     Logical order -      a  b  c  d  e  f  g  h  i  j
+					      i
+	     Input offsets -                  0  1
+	     Backtrack offsets -  3  2  1  0
+	     Lookahead offsets -                    0  1  2  3           */
+
+	if ( OUT_GLYPH( j ) != curr_csr.Backtrack[i] )
+	  goto next_chainsubrule;
+      }
+    }
+
+    /* Start at 1 because [0] is implied */
+
+    for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  return error;
+
+	if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+	  goto next_chainsubrule;
+	j++;
+      }
+
+      if ( IN_GLYPH( j ) != curr_csr.Input[i - 1] )
+	  goto next_chainsubrule;
+    }
+
+    /* we are starting to check for lookahead glyphs right after the
+       last context glyph                                            */
+
+    for ( i = 0; i < lgc; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  return error;
+
+	if ( j + lgc - i == (HB_Int)buffer->in_length )
+	  goto next_chainsubrule;
+	j++;
+      }
+
+      if ( IN_GLYPH( j ) != curr_csr.Lookahead[i] )
+	goto next_chainsubrule;
+    }
+
+    return Do_ContextSubst( gsub, igc,
+			    curr_csr.SubstCount,
+			    curr_csr.SubstLookupRecord,
+			    buffer,
+			    nesting_level );
+
+  next_chainsubrule:
+    ;
+  }
+
+  return HB_Err_Not_Covered;
+}
+
+
+static HB_Error  Lookup_ChainContextSubst2( HB_GSUBHeader*               gsub,
+					    HB_ChainContextSubstFormat2* ccsf2,
+					    HB_Buffer                    buffer,
+					    HB_UShort                     flags,
+					    HB_UShort                     context_length,
+					    int                           nesting_level )
+{
+  HB_UShort              index, property;
+  HB_Error               error;
+  HB_UShort              i, j, k;
+  HB_UShort              bgc, igc, lgc;
+  HB_UShort              known_backtrack_classes,
+			 known_input_classes,
+			 known_lookahead_classes;
+
+  HB_UShort*             backtrack_classes;
+  HB_UShort*             input_classes;
+  HB_UShort*             lookahead_classes;
+
+  HB_UShort*             bc;
+  HB_UShort*             ic;
+  HB_UShort*             lc;
+
+  HB_ChainSubClassSet*  cscs;
+  HB_ChainSubClassRule  ccsr;
+  HB_GDEFHeader*        gdef;
+
+
+  gdef = gsub->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  /* Note: The coverage table in format 2 doesn't give an index into
+	   anything.  It just lets us know whether or not we need to
+	   do any lookup at all.                                     */
+
+  error = _HB_OPEN_Coverage_Index( &ccsf2->Coverage, IN_CURGLYPH(), &index );
+  if ( error )
+    return error;
+
+  if ( ALLOC_ARRAY( backtrack_classes, ccsf2->MaxBacktrackLength, HB_UShort ) )
+    return error;
+  known_backtrack_classes = 0;
+
+  if (ccsf2->MaxInputLength < 1)
+    return HB_Err_Not_Covered;
+
+  if ( ALLOC_ARRAY( input_classes, ccsf2->MaxInputLength, HB_UShort ) )
+    goto End3;
+  known_input_classes = 1;
+
+  if ( ALLOC_ARRAY( lookahead_classes, ccsf2->MaxLookaheadLength, HB_UShort ) )
+    goto End2;
+  known_lookahead_classes = 0;
+
+  error = _HB_OPEN_Get_Class( &ccsf2->InputClassDef, IN_CURGLYPH(),
+		     &input_classes[0], NULL );
+  if ( error && error != HB_Err_Not_Covered )
+    goto End1;
+
+  cscs = &ccsf2->ChainSubClassSet[input_classes[0]];
+  if ( !cscs )
+  {
+    error = ERR(HB_Err_Invalid_SubTable);
+    goto End1;
+  }
+
+  for ( k = 0; k < cscs->ChainSubClassRuleCount; k++ )
+  {
+    ccsr = cscs->ChainSubClassRule[k];
+    bgc  = ccsr.BacktrackGlyphCount;
+    igc  = ccsr.InputGlyphCount;
+    lgc  = ccsr.LookaheadGlyphCount;
+
+    if ( context_length != 0xFFFF && context_length < igc )
+      goto next_chainsubclassrule;
+
+    /* check whether context is too long; it is a first guess only */
+
+    if ( bgc > buffer->out_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+      goto next_chainsubclassrule;
+
+    if ( bgc )
+    {
+      /* Since we don't know in advance the number of glyphs to inspect,
+	 we search backwards for matches in the backtrack glyph array.
+	 Note that `known_backtrack_classes' starts at index 0.         */
+
+      bc       = ccsr.Backtrack;
+
+      for ( i = 0, j = buffer->out_pos - 1; i < bgc; i++, j-- )
+      {
+	while ( CHECK_Property( gdef, OUT_ITEM( j ), flags, &property ) )
+	{
+	  if ( error && error != HB_Err_Not_Covered )
+	    goto End1;
+
+	  if ( j + 1 == bgc - i )
+	    goto next_chainsubclassrule;
+	  j--;
+	}
+
+	if ( i >= known_backtrack_classes )
+	{
+	  /* Keeps us from having to do this for each rule */
+
+	  error = _HB_OPEN_Get_Class( &ccsf2->BacktrackClassDef, OUT_GLYPH( j ),
+			     &backtrack_classes[i], NULL );
+	  if ( error && error != HB_Err_Not_Covered )
+	    goto End1;
+	  known_backtrack_classes = i;
+	}
+
+	if ( bc[i] != backtrack_classes[i] )
+	  goto next_chainsubclassrule;
+      }
+    }
+
+    ic       = ccsr.Input;
+
+    /* Start at 1 because [0] is implied */
+
+    for ( i = 1, j = buffer->in_pos + 1; i < igc; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End1;
+
+	if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+	  goto next_chainsubclassrule;
+	j++;
+      }
+
+      if ( i >= known_input_classes )
+      {
+	error = _HB_OPEN_Get_Class( &ccsf2->InputClassDef, IN_GLYPH( j ),
+			   &input_classes[i], NULL );
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End1;
+	known_input_classes = i;
+      }
+
+      if ( ic[i - 1] != input_classes[i] )
+	goto next_chainsubclassrule;
+    }
+
+    /* we are starting to check for lookahead glyphs right after the
+       last context glyph                                            */
+
+    lc       = ccsr.Lookahead;
+
+    for ( i = 0; i < lgc; i++, j++ )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End1;
+
+	if ( j + lgc - i == (HB_Int)buffer->in_length )
+	  goto next_chainsubclassrule;
+	j++;
+      }
+
+      if ( i >= known_lookahead_classes )
+      {
+	error = _HB_OPEN_Get_Class( &ccsf2->LookaheadClassDef, IN_GLYPH( j ),
+			   &lookahead_classes[i], NULL );
+	if ( error && error != HB_Err_Not_Covered )
+	  goto End1;
+	known_lookahead_classes = i;
+      }
+
+      if ( lc[i] != lookahead_classes[i] )
+	goto next_chainsubclassrule;
+    }
+
+    error = Do_ContextSubst( gsub, igc,
+			     ccsr.SubstCount,
+			     ccsr.SubstLookupRecord,
+			     buffer,
+			     nesting_level );
+    goto End1;
+
+  next_chainsubclassrule:
+    ;
+  }
+
+  error = HB_Err_Not_Covered;
+
+End1:
+  FREE( lookahead_classes );
+
+End2:
+  FREE( input_classes );
+
+End3:
+  FREE( backtrack_classes );
+  return error;
+}
+
+
+static HB_Error  Lookup_ChainContextSubst3( HB_GSUBHeader*               gsub,
+					    HB_ChainContextSubstFormat3* ccsf3,
+					    HB_Buffer                    buffer,
+					    HB_UShort                     flags,
+					    HB_UShort                     context_length,
+					    int                           nesting_level )
+{
+  HB_UShort        index, i, j, property;
+  HB_UShort        bgc, igc, lgc;
+  HB_Error         error;
+
+  HB_Coverage*    bc;
+  HB_Coverage*    ic;
+  HB_Coverage*    lc;
+  HB_GDEFHeader*  gdef;
+
+
+  gdef = gsub->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  bgc = ccsf3->BacktrackGlyphCount;
+  igc = ccsf3->InputGlyphCount;
+  lgc = ccsf3->LookaheadGlyphCount;
+
+  if ( context_length != 0xFFFF && context_length < igc )
+    return HB_Err_Not_Covered;
+
+  /* check whether context is too long; it is a first guess only */
+
+  if ( bgc > buffer->out_pos || buffer->in_pos + igc + lgc > buffer->in_length )
+    return HB_Err_Not_Covered;
+
+  if ( bgc )
+  {
+    /* Since we don't know in advance the number of glyphs to inspect,
+       we search backwards for matches in the backtrack glyph array    */
+
+    bc       = ccsf3->BacktrackCoverage;
+
+    for ( i = 0, j = buffer->out_pos - 1; i < bgc; i++, j-- )
+    {
+      while ( CHECK_Property( gdef, OUT_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  return error;
+
+	if ( j + 1 == bgc - i )
+	  return HB_Err_Not_Covered;
+	j--;
+      }
+
+      error = _HB_OPEN_Coverage_Index( &bc[i], OUT_GLYPH( j ), &index );
+      if ( error )
+	return error;
+    }
+  }
+
+  ic       = ccsf3->InputCoverage;
+
+  for ( i = 0, j = buffer->in_pos; i < igc; i++, j++ )
+  {
+    /* We already called CHECK_Property for IN_GLYPH( buffer->in_pos ) */
+    while ( j > buffer->in_pos && CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+    {
+      if ( error && error != HB_Err_Not_Covered )
+	return error;
+
+      if ( j + igc - i + lgc == (HB_Int)buffer->in_length )
+	return HB_Err_Not_Covered;
+      j++;
+    }
+
+    error = _HB_OPEN_Coverage_Index( &ic[i], IN_GLYPH( j ), &index );
+    if ( error )
+      return error;
+  }
+
+  /* we are starting for lookahead glyphs right after the last context
+     glyph                                                             */
+
+  lc       = ccsf3->LookaheadCoverage;
+
+  for ( i = 0; i < lgc; i++, j++ )
+  {
+    while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+    {
+      if ( error && error != HB_Err_Not_Covered )
+	return error;
+
+      if ( j + lgc - i == (HB_Int)buffer->in_length )
+	return HB_Err_Not_Covered;
+      j++;
+    }
+
+    error = _HB_OPEN_Coverage_Index( &lc[i], IN_GLYPH( j ), &index );
+    if ( error )
+      return error;
+  }
+
+  return Do_ContextSubst( gsub, igc,
+			  ccsf3->SubstCount,
+			  ccsf3->SubstLookupRecord,
+			  buffer,
+			  nesting_level );
+}
+
+
+static HB_Error  Lookup_ChainContextSubst( HB_GSUBHeader*    gsub,
+					   HB_GSUB_SubTable* st,
+					   HB_Buffer         buffer,
+					   HB_UShort          flags,
+					   HB_UShort          context_length,
+					   int                nesting_level )
+{
+  HB_ChainContextSubst*  ccs = &st->chain;
+
+  switch ( ccs->SubstFormat ) {
+    case 1:  return Lookup_ChainContextSubst1( gsub, &ccs->ccsf.ccsf1, buffer, flags, context_length, nesting_level );
+    case 2:  return Lookup_ChainContextSubst2( gsub, &ccs->ccsf.ccsf2, buffer, flags, context_length, nesting_level );
+    case 3:  return Lookup_ChainContextSubst3( gsub, &ccs->ccsf.ccsf3, buffer, flags, context_length, nesting_level );
+    default: return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+}
+
+
+static HB_Error  Load_ReverseChainContextSubst( HB_GSUB_SubTable* st,
+					        HB_Stream         stream )
+{
+  HB_Error error;
+  HB_ReverseChainContextSubst*  rccs = &st->reverse;
+
+  HB_UShort               m, count;
+
+  HB_UShort               nb = 0, nl = 0, n;
+  HB_UShort               backtrack_count, lookahead_count;
+  HB_UInt                cur_offset, new_offset, base_offset;
+
+  HB_Coverage*           b;
+  HB_Coverage*           l;
+  HB_UShort*              sub;
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  rccs->SubstFormat = GET_UShort();
+
+  if ( rccs->SubstFormat != 1 )
+    return ERR(HB_Err_Invalid_SubTable_Format);
+
+  FORGET_Frame();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  cur_offset = FILE_Pos();
+  if ( FILE_Seek( new_offset ) ||
+       ( error = _HB_OPEN_Load_Coverage( &rccs->Coverage, stream ) ) != HB_Err_Ok )
+    return error;
+  (void)FILE_Seek( cur_offset );
+
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail4;
+
+  rccs->BacktrackGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  rccs->BacktrackCoverage = NULL;
+
+  backtrack_count = rccs->BacktrackGlyphCount;
+
+  if ( ALLOC_ARRAY( rccs->BacktrackCoverage, backtrack_count,
+		    HB_Coverage ) )
+    goto Fail4;
+
+  b = rccs->BacktrackCoverage;
+
+  for ( nb = 0; nb < backtrack_count; nb++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail3;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Coverage( &b[nb], stream ) ) != HB_Err_Ok )
+      goto Fail3;
+    (void)FILE_Seek( cur_offset );
+  }
+
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail3;
+
+  rccs->LookaheadGlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  rccs->LookaheadCoverage = NULL;
+
+  lookahead_count = rccs->LookaheadGlyphCount;
+
+  if ( ALLOC_ARRAY( rccs->LookaheadCoverage, lookahead_count,
+		    HB_Coverage ) )
+    goto Fail3;
+
+  l = rccs->LookaheadCoverage;
+
+  for ( nl = 0; nl < lookahead_count; nl++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail2;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = _HB_OPEN_Load_Coverage( &l[nl], stream ) ) != HB_Err_Ok )
+      goto Fail2;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  rccs->GlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  rccs->Substitute = NULL;
+
+  count = rccs->GlyphCount;
+
+  if ( ALLOC_ARRAY( rccs->Substitute, count,
+		    HB_UShort ) )
+    goto Fail2;
+
+  sub = rccs->Substitute;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail1;
+
+  for ( n = 0; n < count; n++ )
+    sub[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( sub );
+
+Fail2:
+  for ( m = 0; m < nl; m++ )
+    _HB_OPEN_Free_Coverage( &l[m] );
+
+  FREE( l );
+
+Fail3:
+  for ( m = 0; m < nb; m++ )
+    _HB_OPEN_Free_Coverage( &b[m] );
+
+  FREE( b );
+
+Fail4:
+  _HB_OPEN_Free_Coverage( &rccs->Coverage );
+  return error;
+}
+
+
+static void  Free_ReverseChainContextSubst( HB_GSUB_SubTable* st )
+{
+  HB_UShort      n, count;
+  HB_ReverseChainContextSubst*  rccs = &st->reverse;
+
+  HB_Coverage*  c;
+
+  _HB_OPEN_Free_Coverage( &rccs->Coverage );
+
+  if ( rccs->LookaheadCoverage )
+  {
+    count = rccs->LookaheadGlyphCount;
+    c     = rccs->LookaheadCoverage;
+
+    for ( n = 0; n < count; n++ )
+      _HB_OPEN_Free_Coverage( &c[n] );
+
+    FREE( c );
+  }
+
+  if ( rccs->BacktrackCoverage )
+  {
+    count = rccs->BacktrackGlyphCount;
+    c     = rccs->BacktrackCoverage;
+
+    for ( n = 0; n < count; n++ )
+      _HB_OPEN_Free_Coverage( &c[n] );
+
+    FREE( c );
+  }
+
+  FREE ( rccs->Substitute );
+}
+
+
+static HB_Error  Lookup_ReverseChainContextSubst( HB_GSUBHeader*    gsub,
+						  HB_GSUB_SubTable* st,
+						  HB_Buffer         buffer,
+						  HB_UShort          flags,
+						  HB_UShort         context_length,
+						  int               nesting_level )
+{
+  HB_UShort        index, input_index, i, j, property;
+  HB_UShort        bgc, lgc;
+  HB_Error         error;
+
+  HB_ReverseChainContextSubst*  rccs = &st->reverse;
+  HB_Coverage*    bc;
+  HB_Coverage*    lc;
+  HB_GDEFHeader*  gdef;
+
+  if ( nesting_level != 1 || context_length != 0xFFFF )
+    return HB_Err_Not_Covered;
+
+  gdef = gsub->gdef;
+
+  if ( CHECK_Property( gdef, IN_CURITEM(), flags, &property ) )
+    return error;
+
+  bgc = rccs->BacktrackGlyphCount;
+  lgc = rccs->LookaheadGlyphCount;
+
+  /* check whether context is too long; it is a first guess only */
+
+  if ( bgc > buffer->in_pos || buffer->in_pos + 1 + lgc > buffer->in_length )
+    return HB_Err_Not_Covered;
+
+  if ( bgc )
+  {
+    /* Since we don't know in advance the number of glyphs to inspect,
+       we search backwards for matches in the backtrack glyph array    */
+
+    bc       = rccs->BacktrackCoverage;
+
+    for ( i = 0, j = buffer->in_pos - 1; i < bgc; i++, j-- )
+    {
+      while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+      {
+	if ( error && error != HB_Err_Not_Covered )
+	  return error;
+
+	if ( j + 1 == bgc - i )
+	  return HB_Err_Not_Covered;
+	j--;
+      }
+
+      error = _HB_OPEN_Coverage_Index( &bc[i], IN_GLYPH( j ), &index );
+      if ( error )
+	return error;
+    }
+  }
+
+  j = buffer->in_pos;
+
+  error = _HB_OPEN_Coverage_Index( &rccs->Coverage, IN_GLYPH( j ), &input_index );
+  if ( error )
+      return error;
+
+  lc       = rccs->LookaheadCoverage;
+
+  for ( i = 0, j = buffer->in_pos + 1; i < lgc; i++, j++ )
+  {
+    while ( CHECK_Property( gdef, IN_ITEM( j ), flags, &property ) )
+    {
+      if ( error && error != HB_Err_Not_Covered )
+	return error;
+
+      if ( j + lgc - i == (HB_Int)buffer->in_length )
+	return HB_Err_Not_Covered;
+      j++;
+    }
+
+    error = _HB_OPEN_Coverage_Index( &lc[i], IN_GLYPH( j ), &index );
+    if ( error )
+      return error;
+  }
+
+  IN_CURGLYPH() = rccs->Substitute[input_index];
+  buffer->in_pos--; /* Reverse! */
+
+  return error;
+}
+
+
+
+/***********
+ * GSUB API
+ ***********/
+
+
+
+HB_Error  HB_GSUB_Select_Script( HB_GSUBHeader*  gsub,
+				 HB_UInt         script_tag,
+				 HB_UShort*       script_index )
+{
+  HB_UShort          n;
+
+  HB_ScriptList*    sl;
+  HB_ScriptRecord*  sr;
+
+
+  if ( !gsub || !script_index )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gsub->ScriptList;
+  sr = sl->ScriptRecord;
+
+  for ( n = 0; n < sl->ScriptCount; n++ )
+    if ( script_tag == sr[n].ScriptTag )
+    {
+      *script_index = n;
+
+      return HB_Err_Ok;
+    }
+
+  return HB_Err_Not_Covered;
+}
+
+
+
+HB_Error  HB_GSUB_Select_Language( HB_GSUBHeader*  gsub,
+				   HB_UInt         language_tag,
+				   HB_UShort        script_index,
+				   HB_UShort*       language_index,
+				   HB_UShort*       req_feature_index )
+{
+  HB_UShort           n;
+
+  HB_ScriptList*     sl;
+  HB_ScriptRecord*   sr;
+  HB_ScriptTable*    s;
+  HB_LangSysRecord*  lsr;
+
+
+  if ( !gsub || !language_index || !req_feature_index )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gsub->ScriptList;
+  sr = sl->ScriptRecord;
+
+  if ( script_index >= sl->ScriptCount )
+    return ERR(HB_Err_Invalid_Argument);
+
+  s   = &sr[script_index].Script;
+  lsr = s->LangSysRecord;
+
+  for ( n = 0; n < s->LangSysCount; n++ )
+    if ( language_tag == lsr[n].LangSysTag )
+    {
+      *language_index = n;
+      *req_feature_index = lsr[n].LangSys.ReqFeatureIndex;
+
+      return HB_Err_Ok;
+    }
+
+  return HB_Err_Not_Covered;
+}
+
+
+/* selecting 0xFFFF for language_index asks for the values of the
+   default language (DefaultLangSys)                              */
+
+
+HB_Error  HB_GSUB_Select_Feature( HB_GSUBHeader*  gsub,
+				  HB_UInt         feature_tag,
+				  HB_UShort        script_index,
+				  HB_UShort        language_index,
+				  HB_UShort*       feature_index )
+{
+  HB_UShort           n;
+
+  HB_ScriptList*     sl;
+  HB_ScriptRecord*   sr;
+  HB_ScriptTable*    s;
+  HB_LangSysRecord*  lsr;
+  HB_LangSys*        ls;
+  HB_UShort*          fi;
+
+  HB_FeatureList*    fl;
+  HB_FeatureRecord*  fr;
+
+
+  if ( !gsub || !feature_index )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gsub->ScriptList;
+  sr = sl->ScriptRecord;
+
+  fl = &gsub->FeatureList;
+  fr = fl->FeatureRecord;
+
+  if ( script_index >= sl->ScriptCount )
+    return ERR(HB_Err_Invalid_Argument);
+
+  s   = &sr[script_index].Script;
+  lsr = s->LangSysRecord;
+
+  if ( language_index == 0xFFFF )
+    ls = &s->DefaultLangSys;
+  else
+  {
+    if ( language_index >= s->LangSysCount )
+      return ERR(HB_Err_Invalid_Argument);
+
+    ls = &lsr[language_index].LangSys;
+  }
+
+  fi = ls->FeatureIndex;
+
+  for ( n = 0; n < ls->FeatureCount; n++ )
+  {
+    if ( fi[n] >= fl->FeatureCount )
+      return ERR(HB_Err_Invalid_SubTable_Format);
+
+    if ( feature_tag == fr[fi[n]].FeatureTag )
+    {
+      *feature_index = fi[n];
+
+      return HB_Err_Ok;
+    }
+  }
+
+  return HB_Err_Not_Covered;
+}
+
+
+/* The next three functions return a null-terminated list */
+
+
+HB_Error  HB_GSUB_Query_Scripts( HB_GSUBHeader*  gsub,
+				 HB_UInt**       script_tag_list )
+{
+  HB_UShort          n;
+  HB_Error           error;
+  HB_UInt*          stl;
+
+  HB_ScriptList*    sl;
+  HB_ScriptRecord*  sr;
+
+
+  if ( !gsub || !script_tag_list )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gsub->ScriptList;
+  sr = sl->ScriptRecord;
+
+  if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, HB_UInt ) )
+    return error;
+
+  for ( n = 0; n < sl->ScriptCount; n++ )
+    stl[n] = sr[n].ScriptTag;
+  stl[n] = 0;
+
+  *script_tag_list = stl;
+
+  return HB_Err_Ok;
+}
+
+
+
+HB_Error  HB_GSUB_Query_Languages( HB_GSUBHeader*  gsub,
+				   HB_UShort        script_index,
+				   HB_UInt**       language_tag_list )
+{
+  HB_UShort           n;
+  HB_Error            error;
+  HB_UInt*           ltl;
+
+  HB_ScriptList*     sl;
+  HB_ScriptRecord*   sr;
+  HB_ScriptTable*    s;
+  HB_LangSysRecord*  lsr;
+
+
+  if ( !gsub || !language_tag_list )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gsub->ScriptList;
+  sr = sl->ScriptRecord;
+
+  if ( script_index >= sl->ScriptCount )
+    return ERR(HB_Err_Invalid_Argument);
+
+  s   = &sr[script_index].Script;
+  lsr = s->LangSysRecord;
+
+  if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, HB_UInt ) )
+    return error;
+
+  for ( n = 0; n < s->LangSysCount; n++ )
+    ltl[n] = lsr[n].LangSysTag;
+  ltl[n] = 0;
+
+  *language_tag_list = ltl;
+
+  return HB_Err_Ok;
+}
+
+
+/* selecting 0xFFFF for language_index asks for the values of the
+   default language (DefaultLangSys)                              */
+
+
+HB_Error  HB_GSUB_Query_Features( HB_GSUBHeader*  gsub,
+				  HB_UShort        script_index,
+				  HB_UShort        language_index,
+				  HB_UInt**       feature_tag_list )
+{
+  HB_UShort           n;
+  HB_Error            error;
+  HB_UInt*           ftl;
+
+  HB_ScriptList*     sl;
+  HB_ScriptRecord*   sr;
+  HB_ScriptTable*    s;
+  HB_LangSysRecord*  lsr;
+  HB_LangSys*        ls;
+  HB_UShort*          fi;
+
+  HB_FeatureList*    fl;
+  HB_FeatureRecord*  fr;
+
+
+  if ( !gsub || !feature_tag_list )
+    return ERR(HB_Err_Invalid_Argument);
+
+  sl = &gsub->ScriptList;
+  sr = sl->ScriptRecord;
+
+  fl = &gsub->FeatureList;
+  fr = fl->FeatureRecord;
+
+  if ( script_index >= sl->ScriptCount )
+    return ERR(HB_Err_Invalid_Argument);
+
+  s   = &sr[script_index].Script;
+  lsr = s->LangSysRecord;
+
+  if ( language_index == 0xFFFF )
+    ls = &s->DefaultLangSys;
+  else
+  {
+    if ( language_index >= s->LangSysCount )
+      return ERR(HB_Err_Invalid_Argument);
+
+    ls = &lsr[language_index].LangSys;
+  }
+
+  fi = ls->FeatureIndex;
+
+  if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, HB_UInt ) )
+    return error;
+
+  for ( n = 0; n < ls->FeatureCount; n++ )
+  {
+    if ( fi[n] >= fl->FeatureCount )
+    {
+      FREE( ftl );
+      return ERR(HB_Err_Invalid_SubTable_Format);
+    }
+    ftl[n] = fr[fi[n]].FeatureTag;
+  }
+  ftl[n] = 0;
+
+  *feature_tag_list = ftl;
+
+  return HB_Err_Ok;
+}
+
+
+/* Do an individual subtable lookup.  Returns HB_Err_Ok if substitution
+   has been done, or HB_Err_Not_Covered if not.                        */
+static HB_Error  GSUB_Do_Glyph_Lookup( HB_GSUBHeader* gsub,
+				       HB_UShort       lookup_index,
+				       HB_Buffer      buffer,
+				       HB_UShort       context_length,
+				       int             nesting_level )
+{
+  HB_Error               error = HB_Err_Not_Covered;
+  HB_UShort              i, flags, lookup_count;
+  HB_Lookup*             lo;
+  int                    lookup_type;
+
+  nesting_level++;
+
+  if ( nesting_level > HB_MAX_NESTING_LEVEL )
+    return ERR(HB_Err_Not_Covered); /* ERR() call intended */
+
+  lookup_count = gsub->LookupList.LookupCount;
+  if (lookup_index >= lookup_count)
+    return error;
+
+  lo    = &gsub->LookupList.Lookup[lookup_index];
+  flags = lo->LookupFlag;
+  lookup_type = lo->LookupType;
+
+  for ( i = 0; i < lo->SubTableCount; i++ )
+  {
+    HB_GSUB_SubTable *st = &lo->SubTable[i].st.gsub;
+
+    switch (lookup_type) {
+      case HB_GSUB_LOOKUP_SINGLE:
+	error = Lookup_SingleSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GSUB_LOOKUP_MULTIPLE:
+	error = Lookup_MultipleSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GSUB_LOOKUP_ALTERNATE:
+	error = Lookup_AlternateSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GSUB_LOOKUP_LIGATURE:
+	error = Lookup_LigatureSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GSUB_LOOKUP_CONTEXT:
+	error = Lookup_ContextSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break;
+      case HB_GSUB_LOOKUP_CHAIN:
+	error = Lookup_ChainContextSubst	( gsub, st, buffer, flags, context_length, nesting_level ); break;
+    /*case HB_GSUB_LOOKUP_EXTENSION:
+	error = Lookup_ExtensionSubst		( gsub, st, buffer, flags, context_length, nesting_level ); break;*/
+      case HB_GSUB_LOOKUP_REVERSE_CHAIN:
+	error = Lookup_ReverseChainContextSubst	( gsub, st, buffer, flags, context_length, nesting_level ); break;
+      default:
+	error = HB_Err_Not_Covered;
+    };
+
+    /* Check whether we have a successful substitution or an error other
+       than HB_Err_Not_Covered                                          */
+    if ( error != HB_Err_Not_Covered )
+      return error;
+  }
+
+  return HB_Err_Not_Covered;
+}
+
+
+HB_INTERNAL HB_Error
+_HB_GSUB_Load_SubTable( HB_GSUB_SubTable* st,
+			HB_Stream         stream,
+			HB_UShort         lookup_type )
+{
+  switch (lookup_type) {
+    case HB_GSUB_LOOKUP_SINGLE:		return Load_SingleSubst			( st, stream );
+    case HB_GSUB_LOOKUP_MULTIPLE:	return Load_MultipleSubst		( st, stream );
+    case HB_GSUB_LOOKUP_ALTERNATE:	return Load_AlternateSubst		( st, stream );
+    case HB_GSUB_LOOKUP_LIGATURE:	return Load_LigatureSubst		( st, stream );
+    case HB_GSUB_LOOKUP_CONTEXT:	return Load_ContextSubst		( st, stream );
+    case HB_GSUB_LOOKUP_CHAIN:		return Load_ChainContextSubst		( st, stream );
+  /*case HB_GSUB_LOOKUP_EXTENSION:	return Load_ExtensionSubst		( st, stream );*/
+    case HB_GSUB_LOOKUP_REVERSE_CHAIN:	return Load_ReverseChainContextSubst	( st, stream );
+    default:				return ERR(HB_Err_Invalid_SubTable_Format);
+  };
+}
+
+
+HB_INTERNAL void
+_HB_GSUB_Free_SubTable( HB_GSUB_SubTable* st,
+			HB_UShort         lookup_type )
+{
+  switch ( lookup_type ) {
+    case HB_GSUB_LOOKUP_SINGLE:		Free_SingleSubst		( st ); return;
+    case HB_GSUB_LOOKUP_MULTIPLE:	Free_MultipleSubst		( st ); return;
+    case HB_GSUB_LOOKUP_ALTERNATE:	Free_AlternateSubst		( st ); return;
+    case HB_GSUB_LOOKUP_LIGATURE:	Free_LigatureSubst		( st ); return;
+    case HB_GSUB_LOOKUP_CONTEXT:	Free_ContextSubst		( st ); return;
+    case HB_GSUB_LOOKUP_CHAIN:		Free_ChainContextSubst		( st ); return;
+  /*case HB_GSUB_LOOKUP_EXTENSION:	Free_ExtensionSubst		( st ); return;*/
+    case HB_GSUB_LOOKUP_REVERSE_CHAIN:	Free_ReverseChainContextSubst	( st ); return;
+    default:									return;
+  };
+}
+
+
+
+/* apply one lookup to the input string object */
+
+static HB_Error  GSUB_Do_String_Lookup( HB_GSUBHeader*   gsub,
+				   HB_UShort         lookup_index,
+				   HB_Buffer        buffer )
+{
+  HB_Error  error, retError = HB_Err_Not_Covered;
+
+  HB_UInt*  properties = gsub->LookupList.Properties;
+  int       lookup_type = gsub->LookupList.Lookup[lookup_index].LookupType;
+
+  const int       nesting_level = 0;
+  /* 0xFFFF indicates that we don't have a context length yet */
+  const HB_UShort context_length = 0xFFFF;
+
+  switch (lookup_type) {
+
+    case HB_GSUB_LOOKUP_SINGLE:
+    case HB_GSUB_LOOKUP_MULTIPLE:
+    case HB_GSUB_LOOKUP_ALTERNATE:
+    case HB_GSUB_LOOKUP_LIGATURE:
+    case HB_GSUB_LOOKUP_CONTEXT:
+    case HB_GSUB_LOOKUP_CHAIN:
+      /* in/out forward substitution (implemented lazy) */
+
+      _hb_buffer_clear_output ( buffer );
+      buffer->in_pos = 0;
+  while ( buffer->in_pos < buffer->in_length )
+  {
+    if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
+    {
+	  error = GSUB_Do_Glyph_Lookup( gsub, lookup_index, buffer, context_length, nesting_level );
+      if ( error )
+      {
+	if ( error != HB_Err_Not_Covered )
+	  return error;
+      }
+      else
+	retError = error;
+    }
+    else
+      error = HB_Err_Not_Covered;
+
+    if ( error == HB_Err_Not_Covered )
+	  if ( COPY_Glyph ( buffer ) )
+	return error;
+  }
+      /* we shouldn't swap if error occurred.
+       *
+       * also don't swap if nothing changed (ie HB_Err_Not_Covered).
+       * shouldn't matter in that case though.
+       */
+      if ( retError == HB_Err_Ok )
+	_hb_buffer_swap( buffer );
+
+  return retError;
+
+    case HB_GSUB_LOOKUP_REVERSE_CHAIN:
+      /* in-place backward substitution */
+
+      buffer->in_pos = buffer->in_length - 1;
+    do
+    {
+      if ( ~IN_PROPERTIES( buffer->in_pos ) & properties[lookup_index] )
+	{
+	  error = GSUB_Do_Glyph_Lookup( gsub, lookup_index, buffer, context_length, nesting_level );
+	  if ( error )
+	    {
+	      if ( error != HB_Err_Not_Covered )
+		return error;
+	    }
+	  else
+	    retError = error;
+	}
+	else
+	  error = HB_Err_Not_Covered;
+
+	if ( error == HB_Err_Not_Covered )
+	  buffer->in_pos--;
+      }
+      while ((HB_Int) buffer->in_pos >= 0);
+
+      return retError;
+
+  /*case HB_GSUB_LOOKUP_EXTENSION:*/
+    default:
+  return retError;
+  };
+}
+
+
+HB_Error  HB_GSUB_Add_Feature( HB_GSUBHeader*  gsub,
+			       HB_UShort        feature_index,
+			       HB_UInt          property )
+{
+  HB_UShort    i;
+
+  HB_Feature  feature;
+  HB_UInt*     properties;
+  HB_UShort*   index;
+  HB_UShort    lookup_count;
+
+  /* Each feature can only be added once */
+
+  if ( !gsub ||
+       feature_index >= gsub->FeatureList.FeatureCount ||
+       gsub->FeatureList.ApplyCount == gsub->FeatureList.FeatureCount )
+    return ERR(HB_Err_Invalid_Argument);
+
+  gsub->FeatureList.ApplyOrder[gsub->FeatureList.ApplyCount++] = feature_index;
+
+  properties = gsub->LookupList.Properties;
+
+  feature = gsub->FeatureList.FeatureRecord[feature_index].Feature;
+  index   = feature.LookupListIndex;
+  lookup_count = gsub->LookupList.LookupCount;
+
+  for ( i = 0; i < feature.LookupListCount; i++ )
+  {
+    HB_UShort lookup_index = index[i];
+    if (lookup_index < lookup_count)
+      properties[lookup_index] |= property;
+  }
+
+  return HB_Err_Ok;
+}
+
+
+
+HB_Error  HB_GSUB_Clear_Features( HB_GSUBHeader*  gsub )
+{
+  HB_UShort i;
+
+  HB_UInt*  properties;
+
+
+  if ( !gsub )
+    return ERR(HB_Err_Invalid_Argument);
+
+  gsub->FeatureList.ApplyCount = 0;
+
+  properties = gsub->LookupList.Properties;
+
+  for ( i = 0; i < gsub->LookupList.LookupCount; i++ )
+    properties[i] = 0;
+
+  return HB_Err_Ok;
+}
+
+
+
+HB_Error  HB_GSUB_Register_Alternate_Function( HB_GSUBHeader*  gsub,
+					       HB_AltFunction  altfunc,
+					       void*            data )
+{
+  if ( !gsub )
+    return ERR(HB_Err_Invalid_Argument);
+
+  gsub->altfunc = altfunc;
+  gsub->data    = data;
+
+  return HB_Err_Ok;
+}
+
+/* returns error if one happened, otherwise returns HB_Err_Not_Covered if no
+ * feature were applied, or HB_Err_Ok otherwise.
+ */
+HB_Error  HB_GSUB_Apply_String( HB_GSUBHeader*   gsub,
+				HB_Buffer        buffer )
+{
+  HB_Error          error, retError = HB_Err_Not_Covered;
+  int               i, j, lookup_count, num_features;
+
+  if ( !gsub ||
+       !buffer)
+    return ERR(HB_Err_Invalid_Argument);
+
+  if ( buffer->in_length == 0 )
+    return retError;
+
+  lookup_count = gsub->LookupList.LookupCount;
+  num_features = gsub->FeatureList.ApplyCount;
+
+  for ( i = 0; i < num_features; i++)
+  {
+    HB_UShort  feature_index = gsub->FeatureList.ApplyOrder[i];
+    HB_Feature feature = gsub->FeatureList.FeatureRecord[feature_index].Feature;
+
+    for ( j = 0; j < feature.LookupListCount; j++ )
+    {
+      HB_UShort         lookup_index = feature.LookupListIndex[j];
+
+      /* Skip nonexistant lookups */
+      if (lookup_index >= lookup_count)
+       continue;
+
+	error = GSUB_Do_String_Lookup( gsub, lookup_index, buffer );
+      if ( error )
+      {
+	if ( error != HB_Err_Not_Covered )
+	  return error;
+      }
+      else
+	retError = error;
+    }
+  }
+
+  error = retError;
+
+  return error;
+}
+
+
+/* END */
diff --git a/third_party/harfbuzz/src/harfbuzz-gsub.h b/third_party/harfbuzz/src/harfbuzz-gsub.h
new file mode 100644
index 0000000..1ca3f0c
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-gsub.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_GSUB_H
+#define HARFBUZZ_GSUB_H
+
+#include "harfbuzz-gdef.h"
+#include "harfbuzz-buffer.h"
+
+HB_BEGIN_HEADER
+
+
+/* Lookup types for glyph substitution */
+
+#define HB_GSUB_LOOKUP_SINGLE        1
+#define HB_GSUB_LOOKUP_MULTIPLE      2
+#define HB_GSUB_LOOKUP_ALTERNATE     3
+#define HB_GSUB_LOOKUP_LIGATURE      4
+#define HB_GSUB_LOOKUP_CONTEXT       5
+#define HB_GSUB_LOOKUP_CHAIN         6
+#define HB_GSUB_LOOKUP_EXTENSION     7
+#define HB_GSUB_LOOKUP_REVERSE_CHAIN 8
+
+
+/* A pointer to a function which selects the alternate glyph.  `pos' is
+   the position of the glyph with index `glyphID', `num_alternates'
+   gives the number of alternates in the `alternates' array.  `data'
+   points to the user-defined structure specified during a call to
+   HB_GSUB_Register_Alternate_Function().  The function must return an
+   index into the `alternates' array.                                   */
+
+typedef HB_UShort  (*HB_AltFunction)(HB_UInt    pos,
+				      HB_UShort   glyphID,
+				      HB_UShort   num_alternates,
+				      HB_UShort*  alternates,
+				      void*       data );
+
+
+struct  HB_GSUBHeader_
+{
+  HB_UInt         offset;
+
+  HB_16Dot16         Version;
+
+  HB_ScriptList   ScriptList;
+  HB_FeatureList  FeatureList;
+  HB_LookupList   LookupList;
+
+  HB_GDEFHeader*  gdef;
+
+  /* the next two fields are used for an alternate substitution callback
+     function to select the proper alternate glyph.                      */
+
+  HB_AltFunction  altfunc;
+  void*            data;
+};
+
+typedef struct HB_GSUBHeader_   HB_GSUBHeader;
+typedef HB_GSUBHeader*  HB_GSUB;
+
+
+HB_Error  HB_Load_GSUB_Table( HB_Stream       stream,
+			      HB_GSUBHeader** gsub,
+			      HB_GDEFHeader*  gdef,
+                              HB_Stream       gdefStream );
+
+
+HB_Error  HB_Done_GSUB_Table( HB_GSUBHeader*  gsub );
+
+
+HB_Error  HB_GSUB_Select_Script( HB_GSUBHeader*  gsub,
+				 HB_UInt         script_tag,
+				 HB_UShort*       script_index );
+
+HB_Error  HB_GSUB_Select_Language( HB_GSUBHeader*  gsub,
+				   HB_UInt         language_tag,
+				   HB_UShort        script_index,
+				   HB_UShort*       language_index,
+				   HB_UShort*       req_feature_index );
+
+HB_Error  HB_GSUB_Select_Feature( HB_GSUBHeader*  gsub,
+				  HB_UInt         feature_tag,
+				  HB_UShort        script_index,
+				  HB_UShort        language_index,
+				  HB_UShort*       feature_index );
+
+
+HB_Error  HB_GSUB_Query_Scripts( HB_GSUBHeader*  gsub,
+				 HB_UInt**       script_tag_list );
+
+HB_Error  HB_GSUB_Query_Languages( HB_GSUBHeader*  gsub,
+				   HB_UShort        script_index,
+				   HB_UInt**       language_tag_list );
+
+HB_Error  HB_GSUB_Query_Features( HB_GSUBHeader*  gsub,
+				  HB_UShort        script_index,
+				  HB_UShort        language_index,
+				  HB_UInt**       feature_tag_list );
+
+
+HB_Error  HB_GSUB_Add_Feature( HB_GSUBHeader*  gsub,
+			       HB_UShort        feature_index,
+			       HB_UInt          property );
+
+HB_Error  HB_GSUB_Clear_Features( HB_GSUBHeader*  gsub );
+
+
+HB_Error  HB_GSUB_Register_Alternate_Function( HB_GSUBHeader*  gsub,
+					       HB_AltFunction  altfunc,
+					       void*            data );
+
+
+HB_Error  HB_GSUB_Apply_String( HB_GSUBHeader*   gsub,
+				HB_Buffer        buffer );
+
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_GSUB_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-hangul.c b/third_party/harfbuzz/src/harfbuzz-hangul.c
new file mode 100644
index 0000000..a819dac
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-hangul.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-shaper-private.h"
+
+#include <assert.h>
+
+/*
+// Hangul is a syllable based script. Unicode reserves a large range
+// for precomposed hangul, where syllables are already precomposed to
+// their final glyph shape. In addition, a so called jamo range is
+// defined, that can be used to express old Hangul. Modern hangul
+// syllables can also be expressed as jamo, and should be composed
+// into syllables. The operation is rather simple and mathematical.
+
+// Every hangul jamo is classified as being either a Leading consonant
+// (L), and intermediat Vowel (V) or a trailing consonant (T). Modern
+// hangul syllables (the ones in the precomposed area can be of type
+// LV or LVT.
+//
+// Syllable breaks do _not_ occur between:
+//
+// L              L, V or precomposed
+// V, LV          V, T
+// LVT, T         T
+//
+// A standard syllable is of the form L+V+T*. The above rules allow
+// nonstandard syllables L*V*T*. To transform them into standard
+// syllables fill characters L_f and V_f can be inserted.
+*/
+
+enum {
+    Hangul_SBase = 0xac00,
+    Hangul_LBase = 0x1100,
+    Hangul_VBase = 0x1161,
+    Hangul_TBase = 0x11a7,
+    Hangul_SCount = 11172,
+    Hangul_LCount = 19,
+    Hangul_VCount = 21,
+    Hangul_TCount = 28,
+    Hangul_NCount = 21*28
+};
+
+#define hangul_isPrecomposed(uc) \
+    (uc >= Hangul_SBase && uc < Hangul_SBase + Hangul_SCount)
+
+#define hangul_isLV(uc) \
+    ((uc - Hangul_SBase) % Hangul_TCount == 0)
+
+typedef enum {
+    L,
+    V,
+    T,
+    LV,
+    LVT,
+    X
+} HangulType;
+
+static HangulType hangul_type(unsigned short uc) {
+    if (uc > Hangul_SBase && uc < Hangul_SBase + Hangul_SCount)
+        return hangul_isLV(uc) ? LV : LVT;
+    if (uc < Hangul_LBase || uc > 0x11ff)
+        return X;
+    if (uc < Hangul_VBase)
+        return L;
+    if (uc < Hangul_TBase)
+        return V;
+    return T;
+}
+
+static int hangul_nextSyllableBoundary(const HB_UChar16 *s, int start, int end)
+{
+    const HB_UChar16 *uc = s + start;
+
+    HangulType state = hangul_type(*uc);
+    int pos = 1;
+
+    while (pos < end - start) {
+        HangulType newState = hangul_type(uc[pos]);
+        switch(newState) {
+        case X:
+            goto finish;
+        case L:
+        case V:
+        case T:
+            if (state > newState)
+                goto finish;
+            state = newState;
+            break;
+        case LV:
+            if (state > L)
+                goto finish;
+            state = V;
+            break;
+        case LVT:
+            if (state > L)
+                goto finish;
+            state = T;
+        }
+        ++pos;
+    }
+
+ finish:
+    return start+pos;
+}
+
+#ifndef NO_OPENTYPE
+static const HB_OpenTypeFeature hangul_features [] = {
+    { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
+    { HB_MAKE_TAG('l', 'j', 'm', 'o'), CcmpProperty },
+    { HB_MAKE_TAG('j', 'j', 'm', 'o'), CcmpProperty },
+    { HB_MAKE_TAG('t', 'j', 'm', 'o'), CcmpProperty },
+    { 0, 0 }
+};
+#endif
+
+static HB_Bool hangul_shape_syllable(HB_ShaperItem *item, HB_Bool openType)
+{
+    const HB_UChar16 *ch = item->string + item->item.pos;
+    int len = item->item.length;
+#ifndef NO_OPENTYPE
+    const int availableGlyphs = item->num_glyphs;
+#endif
+
+    int i;
+    HB_UChar16 composed = 0;
+    /* see if we can compose the syllable into a modern hangul */
+    if (item->item.length == 2) {
+        int LIndex = ch[0] - Hangul_LBase;
+        int VIndex = ch[1] - Hangul_VBase;
+        if (LIndex >= 0 && LIndex < Hangul_LCount &&
+            VIndex >= 0 && VIndex < Hangul_VCount)
+            composed = (LIndex * Hangul_VCount + VIndex) * Hangul_TCount + Hangul_SBase;
+    } else if (item->item.length == 3) {
+        int LIndex = ch[0] - Hangul_LBase;
+        int VIndex = ch[1] - Hangul_VBase;
+        int TIndex = ch[2] - Hangul_TBase;
+        if (LIndex >= 0 && LIndex < Hangul_LCount &&
+            VIndex >= 0 && VIndex < Hangul_VCount &&
+            TIndex >= 0 && TIndex < Hangul_TCount)
+            composed = (LIndex * Hangul_VCount + VIndex) * Hangul_TCount + TIndex + Hangul_SBase;
+    }
+
+
+
+    /* if we have a modern hangul use the composed form */
+    if (composed) {
+        ch = &composed;
+        len = 1;
+    }
+
+    if (!item->font->klass->convertStringToGlyphIndices(item->font,
+                                                        ch, len,
+                                                        item->glyphs, &item->num_glyphs,
+                                                        item->item.bidiLevel % 2))
+        return FALSE;
+    for (i = 0; i < len; i++) {
+        item->attributes[i].mark = FALSE;
+        item->attributes[i].clusterStart = FALSE;
+        item->attributes[i].justification = 0;
+        item->attributes[i].zeroWidth = FALSE;
+        /*IDEBUG("    %d: %4x", i, ch[i].unicode()); */
+    }
+
+#ifndef NO_OPENTYPE
+    if (!composed && openType) {
+        HB_Bool positioned;
+
+        HB_STACKARRAY(unsigned short, logClusters, len);
+        for (i = 0; i < len; ++i)
+            logClusters[i] = i;
+        item->log_clusters = logClusters;
+
+        HB_OpenTypeShape(item, /*properties*/0);
+
+        positioned = HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE);
+
+        HB_FREE_STACKARRAY(logClusters);
+
+        if (!positioned)
+            return FALSE;
+    } else {
+        HB_HeuristicPosition(item);
+    }
+#endif
+
+    item->attributes[0].clusterStart = TRUE;
+    return TRUE;
+}
+
+HB_Bool HB_HangulShape(HB_ShaperItem *item)
+{
+    const HB_UChar16 *uc = item->string + item->item.pos;
+    HB_Bool allPrecomposed = TRUE;
+    int i;
+
+    assert(item->item.script == HB_Script_Hangul);
+
+    for (i = 0; i < (int)item->item.length; ++i) {
+        if (!hangul_isPrecomposed(uc[i])) {
+            allPrecomposed = FALSE;
+            break;
+        }
+    }
+
+    if (!allPrecomposed) {
+        HB_Bool openType = FALSE;
+        unsigned short *logClusters = item->log_clusters;
+        HB_ShaperItem syllable;
+        int first_glyph = 0;
+        int sstart = item->item.pos;
+        int end = sstart + item->item.length;
+
+#ifndef NO_OPENTYPE
+        openType = HB_SelectScript(item, hangul_features);
+#endif
+        syllable = *item;
+
+        while (sstart < end) {
+            int send = hangul_nextSyllableBoundary(item->string, sstart, end);
+
+            syllable.item.pos = sstart;
+            syllable.item.length = send-sstart;
+            syllable.glyphs = item->glyphs + first_glyph;
+            syllable.attributes = item->attributes + first_glyph;
+            syllable.offsets = item->offsets + first_glyph;
+            syllable.advances = item->advances + first_glyph;
+            syllable.num_glyphs = item->num_glyphs - first_glyph;
+            if (!hangul_shape_syllable(&syllable, openType)) {
+                item->num_glyphs += syllable.num_glyphs;
+                return FALSE;
+            }
+            /* fix logcluster array */
+            for (i = sstart; i < send; ++i)
+                logClusters[i-item->item.pos] = first_glyph;
+            sstart = send;
+            first_glyph += syllable.num_glyphs;
+        }
+        item->num_glyphs = first_glyph;
+        return TRUE;
+    }
+
+    return HB_BasicShape(item);
+}
+
+
diff --git a/third_party/harfbuzz/src/harfbuzz-hebrew.c b/third_party/harfbuzz/src/harfbuzz-hebrew.c
new file mode 100644
index 0000000..d2664de
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-hebrew.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-shaper-private.h"
+#include <assert.h>
+
+/*
+// Uniscribe also defines dlig for Hebrew, but we leave this out for now, as it's mostly
+// ligatures one does not want in modern Hebrew (as lam-alef ligatures).
+*/
+#ifndef NO_OPENTYPE
+static const HB_OpenTypeFeature hebrew_features[] = {
+    { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
+    {0, 0}
+};
+#endif
+
+/* Hebrew shaping. In the non opentype case we try to use the
+   presentation forms specified for Hebrew. Especially for the
+   ligatures with Dagesh this gives much better results than we could
+   achieve manually.
+*/
+HB_Bool HB_HebrewShape(HB_ShaperItem *shaper_item)
+{
+    enum {
+        Dagesh = 0x5bc,
+        ShinDot = 0x5c1,
+        SinDot = 0x5c2,
+        Patah = 0x5b7,
+        Qamats = 0x5b8,
+        Holam = 0x5b9,
+        Rafe = 0x5bf
+    };
+
+    assert(shaper_item->item.script == HB_Script_Hebrew);
+
+    HB_HeuristicSetGlyphAttributes(shaper_item);
+
+#ifndef NO_OPENTYPE
+    if (HB_SelectScript(shaper_item, hebrew_features)) {
+
+        const int availableGlyphs = shaper_item->num_glyphs;
+        if (!HB_ConvertStringToGlyphIndices(shaper_item))
+            return FALSE;
+
+
+        HB_OpenTypeShape(shaper_item, /*properties*/0);
+        return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/TRUE);
+    }
+#endif
+
+    {
+        const HB_UChar16 *uc = shaper_item->string + shaper_item->item.pos;
+        unsigned short *logClusters = shaper_item->log_clusters;
+        HB_GlyphAttributes *attributes = shaper_item->attributes;
+
+        HB_Bool haveGlyphs;
+        int slen = 1;
+        int cluster_start = 0;
+        hb_uint32 i;
+
+        HB_STACKARRAY(HB_UChar16, shapedChars, 2 * shaper_item->item.length);
+        *shapedChars = *uc;
+        logClusters[0] = 0;
+
+        for (i = 1; i < shaper_item->item.length; ++i) {
+            hb_uint16 base = shapedChars[cluster_start];
+            hb_uint16 shaped = 0;
+            HB_Bool invalid = FALSE;
+            if (uc[i] == Dagesh) {
+                if (base >= 0x5d0
+                    && base <= 0x5ea
+                    && base != 0x5d7
+                    && base != 0x5dd
+                    && base != 0x5df
+                    && base != 0x5e2
+                    && base != 0x5e5) {
+                    shaped = base - 0x5d0 + 0xfb30;
+                } else if (base == 0xfb2a || base == 0xfb2b /* Shin with Shin or Sin dot */) {
+                    shaped = base + 2;
+                } else {
+                    invalid = TRUE;
+                }
+            } else if (uc[i] == ShinDot) {
+                if (base == 0x05e9)
+                    shaped = 0xfb2a;
+                else if (base == 0xfb49)
+                    shaped = 0xfb2c;
+                else
+                    invalid = TRUE;
+            } else if (uc[i] == SinDot) {
+                if (base == 0x05e9)
+                    shaped = 0xfb2b;
+                else if (base == 0xfb49)
+                    shaped = 0xfb2d;
+                else
+                    invalid = TRUE;
+            } else if (uc[i] == Patah) {
+                if (base == 0x5d0)
+                    shaped = 0xfb2e;
+            } else if (uc[i] == Qamats) {
+                if (base == 0x5d0)
+                    shaped = 0xfb2f;
+            } else if (uc[i] == Holam) {
+                if (base == 0x5d5)
+                    shaped = 0xfb4b;
+            } else if (uc[i] == Rafe) {
+                if (base == 0x5d1)
+                    shaped = 0xfb4c;
+                else if (base == 0x5db)
+                    shaped = 0xfb4d;
+                else if (base == 0x5e4)
+                    shaped = 0xfb4e;
+            }
+
+            if (invalid) {
+                shapedChars[slen] = 0x25cc;
+                attributes[slen].clusterStart = TRUE;
+                attributes[slen].mark = FALSE;
+                attributes[slen].combiningClass = 0;
+                cluster_start = slen;
+                ++slen;
+            }
+            if (shaped) {
+                if (shaper_item->font->klass->canRender(shaper_item->font, (HB_UChar16 *)&shaped, 1)) {
+                    shapedChars[cluster_start] = shaped;
+                } else
+                    shaped = 0;
+            }
+            if (!shaped) {
+                HB_CharCategory category;
+                int cmb;
+                shapedChars[slen] = uc[i];
+                HB_GetUnicodeCharProperties(uc[i], &category, &cmb);
+                if (category != HB_Mark_NonSpacing) {
+                    attributes[slen].clusterStart = TRUE;
+                    attributes[slen].mark = FALSE;
+                    attributes[slen].combiningClass = 0;
+                    attributes[slen].dontPrint = HB_IsControlChar(uc[i]);
+                    cluster_start = slen;
+                } else {
+                    attributes[slen].clusterStart = FALSE;
+                    attributes[slen].mark = TRUE;
+                    attributes[slen].combiningClass = cmb;
+                }
+                ++slen;
+            }
+            logClusters[i] = cluster_start;
+        }
+
+        haveGlyphs = shaper_item->font->klass
+            ->convertStringToGlyphIndices(shaper_item->font,
+                                          shapedChars, slen,
+                                          shaper_item->glyphs, &shaper_item->num_glyphs,
+                                          shaper_item->item.bidiLevel % 2);
+
+        HB_FREE_STACKARRAY(shapedChars);
+
+        if (!haveGlyphs)
+            return FALSE;
+
+        HB_HeuristicPosition(shaper_item);
+    }
+
+    return TRUE;
+}
+
diff --git a/third_party/harfbuzz/src/harfbuzz-impl.c b/third_party/harfbuzz/src/harfbuzz-impl.c
new file mode 100644
index 0000000..ddbf36b
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-impl.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2007  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+
+
+HB_INTERNAL HB_Pointer
+_hb_alloc(size_t     size,
+	  HB_Error  *perror )
+{
+  HB_Error    error = (HB_Error)0;
+  HB_Pointer  block = NULL;
+
+  if ( size > 0 )
+  {
+    block = calloc( 1, size );
+    if ( !block )
+      error = ERR(HB_Err_Out_Of_Memory);
+  }
+
+  *perror = error;
+  return block;
+}
+
+
+HB_INTERNAL HB_Pointer
+_hb_realloc(HB_Pointer  block,
+	    size_t      new_size,
+	    HB_Error   *perror )
+{
+    HB_Pointer  block2 = NULL;
+    HB_Error    error  = (HB_Error)0;
+
+    block2 = realloc( block, new_size );
+    if ( block2 == NULL && new_size != 0 )
+        error = ERR(HB_Err_Out_Of_Memory);
+
+    if ( !error )
+        block = block2;
+
+    *perror = error;
+    return block;
+}
+
+
+HB_INTERNAL void
+_hb_free( HB_Pointer  block )
+{
+  if ( block )
+    free( block );
+}
+
+
+/* helper func to set a breakpoint on */
+HB_INTERNAL HB_Error
+_hb_err (HB_Error code)
+{
+  return code;
+}
diff --git a/third_party/harfbuzz/src/harfbuzz-impl.h b/third_party/harfbuzz/src/harfbuzz-impl.h
new file mode 100644
index 0000000..5f43049
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-impl.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_IMPL_H
+#define HARFBUZZ_IMPL_H
+
+#include "harfbuzz-global.h"
+
+#include <stdlib.h>
+
+HB_BEGIN_HEADER
+
+#ifndef HB_INTERNAL
+# define HB_INTERNAL
+#endif
+
+#ifndef NULL
+# define NULL ((void *)0)
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+#ifndef TTAG_GDEF
+# define TTAG_GDEF  HB_MAKE_TAG( 'G', 'D', 'E', 'F' )
+#endif
+#ifndef TTAG_GPOS
+# define TTAG_GPOS  HB_MAKE_TAG( 'G', 'P', 'O', 'S' )
+#endif
+#ifndef TTAG_GSUB
+# define TTAG_GSUB  HB_MAKE_TAG( 'G', 'S', 'U', 'B' )
+#endif
+
+#ifndef HB_UNUSED
+# define HB_UNUSED(arg) ((arg) = (arg))
+#endif
+
+#define HB_LIKELY(cond) (cond)
+#define HB_UNLIKELY(cond) (cond)
+
+#define ARRAY_LEN(Array) ((int)(sizeof (Array) / sizeof (Array)[0]))
+
+
+
+#define HB_IsHighSurrogate(ucs) \
+    (((ucs) & 0xfc00) == 0xd800)
+
+#define HB_IsLowSurrogate(ucs) \
+    (((ucs) & 0xfc00) == 0xdc00)
+
+#define HB_SurrogateToUcs4(high, low) \
+    (((HB_UChar32)(high))<<10) + (low) - 0x35fdc00;
+
+
+
+
+
+#define  ALLOC(_ptr,_size)   \
+           ( (_ptr) = _hb_alloc( _size, &error ), error != 0 )
+
+#define  REALLOC(_ptr,_newsz)  \
+           ( (_ptr) = _hb_realloc( (_ptr), (_newsz), &error ), error != 0 )
+
+#define  FREE(_ptr)                    \
+  do {                                 \
+    if ( (_ptr) )                      \
+    {                                  \
+      _hb_free( _ptr );     \
+      _ptr = NULL;                     \
+    }                                  \
+  } while (0)
+
+#define  ALLOC_ARRAY(_ptr,_count,_type)   \
+           ALLOC(_ptr,(_count)*sizeof(_type))
+
+#define  REALLOC_ARRAY(_ptr,_newcnt,_type) \
+           REALLOC(_ptr,(_newcnt)*sizeof(_type))
+
+#define  MEM_Copy(dest,source,count)   memcpy( (char*)(dest), (const char*)(source), (size_t)(count) )
+
+#define ERR(err)   _hb_err (err)
+
+
+HB_INTERNAL HB_Pointer
+_hb_alloc( size_t    size,
+	   HB_Error *perror_ );
+
+HB_INTERNAL HB_Pointer
+_hb_realloc( HB_Pointer block,
+	     size_t     new_size,
+	     HB_Error  *perror_ );
+
+HB_INTERNAL void
+_hb_free( HB_Pointer block );
+
+
+/* helper func to set a breakpoint on */
+HB_INTERNAL HB_Error
+_hb_err (HB_Error code);
+
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_IMPL_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-indic.cpp b/third_party/harfbuzz/src/harfbuzz-indic.cpp
new file mode 100644
index 0000000..3c9df93
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-indic.cpp
@@ -0,0 +1,1873 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-shaper-private.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+#define FLAG(x) (1 << (x))
+
+static HB_Bool isLetter(HB_UChar16 ucs)
+{
+    const int test = FLAG(HB_Letter_Uppercase) |
+                     FLAG(HB_Letter_Lowercase) |
+                     FLAG(HB_Letter_Titlecase) |
+                     FLAG(HB_Letter_Modifier) |
+                     FLAG(HB_Letter_Other);
+    return FLAG(HB_GetUnicodeCharCategory(ucs)) & test;
+}
+
+static HB_Bool isMark(HB_UChar16 ucs)
+{
+    const int test = FLAG(HB_Mark_NonSpacing) |
+                     FLAG(HB_Mark_SpacingCombining) |
+                     FLAG(HB_Mark_Enclosing);
+    return FLAG(HB_GetUnicodeCharCategory(ucs)) & test;
+}
+
+enum Form {
+    Invalid = 0x0,
+    UnknownForm = Invalid,
+    Consonant,
+    Nukta,
+    Halant,
+    Matra,
+    VowelMark,
+    StressMark,
+    IndependentVowel,
+    LengthMark,
+    Control,
+    Other
+};
+
+static const unsigned char indicForms[0xe00-0x900] = {
+    // Devangari
+    Invalid, VowelMark, VowelMark, VowelMark,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, UnknownForm, UnknownForm,
+    Nukta, Other, Matra, Matra,
+
+    Matra, Matra, Matra, Matra,
+    Matra, Matra, Matra, Matra,
+    Matra, Matra, Matra, Matra,
+    Matra, Halant, UnknownForm, UnknownForm,
+
+    Other, StressMark, StressMark, StressMark,
+    StressMark, UnknownForm, UnknownForm, UnknownForm,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    IndependentVowel, IndependentVowel, VowelMark, VowelMark,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Consonant,
+    Consonant, Consonant /* ??? */, Consonant, Consonant,
+
+    // Bengali
+    Invalid, VowelMark, VowelMark, VowelMark,
+    Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, Invalid, Invalid, IndependentVowel,
+
+    IndependentVowel, Invalid, Invalid, IndependentVowel,
+    IndependentVowel, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Invalid, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Invalid, Consonant, Invalid,
+    Invalid, Invalid, Consonant, Consonant,
+    Consonant, Consonant, UnknownForm, UnknownForm,
+    Nukta, Other, Matra, Matra,
+
+    Matra, Matra, Matra, Matra,
+    Matra, Invalid, Invalid, Matra,
+    Matra, Invalid, Invalid, Matra,
+    Matra, Halant, Consonant, UnknownForm,
+
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Invalid, VowelMark,
+    Invalid, Invalid, Invalid, Invalid,
+    Consonant, Consonant, Invalid, Consonant,
+
+    IndependentVowel, IndependentVowel, VowelMark, VowelMark,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    Consonant, Consonant, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    // Gurmukhi
+    Invalid, VowelMark, VowelMark, VowelMark,
+    Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, Invalid,
+    Invalid, Invalid, Invalid, IndependentVowel,
+
+    IndependentVowel, Invalid, Invalid, IndependentVowel,
+    IndependentVowel, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Invalid, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Invalid, Consonant, Consonant,
+    Invalid, Consonant, Consonant, Invalid,
+    Consonant, Consonant, UnknownForm, UnknownForm,
+    Nukta, Other, Matra, Matra,
+
+    Matra, Matra, Matra, Invalid,
+    Invalid, Invalid, Invalid, Matra,
+    Matra, Invalid, Invalid, Matra,
+    Matra, Halant, UnknownForm, UnknownForm,
+
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, UnknownForm, UnknownForm, UnknownForm,
+    Invalid, Consonant, Consonant, Consonant,
+    Consonant, Invalid, Consonant, Invalid,
+
+    Other, Other, Invalid, Invalid,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    StressMark, StressMark, Consonant, Consonant,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    // Gujarati
+    Invalid, VowelMark, VowelMark, VowelMark,
+    Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, Invalid, IndependentVowel,
+
+    IndependentVowel, IndependentVowel, Invalid, IndependentVowel,
+    IndependentVowel, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Invalid, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Invalid, Consonant, Consonant,
+    Invalid, Consonant, Consonant, Consonant,
+    Consonant, Consonant, UnknownForm, UnknownForm,
+    Nukta, Other, Matra, Matra,
+
+    Matra, Matra, Matra, Matra,
+    Matra, Matra, Invalid, Matra,
+    Matra, Matra, Invalid, Matra,
+    Matra, Halant, UnknownForm, UnknownForm,
+
+    Other, UnknownForm, UnknownForm, UnknownForm,
+    UnknownForm, UnknownForm, UnknownForm, UnknownForm,
+    UnknownForm, UnknownForm, UnknownForm, UnknownForm,
+    UnknownForm, UnknownForm, UnknownForm, UnknownForm,
+
+    IndependentVowel, IndependentVowel, VowelMark, VowelMark,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    // Oriya
+    Invalid, VowelMark, VowelMark, VowelMark,
+    Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, Invalid, Invalid, IndependentVowel,
+
+    IndependentVowel, Invalid, Invalid, IndependentVowel,
+    IndependentVowel, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Invalid, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Invalid, Consonant, Consonant,
+    Invalid, Consonant, Consonant, Consonant,
+    Consonant, Consonant, UnknownForm, UnknownForm,
+    Nukta, Other, Matra, Matra,
+
+    Matra, Matra, Matra, Matra,
+    Invalid, Invalid, Invalid, Matra,
+    Matra, Invalid, Invalid, Matra,
+    Matra, Halant, UnknownForm, UnknownForm,
+
+    Other, Invalid, Invalid, Invalid,
+    Invalid, UnknownForm, LengthMark, LengthMark,
+    Invalid, Invalid, Invalid, Invalid,
+    Consonant, Consonant, Invalid, Consonant,
+
+    IndependentVowel, IndependentVowel, Invalid, Invalid,
+    Invalid, Invalid, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    Other, Consonant, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    //Tamil
+    Invalid, Invalid, VowelMark, Other,
+    Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, Invalid,
+    Invalid, Invalid, IndependentVowel, IndependentVowel,
+
+    IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
+    IndependentVowel, Consonant, Invalid, Invalid,
+    Invalid, Consonant, Consonant, Invalid,
+    Consonant, Invalid, Consonant, Consonant,
+
+    Invalid, Invalid, Invalid, Consonant,
+    Consonant, Invalid, Invalid, Invalid,
+    Consonant, Consonant, Consonant, Invalid,
+    Invalid, Invalid, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, UnknownForm, UnknownForm,
+    Invalid, Invalid, Matra, Matra,
+
+    Matra, Matra, Matra, Invalid,
+    Invalid, Invalid, Matra, Matra,
+    Matra, Invalid, Matra, Matra,
+    Matra, Halant, Invalid, Invalid,
+
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Invalid, LengthMark,
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Invalid, Invalid,
+
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    // Telugu
+    Invalid, VowelMark, VowelMark, VowelMark,
+    Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
+
+    IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
+    IndependentVowel, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Invalid, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Invalid, Consonant, Consonant, Consonant,
+    Consonant, Consonant, UnknownForm, UnknownForm,
+    Invalid, Invalid, Matra, Matra,
+
+    Matra, Matra, Matra, Matra,
+    Matra, Invalid, Matra, Matra,
+    Matra, Invalid, Matra, Matra,
+    Matra, Halant, Invalid, Invalid,
+
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, LengthMark, Matra, Invalid,
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Invalid, Invalid,
+
+    IndependentVowel, IndependentVowel, Invalid, Invalid,
+    Invalid, Invalid, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    // Kannada
+    Invalid, Invalid, VowelMark, VowelMark,
+    Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
+
+    IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
+    IndependentVowel, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Invalid, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Invalid, Consonant, Consonant, Consonant,
+    Consonant, Consonant, UnknownForm, UnknownForm,
+    Nukta, Other, Matra, Matra,
+
+    Matra, Matra, Matra, Matra,
+    Matra, Invalid, Matra, Matra,
+    Matra, Invalid, Matra, Matra,
+    Matra, Halant, Invalid, Invalid,
+
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, LengthMark, LengthMark, Invalid,
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Consonant, Invalid,
+
+    IndependentVowel, IndependentVowel, VowelMark, VowelMark,
+    Invalid, Invalid, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    // Malayalam
+    Invalid, Invalid, VowelMark, VowelMark,
+    Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
+
+    IndependentVowel, Invalid, IndependentVowel, IndependentVowel,
+    IndependentVowel, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Invalid, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, UnknownForm, UnknownForm,
+    Invalid, Invalid, Matra, Matra,
+
+    Matra, Matra, Matra, Matra,
+    Invalid, Invalid, Matra, Matra,
+    Matra, Invalid, Matra, Matra,
+    Matra, Halant, Invalid, Invalid,
+
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Invalid, Matra,
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Invalid, Invalid,
+
+    IndependentVowel, IndependentVowel, Invalid, Invalid,
+    Invalid, Invalid, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+
+    // Sinhala
+    Invalid, Invalid, VowelMark, VowelMark,
+    Invalid, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+
+    IndependentVowel, IndependentVowel, IndependentVowel, IndependentVowel,
+    IndependentVowel, IndependentVowel, IndependentVowel, Invalid,
+    Invalid, Invalid, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+
+    Consonant, Consonant, Invalid, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Consonant,
+    Invalid, Consonant, Invalid, Invalid,
+
+    Consonant, Consonant, Consonant, Consonant,
+    Consonant, Consonant, Consonant, Invalid,
+    Invalid, Invalid, Halant, Invalid,
+    Invalid, Invalid, Invalid, Matra,
+
+    Matra, Matra, Matra, Matra,
+    Matra, Invalid, Matra, Invalid,
+    Matra, Matra, Matra, Matra,
+    Matra, Matra, Matra, Matra,
+
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Invalid, Invalid,
+    Invalid, Invalid, Invalid, Invalid,
+
+    Invalid, Invalid, Matra, Matra,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+    Other, Other, Other, Other,
+};
+
+enum Position {
+    None,
+    Pre,
+    Above,
+    Below,
+    Post,
+    Split,
+    Base,
+    Reph,
+    Vattu,
+    Inherit
+};
+
+static const unsigned char indicPosition[0xe00-0x900] = {
+    // Devanagari
+    None, Above, Above, Post,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    Below, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, Post, Pre,
+
+    Post, Below, Below, Below,
+    Below, Above, Above, Above,
+    Above, Post, Post, Post,
+    Post, None, None, None,
+
+    None, Above, Below, Above,
+    Above, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, Below, Below,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    // Bengali
+    None, Above, Post, Post,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    Below, None, None, Post,
+
+    Below, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    Below, None, Post, Pre,
+
+    Post, Below, Below, Below,
+    Below, None, None, Pre,
+    Pre, None, None, Split,
+    Split, Below, None, None,
+
+    None, None, None, None,
+    None, None, None, Post,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, Below, Below,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    Below, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    // Gurmukhi
+    None, Above, Above, Post,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, Post,
+
+    Below, None, None, None,
+    None, Below, None, None,
+    None, Below, None, None,
+    Below, None, Post, Pre,
+
+    Post, Below, Below, None,
+    None, None, None, Above,
+    Above, None, None, Above,
+    Above, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    Above, Above, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    // Gujarati
+    None, Above, Above, Post,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    Below, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, Post, Pre,
+
+    Post, Below, Below, Below,
+    Below, Above, None, Above,
+    Above, Post, None, Post,
+    Post, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, Below, Below,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    // Oriya
+    None, Above, Post, Post,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    Below, None, None, None,
+    Below, None, None, None,
+    Below, Below, Below, Post,
+
+    Below, None, Below, Below,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, Post, Above,
+
+    Post, Below, Below, Below,
+    None, None, None, Pre,
+    Split, None, None, Split,
+    Split, None, None, None,
+
+    None, None, None, None,
+    None, None, Above, Post,
+    None, None, None, None,
+    None, None, None, Post,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, Below, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    // Tamil
+    None, None, Above, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, Post, Post,
+
+    Above, Below, Below, None,
+    None, None, Pre, Pre,
+    Pre, None, Split, Split,
+    Split, Halant, None, None,
+
+    None, None, None, None,
+    None, None, None, Post,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    // Telugu
+    None, Post, Post, Post,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, Below, Below, Below,
+    Below, Below, Below, Below,
+    Below, Below, Below, Below,
+
+    Below, Below, Below, Below,
+    Below, Below, Below, Below,
+    Below, None, Below, Below,
+    Below, Below, Below, Below,
+
+    Below, None, Below, Below,
+    None, Below, Below, Below,
+    Below, Below, None, None,
+    None, None, Post, Above,
+
+    Above, Post, Post, Post,
+    Post, None, Above, Above,
+    Split, None, Post, Above,
+    Above, Halant, None, None,
+
+    None, None, None, None,
+    None, Above, Below, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    // Kannada
+    None, None, Post, Post,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, Below, Below, Below,
+    Below, Below, Below, Below,
+    Below, Below, Below, Below,
+
+    Below, Below, Below, Below,
+    Below, Below, Below, Below,
+    Below, Below, Below, Below,
+    Below, Below, Below, Below,
+
+    Below, None, Below, Below,
+    None, Below, Below, Below,
+    Below, Below, None, None,
+    None, None, Post, Above,
+
+    Split, Post, Post, Post,
+    Post, None, Above, Split,
+    Split, None, Split, Split,
+    Above, Halant, None, None,
+
+    None, None, None, None,
+    None, Post, Post, None,
+    None, None, None, None,
+    None, None, Below, None,
+
+    None, None, Below, Below,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    // Malayalam
+    None, None, Post, Post,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, Post,
+
+    Post, None, Below, None,
+    None, Post, None, None,
+    None, None, None, None,
+    None, None, Post, Post,
+
+    Post, Post, Post, Post,
+    None, None, Pre, Pre,
+    Pre, None, Split, Split,
+    Split, Halant, None, None,
+
+    None, None, None, None,
+    None, None, None, Post,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    // Sinhala
+    None, None, Post, Post,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, Post,
+
+    Post, Post, Above, Above,
+    Below, None, Below, None,
+    Post, Pre, Split, Pre,
+    Split, Split, Split, Post,
+
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None,
+
+    None, None, Post, Post,
+    None, None, None, None,
+    None, None, None, None,
+    None, None, None, None
+};
+
+static inline Form form(unsigned short uc) {
+    if (uc < 0x900 || uc > 0xdff) {
+        if (uc == 0x25cc)
+            return Consonant;
+        if (uc == 0x200c || uc == 0x200d)
+            return Control;
+        return Other;
+    }
+    return (Form)indicForms[uc-0x900];
+}
+
+static inline Position indic_position(unsigned short uc) {
+    if (uc < 0x900 || uc > 0xdff)
+        return None;
+    return (Position) indicPosition[uc-0x900];
+}
+
+
+enum IndicScriptProperties {
+    HasReph = 0x01,
+    HasSplit = 0x02
+};
+
+const hb_uint8 scriptProperties[10] = {
+    // Devanagari,
+    HasReph,
+    // Bengali,
+    HasReph|HasSplit,
+    // Gurmukhi,
+    0,
+    // Gujarati,
+    HasReph,
+    // Oriya,
+    HasReph|HasSplit,
+    // Tamil,
+    HasSplit,
+    // Telugu,
+    HasSplit,
+    // Kannada,
+    HasSplit|HasReph,
+    // Malayalam,
+    HasSplit,
+    // Sinhala,
+    HasSplit
+};
+
+struct IndicOrdering {
+    Form form;
+    Position position;
+};
+
+static const IndicOrdering devanagari_order [] = {
+    { Consonant, Below },
+    { Matra, Below },
+    { VowelMark, Below },
+    { StressMark, Below },
+    { Matra, Above },
+    { Matra, Post },
+    { Consonant, Reph },
+    { VowelMark, Above },
+    { StressMark, Above },
+    { VowelMark, Post },
+    { (Form)0, None }
+};
+
+static const IndicOrdering bengali_order [] = {
+    { Consonant, Below },
+    { Matra, Below },
+    { Matra, Above },
+    { Consonant, Reph },
+    { VowelMark, Above },
+    { Consonant, Post },
+    { Matra, Post },
+    { VowelMark, Post },
+    { (Form)0, None }
+};
+
+static const IndicOrdering gurmukhi_order [] = {
+    { Consonant, Below },
+    { Matra, Below },
+    { Matra, Above },
+    { Consonant, Post },
+    { Matra, Post },
+    { VowelMark, Above },
+    { (Form)0, None }
+};
+
+static const IndicOrdering tamil_order [] = {
+    { Matra, Above },
+    { Matra, Post },
+    { VowelMark, Post },
+    { (Form)0, None }
+};
+
+static const IndicOrdering telugu_order [] = {
+    { Matra, Above },
+    { Matra, Below },
+    { Matra, Post },
+    { Consonant, Below },
+    { Consonant, Post },
+    { VowelMark, Post },
+    { (Form)0, None }
+};
+
+static const IndicOrdering kannada_order [] = {
+    { Matra, Above },
+    { Matra, Post },
+    { Consonant, Below },
+    { Consonant, Post },
+    { LengthMark, Post },
+    { Consonant, Reph },
+    { VowelMark, Post },
+    { (Form)0, None }
+};
+
+static const IndicOrdering malayalam_order [] = {
+    { Consonant, Below },
+    { Matra, Below },
+    { Consonant, Reph },
+    { Consonant, Post },
+    { Matra, Post },
+    { VowelMark, Post },
+    { (Form)0, None }
+};
+
+static const IndicOrdering sinhala_order [] = {
+    { Matra, Below },
+    { Matra, Above },
+    { Matra, Post },
+    { VowelMark, Post },
+    { (Form)0, None }
+};
+
+static const IndicOrdering * const indic_order[] = {
+    devanagari_order, // Devanagari
+    bengali_order, // Bengali
+    gurmukhi_order, // Gurmukhi
+    devanagari_order, // Gujarati
+    bengali_order, // Oriya
+    tamil_order, // Tamil
+    telugu_order, // Telugu
+    kannada_order, // Kannada
+    malayalam_order, // Malayalam
+    sinhala_order // Sinhala
+};
+
+
+
+// vowel matras that have to be split into two parts.
+static const unsigned short split_matras[]  = {
+    //  matra, split1, split2, split3
+
+    // bengalis
+    0x9cb, 0x9c7, 0x9be, 0x0,
+    0x9cc, 0x9c7, 0x9d7, 0x0,
+    // oriya
+    0xb48, 0xb47, 0xb56, 0x0,
+    0xb4b, 0xb47, 0xb3e, 0x0,
+    0xb4c, 0xb47, 0xb57, 0x0,
+    // tamil
+    0xbca, 0xbc6, 0xbbe, 0x0,
+    0xbcb, 0xbc7, 0xbbe, 0x0,
+    0xbcc, 0xbc6, 0xbd7, 0x0,
+    // telugu
+    0xc48, 0xc46, 0xc56, 0x0,
+    // kannada
+    0xcc0, 0xcbf, 0xcd5, 0x0,
+    0xcc7, 0xcc6, 0xcd5, 0x0,
+    0xcc8, 0xcc6, 0xcd6, 0x0,
+    0xcca, 0xcc6, 0xcc2, 0x0,
+    0xccb, 0xcc6, 0xcc2, 0xcd5,
+    // malayalam
+    0xd4a, 0xd46, 0xd3e, 0x0,
+    0xd4b, 0xd47, 0xd3e, 0x0,
+    0xd4c, 0xd46, 0xd57, 0x0,
+    // sinhala
+    0xdda, 0xdd9, 0xdca, 0x0,
+    0xddc, 0xdd9, 0xdcf, 0x0,
+    0xddd, 0xdd9, 0xdcf, 0xdca,
+    0xdde, 0xdd9, 0xddf, 0x0,
+    0xffff
+};
+
+static inline void splitMatra(unsigned short *reordered, int matra, int &len)
+{
+    unsigned short matra_uc = reordered[matra];
+    //qDebug("matra=%d, reordered[matra]=%x", matra, reordered[matra]);
+
+    const unsigned short *split = split_matras;
+    while (split[0] < matra_uc)
+        split += 4;
+
+    assert(*split == matra_uc);
+    ++split;
+
+    int added_chars = split[2] == 0x0 ? 1 : 2;
+
+    memmove(reordered + matra + added_chars, reordered + matra, (len-matra)*sizeof(unsigned short));
+    reordered[matra] = split[0];
+    reordered[matra+1] = split[1];
+    if(added_chars == 2)
+        reordered[matra+2] = split[2];
+    len += added_chars;
+}
+
+#ifndef NO_OPENTYPE
+static const HB_OpenTypeFeature indic_features[] = {
+    { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
+    { HB_MAKE_TAG('i', 'n', 'i', 't'), InitProperty },
+    { HB_MAKE_TAG('n', 'u', 'k', 't'), NuktaProperty },
+    { HB_MAKE_TAG('a', 'k', 'h', 'n'), AkhantProperty },
+    { HB_MAKE_TAG('r', 'p', 'h', 'f'), RephProperty },
+    { HB_MAKE_TAG('b', 'l', 'w', 'f'), BelowFormProperty },
+    { HB_MAKE_TAG('h', 'a', 'l', 'f'), HalfFormProperty },
+    { HB_MAKE_TAG('p', 's', 't', 'f'), PostFormProperty },
+    { HB_MAKE_TAG('v', 'a', 't', 'u'), VattuProperty },
+    { HB_MAKE_TAG('p', 'r', 'e', 's'), PreSubstProperty },
+    { HB_MAKE_TAG('b', 'l', 'w', 's'), BelowSubstProperty },
+    { HB_MAKE_TAG('a', 'b', 'v', 's'), AboveSubstProperty },
+    { HB_MAKE_TAG('p', 's', 't', 's'), PostSubstProperty },
+    { HB_MAKE_TAG('h', 'a', 'l', 'n'), HalantProperty },
+    { 0, 0 }
+};
+#endif
+
+// #define INDIC_DEBUG
+#ifdef INDIC_DEBUG
+#define IDEBUG hb_debug
+#include <stdarg.h>
+
+static void hb_debug(const char *msg, ...)
+{
+    va_list ap;
+    va_start(ap, msg); // use variable arg list
+    vfprintf(stderr, msg, ap);
+    va_end(ap);
+    fprintf(stderr, "\n");
+}
+
+#else
+#define IDEBUG if(0) printf
+#endif
+
+#if 0 //def INDIC_DEBUG
+static QString propertiesToString(int properties)
+{
+    QString res;
+    properties = ~properties;
+    if (properties & CcmpProperty)
+        res += "Ccmp ";
+    if (properties & InitProperty)
+        res += "Init ";
+    if (properties & NuktaProperty)
+        res += "Nukta ";
+    if (properties & AkhantProperty)
+        res += "Akhant ";
+    if (properties & RephProperty)
+        res += "Reph ";
+    if (properties & PreFormProperty)
+        res += "PreForm ";
+    if (properties & BelowFormProperty)
+        res += "BelowForm ";
+    if (properties & AboveFormProperty)
+        res += "AboveForm ";
+    if (properties & HalfFormProperty)
+        res += "HalfForm ";
+    if (properties & PostFormProperty)
+        res += "PostForm ";
+    if (properties & VattuProperty)
+        res += "Vattu ";
+    if (properties & PreSubstProperty)
+        res += "PreSubst ";
+    if (properties & BelowSubstProperty)
+        res += "BelowSubst ";
+    if (properties & AboveSubstProperty)
+        res += "AboveSubst ";
+    if (properties & PostSubstProperty)
+        res += "PostSubst ";
+    if (properties & HalantProperty)
+        res += "Halant ";
+    if (properties & CligProperty)
+        res += "Clig ";
+    return res;
+}
+#endif
+
+static bool indic_shape_syllable(HB_Bool openType, HB_ShaperItem *item, bool invalid)
+{
+    HB_Script script = item->item.script;
+    assert(script >= HB_Script_Devanagari && script <= HB_Script_Sinhala);
+    const unsigned short script_base = 0x0900 + 0x80*(script-HB_Script_Devanagari);
+    const unsigned short ra = script_base + 0x30;
+    const unsigned short halant = script_base + 0x4d;
+    const unsigned short nukta = script_base + 0x3c;
+    bool control = false;
+
+    int len = (int)item->item.length;
+    IDEBUG(">>>>> indic shape: from=%d, len=%d invalid=%d", item->item.pos, item->item.length, invalid);
+
+    if ((int)item->num_glyphs < len+4) {
+        item->num_glyphs = len+4;
+        return false;
+    }
+
+    HB_STACKARRAY(HB_UChar16, reordered, len + 4);
+    HB_STACKARRAY(hb_uint8, position, len + 4);
+
+    unsigned char properties = scriptProperties[script-HB_Script_Devanagari];
+
+    if (invalid) {
+        *reordered = 0x25cc;
+        memcpy(reordered+1, item->string + item->item.pos, len*sizeof(HB_UChar16));
+        len++;
+    } else {
+        memcpy(reordered, item->string + item->item.pos, len*sizeof(HB_UChar16));
+    }
+    if (reordered[len-1] == 0x200c) // zero width non joiner
+        len--;
+
+    int i;
+    int base = 0;
+    int reph = -1;
+
+#ifdef INDIC_DEBUG
+    IDEBUG("original:");
+    for (i = 0; i < len; i++) {
+        IDEBUG("    %d: %4x", i, reordered[i]);
+    }
+#endif
+
+    if (len != 1) {
+        HB_UChar16 *uc = reordered;
+        bool beginsWithRa = false;
+
+        // Rule 1: find base consonant
+        //
+        // The shaping engine finds the base consonant of the
+        // syllable, using the following algorithm: starting from the
+        // end of the syllable, move backwards until a consonant is
+        // found that does not have a below-base or post-base form
+        // (post-base forms have to follow below-base forms), or
+        // arrive at the first consonant. The consonant stopped at
+        // will be the base.
+        //
+        //  * If the syllable starts with Ra + H (in a script that has
+        //    'Reph'), Ra is excluded from candidates for base
+        //    consonants.
+        //
+        // * In Kannada and Telugu, the base consonant cannot be
+        //   farther than 3 consonants from the end of the syllable.
+        // #### replace the HasReph property by testing if the feature exists in the font!
+        if (form(*uc) == Consonant || (script == HB_Script_Bengali && form(*uc) == IndependentVowel)) {
+            if ((properties & HasReph) && (len > 2) &&
+                (*uc == ra || *uc == 0x9f0) && *(uc+1) == halant)
+                beginsWithRa = true;
+
+            if (beginsWithRa && form(*(uc+2)) == Control)
+                beginsWithRa = false;
+
+            base = (beginsWithRa ? 2 : 0);
+            IDEBUG("    length = %d, beginsWithRa = %d, base=%d", len, beginsWithRa, base);
+
+            int lastConsonant = 0;
+            int matra = -1;
+            // we remember:
+            // * the last consonant since we need it for rule 2
+            // * the matras position for rule 3 and 4
+
+            // figure out possible base glyphs
+            memset(position, 0, len);
+            if (script == HB_Script_Devanagari || script == HB_Script_Gujarati) {
+                bool vattu = false;
+                for (i = base; i < len; ++i) {
+                    position[i] = form(uc[i]);
+                    if (position[i] == Consonant) {
+                        lastConsonant = i;
+                        vattu = (!vattu && uc[i] == ra);
+                        if (vattu) {
+                            IDEBUG("excluding vattu glyph at %d from base candidates", i);
+                            position[i] = Vattu;
+                        }
+                    } else if (position[i] == Matra) {
+                        matra = i;
+                    }
+                }
+            } else {
+                for (i = base; i < len; ++i) {
+                    position[i] = form(uc[i]);
+                    if (position[i] == Consonant)
+                        lastConsonant = i;
+                    else if (matra < 0 && position[i] == Matra)
+                        matra = i;
+                }
+            }
+            int skipped = 0;
+            Position pos = Post;
+            for (i = len-1; i > base; i--) {
+                if (position[i] != Consonant && (position[i] != Control || script == HB_Script_Kannada))
+                    continue;
+
+                Position charPosition = indic_position(uc[i]);
+                if (pos == Post && charPosition == Post) {
+                    pos = Post;
+                } else if ((pos == Post || pos == Below) && charPosition == Below) {
+                    if (script == HB_Script_Devanagari || script == HB_Script_Gujarati)
+                        base = i;
+                    pos = Below;
+                } else {
+                    base = i;
+                    break;
+                }
+                if (skipped == 2 && (script == HB_Script_Kannada || script == HB_Script_Telugu)) {
+                    base = i;
+                    break;
+                }
+                ++skipped;
+            }
+
+            IDEBUG("    base consonant at %d skipped=%d, lastConsonant=%d", base, skipped, lastConsonant);
+
+            // Rule 2:
+            //
+            // If the base consonant is not the last one, Uniscribe
+            // moves the halant from the base consonant to the last
+            // one.
+            if (lastConsonant > base) {
+                int halantPos = 0;
+                if (uc[base+1] == halant)
+                    halantPos = base + 1;
+                else if (uc[base+1] == nukta && uc[base+2] == halant)
+                    halantPos = base + 2;
+                if (halantPos > 0) {
+                    IDEBUG("    moving halant from %d to %d!", base+1, lastConsonant);
+                    for (i = halantPos; i < lastConsonant; i++)
+                        uc[i] = uc[i+1];
+                    uc[lastConsonant] = halant;
+                }
+            }
+
+            // Rule 3:
+            //
+            // If the syllable starts with Ra + H, Uniscribe moves
+            // this combination so that it follows either:
+
+            // * the post-base 'matra' (if any) or the base consonant
+            //   (in scripts that show similarity to Devanagari, i.e.,
+            //   Devanagari, Gujarati, Bengali)
+            // * the base consonant (other scripts)
+            // * the end of the syllable (Kannada)
+
+            Position matra_position = None;
+            if (matra > 0)
+                matra_position = indic_position(uc[matra]);
+            IDEBUG("    matra at %d with form %d, base=%d", matra, matra_position, base);
+
+            if (beginsWithRa && base != 0) {
+                int toPos = base+1;
+                if (toPos < len && uc[toPos] == nukta)
+                    toPos++;
+                if (toPos < len && uc[toPos] == halant)
+                    toPos++;
+                if (toPos < len && uc[toPos] == 0x200d)
+                    toPos++;
+                if (toPos < len-1 && uc[toPos] == ra && uc[toPos+1] == halant)
+                    toPos += 2;
+                if (script == HB_Script_Devanagari || script == HB_Script_Gujarati || script == HB_Script_Bengali) {
+                    if (matra_position == Post || matra_position == Split) {
+                        toPos = matra+1;
+                        matra -= 2;
+                    }
+                } else if (script == HB_Script_Kannada) {
+                    toPos = len;
+                    matra -= 2;
+                }
+
+                IDEBUG("moving leading ra+halant to position %d", toPos);
+                for (i = 2; i < toPos; i++)
+                    uc[i-2] = uc[i];
+                uc[toPos-2] = ra;
+                uc[toPos-1] = halant;
+                base -= 2;
+                if (properties & HasReph)
+                    reph = toPos-2;
+            }
+
+            // Rule 4:
+
+            // Uniscribe splits two- or three-part matras into their
+            // parts. This splitting is a character-to-character
+            // operation).
+            //
+            //      Uniscribe describes some moving operations for these
+            //      matras here. For shaping however all pre matras need
+            //      to be at the beginning of the syllable, so we just move
+            //      them there now.
+            if (matra_position == Split) {
+                splitMatra(uc, matra, len);
+                // Handle three-part matras (0xccb in Kannada)
+                matra_position = indic_position(uc[matra]);
+            }
+
+            if (matra_position == Pre) {
+                unsigned short m = uc[matra];
+                while (matra--)
+                    uc[matra+1] = uc[matra];
+                uc[0] = m;
+                base++;
+            }
+        }
+
+        // Rule 5:
+        //
+        // Uniscribe classifies consonants and 'matra' parts as
+        // pre-base, above-base (Reph), below-base or post-base. This
+        // classification exists on the character code level and is
+        // language-dependent, not font-dependent.
+        for (i = 0; i < base; ++i)
+            position[i] = Pre;
+        position[base] = Base;
+        for (i = base+1; i < len; ++i) {
+            position[i] = indic_position(uc[i]);
+            // #### replace by adjusting table
+            if (uc[i] == nukta || uc[i] == halant)
+                position[i] = Inherit;
+        }
+        if (reph > 0) {
+            // recalculate reph, it might have changed.
+            for (i = base+1; i < len; ++i)
+                if (uc[i] == ra)
+                    reph = i;
+            position[reph] = Reph;
+            position[reph+1] = Inherit;
+        }
+
+        // all reordering happens now to the chars after the base
+        int fixed = base+1;
+        if (fixed < len && uc[fixed] == nukta)
+            fixed++;
+        if (fixed < len && uc[fixed] == halant)
+            fixed++;
+        if (fixed < len && uc[fixed] == 0x200d)
+            fixed++;
+
+#ifdef INDIC_DEBUG
+        for (i = fixed; i < len; ++i)
+            IDEBUG("position[%d] = %d, form=%d uc=%x", i, position[i], form(uc[i]), uc[i]);
+#endif
+        // we continuosly position the matras and vowel marks and increase the fixed
+        // until we reached the end.
+        const IndicOrdering *finalOrder = indic_order[script-HB_Script_Devanagari];
+
+        IDEBUG("    reordering pass:");
+        IDEBUG("        base=%d fixed=%d", base, fixed);
+        int toMove = 0;
+        while (finalOrder[toMove].form && fixed < len-1) {
+            IDEBUG("        fixed = %d, toMove=%d, moving form %d with pos %d", fixed, toMove, finalOrder[toMove].form, finalOrder[toMove].position);
+            for (i = fixed; i < len; i++) {
+//                IDEBUG() << "           i=" << i << "uc=" << hex << uc[i] << "form=" << form(uc[i])
+//                         << "position=" << position[i];
+                if (form(uc[i]) == finalOrder[toMove].form &&
+                     position[i] == finalOrder[toMove].position) {
+                    // need to move this glyph
+                    int to = fixed;
+                    if (i < len-1 && position[i+1] == Inherit) {
+                        IDEBUG("         moving two chars from %d to %d", i, to);
+                        unsigned short ch = uc[i];
+                        unsigned short ch2 = uc[i+1];
+                        unsigned char pos = position[i];
+                        for (int j = i+1; j > to+1; j--) {
+                            uc[j] = uc[j-2];
+                            position[j] = position[j-2];
+                        }
+                        uc[to] = ch;
+                        uc[to+1] = ch2;
+                        position[to] = pos;
+                        position[to+1] = pos;
+                        fixed += 2;
+                    } else {
+                        IDEBUG("         moving one char from %d to %d", i, to);
+                        unsigned short ch = uc[i];
+                        unsigned char pos = position[i];
+                        for (int j = i; j > to; j--) {
+                            uc[j] = uc[j-1];
+                            position[j] = position[j-1];
+                        }
+                        uc[to] = ch;
+                        position[to] = pos;
+                        fixed++;
+                    }
+                }
+            }
+            toMove++;
+        }
+
+    }
+
+    if (reph > 0) {
+        // recalculate reph, it might have changed.
+        for (i = base+1; i < len; ++i)
+            if (reordered[i] == ra)
+                reph = i;
+    }
+
+#ifndef NO_OPENTYPE
+    const int availableGlyphs = item->num_glyphs;
+#endif
+    if (!item->font->klass->convertStringToGlyphIndices(item->font,
+                                                        reordered, len,
+                                                        item->glyphs, &item->num_glyphs,
+                                                        item->item.bidiLevel % 2))
+        goto error;
+
+
+    IDEBUG("  base=%d, reph=%d", base, reph);
+    IDEBUG("reordered:");
+    for (i = 0; i < len; i++) {
+        item->attributes[i].mark = false;
+        item->attributes[i].clusterStart = false;
+        item->attributes[i].justification = 0;
+        item->attributes[i].zeroWidth = false;
+        IDEBUG("    %d: %4x", i, reordered[i]);
+    }
+
+    // now we have the syllable in the right order, and can start running it through open type.
+
+    for (i = 0; i < len; ++i)
+        control |= (form(reordered[i]) == Control);
+
+#ifndef NO_OPENTYPE
+    if (openType) {
+
+        // we need to keep track of where the base glyph is for some
+        // scripts and use the cluster feature for this.  This
+        // also means we have to correct the logCluster output from
+        // the open type engine manually afterwards.  for indic this
+        // is rather simple, as all chars just point to the first
+        // glyph in the syllable.
+        HB_STACKARRAY(unsigned short, clusters, len);
+        HB_STACKARRAY(unsigned int, properties, len);
+
+        for (i = 0; i < len; ++i)
+            clusters[i] = i;
+
+        // features we should always apply
+        for (i = 0; i < len; ++i)
+            properties[i] = ~(CcmpProperty
+                              | NuktaProperty
+                              | VattuProperty
+                              | PreSubstProperty
+                              | BelowSubstProperty
+                              | AboveSubstProperty
+                              | PostSubstProperty
+                              | HalantProperty
+                              | PositioningProperties);
+
+        // Ccmp always applies
+        // Init
+        if (item->item.pos == 0
+            || !(isLetter(item->string[item->item.pos-1]) || isMark(item->string[item->item.pos-1])))
+            properties[0] &= ~InitProperty;
+
+        // Nukta always applies
+        // Akhant
+        for (i = 0; i <= base; ++i)
+            properties[i] &= ~AkhantProperty;
+        // Reph
+        if (reph >= 0) {
+            properties[reph] &= ~RephProperty;
+            properties[reph+1] &= ~RephProperty;
+        }
+        // BelowForm
+        for (i = base+1; i < len; ++i)
+            properties[i] &= ~BelowFormProperty;
+
+        if (script == HB_Script_Devanagari || script == HB_Script_Gujarati) {
+            // vattu glyphs need this aswell
+            bool vattu = false;
+            for (i = base-2; i > 1; --i) {
+                if (form(reordered[i]) == Consonant) {
+                    vattu = (!vattu && reordered[i] == ra);
+                    if (vattu) {
+                        IDEBUG("forming vattu ligature at %d", i);
+                        properties[i] &= ~BelowFormProperty;
+                        properties[i+1] &= ~BelowFormProperty;
+                    }
+                }
+            }
+        }
+        // HalfFormProperty
+        for (i = 0; i < base; ++i)
+            properties[i] &= ~HalfFormProperty;
+        if (control) {
+            for (i = 2; i < len; ++i) {
+                if (reordered[i] == 0x200d /* ZWJ */) {
+                    properties[i-1] &= ~HalfFormProperty;
+                    properties[i-2] &= ~HalfFormProperty;
+                } else if (reordered[i] == 0x200c /* ZWNJ */) {
+                    properties[i-1] &= ~HalfFormProperty;
+                    properties[i-2] &= ~HalfFormProperty;
+                }
+            }
+        }
+        // PostFormProperty
+        for (i = base+1; i < len; ++i)
+            properties[i] &= ~PostFormProperty;
+        // vattu always applies
+        // pres always applies
+        // blws always applies
+        // abvs always applies
+        // psts always applies
+        // halant always applies
+
+#ifdef INDIC_DEBUG
+//        {
+//            IDEBUG("OT properties:");
+//            for (int i = 0; i < len; ++i)
+//                qDebug("    i: %s", ::propertiesToString(properties[i]).toLatin1().data());
+//        }
+#endif
+
+        // initialize
+        item->log_clusters = clusters;
+        HB_OpenTypeShape(item, properties);
+
+        int newLen = item->face->buffer->in_length;
+        HB_GlyphItem otl_glyphs = item->face->buffer->in_string;
+
+        // move the left matra back to its correct position in malayalam and tamil
+        if ((script == HB_Script_Malayalam || script == HB_Script_Tamil) && (form(reordered[0]) == Matra)) {
+//             qDebug("reordering matra, len=%d", newLen);
+            // need to find the base in the shaped string and move the matra there
+            int basePos = 0;
+            while (basePos < newLen && (int)otl_glyphs[basePos].cluster <= base)
+                basePos++;
+            --basePos;
+            if (basePos < newLen && basePos > 1) {
+//                 qDebug("moving prebase matra to position %d in syllable newlen=%d", basePos, newLen);
+                HB_GlyphItemRec m = otl_glyphs[0];
+                --basePos;
+                for (i = 0; i < basePos; ++i)
+                    otl_glyphs[i] = otl_glyphs[i+1];
+                otl_glyphs[basePos] = m;
+            }
+        }
+
+        HB_Bool positioned = HB_OpenTypePosition(item, availableGlyphs, false);
+
+        HB_FREE_STACKARRAY(clusters);
+        HB_FREE_STACKARRAY(properties);
+
+        if (!positioned)
+            goto error;
+
+        if (control) {
+            IDEBUG("found a control char in the syllable");
+            hb_uint32 i = 0, j = 0;
+            while (i < item->num_glyphs) {
+                if (form(reordered[otl_glyphs[i].cluster]) == Control) {
+                    ++i;
+                    if (i >= item->num_glyphs)
+                        break;
+                }
+                item->glyphs[j] = item->glyphs[i];
+                item->attributes[j] = item->attributes[i];
+                ++i;
+                ++j;
+            }
+            item->num_glyphs = j;
+        }
+
+    } else {
+        HB_HeuristicPosition(item);
+    }
+#endif // NO_OPENTYPE
+    item->attributes[0].clusterStart = true;
+
+    HB_FREE_STACKARRAY(reordered);
+    HB_FREE_STACKARRAY(position);
+
+    IDEBUG("<<<<<<");
+    return true;
+
+error:
+    HB_FREE_STACKARRAY(reordered);
+    HB_FREE_STACKARRAY(position);
+    return false;
+}
+
+/* syllables are of the form:
+
+   (Consonant Nukta? Halant)* Consonant Matra? VowelMark? StressMark?
+   (Consonant Nukta? Halant)* Consonant Halant
+   IndependentVowel VowelMark? StressMark?
+
+   We return syllable boundaries on invalid combinations aswell
+*/
+static int indic_nextSyllableBoundary(HB_Script script, const HB_UChar16 *s, int start, int end, bool *invalid)
+{
+    *invalid = false;
+    IDEBUG("indic_nextSyllableBoundary: start=%d, end=%d", start, end);
+    const HB_UChar16 *uc = s+start;
+
+    int pos = 0;
+    Form state = form(uc[pos]);
+    IDEBUG("state[%d]=%d (uc=%4x)", pos, state, uc[pos]);
+    pos++;
+
+    if (state != Consonant && state != IndependentVowel) {
+        if (state != Other)
+            *invalid = true;
+        goto finish;
+    }
+
+    while (pos < end - start) {
+        Form newState = form(uc[pos]);
+        IDEBUG("state[%d]=%d (uc=%4x)", pos, newState, uc[pos]);
+        switch(newState) {
+        case Control:
+            newState = state;
+ 	    if (state == Halant && uc[pos] == 0x200d /* ZWJ */)
+  		break;
+            // the control character should be the last char in the item
+            ++pos;
+            goto finish;
+        case Consonant:
+	    if (state == Halant && (script != HB_Script_Sinhala || uc[pos-1] == 0x200d /* ZWJ */))
+                break;
+            goto finish;
+        case Halant:
+            if (state == Nukta || state == Consonant)
+                break;
+            // Bengali has a special exception allowing the combination Vowel_A/E + Halant + Ya
+            if (script == HB_Script_Bengali && pos == 1 &&
+                 (uc[0] == 0x0985 || uc[0] == 0x098f))
+                break;
+            // Sinhala uses the Halant as a component of certain matras. Allow these, but keep the state on Matra.
+            if (script == HB_Script_Sinhala && state == Matra) {
+                ++pos;
+                continue;
+            }
+            if (script == HB_Script_Malayalam && state == Matra && uc[pos-1] == 0x0d41) {
+                ++pos;
+                continue;
+            }
+            goto finish;
+        case Nukta:
+            if (state == Consonant)
+                break;
+            goto finish;
+        case StressMark:
+            if (state == VowelMark)
+                break;
+            // fall through
+        case VowelMark:
+            if (state == Matra || state == LengthMark || state == IndependentVowel)
+                break;
+            // fall through
+        case Matra:
+            if (state == Consonant || state == Nukta)
+                break;
+            if (state == Matra) {
+                // ### needs proper testing for correct two/three part matras
+                break;
+            }
+            // ### not sure if this is correct. If it is, does it apply only to Bengali or should
+            // it work for all Indic languages?
+            // the combination Independent_A + Vowel Sign AA is allowed.
+            if (script == HB_Script_Bengali && uc[pos] == 0x9be && uc[pos-1] == 0x985)
+                break;
+            if (script == HB_Script_Tamil && state == Matra) {
+                if (uc[pos-1] == 0x0bc6 &&
+                     (uc[pos] == 0xbbe || uc[pos] == 0xbd7))
+                    break;
+                if (uc[pos-1] == 0x0bc7 && uc[pos] == 0xbbe)
+                    break;
+            }
+            goto finish;
+
+        case LengthMark:
+            if (state == Matra) {
+                // ### needs proper testing for correct two/three part matras
+                break;
+            }
+        case IndependentVowel:
+        case Invalid:
+        case Other:
+            goto finish;
+        }
+        state = newState;
+        pos++;
+    }
+ finish:
+    return pos+start;
+}
+
+HB_Bool HB_IndicShape(HB_ShaperItem *item)
+{
+    assert(item->item.script >= HB_Script_Devanagari && item->item.script <= HB_Script_Sinhala);
+
+    HB_Bool openType = false;
+#ifndef NO_OPENTYPE
+    openType = HB_SelectScript(item, indic_features);
+#endif
+    unsigned short *logClusters = item->log_clusters;
+
+    HB_ShaperItem syllable = *item;
+    int first_glyph = 0;
+
+    int sstart = item->item.pos;
+    int end = sstart + item->item.length;
+    IDEBUG("indic_shape: from %d length %d", item->item.pos, item->item.length);
+    while (sstart < end) {
+        bool invalid;
+        int send = indic_nextSyllableBoundary(item->item.script, item->string, sstart, end, &invalid);
+        IDEBUG("syllable from %d, length %d, invalid=%s", sstart, send-sstart,
+               invalid ? "true" : "false");
+        syllable.item.pos = sstart;
+        syllable.item.length = send-sstart;
+        syllable.glyphs = item->glyphs + first_glyph;
+        syllable.attributes = item->attributes + first_glyph;
+        syllable.offsets = item->offsets + first_glyph;
+        syllable.advances = item->advances + first_glyph;
+        syllable.num_glyphs = item->num_glyphs - first_glyph;
+        if (!indic_shape_syllable(openType, &syllable, invalid)) {
+            IDEBUG("syllable shaping failed, syllable requests %d glyphs", syllable.num_glyphs);
+            item->num_glyphs += syllable.num_glyphs;
+            return false;
+        }
+        // fix logcluster array
+        IDEBUG("syllable:");
+        hb_uint32 g;
+        for (g = first_glyph; g < first_glyph + syllable.num_glyphs; ++g)
+            IDEBUG("        %d -> glyph %x", g, item->glyphs[g]);
+        IDEBUG("    logclusters:");
+        int i;
+        for (i = sstart; i < send; ++i) {
+            IDEBUG("        %d -> glyph %d", i, first_glyph);
+            logClusters[i-item->item.pos] = first_glyph;
+        }
+        sstart = send;
+        first_glyph += syllable.num_glyphs;
+    }
+    item->num_glyphs = first_glyph;
+    return true;
+}
+
+void HB_IndicAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes)
+{
+    int end = from + len;
+    const HB_UChar16 *uc = text + from;
+    attributes += from;
+    hb_uint32 i = 0;
+    while (i < len) {
+        bool invalid;
+        hb_uint32 boundary = indic_nextSyllableBoundary(script, text, from+i, end, &invalid) - from;
+         attributes[i].charStop = true;
+
+        if (boundary > len-1) boundary = len;
+        i++;
+        while (i < boundary) {
+            attributes[i].charStop = false;
+            ++uc;
+            ++i;
+        }
+        assert(i == boundary);
+    }
+
+
+}
+
+
diff --git a/third_party/harfbuzz/src/harfbuzz-khmer.c b/third_party/harfbuzz/src/harfbuzz-khmer.c
new file mode 100644
index 0000000..958069e
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-khmer.c
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-shaper-private.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+/*
+//  Vocabulary
+//      Base ->         A consonant or an independent vowel in its full (not subscript) form. It is the
+//                      center of the syllable, it can be surrounded by coeng (subscript) consonants, vowels,
+//                      split vowels, signs... but there is only one base in a syllable, it has to be coded as
+//                      the first character of the syllable.
+//      split vowel --> vowel that has two parts placed separately (e.g. Before and after the consonant).
+//                      Khmer language has five of them. Khmer split vowels either have one part before the
+//                      base and one after the base or they have a part before the base and a part above the base.
+//                      The first part of all Khmer split vowels is the same character, identical to
+//                      the glyph of Khmer dependent vowel SRA EI
+//      coeng -->  modifier used in Khmer to construct coeng (subscript) consonants
+//                 Differently than indian languages, the coeng modifies the consonant that follows it,
+//                 not the one preceding it  Each consonant has two forms, the base form and the subscript form
+//                 the base form is the normal one (using the consonants code-point), the subscript form is
+//                 displayed when the combination coeng + consonant is encountered.
+//      Consonant of type 1 -> A consonant which has subscript for that only occupies space under a base consonant
+//      Consonant of type 2.-> Its subscript form occupies space under and before the base (only one, RO)
+//      Consonant of Type 3 -> Its subscript form occupies space under and after the base (KHO, CHHO, THHO, BA, YO, SA)
+//      Consonant shifter -> Khmer has to series of consonants. The same dependent vowel has different sounds
+//                           if it is attached to a consonant of the first series or a consonant of the second series
+//                           Most consonants have an equivalent in the other series, but some of theme exist only in
+//                           one series (for example SA). If we want to use the consonant SA with a vowel sound that
+//                           can only be done with a vowel sound that corresponds to a vowel accompanying a consonant
+//                           of the other series, then we need to use a consonant shifter: TRIISAP or MUSIKATOAN
+//                           x17C9 y x17CA. TRIISAP changes a first series consonant to second series sound and
+//                           MUSIKATOAN a second series consonant to have a first series vowel sound.
+//                           Consonant shifter are both normally supercript marks, but, when they are followed by a
+//                           superscript, they change shape and take the form of subscript dependent vowel SRA U.
+//                           If they are in the same syllable as a coeng consonant, Unicode 3.0 says that they
+//                           should be typed before the coeng. Unicode 4.0 breaks the standard and says that it should
+//                           be placed after the coeng consonant.
+//      Dependent vowel ->   In khmer dependent vowels can be placed above, below, before or after the base
+//                           Each vowel has its own position. Only one vowel per syllable is allowed.
+//      Signs            ->  Khmer has above signs and post signs. Only one above sign and/or one post sign are
+//                           Allowed in a syllable.
+//
+//
+//   order is important here! This order must be the same that is found in each horizontal
+//   line in the statetable for Khmer (see khmerStateTable) .
+*/
+enum KhmerCharClassValues {
+    CC_RESERVED             =  0,
+    CC_CONSONANT            =  1, /* Consonant of type 1 or independent vowel */
+    CC_CONSONANT2           =  2, /* Consonant of type 2 */
+    CC_CONSONANT3           =  3, /* Consonant of type 3 */
+    CC_ZERO_WIDTH_NJ_MARK   =  4, /* Zero Width non joiner character (0x200C) */
+    CC_CONSONANT_SHIFTER    =  5,
+    CC_ROBAT                =  6, /* Khmer special diacritic accent -treated differently in state table */
+    CC_COENG                =  7, /* Subscript consonant combining character */
+    CC_DEPENDENT_VOWEL      =  8,
+    CC_SIGN_ABOVE           =  9,
+    CC_SIGN_AFTER           = 10,
+    CC_ZERO_WIDTH_J_MARK    = 11, /* Zero width joiner character */
+    CC_COUNT                = 12  /* This is the number of character classes */
+};
+
+
+enum KhmerCharClassFlags {
+    CF_CLASS_MASK    = 0x0000FFFF,
+
+    CF_CONSONANT     = 0x01000000,  /* flag to speed up comparing */
+    CF_SPLIT_VOWEL   = 0x02000000,  /* flag for a split vowel -> the first part is added in front of the syllable */
+    CF_DOTTED_CIRCLE = 0x04000000,  /* add a dotted circle if a character with this flag is the first in a syllable */
+    CF_COENG         = 0x08000000,  /* flag to speed up comparing */
+    CF_SHIFTER       = 0x10000000,  /* flag to speed up comparing */
+    CF_ABOVE_VOWEL   = 0x20000000,  /* flag to speed up comparing */
+
+    /* position flags */
+    CF_POS_BEFORE    = 0x00080000,
+    CF_POS_BELOW     = 0x00040000,
+    CF_POS_ABOVE     = 0x00020000,
+    CF_POS_AFTER     = 0x00010000,
+    CF_POS_MASK      = 0x000f0000
+};
+
+
+/* Characters that get referred to by name */
+enum KhmerChar {
+    C_SIGN_ZWNJ     = 0x200C,
+    C_SIGN_ZWJ      = 0x200D,
+    C_RO            = 0x179A,
+    C_VOWEL_AA      = 0x17B6,
+    C_SIGN_NIKAHIT  = 0x17C6,
+    C_VOWEL_E       = 0x17C1,
+    C_COENG         = 0x17D2
+};
+
+
+/*
+//  simple classes, they are used in the statetable (in this file) to control the length of a syllable
+//  they are also used to know where a character should be placed (location in reference to the base character)
+//  and also to know if a character, when independently displayed, should be displayed with a dotted-circle to
+//  indicate error in syllable construction
+*/
+enum {
+    _xx = CC_RESERVED,
+    _sa = CC_SIGN_ABOVE | CF_DOTTED_CIRCLE | CF_POS_ABOVE,
+    _sp = CC_SIGN_AFTER | CF_DOTTED_CIRCLE| CF_POS_AFTER,
+    _c1 = CC_CONSONANT | CF_CONSONANT,
+    _c2 = CC_CONSONANT2 | CF_CONSONANT,
+    _c3 = CC_CONSONANT3 | CF_CONSONANT,
+    _rb = CC_ROBAT | CF_POS_ABOVE | CF_DOTTED_CIRCLE,
+    _cs = CC_CONSONANT_SHIFTER | CF_DOTTED_CIRCLE | CF_SHIFTER,
+    _dl = CC_DEPENDENT_VOWEL | CF_POS_BEFORE | CF_DOTTED_CIRCLE,
+    _db = CC_DEPENDENT_VOWEL | CF_POS_BELOW | CF_DOTTED_CIRCLE,
+    _da = CC_DEPENDENT_VOWEL | CF_POS_ABOVE | CF_DOTTED_CIRCLE | CF_ABOVE_VOWEL,
+    _dr = CC_DEPENDENT_VOWEL | CF_POS_AFTER | CF_DOTTED_CIRCLE,
+    _co = CC_COENG | CF_COENG | CF_DOTTED_CIRCLE,
+
+    /* split vowel */
+    _va = _da | CF_SPLIT_VOWEL,
+    _vr = _dr | CF_SPLIT_VOWEL
+};
+
+
+/*
+//   Character class: a character class value
+//   ORed with character class flags.
+*/
+typedef unsigned long KhmerCharClass;
+
+
+/*
+//  Character class tables
+//  _xx character does not combine into syllable, such as numbers, puntuation marks, non-Khmer signs...
+//  _sa Sign placed above the base
+//  _sp Sign placed after the base
+//  _c1 Consonant of type 1 or independent vowel (independent vowels behave as type 1 consonants)
+//  _c2 Consonant of type 2 (only RO)
+//  _c3 Consonant of type 3
+//  _rb Khmer sign robat u17CC. combining mark for subscript consonants
+//  _cd Consonant-shifter
+//  _dl Dependent vowel placed before the base (left of the base)
+//  _db Dependent vowel placed below the base
+//  _da Dependent vowel placed above the base
+//  _dr Dependent vowel placed behind the base (right of the base)
+//  _co Khmer combining mark COENG u17D2, combines with the consonant or independent vowel following
+//      it to create a subscript consonant or independent vowel
+//  _va Khmer split vowel in which the first part is before the base and the second one above the base
+//  _vr Khmer split vowel in which the first part is before the base and the second one behind (right of) the base
+*/
+static const KhmerCharClass khmerCharClasses[] = {
+    _c1, _c1, _c1, _c3, _c1, _c1, _c1, _c1, _c3, _c1, _c1, _c1, _c1, _c3, _c1, _c1, /* 1780 - 178F */
+    _c1, _c1, _c1, _c1, _c3, _c1, _c1, _c1, _c1, _c3, _c2, _c1, _c1, _c1, _c3, _c3, /* 1790 - 179F */
+    _c1, _c3, _c1, _c1, _c1, _c1, _c1, _c1, _c1, _c1, _c1, _c1, _c1, _c1, _c1, _c1, /* 17A0 - 17AF */
+    _c1, _c1, _c1, _c1, _dr, _dr, _dr, _da, _da, _da, _da, _db, _db, _db, _va, _vr, /* 17B0 - 17BF */
+    _vr, _dl, _dl, _dl, _vr, _vr, _sa, _sp, _sp, _cs, _cs, _sa, _rb, _sa, _sa, _sa, /* 17C0 - 17CF */
+    _sa, _sa, _co, _sa, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _xx, _sa, _xx, _xx  /* 17D0 - 17DF */
+};
+
+/* this enum must reflect the range of khmerCharClasses */
+enum KhmerCharClassesRange {
+    KhmerFirstChar = 0x1780,
+    KhmerLastChar  = 0x17df
+};
+
+/*
+//  Below we define how a character in the input string is either in the khmerCharClasses table
+//  (in which case we get its type back), a ZWJ or ZWNJ (two characters that may appear
+//  within the syllable, but are not in the table) we also get their type back, or an unknown object
+//  in which case we get _xx (CC_RESERVED) back
+*/
+static KhmerCharClass getKhmerCharClass(HB_UChar16 uc)
+{
+    if (uc == C_SIGN_ZWJ) {
+        return CC_ZERO_WIDTH_J_MARK;
+    }
+
+    if (uc == C_SIGN_ZWNJ) {
+        return CC_ZERO_WIDTH_NJ_MARK;
+    }
+
+    if (uc < KhmerFirstChar || uc > KhmerLastChar) {
+        return CC_RESERVED;
+    }
+
+    return khmerCharClasses[uc - KhmerFirstChar];
+}
+
+
+/*
+//  The stateTable is used to calculate the end (the length) of a well
+//  formed Khmer Syllable.
+//
+//  Each horizontal line is ordered exactly the same way as the values in KhmerClassTable
+//  CharClassValues. This coincidence of values allows the follow up of the table.
+//
+//  Each line corresponds to a state, which does not necessarily need to be a type
+//  of component... for example, state 2 is a base, with is always a first character
+//  in the syllable, but the state could be produced a consonant of any type when
+//  it is the first character that is analysed (in ground state).
+//
+//  Differentiating 3 types of consonants is necessary in order to
+//  forbid the use of certain combinations, such as having a second
+//  coeng after a coeng RO,
+//  The inexistent possibility of having a type 3 after another type 3 is permitted,
+//  eliminating it would very much complicate the table, and it does not create typing
+//  problems, as the case above.
+//
+//  The table is quite complex, in order to limit the number of coeng consonants
+//  to 2 (by means of the table).
+//
+//  There a peculiarity, as far as Unicode is concerned:
+//  - The consonant-shifter is considered in two possible different
+//    locations, the one considered in Unicode 3.0 and the one considered in
+//    Unicode 4.0. (there is a backwards compatibility problem in this standard).
+//
+//
+//  xx    independent character, such as a number, punctuation sign or non-khmer char
+//
+//  c1    Khmer consonant of type 1 or an independent vowel
+//        that is, a letter in which the subscript for is only under the
+//        base, not taking any space to the right or to the left
+//
+//  c2    Khmer consonant of type 2, the coeng form takes space under
+//        and to the left of the base (only RO is of this type)
+//
+//  c3    Khmer consonant of type 3. Its subscript form takes space under
+//        and to the right of the base.
+//
+//  cs    Khmer consonant shifter
+//
+//  rb    Khmer robat
+//
+//  co    coeng character (u17D2)
+//
+//  dv    dependent vowel (including split vowels, they are treated in the same way).
+//        even if dv is not defined above, the component that is really tested for is
+//        KhmerClassTable::CC_DEPENDENT_VOWEL, which is common to all dependent vowels
+//
+//  zwj   Zero Width joiner
+//
+//  zwnj  Zero width non joiner
+//
+//  sa    above sign
+//
+//  sp    post sign
+//
+//  there are lines with equal content but for an easier understanding
+//  (and maybe change in the future) we did not join them
+*/
+static const signed char khmerStateTable[][CC_COUNT] =
+{
+    /* xx  c1  c2  c3 zwnj cs  rb  co  dv  sa  sp zwj */
+    { 1,  2,  2,  2,  1,  1,  1,  6,  1,  1,  1,  2}, /*  0 - ground state */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /*  1 - exit state (or sign to the right of the syllable) */
+    {-1, -1, -1, -1,  3,  4,  5,  6, 16, 17,  1, -1}, /*  2 - Base consonant */
+    {-1, -1, -1, -1, -1,  4, -1, -1, 16, -1, -1, -1}, /*  3 - First ZWNJ before a register shifter It can only be followed by a shifter or a vowel */
+    {-1, -1, -1, -1, 15, -1, -1,  6, 16, 17,  1, 14}, /*  4 - First register shifter */
+    {-1, -1, -1, -1, -1, -1, -1, -1, 20, -1,  1, -1}, /*  5 - Robat */
+    {-1,  7,  8,  9, -1, -1, -1, -1, -1, -1, -1, -1}, /*  6 - First Coeng */
+    {-1, -1, -1, -1, 12, 13, -1, 10, 16, 17,  1, 14}, /*  7 - First consonant of type 1 after coeng */
+    {-1, -1, -1, -1, 12, 13, -1, -1, 16, 17,  1, 14}, /*  8 - First consonant of type 2 after coeng */
+    {-1, -1, -1, -1, 12, 13, -1, 10, 16, 17,  1, 14}, /*  9 - First consonant or type 3 after ceong */
+    {-1, 11, 11, 11, -1, -1, -1, -1, -1, -1, -1, -1}, /* 10 - Second Coeng (no register shifter before) */
+    {-1, -1, -1, -1, 15, -1, -1, -1, 16, 17,  1, 14}, /* 11 - Second coeng consonant (or ind. vowel) no register shifter before */
+    {-1, -1, -1, -1, -1, 13, -1, -1, 16, -1, -1, -1}, /* 12 - Second ZWNJ before a register shifter */
+    {-1, -1, -1, -1, 15, -1, -1, -1, 16, 17,  1, 14}, /* 13 - Second register shifter */
+    {-1, -1, -1, -1, -1, -1, -1, -1, 16, -1, -1, -1}, /* 14 - ZWJ before vowel */
+    {-1, -1, -1, -1, -1, -1, -1, -1, 16, -1, -1, -1}, /* 15 - ZWNJ before vowel */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, 17,  1, 18}, /* 16 - dependent vowel */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1, 18}, /* 17 - sign above */
+    {-1, -1, -1, -1, -1, -1, -1, 19, -1, -1, -1, -1}, /* 18 - ZWJ after vowel */
+    {-1,  1, -1,  1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 19 - Third coeng */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1, -1}, /* 20 - dependent vowel after a Robat */
+};
+
+
+/*  #define KHMER_DEBUG */
+#ifdef KHMER_DEBUG
+#define KHDEBUG qDebug
+#else
+#define KHDEBUG if(0) printf
+#endif
+
+/*
+//  Given an input string of characters and a location in which to start looking
+//  calculate, using the state table, which one is the last character of the syllable
+//  that starts in the starting position.
+*/
+static int khmer_nextSyllableBoundary(const HB_UChar16 *s, int start, int end, HB_Bool *invalid)
+{
+    const HB_UChar16 *uc = s + start;
+    int state = 0;
+    int pos = start;
+    *invalid = FALSE;
+
+    while (pos < end) {
+        KhmerCharClass charClass = getKhmerCharClass(*uc);
+        if (pos == start) {
+            *invalid = (charClass > 0) && ! (charClass & CF_CONSONANT);
+        }
+        state = khmerStateTable[state][charClass & CF_CLASS_MASK];
+
+        KHDEBUG("state[%d]=%d class=%8lx (uc=%4x)", pos - start, state,
+                charClass, *uc );
+
+        if (state < 0) {
+            break;
+        }
+        ++uc;
+        ++pos;
+    }
+    return pos;
+}
+
+#ifndef NO_OPENTYPE
+static const HB_OpenTypeFeature khmer_features[] = {
+    { HB_MAKE_TAG( 'p', 'r', 'e', 'f' ), PreFormProperty },
+    { HB_MAKE_TAG( 'b', 'l', 'w', 'f' ), BelowFormProperty },
+    { HB_MAKE_TAG( 'a', 'b', 'v', 'f' ), AboveFormProperty },
+    { HB_MAKE_TAG( 'p', 's', 't', 'f' ), PostFormProperty },
+    { HB_MAKE_TAG( 'p', 'r', 'e', 's' ), PreSubstProperty },
+    { HB_MAKE_TAG( 'b', 'l', 'w', 's' ), BelowSubstProperty },
+    { HB_MAKE_TAG( 'a', 'b', 'v', 's' ), AboveSubstProperty },
+    { HB_MAKE_TAG( 'p', 's', 't', 's' ), PostSubstProperty },
+    { HB_MAKE_TAG( 'c', 'l', 'i', 'g' ), CligProperty },
+    { 0, 0 }
+};
+#endif
+
+
+static HB_Bool khmer_shape_syllable(HB_Bool openType, HB_ShaperItem *item)
+{
+/*    KHDEBUG("syllable from %d len %d, str='%s'", item->from, item->length,
+  	    item->string->mid(item->from, item->length).toUtf8().data()); */
+
+    int len = 0;
+    int syllableEnd = item->item.pos + item->item.length;
+    unsigned short reordered[16];
+    unsigned char properties[16];
+    enum {
+	AboveForm = 0x01,
+	PreForm = 0x02,
+	PostForm = 0x04,
+	BelowForm = 0x08
+    };
+#ifndef NO_OPENTYPE
+    const int availableGlyphs = item->num_glyphs;
+#endif
+    int coengRo;
+    int i;
+
+    /* according to the specs this is the max length one can get
+       ### the real value should be smaller */
+    assert(item->item.length < 13);
+
+    memset(properties, 0, 16*sizeof(unsigned char));
+
+#ifdef KHMER_DEBUG
+    qDebug("original:");
+    for (int i = from; i < syllableEnd; i++) {
+        qDebug("    %d: %4x", i, string[i]);
+    }
+#endif
+
+    /*
+    // write a pre vowel or the pre part of a split vowel first
+    // and look out for coeng + ro. RO is the only vowel of type 2, and
+    // therefore the only one that requires saving space before the base.
+    */
+    coengRo = -1;  /* There is no Coeng Ro, if found this value will change */
+    for (i = item->item.pos; i < syllableEnd; i += 1) {
+        KhmerCharClass charClass = getKhmerCharClass(item->string[i]);
+
+        /* if a split vowel, write the pre part. In Khmer the pre part
+           is the same for all split vowels, same glyph as pre vowel C_VOWEL_E */
+        if (charClass & CF_SPLIT_VOWEL) {
+            reordered[len] = C_VOWEL_E;
+            properties[len] = PreForm;
+            ++len;
+            break; /* there can be only one vowel */
+        }
+        /* if a vowel with pos before write it out */
+        if (charClass & CF_POS_BEFORE) {
+            reordered[len] = item->string[i];
+            properties[len] = PreForm;
+            ++len;
+            break; /* there can be only one vowel */
+        }
+        /* look for coeng + ro and remember position
+           works because coeng + ro is always in front of a vowel (if there is a vowel)
+           and because CC_CONSONANT2 is enough to identify it, as it is the only consonant
+           with this flag */
+        if ( (charClass & CF_COENG) && (i + 1 < syllableEnd) &&
+              ( (getKhmerCharClass(item->string[i+1]) & CF_CLASS_MASK) == CC_CONSONANT2) ) {
+            coengRo = i;
+        }
+    }
+
+    /* write coeng + ro if found */
+    if (coengRo > -1) {
+        reordered[len] = C_COENG;
+        properties[len] = PreForm;
+        ++len;
+        reordered[len] = C_RO;
+        properties[len] = PreForm;
+        ++len;
+    }
+
+    /*
+       shall we add a dotted circle?
+       If in the position in which the base should be (first char in the string) there is
+       a character that has the Dotted circle flag (a character that cannot be a base)
+       then write a dotted circle */
+    if (getKhmerCharClass(item->string[item->item.pos]) & CF_DOTTED_CIRCLE) {
+        reordered[len] = C_DOTTED_CIRCLE;
+        ++len;
+    }
+
+    /* copy what is left to the output, skipping before vowels and
+       coeng Ro if they are present */
+    for (i = item->item.pos; i < syllableEnd; i += 1) {
+        HB_UChar16 uc = item->string[i];
+        KhmerCharClass charClass = getKhmerCharClass(uc);
+
+        /* skip a before vowel, it was already processed */
+        if (charClass & CF_POS_BEFORE) {
+            continue;
+        }
+
+        /* skip coeng + ro, it was already processed */
+        if (i == coengRo) {
+            i += 1;
+            continue;
+        }
+
+        switch (charClass & CF_POS_MASK)
+        {
+            case CF_POS_ABOVE :
+                reordered[len] = uc;
+                properties[len] = AboveForm;
+                ++len;
+                break;
+
+            case CF_POS_AFTER :
+                reordered[len] = uc;
+                properties[len] = PostForm;
+                ++len;
+                break;
+
+            case CF_POS_BELOW :
+                reordered[len] = uc;
+                properties[len] = BelowForm;
+                ++len;
+                break;
+
+            default:
+                /* assign the correct flags to a coeng consonant
+                   Consonants of type 3 are taged as Post forms and those type 1 as below forms */
+                if ( (charClass & CF_COENG) && i + 1 < syllableEnd ) {
+                    unsigned char property = (getKhmerCharClass(item->string[i+1]) & CF_CLASS_MASK) == CC_CONSONANT3 ?
+                                              PostForm : BelowForm;
+                    reordered[len] = uc;
+                    properties[len] = property;
+                    ++len;
+                    i += 1;
+                    reordered[len] = item->string[i];
+                    properties[len] = property;
+                    ++len;
+                    break;
+                }
+
+                /* if a shifter is followed by an above vowel change the shifter to below form,
+                   an above vowel can have two possible positions i + 1 or i + 3
+                   (position i+1 corresponds to unicode 3, position i+3 to Unicode 4)
+                   and there is an extra rule for C_VOWEL_AA + C_SIGN_NIKAHIT also for two
+                   different positions, right after the shifter or after a vowel (Unicode 4) */
+                if ( (charClass & CF_SHIFTER) && (i + 1 < syllableEnd) ) {
+                    if (getKhmerCharClass(item->string[i+1]) & CF_ABOVE_VOWEL ) {
+                        reordered[len] = uc;
+                        properties[len] = BelowForm;
+                        ++len;
+                        break;
+                    }
+                    if (i + 2 < syllableEnd &&
+                        (item->string[i+1] == C_VOWEL_AA) &&
+                        (item->string[i+2] == C_SIGN_NIKAHIT) )
+                    {
+                        reordered[len] = uc;
+                        properties[len] = BelowForm;
+                        ++len;
+                        break;
+                    }
+                    if (i + 3 < syllableEnd && (getKhmerCharClass(item->string[i+3]) & CF_ABOVE_VOWEL) ) {
+                        reordered[len] = uc;
+                        properties[len] = BelowForm;
+                        ++len;
+                        break;
+                    }
+                    if (i + 4 < syllableEnd &&
+                        (item->string[i+3] == C_VOWEL_AA) &&
+                        (item->string[i+4] == C_SIGN_NIKAHIT) )
+                    {
+                        reordered[len] = uc;
+                        properties[len] = BelowForm;
+                        ++len;
+                        break;
+                    }
+                }
+
+                /* default - any other characters */
+                reordered[len] = uc;
+                ++len;
+                break;
+        } /* switch */
+    } /* for */
+
+    if (!item->font->klass->convertStringToGlyphIndices(item->font,
+                                                        reordered, len,
+                                                        item->glyphs, &item->num_glyphs,
+                                                        item->item.bidiLevel % 2))
+        return FALSE;
+
+
+    KHDEBUG("after shaping: len=%d", len);
+    for (i = 0; i < len; i++) {
+	item->attributes[i].mark = FALSE;
+	item->attributes[i].clusterStart = FALSE;
+	item->attributes[i].justification = 0;
+	item->attributes[i].zeroWidth = FALSE;
+	KHDEBUG("    %d: %4x property=%x", i, reordered[i], properties[i]);
+    }
+
+    /* now we have the syllable in the right order, and can start running it through open type. */
+
+#ifndef NO_OPENTYPE
+    if (openType) {
+ 	hb_uint32 where[16];
+        for (i = 0; i < len; ++i) {
+            where[i] = ~(PreSubstProperty
+                         | BelowSubstProperty
+                         | AboveSubstProperty
+                         | PostSubstProperty
+                         | CligProperty
+                         | PositioningProperties);
+            if (properties[i] == PreForm)
+                where[i] &= ~PreFormProperty;
+            else if (properties[i] == BelowForm)
+                where[i] &= ~BelowFormProperty;
+            else if (properties[i] == AboveForm)
+                where[i] &= ~AboveFormProperty;
+            else if (properties[i] == PostForm)
+                where[i] &= ~PostFormProperty;
+        }
+
+        HB_OpenTypeShape(item, where);
+        if (!HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE))
+            return FALSE;
+    } else
+#endif
+    {
+	KHDEBUG("Not using openType");
+        HB_HeuristicPosition(item);
+    }
+
+    item->attributes[0].clusterStart = TRUE;
+    return TRUE;
+}
+
+HB_Bool HB_KhmerShape(HB_ShaperItem *item)
+{
+    HB_Bool openType = FALSE;
+    unsigned short *logClusters = item->log_clusters;
+    int i;
+
+    HB_ShaperItem syllable = *item;
+    int first_glyph = 0;
+
+    int sstart = item->item.pos;
+    int end = sstart + item->item.length;
+
+    assert(item->item.script == HB_Script_Khmer);
+
+#ifndef NO_OPENTYPE
+    openType = HB_SelectScript(item, khmer_features);
+#endif
+
+    KHDEBUG("khmer_shape: from %d length %d", item->item.pos, item->item.length);
+    while (sstart < end) {
+        HB_Bool invalid;
+        int send = khmer_nextSyllableBoundary(item->string, sstart, end, &invalid);
+        KHDEBUG("syllable from %d, length %d, invalid=%s", sstart, send-sstart,
+               invalid ? "TRUE" : "FALSE");
+        syllable.item.pos = sstart;
+        syllable.item.length = send-sstart;
+        syllable.glyphs = item->glyphs + first_glyph;
+        syllable.attributes = item->attributes + first_glyph;
+        syllable.offsets = item->offsets + first_glyph;
+        syllable.advances = item->advances + first_glyph;
+        syllable.num_glyphs = item->num_glyphs - first_glyph;
+        if (!khmer_shape_syllable(openType, &syllable)) {
+            KHDEBUG("syllable shaping failed, syllable requests %d glyphs", syllable.num_glyphs);
+            item->num_glyphs += syllable.num_glyphs;
+            return FALSE;
+        }
+        /* fix logcluster array */
+        KHDEBUG("syllable:");
+        for (i = first_glyph; i < first_glyph + (int)syllable.num_glyphs; ++i)
+            KHDEBUG("        %d -> glyph %x", i, item->glyphs[i]);
+        KHDEBUG("    logclusters:");
+        for (i = sstart; i < send; ++i) {
+            KHDEBUG("        %d -> glyph %d", i, first_glyph);
+            logClusters[i-item->item.pos] = first_glyph;
+        }
+        sstart = send;
+        first_glyph += syllable.num_glyphs;
+    }
+    item->num_glyphs = first_glyph;
+    return TRUE;
+}
+
+void HB_KhmerAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes)
+{
+    int end = from + len;
+    const HB_UChar16 *uc = text + from;
+    hb_uint32 i = 0;
+    HB_UNUSED(script);
+    attributes += from;
+    while ( i < len ) {
+	HB_Bool invalid;
+	hb_uint32 boundary = khmer_nextSyllableBoundary( text, from+i, end, &invalid ) - from;
+
+	attributes[i].charStop = TRUE;
+
+	if ( boundary > len-1 ) boundary = len;
+	i++;
+	while ( i < boundary ) {
+	    attributes[i].charStop = FALSE;
+	    ++uc;
+	    ++i;
+	}
+	assert( i == boundary );
+    }
+}
+
diff --git a/third_party/harfbuzz/src/harfbuzz-myanmar.c b/third_party/harfbuzz/src/harfbuzz-myanmar.c
new file mode 100644
index 0000000..7cd82bb
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-myanmar.c
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-shaper-private.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+enum MymrCharClassValues
+{
+    Mymr_CC_RESERVED             =  0,
+    Mymr_CC_CONSONANT            =  1, /* Consonant of type 1, that has subscript form */
+    Mymr_CC_CONSONANT2           =  2, /* Consonant of type 2, that has no subscript form */
+    Mymr_CC_NGA	          =  3, /* Consonant NGA */
+    Mymr_CC_YA		          =  4, /* Consonant YA */
+    Mymr_CC_RA		          =  5, /* Consonant RA */
+    Mymr_CC_WA		          =  6, /* Consonant WA */
+    Mymr_CC_HA		          =  7, /* Consonant HA */
+    Mymr_CC_IND_VOWEL		  =  8, /* Independent vowel */
+    Mymr_CC_ZERO_WIDTH_NJ_MARK   =  9, /* Zero Width non joiner character (0x200C) */
+    Mymr_CC_VIRAMA               = 10, /* Subscript consonant combining character */
+    Mymr_CC_PRE_VOWEL   	  = 11, /* Dependent vowel, prebase (Vowel e) */
+    Mymr_CC_BELOW_VOWEL   	  = 12, /* Dependent vowel, prebase (Vowel u, uu) */
+    Mymr_CC_ABOVE_VOWEL   	  = 13, /* Dependent vowel, prebase (Vowel i, ii, ai) */
+    Mymr_CC_POST_VOWEL   	  = 14, /* Dependent vowel, prebase (Vowel aa) */
+    Mymr_CC_SIGN_ABOVE           = 15,
+    Mymr_CC_SIGN_BELOW           = 16,
+    Mymr_CC_SIGN_AFTER           = 17,
+    Mymr_CC_ZERO_WIDTH_J_MARK    = 18, /* Zero width joiner character */
+    Mymr_CC_COUNT                = 19  /* This is the number of character classes */
+};
+
+enum MymrCharClassFlags
+{
+    Mymr_CF_CLASS_MASK    = 0x0000FFFF,
+
+    Mymr_CF_CONSONANT     = 0x01000000,  /* flag to speed up comparing */
+    Mymr_CF_MEDIAL	   = 0x02000000,  /* flag to speed up comparing */
+    Mymr_CF_IND_VOWEL 	   = 0x04000000,  /* flag to speed up comparing */
+    Mymr_CF_DEP_VOWEL 	   = 0x08000000,  /* flag to speed up comparing */
+    Mymr_CF_DOTTED_CIRCLE = 0x10000000,  /* add a dotted circle if a character with this flag is the first in a syllable */
+    Mymr_CF_VIRAMA        = 0x20000000,  /* flag to speed up comparing */
+
+    /* position flags */
+    Mymr_CF_POS_BEFORE    = 0x00080000,
+    Mymr_CF_POS_BELOW     = 0x00040000,
+    Mymr_CF_POS_ABOVE     = 0x00020000,
+    Mymr_CF_POS_AFTER     = 0x00010000,
+    Mymr_CF_POS_MASK      = 0x000f0000,
+
+    Mymr_CF_AFTER_KINZI   = 0x00100000
+};
+
+/* Characters that get refrered to by name */
+enum MymrChar
+{
+    Mymr_C_SIGN_ZWNJ     = 0x200C,
+    Mymr_C_SIGN_ZWJ      = 0x200D,
+    Mymr_C_DOTTED_CIRCLE = 0x25CC,
+    Mymr_C_RA            = 0x101B,
+    Mymr_C_YA            = 0x101A,
+    Mymr_C_NGA           = 0x1004,
+    Mymr_C_VOWEL_E       = 0x1031,
+    Mymr_C_VIRAMA        = 0x1039
+};
+
+enum
+{
+    Mymr_xx = Mymr_CC_RESERVED,
+    Mymr_c1 = Mymr_CC_CONSONANT | Mymr_CF_CONSONANT | Mymr_CF_POS_BELOW,
+    Mymr_c2 = Mymr_CC_CONSONANT2 | Mymr_CF_CONSONANT,
+    Mymr_ng = Mymr_CC_NGA | Mymr_CF_CONSONANT | Mymr_CF_POS_ABOVE,
+    Mymr_ya = Mymr_CC_YA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_AFTER | Mymr_CF_AFTER_KINZI,
+    Mymr_ra = Mymr_CC_RA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_BEFORE,
+    Mymr_wa = Mymr_CC_WA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_BELOW,
+    Mymr_ha = Mymr_CC_HA | Mymr_CF_CONSONANT | Mymr_CF_MEDIAL | Mymr_CF_POS_BELOW,
+    Mymr_id = Mymr_CC_IND_VOWEL | Mymr_CF_IND_VOWEL,
+    Mymr_vi = Mymr_CC_VIRAMA | Mymr_CF_VIRAMA | Mymr_CF_POS_ABOVE | Mymr_CF_DOTTED_CIRCLE,
+    Mymr_dl = Mymr_CC_PRE_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_BEFORE | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI,
+    Mymr_db = Mymr_CC_BELOW_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_BELOW | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI,
+    Mymr_da = Mymr_CC_ABOVE_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_ABOVE | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI,
+    Mymr_dr = Mymr_CC_POST_VOWEL | Mymr_CF_DEP_VOWEL | Mymr_CF_POS_AFTER | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI,
+    Mymr_sa = Mymr_CC_SIGN_ABOVE | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_POS_ABOVE | Mymr_CF_AFTER_KINZI,
+    Mymr_sb = Mymr_CC_SIGN_BELOW | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_POS_BELOW | Mymr_CF_AFTER_KINZI,
+    Mymr_sp = Mymr_CC_SIGN_AFTER | Mymr_CF_DOTTED_CIRCLE | Mymr_CF_AFTER_KINZI
+};
+
+
+typedef int MymrCharClass;
+
+
+static const MymrCharClass mymrCharClasses[] =
+{
+    Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_ng, Mymr_c1, Mymr_c1, Mymr_c1,
+    Mymr_c1, Mymr_c1, Mymr_c2, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, /* 1000 - 100F */
+    Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1, Mymr_c1,
+    Mymr_c1, Mymr_c1, Mymr_ya, Mymr_ra, Mymr_c1, Mymr_wa, Mymr_c1, Mymr_ha, /* 1010 - 101F */
+    Mymr_c2, Mymr_c2, Mymr_xx, Mymr_id, Mymr_id, Mymr_id, Mymr_id, Mymr_id,
+    Mymr_xx, Mymr_id, Mymr_id, Mymr_xx, Mymr_dr, Mymr_da, Mymr_da, Mymr_db, /* 1020 - 102F */
+    Mymr_db, Mymr_dl, Mymr_da, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_sa, Mymr_sb,
+    Mymr_sp, Mymr_vi, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, /* 1030 - 103F */
+    Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx,
+    Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, /* 1040 - 104F */
+    Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx,
+    Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, Mymr_xx, /* 1050 - 105F */
+};
+
+static MymrCharClass
+getMyanmarCharClass (HB_UChar16 ch)
+{
+    if (ch == Mymr_C_SIGN_ZWJ)
+        return Mymr_CC_ZERO_WIDTH_J_MARK;
+
+    if (ch == Mymr_C_SIGN_ZWNJ)
+        return Mymr_CC_ZERO_WIDTH_NJ_MARK;
+
+    if (ch < 0x1000 || ch > 0x105f)
+        return Mymr_CC_RESERVED;
+
+    return mymrCharClasses[ch - 0x1000];
+}
+
+static const signed char mymrStateTable[][Mymr_CC_COUNT] =
+{
+/*   xx  c1, c2  ng  ya  ra  wa  ha  id zwnj vi  dl  db  da  dr  sa  sb  sp zwj */
+    { 1,  4,  4,  2,  4,  4,  4,  4, 24,  1, 27, 17, 18, 19, 20, 21,  1,  1,  4}, /*  0 - ground state */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /*  1 - exit state (or sp to the right of the syllable) */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  3, 17, 18, 19, 20, 21, -1, -1,  4}, /*  2 - NGA */
+    {-1,  4,  4,  4,  4,  4,  4,  4, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /*  3 - Virama after NGA */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  5, 17, 18, 19, 20, 21,  1,  1, -1}, /*  4 - Base consonant */
+    {-2,  6, -2, -2,  7,  8,  9, 10, -2, 23, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /*  5 - First virama */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 25, 17, 18, 19, 20, 21, -1, -1, -1}, /*  6 - c1 after virama */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1, -1, -1}, /*  7 - ya after virama */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1, -1, -1}, /*  8 - ra after virama */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1, -1, -1}, /*  9 - wa after virama */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 18, 19, 20, 21, -1, -1, -1}, /* 10 - ha after virama */
+    {-1, -1, -1, -1,  7,  8,  9, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 11 - Virama after NGA+zwj */
+    {-2, -2, -2, -2, -2, -2, 13, 14, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /* 12 - Second virama */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 17, 18, 19, 20, 21, -1, -1, -1}, /* 13 - wa after virama */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 18, 19, 20, 21, -1, -1, -1}, /* 14 - ha after virama */
+    {-2, -2, -2, -2, -2, -2, -2, 16, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /* 15 - Third virama */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 17, 18, 19, 20, 21, -1, -1, -1}, /* 16 - ha after virama */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 21,  1,  1, -1}, /* 17 - dl, Dependent vowel e */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 19, -1, 21,  1,  1, -1}, /* 18 - db, Dependent vowel u,uu */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1,  1,  1, -1}, /* 19 - da, Dependent vowel i,ii,ai */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1,  1,  1, -1}, /* 20 - dr, Dependent vowel aa */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1,  1, -1}, /* 21 - sa, Sign anusvara */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 22 - atha */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1,  1, -1}, /* 23 - zwnj for atha */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1, -1}, /* 24 - Independent vowel */
+    {-2, -2, -2, -2, 26, 26, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2}, /* 25 - Virama after subscript consonant */
+    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 17, 18, 19, 20, 21, -1,  1, -1}, /* 26 - ra/ya after subscript consonant + virama */
+    {-1,  6, -1, -1,  7,  8,  9, 10, -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 27 - Virama after ground state */
+/* exit state -2 is for invalid order of medials and combination of invalids
+   with virama where virama should treat as start of next syllable
+ */
+};
+
+
+
+/*#define MYANMAR_DEBUG */
+#ifdef MYANMAR_DEBUG
+#define MMDEBUG qDebug
+#else
+#define MMDEBUG if(0) printf
+#endif
+
+/*
+//  Given an input string of characters and a location in which to start looking
+//  calculate, using the state table, which one is the last character of the syllable
+//  that starts in the starting position.
+*/
+static int myanmar_nextSyllableBoundary(const HB_UChar16 *s, int start, int end, HB_Bool *invalid)
+{
+    const HB_UChar16 *uc = s + start;
+    int state = 0;
+    int pos = start;
+    *invalid = FALSE;
+
+    while (pos < end) {
+        MymrCharClass charClass = getMyanmarCharClass(*uc);
+        state = mymrStateTable[state][charClass & Mymr_CF_CLASS_MASK];
+        if (pos == start)
+            *invalid = (HB_Bool)(charClass & Mymr_CF_DOTTED_CIRCLE);
+
+        MMDEBUG("state[%d]=%d class=%8x (uc=%4x)", pos - start, state, charClass, *uc);
+
+        if (state < 0) {
+            if (state < -1)
+                --pos;
+            break;
+        }
+        ++uc;
+        ++pos;
+    }
+    return pos;
+}
+
+#ifndef NO_OPENTYPE
+/* ###### might have to change order of above and below forms and substitutions,
+   but according to Unicode below comes before above */
+static const HB_OpenTypeFeature myanmar_features[] = {
+    { HB_MAKE_TAG('p', 'r', 'e', 'f'), PreFormProperty },
+    { HB_MAKE_TAG('b', 'l', 'w', 'f'), BelowFormProperty },
+    { HB_MAKE_TAG('a', 'b', 'v', 'f'), AboveFormProperty },
+    { HB_MAKE_TAG('p', 's', 't', 'f'), PostFormProperty },
+    { HB_MAKE_TAG('p', 'r', 'e', 's'), PreSubstProperty },
+    { HB_MAKE_TAG('b', 'l', 'w', 's'), BelowSubstProperty },
+    { HB_MAKE_TAG('a', 'b', 'v', 's'), AboveSubstProperty },
+    { HB_MAKE_TAG('p', 's', 't', 's'), PostSubstProperty },
+    { HB_MAKE_TAG('r', 'l', 'i', 'g'), CligProperty }, /* Myanmar1 uses this instead of the other features */
+    { 0, 0 }
+};
+#endif
+
+
+/*
+// Visual order before shaping should be:
+//
+//    [Vowel Mark E]
+//    [Virama + Medial Ra]
+//    [Base]
+//    [Virama + Consonant]
+//    [Nga + Virama] (Kinzi) ### should probably come before post forms (medial ya)
+//    [Vowels]
+//    [Marks]
+//
+// This means that we can keep the logical order apart from having to
+// move the pre vowel, medial ra and kinzi
+*/
+
+static HB_Bool myanmar_shape_syllable(HB_Bool openType, HB_ShaperItem *item, HB_Bool invalid)
+{
+    /*
+//    MMDEBUG("\nsyllable from %d len %d, str='%s'", item->item.pos, item->item.length,
+//	    item->string->mid(item->from, item->length).toUtf8().data());
+    */
+
+#ifndef NO_OPENTYPE
+    const int availableGlyphs = item->num_glyphs;
+#endif
+    const HB_UChar16 *uc = item->string + item->item.pos;
+    int vowel_e = -1;
+    int kinzi = -1;
+    int medial_ra = -1;
+    int base = -1;
+    int i;
+    int len = 0;
+    unsigned short reordered[32];
+    unsigned char properties[32];
+    enum {
+	AboveForm = 0x01,
+	PreForm = 0x02,
+	PostForm = 0x04,
+	BelowForm = 0x08
+    };
+    HB_Bool lastWasVirama = FALSE;
+    int basePos = -1;
+
+    memset(properties, 0, 32*sizeof(unsigned char));
+
+    /* according to the table the max length of a syllable should be around 14 chars */
+    assert(item->item.length < 32);
+
+#ifdef MYANMAR_DEBUG
+    printf("original:");
+    for (i = 0; i < (int)item->item.length; i++) {
+        printf("    %d: %4x", i, uc[i]);
+    }
+#endif
+    for (i = 0; i < (int)item->item.length; ++i) {
+        HB_UChar16 chr = uc[i];
+
+        if (chr == Mymr_C_VOWEL_E) {
+            vowel_e = i;
+            continue;
+        }
+        if (i == 0
+            && chr == Mymr_C_NGA
+            && i + 2 < (int)item->item.length
+            && uc[i+1] == Mymr_C_VIRAMA) {
+            int mc = getMyanmarCharClass(uc[i+2]);
+            /*MMDEBUG("maybe kinzi: mc=%x", mc);*/
+            if ((mc & Mymr_CF_CONSONANT) == Mymr_CF_CONSONANT) {
+                kinzi = i;
+                continue;
+            }
+        }
+        if (base >= 0
+            && chr == Mymr_C_VIRAMA
+            && i + 1 < (int)item->item.length
+            && uc[i+1] == Mymr_C_RA) {
+            medial_ra = i;
+            continue;
+        }
+        if (base < 0)
+            base = i;
+    }
+
+    MMDEBUG("\n  base=%d, vowel_e=%d, kinzi=%d, medial_ra=%d", base, vowel_e, kinzi, medial_ra);
+    /* write vowel_e if found */
+    if (vowel_e >= 0) {
+        reordered[0] = Mymr_C_VOWEL_E;
+        len = 1;
+    }
+    /* write medial_ra */
+    if (medial_ra >= 0) {
+        reordered[len] = Mymr_C_VIRAMA;
+        reordered[len+1] = Mymr_C_RA;
+        properties[len] = PreForm;
+        properties[len+1] = PreForm;
+        len += 2;
+    }
+
+    /* shall we add a dotted circle?
+       If in the position in which the base should be (first char in the string) there is
+       a character that has the Dotted circle flag (a character that cannot be a base)
+       then write a dotted circle */
+    if (invalid) {
+        reordered[len] = C_DOTTED_CIRCLE;
+        ++len;
+    }
+
+    /* copy the rest of the syllable to the output, inserting the kinzi
+       at the correct place */
+    for (i = 0; i < (int)item->item.length; ++i) {
+        hb_uint16 chr = uc[i];
+        MymrCharClass cc;
+        if (i == vowel_e)
+            continue;
+        if (i == medial_ra || i == kinzi) {
+            ++i;
+            continue;
+        }
+
+        cc = getMyanmarCharClass(uc[i]);
+        if (kinzi >= 0 && i > base && (cc & Mymr_CF_AFTER_KINZI)) {
+            reordered[len] = Mymr_C_NGA;
+            reordered[len+1] = Mymr_C_VIRAMA;
+            properties[len-1] = AboveForm;
+            properties[len] = AboveForm;
+            len += 2;
+            kinzi = -1;
+        }
+
+        if (lastWasVirama) {
+            int prop = 0;
+            switch(cc & Mymr_CF_POS_MASK) {
+            case Mymr_CF_POS_BEFORE:
+                prop = PreForm;
+                break;
+            case Mymr_CF_POS_BELOW:
+                prop = BelowForm;
+                break;
+            case Mymr_CF_POS_ABOVE:
+                prop = AboveForm;
+                break;
+            case Mymr_CF_POS_AFTER:
+                prop = PostForm;
+                break;
+            default:
+                break;
+            }
+            properties[len-1] = prop;
+            properties[len] = prop;
+            if(basePos >= 0 && basePos == len-2)
+                properties[len-2] = prop;
+        }
+        lastWasVirama = (chr == Mymr_C_VIRAMA);
+        if(i == base)
+            basePos = len;
+
+        if ((chr != Mymr_C_SIGN_ZWNJ && chr != Mymr_C_SIGN_ZWJ) || !len) {
+            reordered[len] = chr;
+            ++len;
+        }
+    }
+    if (kinzi >= 0) {
+        reordered[len] = Mymr_C_NGA;
+        reordered[len+1] = Mymr_C_VIRAMA;
+        properties[len] = AboveForm;
+        properties[len+1] = AboveForm;
+        len += 2;
+    }
+
+    if (!item->font->klass->convertStringToGlyphIndices(item->font,
+                                                        reordered, len,
+                                                        item->glyphs, &item->num_glyphs,
+                                                        item->item.bidiLevel % 2))
+        return FALSE;
+
+    MMDEBUG("after shaping: len=%d", len);
+    for (i = 0; i < len; i++) {
+	item->attributes[i].mark = FALSE;
+	item->attributes[i].clusterStart = FALSE;
+	item->attributes[i].justification = 0;
+	item->attributes[i].zeroWidth = FALSE;
+	MMDEBUG("    %d: %4x property=%x", i, reordered[i], properties[i]);
+    }
+
+    /* now we have the syllable in the right order, and can start running it through open type. */
+
+#ifndef NO_OPENTYPE
+    if (openType) {
+	unsigned short logClusters[32];
+ 	hb_uint32 where[32];
+
+	for (i = 0; i < len; ++i)
+	    logClusters[i] = i;
+
+        for (i = 0; i < len; ++i) {
+            where[i] = ~(PreSubstProperty
+                         | BelowSubstProperty
+                         | AboveSubstProperty
+                         | PostSubstProperty
+                         | CligProperty
+                         | PositioningProperties);
+            if (properties[i] & PreForm)
+                where[i] &= ~PreFormProperty;
+            if (properties[i] & BelowForm)
+                where[i] &= ~BelowFormProperty;
+            if (properties[i] & AboveForm)
+                where[i] &= ~AboveFormProperty;
+            if (properties[i] & PostForm)
+                where[i] &= ~PostFormProperty;
+        }
+
+        HB_OpenTypeShape(item, where);
+        if (!HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE))
+            return FALSE;
+    } else
+#endif
+    {
+	MMDEBUG("Not using openType");
+        HB_HeuristicPosition(item);
+    }
+
+    item->attributes[0].clusterStart = TRUE;
+    return TRUE;
+}
+
+HB_Bool HB_MyanmarShape(HB_ShaperItem *item)
+{
+    HB_Bool openType = FALSE;
+    unsigned short *logClusters = item->log_clusters;
+
+    HB_ShaperItem syllable = *item;
+    int first_glyph = 0;
+
+    int sstart = item->item.pos;
+    int end = sstart + item->item.length;
+    int i = 0;
+
+    assert(item->item.script == HB_Script_Myanmar);
+#ifndef NO_OPENTYPE
+    openType = HB_SelectScript(item, myanmar_features);
+#endif
+
+    MMDEBUG("myanmar_shape: from %d length %d", item->item.pos, item->item.length);
+    while (sstart < end) {
+        HB_Bool invalid;
+        int send = myanmar_nextSyllableBoundary(item->string, sstart, end, &invalid);
+        MMDEBUG("syllable from %d, length %d, invalid=%s", sstart, send-sstart,
+               invalid ? "TRUE" : "FALSE");
+        syllable.item.pos = sstart;
+        syllable.item.length = send-sstart;
+        syllable.glyphs = item->glyphs + first_glyph;
+        syllable.attributes = item->attributes + first_glyph;
+        syllable.advances = item->advances + first_glyph;
+        syllable.offsets = item->offsets + first_glyph;
+        syllable.num_glyphs = item->num_glyphs - first_glyph;
+        if (!myanmar_shape_syllable(openType, &syllable, invalid)) {
+            MMDEBUG("syllable shaping failed, syllable requests %d glyphs", syllable.num_glyphs);
+            item->num_glyphs += syllable.num_glyphs;
+            return FALSE;
+        }
+
+        /* fix logcluster array */
+        MMDEBUG("syllable:");
+        for (i = first_glyph; i < first_glyph + (int)syllable.num_glyphs; ++i)
+            MMDEBUG("        %d -> glyph %x", i, item->glyphs[i]);
+        MMDEBUG("    logclusters:");
+        for (i = sstart; i < send; ++i) {
+            MMDEBUG("        %d -> glyph %d", i, first_glyph);
+            logClusters[i-item->item.pos] = first_glyph;
+        }
+        sstart = send;
+        first_glyph += syllable.num_glyphs;
+    }
+    item->num_glyphs = first_glyph;
+    return TRUE;
+}
+
+void HB_MyanmarAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes)
+{
+    int end = from + len;
+    const HB_UChar16 *uc = text + from;
+    hb_uint32 i = 0;
+    HB_UNUSED(script);
+    attributes += from;
+    while (i < len) {
+	HB_Bool invalid;
+	hb_uint32 boundary = myanmar_nextSyllableBoundary(text, from+i, end, &invalid) - from;
+
+	attributes[i].charStop = TRUE;
+        if (i)
+            attributes[i-1].lineBreakType = HB_Break;
+
+	if (boundary > len-1)
+            boundary = len;
+	i++;
+	while (i < boundary) {
+	    attributes[i].charStop = FALSE;
+	    ++uc;
+	    ++i;
+	}
+	assert(i == boundary);
+    }
+}
+
diff --git a/third_party/harfbuzz/src/harfbuzz-open-private.h b/third_party/harfbuzz/src/harfbuzz-open-private.h
new file mode 100644
index 0000000..73dd383
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-open-private.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_OPEN_PRIVATE_H
+#define HARFBUZZ_OPEN_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-open.h"
+#include "harfbuzz-gsub-private.h"
+#include "harfbuzz-gpos-private.h"
+
+HB_BEGIN_HEADER
+
+
+struct  HB_SubTable_
+{
+  union
+  {
+    HB_GSUB_SubTable  gsub;
+    HB_GPOS_SubTable  gpos;
+  } st;
+};
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_ScriptList( HB_ScriptList* sl,
+			   HB_Stream     input );
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_FeatureList( HB_FeatureList* fl,
+			    HB_Stream         input );
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_LookupList( HB_LookupList*  ll,
+			   HB_Stream        input,
+			   HB_Type         type );
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_Coverage( HB_Coverage* c,
+			 HB_Stream      input );
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_ClassDefinition( HB_ClassDefinition* cd,
+				HB_UShort             limit,
+				HB_Stream             input );
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_EmptyOrClassDefinition( HB_ClassDefinition* cd,
+					       HB_UShort             limit,
+					       HB_UInt              class_offset,
+					       HB_UInt              base_offset,
+					       HB_Stream             input );
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_Device( HB_Device* d,
+		       HB_Stream    input );
+
+HB_INTERNAL void  _HB_OPEN_Free_ScriptList( HB_ScriptList*  sl );
+HB_INTERNAL void  _HB_OPEN_Free_FeatureList( HB_FeatureList*  fl );
+HB_INTERNAL void  _HB_OPEN_Free_LookupList( HB_LookupList*  ll,
+		       HB_Type         type );
+
+HB_INTERNAL void  _HB_OPEN_Free_Coverage( HB_Coverage*  c );
+HB_INTERNAL void  _HB_OPEN_Free_ClassDefinition( HB_ClassDefinition*  cd );
+HB_INTERNAL void  _HB_OPEN_Free_Device( HB_Device*  d );
+
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Coverage_Index( HB_Coverage* c,
+			  HB_UShort      glyphID,
+			  HB_UShort*     index );
+HB_INTERNAL HB_Error
+_HB_OPEN_Get_Class( HB_ClassDefinition* cd,
+		     HB_UShort             glyphID,
+		    HB_UShort*          klass,
+		     HB_UShort*            index );
+HB_INTERNAL HB_Error
+_HB_OPEN_Get_Device( HB_Device* d,
+		      HB_UShort    size,
+		      HB_Short*    value );
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_OPEN_PRIVATE_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-open.c b/third_party/harfbuzz/src/harfbuzz-open.c
new file mode 100644
index 0000000..0fe1e4d
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-open.c
@@ -0,0 +1,1416 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-open-private.h"
+
+
+/***************************
+ * Script related functions
+ ***************************/
+
+
+/* LangSys */
+
+static HB_Error  Load_LangSys( HB_LangSys*  ls,
+			       HB_Stream     stream )
+{
+  HB_Error   error;
+  HB_UShort  n, count;
+  HB_UShort* fi;
+
+
+  if ( ACCESS_Frame( 6L ) )
+    return error;
+
+  ls->LookupOrderOffset    = GET_UShort();    /* should be 0 */
+  ls->ReqFeatureIndex      = GET_UShort();
+  count = ls->FeatureCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ls->FeatureIndex = NULL;
+
+  if ( ALLOC_ARRAY( ls->FeatureIndex, count, HB_UShort ) )
+    return error;
+
+  if ( ACCESS_Frame( count * 2L ) )
+  {
+    FREE( ls->FeatureIndex );
+    return error;
+  }
+
+  fi = ls->FeatureIndex;
+
+  for ( n = 0; n < count; n++ )
+    fi[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+}
+
+
+static void  Free_LangSys( HB_LangSys*  ls )
+{
+  FREE( ls->FeatureIndex );
+}
+
+
+/* Script */
+
+static HB_Error  Load_Script( HB_ScriptTable*  s,
+			      HB_Stream    stream )
+{
+  HB_Error   error;
+  HB_UShort  n, m, count;
+  HB_UInt   cur_offset, new_offset, base_offset;
+
+  HB_LangSysRecord*  lsr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  new_offset = GET_UShort() + base_offset;
+
+  FORGET_Frame();
+
+  if ( new_offset != base_offset )        /* not a NULL offset */
+  {
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_LangSys( &s->DefaultLangSys,
+				 stream ) ) != HB_Err_Ok )
+      return error;
+    (void)FILE_Seek( cur_offset );
+  }
+  else
+  {
+    /* we create a DefaultLangSys table with no entries */
+
+    s->DefaultLangSys.LookupOrderOffset = 0;
+    s->DefaultLangSys.ReqFeatureIndex   = 0xFFFF;
+    s->DefaultLangSys.FeatureCount      = 0;
+    s->DefaultLangSys.FeatureIndex      = NULL;
+  }
+
+  if ( ACCESS_Frame( 2L ) )
+    goto Fail2;
+
+  count = s->LangSysCount = GET_UShort();
+
+  /* safety check; otherwise the official handling of TrueType Open
+     fonts won't work */
+
+  if ( s->LangSysCount == 0 && s->DefaultLangSys.FeatureCount == 0 )
+  {
+    error = HB_Err_Not_Covered;
+    goto Fail2;
+  }
+
+  FORGET_Frame();
+
+  s->LangSysRecord = NULL;
+
+  if ( ALLOC_ARRAY( s->LangSysRecord, count, HB_LangSysRecord ) )
+    goto Fail2;
+
+  lsr = s->LangSysRecord;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 6L ) )
+      goto Fail1;
+
+    lsr[n].LangSysTag = GET_ULong();
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_LangSys( &lsr[n].LangSys, stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_LangSys( &lsr[m].LangSys );
+
+  FREE( s->LangSysRecord );
+
+Fail2:
+  Free_LangSys( &s->DefaultLangSys );
+  return error;
+}
+
+
+static void  Free_Script( HB_ScriptTable*  s )
+{
+  HB_UShort           n, count;
+
+  HB_LangSysRecord*  lsr;
+
+
+  Free_LangSys( &s->DefaultLangSys );
+
+  if ( s->LangSysRecord )
+  {
+    count = s->LangSysCount;
+    lsr   = s->LangSysRecord;
+
+    for ( n = 0; n < count; n++ )
+      Free_LangSys( &lsr[n].LangSys );
+
+    FREE( lsr );
+  }
+}
+
+
+/* ScriptList */
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_ScriptList( HB_ScriptList* sl,
+			   HB_Stream        stream )
+{
+  HB_Error   error;
+
+  HB_UShort          n, script_count;
+  HB_UInt           cur_offset, new_offset, base_offset;
+
+  HB_ScriptRecord*  sr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  script_count = GET_UShort();
+
+  FORGET_Frame();
+
+  sl->ScriptRecord = NULL;
+
+  if ( ALLOC_ARRAY( sl->ScriptRecord, script_count, HB_ScriptRecord ) )
+    return error;
+
+  sr = sl->ScriptRecord;
+
+  sl->ScriptCount= 0;
+  for ( n = 0; n < script_count; n++ )
+  {
+    if ( ACCESS_Frame( 6L ) )
+      goto Fail;
+
+    sr[sl->ScriptCount].ScriptTag = GET_ULong();
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+
+    if ( FILE_Seek( new_offset ) )
+      goto Fail;
+
+    error = Load_Script( &sr[sl->ScriptCount].Script, stream );
+    if ( error == HB_Err_Ok )
+      sl->ScriptCount += 1;
+    else if ( error != HB_Err_Not_Covered )
+      goto Fail;
+
+    (void)FILE_Seek( cur_offset );
+  }
+
+  /* Empty tables are harmless and generated by fontforge.
+   * See http://bugzilla.gnome.org/show_bug.cgi?id=347073
+   */
+#if 0
+  if ( sl->ScriptCount == 0 )
+  {
+    error = ERR(HB_Err_Invalid_SubTable);
+    goto Fail;
+  }
+#endif
+  
+  return HB_Err_Ok;
+
+Fail:
+  for ( n = 0; n < sl->ScriptCount; n++ )
+    Free_Script( &sr[n].Script );
+
+  FREE( sl->ScriptRecord );
+  return error;
+}
+
+
+HB_INTERNAL void
+_HB_OPEN_Free_ScriptList( HB_ScriptList* sl )
+{
+  HB_UShort          n, count;
+
+  HB_ScriptRecord*  sr;
+
+
+  if ( sl->ScriptRecord )
+  {
+    count = sl->ScriptCount;
+    sr    = sl->ScriptRecord;
+
+    for ( n = 0; n < count; n++ )
+      Free_Script( &sr[n].Script );
+
+    FREE( sr );
+  }
+}
+
+
+
+/*********************************
+ * Feature List related functions
+ *********************************/
+
+
+/* Feature */
+
+static HB_Error  Load_Feature( HB_Feature*  f,
+			       HB_Stream     stream )
+{
+  HB_Error   error;
+
+  HB_UShort   n, count;
+
+  HB_UShort*  lli;
+
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  f->FeatureParams           = GET_UShort();    /* should be 0 */
+  count = f->LookupListCount = GET_UShort();
+
+  FORGET_Frame();
+
+  f->LookupListIndex = NULL;
+
+  if ( ALLOC_ARRAY( f->LookupListIndex, count, HB_UShort ) )
+    return error;
+
+  lli = f->LookupListIndex;
+
+  if ( ACCESS_Frame( count * 2L ) )
+  {
+    FREE( f->LookupListIndex );
+    return error;
+  }
+
+  for ( n = 0; n < count; n++ )
+    lli[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+}
+
+
+static void  Free_Feature( HB_Feature*  f )
+{
+  FREE( f->LookupListIndex );
+}
+
+
+/* FeatureList */
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_FeatureList( HB_FeatureList* fl,
+			    HB_Stream         stream )
+{
+  HB_Error   error;
+
+  HB_UShort           n, m, count;
+  HB_UInt            cur_offset, new_offset, base_offset;
+
+  HB_FeatureRecord*  fr;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = fl->FeatureCount = GET_UShort();
+
+  FORGET_Frame();
+
+  fl->FeatureRecord = NULL;
+
+  if ( ALLOC_ARRAY( fl->FeatureRecord, count, HB_FeatureRecord ) )
+    return error;
+  if ( ALLOC_ARRAY( fl->ApplyOrder, count, HB_UShort ) )
+    goto Fail2;
+  
+  fl->ApplyCount = 0;
+
+  fr = fl->FeatureRecord;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 6L ) )
+      goto Fail1;
+
+    fr[n].FeatureTag = GET_ULong();
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_Feature( &fr[n].Feature, stream ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  for ( m = 0; m < n; m++ )
+    Free_Feature( &fr[m].Feature );
+
+  FREE( fl->ApplyOrder );
+
+Fail2:
+  FREE( fl->FeatureRecord );
+
+  return error;
+}
+
+
+HB_INTERNAL void
+_HB_OPEN_Free_FeatureList( HB_FeatureList*  fl )
+{
+  HB_UShort           n, count;
+
+  HB_FeatureRecord*  fr;
+
+
+  if ( fl->FeatureRecord )
+  {
+    count = fl->FeatureCount;
+    fr    = fl->FeatureRecord;
+
+    for ( n = 0; n < count; n++ )
+      Free_Feature( &fr[n].Feature );
+
+    FREE( fr );
+  }
+  
+  FREE( fl->ApplyOrder );
+}
+
+
+
+/********************************
+ * Lookup List related functions
+ ********************************/
+
+/* the subroutines of the following two functions are defined in
+   ftxgsub.c and ftxgpos.c respectively                          */
+
+
+/* SubTable */
+
+static HB_Error  Load_SubTable( HB_SubTable*  st,
+				HB_Stream     stream,
+				HB_Type       table_type,
+				HB_UShort     lookup_type )
+{
+  if ( table_type == HB_Type_GSUB )
+    return _HB_GSUB_Load_SubTable ( &st->st.gsub, stream, lookup_type );
+  else
+    return _HB_GPOS_Load_SubTable ( &st->st.gpos, stream, lookup_type );
+}
+
+
+static void  Free_SubTable( HB_SubTable*  st,
+			    HB_Type       table_type,
+			    HB_UShort      lookup_type )
+{
+  if ( table_type == HB_Type_GSUB )
+    _HB_GSUB_Free_SubTable ( &st->st.gsub, lookup_type );
+  else
+    _HB_GPOS_Free_SubTable ( &st->st.gpos, lookup_type );
+}
+
+
+/* Lookup */
+
+static HB_Error  Load_Lookup( HB_Lookup*   l,
+			      HB_Stream     stream,
+			      HB_Type      type )
+{
+  HB_Error   error;
+
+  HB_UShort      n, m, count;
+  HB_UInt       cur_offset, new_offset, base_offset;
+
+  HB_SubTable*  st;
+
+  HB_Bool        is_extension = FALSE;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 6L ) )
+    return error;
+
+  l->LookupType            = GET_UShort();
+  l->LookupFlag            = GET_UShort();
+  count = l->SubTableCount = GET_UShort();
+
+  FORGET_Frame();
+
+  l->SubTable = NULL;
+
+  if ( ALLOC_ARRAY( l->SubTable, count, HB_SubTable ) )
+    return error;
+
+  st = l->SubTable;
+
+  if ( ( type == HB_Type_GSUB && l->LookupType == HB_GSUB_LOOKUP_EXTENSION ) ||
+       ( type == HB_Type_GPOS && l->LookupType == HB_GPOS_LOOKUP_EXTENSION ) )
+    is_extension = TRUE;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+
+    if ( is_extension )
+    {
+      if ( FILE_Seek( new_offset ) || ACCESS_Frame( 8L ) )
+	goto Fail;
+
+      if (GET_UShort() != 1) /* format should be 1 */
+	goto Fail;
+
+      l->LookupType = GET_UShort();
+      new_offset += GET_ULong();
+
+      FORGET_Frame();
+    }
+
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_SubTable( &st[n], stream,
+				  type, l->LookupType ) ) != HB_Err_Ok )
+      goto Fail;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail:
+  for ( m = 0; m < n; m++ )
+    Free_SubTable( &st[m], type, l->LookupType );
+
+  FREE( l->SubTable );
+  return error;
+}
+
+
+static void  Free_Lookup( HB_Lookup*   l,
+			  HB_Type      type)
+{
+  HB_UShort      n, count;
+
+  HB_SubTable*  st;
+
+
+  if ( l->SubTable )
+  {
+    count = l->SubTableCount;
+    st    = l->SubTable;
+
+    for ( n = 0; n < count; n++ )
+      Free_SubTable( &st[n], type, l->LookupType );
+
+    FREE( st );
+  }
+}
+
+
+/* LookupList */
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_LookupList( HB_LookupList* ll,
+			   HB_Stream        stream,
+			   HB_Type         type )
+{
+  HB_Error   error;
+
+  HB_UShort    n, m, count;
+  HB_UInt     cur_offset, new_offset, base_offset;
+
+  HB_Lookup*  l;
+
+
+  base_offset = FILE_Pos();
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = ll->LookupCount = GET_UShort();
+
+  FORGET_Frame();
+
+  ll->Lookup = NULL;
+
+  if ( ALLOC_ARRAY( ll->Lookup, count, HB_Lookup ) )
+    return error;
+  if ( ALLOC_ARRAY( ll->Properties, count, HB_UInt ) )
+    goto Fail2;
+
+  l = ll->Lookup;
+
+  for ( n = 0; n < count; n++ )
+  {
+    if ( ACCESS_Frame( 2L ) )
+      goto Fail1;
+
+    new_offset = GET_UShort() + base_offset;
+
+    FORGET_Frame();
+
+    cur_offset = FILE_Pos();
+    if ( FILE_Seek( new_offset ) ||
+	 ( error = Load_Lookup( &l[n], stream, type ) ) != HB_Err_Ok )
+      goto Fail1;
+    (void)FILE_Seek( cur_offset );
+  }
+
+  return HB_Err_Ok;
+
+Fail1:
+  FREE( ll->Properties );
+
+  for ( m = 0; m < n; m++ )
+    Free_Lookup( &l[m], type );
+
+Fail2:
+  FREE( ll->Lookup );
+  return error;
+}
+
+
+HB_INTERNAL void
+_HB_OPEN_Free_LookupList( HB_LookupList* ll,
+		       HB_Type         type )
+{
+  HB_UShort    n, count;
+
+  HB_Lookup*  l;
+
+
+  FREE( ll->Properties );
+
+  if ( ll->Lookup )
+  {
+    count = ll->LookupCount;
+    l     = ll->Lookup;
+
+    for ( n = 0; n < count; n++ )
+      Free_Lookup( &l[n], type );
+
+    FREE( l );
+  }
+}
+
+
+
+/*****************************
+ * Coverage related functions
+ *****************************/
+
+
+/* CoverageFormat1 */
+
+static HB_Error  Load_Coverage1( HB_CoverageFormat1*  cf1,
+				 HB_Stream             stream )
+{
+  HB_Error   error;
+
+  HB_UShort  n, count;
+
+  HB_UShort* ga;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = cf1->GlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cf1->GlyphArray = NULL;
+
+  if ( ALLOC_ARRAY( cf1->GlyphArray, count, HB_UShort ) )
+    return error;
+
+  ga = cf1->GlyphArray;
+
+  if ( ACCESS_Frame( count * 2L ) )
+  {
+    FREE( cf1->GlyphArray );
+    return error;
+  }
+
+  for ( n = 0; n < count; n++ )
+    ga[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+}
+
+
+static void  Free_Coverage1( HB_CoverageFormat1*  cf1)
+{
+  FREE( cf1->GlyphArray );
+}
+
+
+/* CoverageFormat2 */
+
+static HB_Error  Load_Coverage2( HB_CoverageFormat2*  cf2,
+				 HB_Stream             stream )
+{
+  HB_Error   error;
+
+  HB_UShort         n, count;
+
+  HB_RangeRecord*  rr;
+
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = cf2->RangeCount = GET_UShort();
+
+  FORGET_Frame();
+
+  cf2->RangeRecord = NULL;
+
+  if ( ALLOC_ARRAY( cf2->RangeRecord, count, HB_RangeRecord ) )
+    return error;
+
+  rr = cf2->RangeRecord;
+
+  if ( ACCESS_Frame( count * 6L ) )
+    goto Fail;
+
+  for ( n = 0; n < count; n++ )
+  {
+    rr[n].Start              = GET_UShort();
+    rr[n].End                = GET_UShort();
+    rr[n].StartCoverageIndex = GET_UShort();
+
+    /* sanity check; we are limited to 16bit integers */
+    if ( rr[n].Start > rr[n].End ||
+	 ( rr[n].End - rr[n].Start + (long)rr[n].StartCoverageIndex ) >=
+	   0x10000L )
+    {
+      error = ERR(HB_Err_Invalid_SubTable);
+      goto Fail;
+    }
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail:
+  FREE( cf2->RangeRecord );
+  return error;
+}
+
+
+static void  Free_Coverage2( HB_CoverageFormat2*  cf2 )
+{
+  FREE( cf2->RangeRecord );
+}
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_Coverage( HB_Coverage* c,
+			 HB_Stream      stream )
+{
+  HB_Error   error;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  c->CoverageFormat = GET_UShort();
+
+  FORGET_Frame();
+
+  switch ( c->CoverageFormat )
+  {
+  case 1:  return Load_Coverage1( &c->cf.cf1, stream );
+  case 2:  return Load_Coverage2( &c->cf.cf2, stream );
+  default: return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;               /* never reached */
+}
+
+
+HB_INTERNAL void
+_HB_OPEN_Free_Coverage( HB_Coverage* c )
+{
+  switch ( c->CoverageFormat )
+  {
+  case 1:  Free_Coverage1( &c->cf.cf1 ); break;
+  case 2:  Free_Coverage2( &c->cf.cf2 ); break;
+  default:					 break;
+  }
+}
+
+
+static HB_Error  Coverage_Index1( HB_CoverageFormat1*  cf1,
+				  HB_UShort             glyphID,
+				  HB_UShort*            index )
+{
+  HB_UShort min, max, new_min, new_max, middle;
+
+  HB_UShort*  array = cf1->GlyphArray;
+
+
+  /* binary search */
+
+  if ( cf1->GlyphCount == 0 )
+    return HB_Err_Not_Covered;
+
+  new_min = 0;
+  new_max = cf1->GlyphCount - 1;
+
+  do
+  {
+    min = new_min;
+    max = new_max;
+
+    /* we use (min + max) / 2 = max - (max - min) / 2  to avoid
+       overflow and rounding errors                             */
+
+    middle = max - ( ( max - min ) >> 1 );
+
+    if ( glyphID == array[middle] )
+    {
+      *index = middle;
+      return HB_Err_Ok;
+    }
+    else if ( glyphID < array[middle] )
+    {
+      if ( middle == min )
+	break;
+      new_max = middle - 1;
+    }
+    else
+    {
+      if ( middle == max )
+	break;
+      new_min = middle + 1;
+    }
+  } while ( min < max );
+
+  return HB_Err_Not_Covered;
+}
+
+
+static HB_Error  Coverage_Index2( HB_CoverageFormat2*  cf2,
+				  HB_UShort             glyphID,
+				  HB_UShort*            index )
+{
+  HB_UShort         min, max, new_min, new_max, middle;
+
+  HB_RangeRecord*  rr = cf2->RangeRecord;
+
+
+  /* binary search */
+
+  if ( cf2->RangeCount == 0 )
+    return HB_Err_Not_Covered;
+
+  new_min = 0;
+  new_max = cf2->RangeCount - 1;
+
+  do
+  {
+    min = new_min;
+    max = new_max;
+
+    /* we use (min + max) / 2 = max - (max - min) / 2  to avoid
+       overflow and rounding errors                             */
+
+    middle = max - ( ( max - min ) >> 1 );
+
+    if ( glyphID >= rr[middle].Start && glyphID <= rr[middle].End )
+    {
+      *index = rr[middle].StartCoverageIndex + glyphID - rr[middle].Start;
+      return HB_Err_Ok;
+    }
+    else if ( glyphID < rr[middle].Start )
+    {
+      if ( middle == min )
+	break;
+      new_max = middle - 1;
+    }
+    else
+    {
+      if ( middle == max )
+	break;
+      new_min = middle + 1;
+    }
+  } while ( min < max );
+
+  return HB_Err_Not_Covered;
+}
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Coverage_Index( HB_Coverage* c,
+			  HB_UShort      glyphID,
+			  HB_UShort*     index )
+{
+  switch ( c->CoverageFormat )
+  {
+  case 1:  return Coverage_Index1( &c->cf.cf1, glyphID, index );
+  case 2:  return Coverage_Index2( &c->cf.cf2, glyphID, index );
+  default: return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;               /* never reached */
+}
+
+
+
+/*************************************
+ * Class Definition related functions
+ *************************************/
+
+
+/* ClassDefFormat1 */
+
+static HB_Error  Load_ClassDef1( HB_ClassDefinition*  cd,
+				 HB_UShort             limit,
+				 HB_Stream             stream )
+{
+  HB_Error   error;
+
+  HB_UShort             n, count;
+
+  HB_UShort*            cva;
+
+  HB_ClassDefFormat1*  cdf1;
+
+
+  cdf1 = &cd->cd.cd1;
+
+  if ( ACCESS_Frame( 4L ) )
+    return error;
+
+  cdf1->StartGlyph         = GET_UShort();
+  count = cdf1->GlyphCount = GET_UShort();
+
+  FORGET_Frame();
+
+  /* sanity check; we are limited to 16bit integers */
+
+  if ( cdf1->StartGlyph + (long)count >= 0x10000L )
+    return ERR(HB_Err_Invalid_SubTable);
+
+  cdf1->ClassValueArray = NULL;
+
+  if ( ALLOC_ARRAY( cdf1->ClassValueArray, count, HB_UShort ) )
+    return error;
+
+  cva = cdf1->ClassValueArray;
+
+  if ( ACCESS_Frame( count * 2L ) )
+    goto Fail;
+
+  for ( n = 0; n < count; n++ )
+  {
+    cva[n] = GET_UShort();
+    if ( cva[n] >= limit )
+    {
+      error = ERR(HB_Err_Invalid_SubTable);
+      goto Fail;
+    }
+  }
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+
+Fail:
+  FREE( cva );
+
+  return error;
+}
+
+
+static void  Free_ClassDef1( HB_ClassDefFormat1*  cdf1 )
+{
+  FREE( cdf1->ClassValueArray );
+}
+
+
+/* ClassDefFormat2 */
+
+static HB_Error  Load_ClassDef2( HB_ClassDefinition*  cd,
+				 HB_UShort             limit,
+				 HB_Stream             stream )
+{
+  HB_Error   error;
+
+  HB_UShort              n, count;
+
+  HB_ClassRangeRecord*  crr;
+
+  HB_ClassDefFormat2*   cdf2;
+
+
+  cdf2 = &cd->cd.cd2;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  count = GET_UShort();
+  cdf2->ClassRangeCount = 0; /* zero for now.  we fill with the number of good entries later */
+
+  FORGET_Frame();
+
+  cdf2->ClassRangeRecord = NULL;
+
+  if ( ALLOC_ARRAY( cdf2->ClassRangeRecord, count, HB_ClassRangeRecord ) )
+    return error;
+
+  crr = cdf2->ClassRangeRecord;
+
+  if ( ACCESS_Frame( count * 6L ) )
+    goto Fail;
+
+  for ( n = 0; n < count; n++ )
+  {
+    crr[n].Start = GET_UShort();
+    crr[n].End   = GET_UShort();
+    crr[n].Class = GET_UShort();
+
+    /* sanity check */
+
+    if ( crr[n].Start > crr[n].End ||
+	 crr[n].Class >= limit )
+    {
+      /* XXX
+       * Corrupt entry.  Skip it.
+       * This is hit by Nafees Nastaliq font for example
+       */
+       n--;
+       count--;
+    }
+  }
+
+  FORGET_Frame();
+
+  cdf2->ClassRangeCount = count;
+
+  return HB_Err_Ok;
+
+Fail:
+  FREE( crr );
+
+  return error;
+}
+
+
+static void  Free_ClassDef2( HB_ClassDefFormat2*  cdf2 )
+{
+  FREE( cdf2->ClassRangeRecord );
+}
+
+
+/* ClassDefinition */
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_ClassDefinition( HB_ClassDefinition* cd,
+				HB_UShort             limit,
+				HB_Stream             stream )
+{
+  HB_Error   error;
+
+  if ( ACCESS_Frame( 2L ) )
+    return error;
+
+  cd->ClassFormat = GET_UShort();
+
+  FORGET_Frame();
+
+  switch ( cd->ClassFormat )
+  {
+  case 1:  error = Load_ClassDef1( cd, limit, stream ); break;
+  case 2:  error = Load_ClassDef2( cd, limit, stream ); break;
+  default: error = ERR(HB_Err_Invalid_SubTable_Format);	break;
+  }
+
+  if ( error )
+    return error;
+
+  cd->loaded = TRUE;
+
+  return HB_Err_Ok;
+}
+
+
+static HB_Error
+_HB_OPEN_Load_EmptyClassDefinition( HB_ClassDefinition*  cd )
+{
+  HB_Error   error;
+
+  cd->ClassFormat = 1; /* Meaningless */
+
+  if ( ALLOC_ARRAY( cd->cd.cd1.ClassValueArray, 1, HB_UShort ) )
+    return error;
+
+  cd->loaded = TRUE;
+
+  return HB_Err_Ok;
+}
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_EmptyOrClassDefinition( HB_ClassDefinition* cd,
+					       HB_UShort             limit,
+					       HB_UInt              class_offset,
+					       HB_UInt              base_offset,
+					       HB_Stream             stream )
+{
+  HB_Error error;
+  HB_UInt               cur_offset;
+
+  cur_offset = FILE_Pos();
+
+  if ( class_offset )
+    {
+      if ( !FILE_Seek( class_offset + base_offset ) )
+	error = _HB_OPEN_Load_ClassDefinition( cd, limit, stream );
+    }
+  else
+     error = _HB_OPEN_Load_EmptyClassDefinition ( cd );
+
+  if (error == HB_Err_Ok)
+    (void)FILE_Seek( cur_offset ); /* Changes error as a side-effect */
+
+  return error;
+}
+
+HB_INTERNAL void
+_HB_OPEN_Free_ClassDefinition( HB_ClassDefinition*  cd )
+{
+  if ( !cd->loaded )
+    return;
+
+  switch ( cd->ClassFormat )
+  {
+  case 1:  Free_ClassDef1( &cd->cd.cd1 ); break;
+  case 2:  Free_ClassDef2( &cd->cd.cd2 ); break;
+  default:				  break;
+  }
+}
+
+
+static HB_Error  Get_Class1( HB_ClassDefFormat1*  cdf1,
+			     HB_UShort             glyphID,
+			     HB_UShort*            klass,
+			     HB_UShort*            index )
+{
+  HB_UShort*  cva = cdf1->ClassValueArray;
+
+
+  if ( index )
+    *index = 0;
+
+  if ( glyphID >= cdf1->StartGlyph &&
+       glyphID < cdf1->StartGlyph + cdf1->GlyphCount )
+  {
+    *klass = cva[glyphID - cdf1->StartGlyph];
+    return HB_Err_Ok;
+  }
+  else
+  {
+    *klass = 0;
+    return HB_Err_Not_Covered;
+  }
+}
+
+
+/* we need the index value of the last searched class range record
+   in case of failure for constructed GDEF tables                  */
+
+static HB_Error  Get_Class2( HB_ClassDefFormat2*  cdf2,
+			     HB_UShort             glyphID,
+			     HB_UShort*            klass,
+			     HB_UShort*            index )
+{
+  HB_Error               error = HB_Err_Ok;
+  HB_UShort              min, max, new_min, new_max, middle;
+
+  HB_ClassRangeRecord*  crr = cdf2->ClassRangeRecord;
+
+
+  /* binary search */
+
+  if ( cdf2->ClassRangeCount == 0 )
+    {
+      *klass = 0;
+      if ( index )
+	*index = 0;
+      
+      return HB_Err_Not_Covered;
+    }
+
+  new_min = 0;
+  new_max = cdf2->ClassRangeCount - 1;
+
+  do
+  {
+    min = new_min;
+    max = new_max;
+
+    /* we use (min + max) / 2 = max - (max - min) / 2  to avoid
+       overflow and rounding errors                             */
+
+    middle = max - ( ( max - min ) >> 1 );
+
+    if ( glyphID >= crr[middle].Start && glyphID <= crr[middle].End )
+    {
+      *klass = crr[middle].Class;
+      error  = HB_Err_Ok;
+      break;
+    }
+    else if ( glyphID < crr[middle].Start )
+    {
+      if ( middle == min )
+      {
+	*klass = 0;
+	error  = HB_Err_Not_Covered;
+	break;
+      }
+      new_max = middle - 1;
+    }
+    else
+    {
+      if ( middle == max )
+      {
+	*klass = 0;
+	error  = HB_Err_Not_Covered;
+	break;
+      }
+      new_min = middle + 1;
+    }
+  } while ( min < max );
+
+  if ( index )
+    *index = middle;
+
+  return error;
+}
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Get_Class( HB_ClassDefinition* cd,
+		     HB_UShort             glyphID,
+		    HB_UShort*          klass,
+		     HB_UShort*            index )
+{
+  switch ( cd->ClassFormat )
+  {
+  case 1:  return Get_Class1( &cd->cd.cd1, glyphID, klass, index );
+  case 2:  return Get_Class2( &cd->cd.cd2, glyphID, klass, index );
+  default: return ERR(HB_Err_Invalid_SubTable_Format);
+  }
+
+  return HB_Err_Ok;               /* never reached */
+}
+
+
+
+/***************************
+ * Device related functions
+ ***************************/
+
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Load_Device( HB_Device* d,
+		       HB_Stream    stream )
+{
+  HB_Error   error;
+
+  HB_UShort   n, count;
+
+  HB_UShort*  dv;
+
+
+  if ( ACCESS_Frame( 6L ) )
+    return error;
+
+  d->StartSize   = GET_UShort();
+  d->EndSize     = GET_UShort();
+  d->DeltaFormat = GET_UShort();
+
+  FORGET_Frame();
+
+  d->DeltaValue = NULL;
+
+  if ( d->StartSize > d->EndSize ||
+       d->DeltaFormat == 0 || d->DeltaFormat > 3 )
+    {
+      /* XXX
+       * I've seen fontforge generate DeltaFormat == 0.
+       * Just return Ok and let the NULL DeltaValue disable
+       * this table.
+       */
+      return HB_Err_Ok;
+    }
+
+  count = ( ( d->EndSize - d->StartSize + 1 ) >>
+	      ( 4 - d->DeltaFormat ) ) + 1;
+
+  if ( ALLOC_ARRAY( d->DeltaValue, count, HB_UShort ) )
+    return error;
+
+  if ( ACCESS_Frame( count * 2L ) )
+  {
+    FREE( d->DeltaValue );
+    return error;
+  }
+
+  dv = d->DeltaValue;
+
+  for ( n = 0; n < count; n++ )
+    dv[n] = GET_UShort();
+
+  FORGET_Frame();
+
+  return HB_Err_Ok;
+}
+
+
+HB_INTERNAL void
+_HB_OPEN_Free_Device( HB_Device* d )
+{
+  FREE( d->DeltaValue );
+}
+
+
+/* Since we have the delta values stored in compressed form, we must
+   uncompress it now.  To simplify the interface, the function always
+   returns a meaningful value in `value'; the error is just for
+   information.
+			       |                |
+   format = 1: 0011223344556677|8899101112131415|...
+			       |                |
+		    byte 1           byte 2
+
+     00: (byte >> 14) & mask
+     11: (byte >> 12) & mask
+     ...
+
+     mask = 0x0003
+			       |                |
+   format = 2: 0000111122223333|4444555566667777|...
+			       |                |
+		    byte 1           byte 2
+
+     0000: (byte >> 12) & mask
+     1111: (byte >>  8) & mask
+     ...
+
+     mask = 0x000F
+			       |                |
+   format = 3: 0000000011111111|2222222233333333|...
+			       |                |
+		    byte 1           byte 2
+
+     00000000: (byte >> 8) & mask
+     11111111: (byte >> 0) & mask
+     ....
+
+     mask = 0x00FF                                    */
+
+HB_INTERNAL HB_Error
+_HB_OPEN_Get_Device( HB_Device* d,
+		      HB_UShort    size,
+		      HB_Short*    value )
+{
+  HB_UShort  byte, bits, mask, f, s;
+
+
+  f = d->DeltaFormat;
+
+  if ( d->DeltaValue && size >= d->StartSize && size <= d->EndSize )
+  {
+    s    = size - d->StartSize;
+    byte = d->DeltaValue[s >> ( 4 - f )];
+    bits = byte >> ( 16 - ( ( s % ( 1 << ( 4 - f ) ) + 1 ) << f ) );
+    mask = 0xFFFF >> ( 16 - ( 1 << f ) );
+
+    *value = (HB_Short)( bits & mask );
+
+    /* conversion to a signed value */
+
+    if ( *value >= ( ( mask + 1 ) >> 1 ) )
+      *value -= mask + 1;
+
+    return HB_Err_Ok;
+  }
+  else
+  {
+    *value = 0;
+    return HB_Err_Not_Covered;
+  }
+}
+
+
+/* END */
diff --git a/third_party/harfbuzz/src/harfbuzz-open.h b/third_party/harfbuzz/src/harfbuzz-open.h
new file mode 100644
index 0000000..bdc6358
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-open.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_OPEN_H
+#define HARFBUZZ_OPEN_H
+
+#include "harfbuzz-global.h"
+
+HB_BEGIN_HEADER
+
+/* Use this if a feature applies to all glyphs */
+#define HB_ALL_GLYPHS                    0xFFFF
+
+#define HB_DEFAULT_LANGUAGE              0xFFFF
+
+#define HB_MAX_NESTING_LEVEL             100
+
+
+/* Script list related structures */
+
+struct  HB_LangSys_
+{
+  HB_UShort   LookupOrderOffset;      /* always 0 for TT Open 1.0  */
+  HB_UShort   ReqFeatureIndex;        /* required FeatureIndex     */
+  HB_UShort   FeatureCount;           /* number of Feature indices */
+  HB_UShort*  FeatureIndex;           /* array of Feature indices  */
+};
+
+typedef struct HB_LangSys_  HB_LangSys;
+
+
+struct  HB_LangSysRecord_
+{
+  HB_UInt     LangSysTag;            /* LangSysTag identifier */
+  HB_LangSys  LangSys;               /* LangSys table         */
+};
+
+typedef struct HB_LangSysRecord_  HB_LangSysRecord;
+
+
+struct  HB_ScriptTable_
+{
+  HB_LangSys         DefaultLangSys; /* DefaultLangSys table     */
+  HB_UShort           LangSysCount;   /* number of LangSysRecords */
+  HB_LangSysRecord*  LangSysRecord;  /* array of LangSysRecords  */
+};
+
+typedef struct HB_ScriptTable_  HB_ScriptTable;
+
+
+struct  HB_ScriptRecord_
+{
+  HB_UInt        ScriptTag;              /* ScriptTag identifier */
+  HB_ScriptTable  Script;                 /* Script table         */
+};
+
+typedef struct HB_ScriptRecord_  HB_ScriptRecord;
+
+
+struct  HB_ScriptList_
+{
+  HB_UShort          ScriptCount;     /* number of ScriptRecords */
+  HB_ScriptRecord*  ScriptRecord;    /* array of ScriptRecords  */
+};
+
+typedef struct HB_ScriptList_  HB_ScriptList;
+
+
+/* Feature list related structures */
+
+struct HB_Feature_
+{
+  HB_UShort   FeatureParams;          /* always 0 for TT Open 1.0     */
+  HB_UShort   LookupListCount;        /* number of LookupList indices */
+  HB_UShort*  LookupListIndex;        /* array of LookupList indices  */
+};
+
+typedef struct HB_Feature_  HB_Feature;
+
+
+struct  HB_FeatureRecord_
+{
+  HB_UInt     FeatureTag;            /* FeatureTag identifier */
+  HB_Feature  Feature;               /* Feature table         */
+};
+
+typedef struct HB_FeatureRecord_  HB_FeatureRecord;
+
+
+struct  HB_FeatureList_
+{
+  HB_UShort           FeatureCount;   /* number of FeatureRecords */
+  HB_FeatureRecord*  FeatureRecord;  /* array of FeatureRecords  */
+  HB_UShort*		ApplyOrder;	/* order to apply features */
+  HB_UShort		ApplyCount;	/* number of elements in ApplyOrder */
+};
+
+typedef struct HB_FeatureList_  HB_FeatureList;
+
+
+/* Lookup list related structures */
+
+typedef struct HB_SubTable_  HB_SubTable;
+
+
+struct  HB_Lookup_
+{
+  HB_UShort      LookupType;          /* Lookup type         */
+  HB_UShort      LookupFlag;          /* Lookup qualifiers   */
+  HB_UShort      SubTableCount;       /* number of SubTables */
+  HB_SubTable*  SubTable;            /* array of SubTables  */
+};
+
+typedef struct HB_Lookup_  HB_Lookup;
+
+
+/* The `Properties' field is not defined in the OpenType specification but
+   is needed for processing lookups.  If properties[n] is > 0, the
+   functions HB_GSUB_Apply_String() resp. HB_GPOS_Apply_String() will
+   process Lookup[n] for glyphs which have the specific bit not set in
+   the `properties' field of the input string object.                  */
+
+struct  HB_LookupList_
+{
+  HB_UShort    LookupCount;           /* number of Lookups       */
+  HB_Lookup*  Lookup;                /* array of Lookup records */
+  HB_UInt*     Properties;            /* array of flags          */
+};
+
+typedef struct HB_LookupList_  HB_LookupList;
+
+
+/* Possible LookupFlag bit masks.  `HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS' comes from the
+   OpenType 1.2 specification; HB_LOOKUP_FLAG_RIGHT_TO_LEFT has been (re)introduced in
+   OpenType 1.3 -- if set, the last glyph in a cursive attachment
+   sequence has to be positioned on the baseline -- regardless of the
+   writing direction.                                                    */
+
+#define HB_LOOKUP_FLAG_RIGHT_TO_LEFT         0x0001
+#define HB_LOOKUP_FLAG_IGNORE_BASE_GLYPHS    0x0002
+#define HB_LOOKUP_FLAG_IGNORE_LIGATURES      0x0004
+#define HB_LOOKUP_FLAG_IGNORE_MARKS          0x0008
+#define HB_LOOKUP_FLAG_IGNORE_SPECIAL_MARKS  0xFF00
+
+
+struct  HB_CoverageFormat1_
+{
+  HB_UShort   GlyphCount;             /* number of glyphs in GlyphArray */
+  HB_UShort*  GlyphArray;             /* array of glyph IDs             */
+};
+
+typedef struct HB_CoverageFormat1_  HB_CoverageFormat1;
+
+
+struct HB_RangeRecord_
+{
+  HB_UShort  Start;                   /* first glyph ID in the range */
+  HB_UShort  End;                     /* last glyph ID in the range  */
+  HB_UShort  StartCoverageIndex;      /* coverage index of first
+					 glyph ID in the range       */
+};
+
+typedef struct HB_RangeRecord_  HB_RangeRecord;
+
+
+struct  HB_CoverageFormat2_
+{
+  HB_UShort         RangeCount;       /* number of RangeRecords */
+  HB_RangeRecord*  RangeRecord;      /* array of RangeRecords  */
+};
+
+typedef struct HB_CoverageFormat2_  HB_CoverageFormat2;
+
+
+struct  HB_Coverage_
+{
+  HB_UShort  CoverageFormat;          /* 1 or 2 */
+
+  union
+  {
+    HB_CoverageFormat1  cf1;
+    HB_CoverageFormat2  cf2;
+  } cf;
+};
+
+typedef struct HB_Coverage_  HB_Coverage;
+
+
+struct  HB_ClassDefFormat1_
+{
+  HB_UShort   StartGlyph;             /* first glyph ID of the
+					 ClassValueArray             */
+  HB_UShort   GlyphCount;             /* size of the ClassValueArray */
+  HB_UShort*  ClassValueArray;        /* array of class values       */
+};
+
+typedef struct HB_ClassDefFormat1_  HB_ClassDefFormat1;
+
+
+struct  HB_ClassRangeRecord_
+{
+  HB_UShort  Start;                   /* first glyph ID in the range    */
+  HB_UShort  End;                     /* last glyph ID in the range     */
+  HB_UShort  Class;                   /* applied to all glyphs in range */
+};
+
+typedef struct HB_ClassRangeRecord_  HB_ClassRangeRecord;
+
+
+struct  HB_ClassDefFormat2_
+{
+  HB_UShort              ClassRangeCount;
+				      /* number of ClassRangeRecords */
+  HB_ClassRangeRecord*  ClassRangeRecord;
+				      /* array of ClassRangeRecords  */
+};
+
+typedef struct HB_ClassDefFormat2_  HB_ClassDefFormat2;
+
+
+struct  HB_ClassDefinition_
+{
+  HB_Bool    loaded;
+
+  HB_UShort  ClassFormat;             /* 1 or 2                      */
+
+  union
+  {
+    HB_ClassDefFormat1  cd1;
+    HB_ClassDefFormat2  cd2;
+  } cd;
+};
+
+typedef struct HB_ClassDefinition_  HB_ClassDefinition;
+
+
+struct HB_Device_
+{
+  HB_UShort   StartSize;              /* smallest size to correct      */
+  HB_UShort   EndSize;                /* largest size to correct       */
+  HB_UShort   DeltaFormat;            /* DeltaValue array data format:
+					 1, 2, or 3                    */
+  HB_UShort*  DeltaValue;             /* array of compressed data      */
+};
+
+typedef struct HB_Device_  HB_Device;
+
+
+enum  HB_Type_
+{
+  HB_Type_GSUB,
+  HB_Type_GPOS
+};
+
+typedef enum HB_Type_  HB_Type;
+
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_OPEN_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-shape.h b/third_party/harfbuzz/src/harfbuzz-shape.h
new file mode 100644
index 0000000..e4b5f9a
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-shape.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2006  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Owen Taylor
+ */
+
+#include <stdint.h>
+
+/* Base Types */
+
+typedef hb_uint16 HB_CodePoint; /* UTF-16 codepoint (not character ) */
+typedef char HB_Boolean;
+typedef hb_uint32 HB_Fixed; /* 26.6 */
+typedef hb_uint32 HB_Glyph;
+typedef hb_uint32 HB_Unichar;
+
+/* Metrics reported by the font backend for use of the shaper */
+typedef struct _HB_GlyphMetrics HB_GlyphMetrics;
+struct _HB_GlyphMetrics
+{
+    HB_Fixed advance;
+    
+    /* Do we need ink/logical extents for the glyph here? */
+};
+
+/*
+ * HB_Font: Abstract font interface.
+ *  First pass of this might just have FT_Face *getFace();
+ */
+typedef struct _HB_Font HB_Font;
+typedef struct _HB_FontClass HB_FontClass;
+
+struct HB_FontClass {
+    HB_Glyph   (*charToGlyph)(HB_Font *font, HB_Unichar chr);
+    void       (*getMetrics)(HB_Font *font, HB_Glyph glyph, HB_GlyphMetrics *metrics);
+    HB_Boolean (*getSFontTable)(HB_Font *font, void **cookie, char **start, int *len);
+    HB_Boolean (*freeSFontTable)(void **cookie);
+};
+
+struct _HB_Font {
+    HB_FontClass *clazz;
+};
+
+/*
+ * Language tags, of the form en-us; represented as interned, canonicalized
+ * strings. hb_language_from_string("en_US"), hb_language_from_string("en-us")
+ * both return the same (pointer-comparable) HB_Language).
+ */
+typedef struct HB_Language_ *HB_Language;
+
+HB_Language hb_language_from_string(const char *str);
+const char *hb_language_to_string(HB_Language language);
+
+/* Special treatment for the edges of runs.
+ */
+typedef enum {
+    HB_RUN_EDGE_LINE_VISUAL_EDGE    = 1 << 0,
+    HB_RUN_EDGE_LINE_LOGICAL_EDGE   = 1 << 1,
+    HB_RUN_EDGE_LINE_ADD_HYPHEN     = 1 << 2  /* ???? */
+} HB_RunEdge;
+
+/* Defines optional informaiton in HB_ShapeInput; this allows extension
+ * of HB_ShapeInput while keeping binary compatibility
+ */
+typedef enum {
+    HB_SHAPE_START_TYPE = 1 << 0,
+    HB_SHAPE_END_TYPE   = 1 << 1
+} HB_ShapeFlags;
+
+/* Attributes types are described by "interned strings"; this is a little
+ * annoying if you want to write a switch statement, but keeps things
+ * simple.
+ */
+typedef struct _HB_AttributeType *HB_AttributeType;
+
+HB_AttributeType hb_attribute_type_from_string(const char *str);
+const char *hb_attribute_type_to_string(HB_AttributeType attribute_type);
+
+struct HB_Attribute {
+    HB_AttributeType type;
+    int start; 
+    int end;
+};
+
+
+/**
+ * You could handle this like HB_Language, but an enum seems a little nicer;
+ * another approach would be to use OpenType script tags.
+ */
+typedef enum {
+    HB_SCRIPT_LATIN
+    /* ... */
+} HB_ShapeScript;
+
+/* This is just the subset of direction information needed by the shaper */
+typedef enum {
+    HB_DIRECTION_LTR,
+    HB_DIRECTION_RTL,
+    HB_DIRECTION_TTB
+} HB_Direction;
+
+typedef struct _HB_ShapeInput HB_ShapeInput;
+struct _HB_ShapeInput {
+    /* Defines what fields the caller has initialized - fields not in
+     * the enum are mandatory.
+     */
+    HB_ShapeFlags flags;
+    
+    HB_CodePoint *text;
+    int length;       /* total length of text to shape */
+    int shape_offset; /* start of section to shape */
+    int shape_length; /* number of code points to shape */
+
+    HB_Direction direction;
+    HB_ShapeScript script;
+    HB_Language language;
+
+    HB_AttributeType *attributes;
+    int n_attributes;
+
+    HB_RunEdge start_type;
+    HB_RunEdge end_type;
+};
+
+struct HB_GlyphItem {
+    HB_Glyph glyph;
+    
+    HB_Fixed x_offset;
+    HB_Fixed y_offset;
+    HB_Fixed advance;
+
+    /* Add kashida information, etc, here */
+};
+
+typedef enum {
+    HB_RESULT_SUCCESS,
+    HB_RESULT_NO_MEMORY,
+    HB_SHAPE_RESULT_FAILED
+} HB_Result;
+
+/*
+ * Buffer for output 
+ */
+typedef struct _HB_GlyphBuffer HB_GlyphBuffer;
+struct _HB_GlyphBuffer {
+    int glyph_item_size;
+    int total_glyphs;
+    
+    int *log_clusters; /* Uniscribe style */
+    int cluster_space;
+  
+    int glyph_space;
+    void *glyph_buffer;
+};
+
+/* Making this self-allocating simplifies writing shapers and
+ * also keeps things easier for caller. item_size passed in
+ * must be at least sizeof(HB_GlyphItem) but can be bigger,
+ * to accomodate application structures that extend HB_GlyphItem.
+ * The allocated items will be zero-initialized.
+ *
+ * (Hack: Harfbuzz could choose to use even a *bigger* item size
+ * and stick internal information before the public item structure.
+ * This hack could possibly be used to unify this with HB_Buffer)
+ */
+HB_GlyphBuffer *hb_glyph_buffer_new             (size_t item_size);
+void            hb_glyph_buffer_clear           (HB_GlyphBuffer *buf);
+HB_Result       hb_glyph_buffer_extend_glyphs   (HB_GlyphBuffer *buf, int n_items);
+HB_Result       hb_glyph_buffer_extend_clusters (HB_GlyphBuffer *buf, int n_clusters);
+void            hb_glyph_buffer_free            (HB_GlyphBuffer *buf);
+
+
+/* Accessor for a particular glyph */
+#define HB_GLYPH_BUFFER_ITEM(buffer, index)
+
+/*
+ * Main shaping function
+ */
+HB_Result hb_shape(HB_ShapeInput *input, HB_GlyphBuffer *output);
diff --git a/third_party/harfbuzz/src/harfbuzz-shaper-all.cpp b/third_party/harfbuzz/src/harfbuzz-shaper-all.cpp
new file mode 100644
index 0000000..d2f902f
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-shaper-all.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-shaper.cpp"
+#include "harfbuzz-indic.cpp"
+extern "C" {
+#include "harfbuzz-tibetan.c"
+#include "harfbuzz-khmer.c"
+#include "harfbuzz-hebrew.c"
+#include "harfbuzz-arabic.c"
+#include "harfbuzz-hangul.c"
+#include "harfbuzz-myanmar.c"
+#include "harfbuzz-thai.c"
+}
+
diff --git a/third_party/harfbuzz/src/harfbuzz-shaper-private.h b/third_party/harfbuzz/src/harfbuzz-shaper-private.h
new file mode 100644
index 0000000..80bccf8
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-shaper-private.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_SHAPER_PRIVATE_H
+#define HARFBUZZ_SHAPER_PRIVATE_H
+
+HB_BEGIN_HEADER
+
+enum {
+    C_DOTTED_CIRCLE = 0x25CC
+};
+
+typedef enum 
+{
+    HB_Combining_BelowLeftAttached       = 200,
+    HB_Combining_BelowAttached           = 202,
+    HB_Combining_BelowRightAttached      = 204,
+    HB_Combining_LeftAttached            = 208,
+    HB_Combining_RightAttached           = 210,
+    HB_Combining_AboveLeftAttached       = 212,
+    HB_Combining_AboveAttached           = 214,
+    HB_Combining_AboveRightAttached      = 216,
+
+    HB_Combining_BelowLeft               = 218,
+    HB_Combining_Below                   = 220,
+    HB_Combining_BelowRight              = 222,
+    HB_Combining_Left                    = 224,
+    HB_Combining_Right                   = 226,
+    HB_Combining_AboveLeft               = 228,
+    HB_Combining_Above                   = 230,
+    HB_Combining_AboveRight              = 232,
+
+    HB_Combining_DoubleBelow             = 233,
+    HB_Combining_DoubleAbove             = 234,
+    HB_Combining_IotaSubscript           = 240
+} HB_CombiningClass;
+
+typedef enum {
+    CcmpProperty = 0x1,
+    InitProperty = 0x2,
+    IsolProperty = 0x4,
+    FinaProperty = 0x8,
+    MediProperty = 0x10,
+    RligProperty = 0x20,
+    CaltProperty = 0x40,
+    LigaProperty = 0x80,
+    DligProperty = 0x100,
+    CswhProperty = 0x200,
+    MsetProperty = 0x400,
+
+    /* used by indic and myanmar shaper */
+    NuktaProperty = 0x4,
+    AkhantProperty = 0x8,
+    RephProperty = 0x10,
+    PreFormProperty = 0x20,
+    BelowFormProperty = 0x40,
+    AboveFormProperty = 0x80,
+    HalfFormProperty = 0x100,
+    PostFormProperty = 0x200,
+    VattuProperty = 0x400,
+    PreSubstProperty = 0x800,
+    BelowSubstProperty = 0x1000,
+    AboveSubstProperty = 0x2000,
+    PostSubstProperty = 0x4000,
+    HalantProperty = 0x8000,
+    CligProperty = 0x10000
+
+} HB_OpenTypeProperty;
+
+/* return true if ok. */
+typedef HB_Bool (*HB_ShapeFunction)(HB_ShaperItem *shaper_item);
+typedef void (*HB_AttributeFunction)(HB_Script script, const HB_UChar16 *string, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes);
+
+typedef struct {
+    HB_ShapeFunction shape;
+    HB_AttributeFunction charAttributes;
+} HB_ScriptEngine;
+
+extern const HB_ScriptEngine hb_scriptEngines[];
+
+extern HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item);
+extern HB_Bool HB_TibetanShape(HB_ShaperItem *shaper_item);
+extern HB_Bool HB_HebrewShape(HB_ShaperItem *shaper_item);
+extern HB_Bool HB_ArabicShape(HB_ShaperItem *shaper_item);
+extern HB_Bool HB_HangulShape(HB_ShaperItem *shaper_item);
+extern HB_Bool HB_MyanmarShape(HB_ShaperItem *shaper_item);
+extern HB_Bool HB_KhmerShape(HB_ShaperItem *shaper_item);
+extern HB_Bool HB_IndicShape(HB_ShaperItem *shaper_item);
+
+extern void HB_TibetanAttributes(HB_Script script, const HB_UChar16 *string, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes);
+
+extern void HB_MyanmarAttributes(HB_Script script, const HB_UChar16 *string, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes);
+
+extern void HB_KhmerAttributes(HB_Script script, const HB_UChar16 *string, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes);
+
+extern void HB_IndicAttributes(HB_Script script, const HB_UChar16 *string, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes);
+
+extern void HB_ThaiAttributes(HB_Script script, const HB_UChar16 *string, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes);
+
+typedef struct {
+    hb_uint32 tag;
+    hb_uint32 property;
+} HB_OpenTypeFeature;
+
+#define PositioningProperties 0x80000000
+
+HB_Bool HB_SelectScript(HB_ShaperItem *item, const HB_OpenTypeFeature *features);
+
+HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties);
+HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters);
+
+void HB_HeuristicPosition(HB_ShaperItem *item);
+void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item);
+
+#define HB_IsControlChar(uc) \
+    ((uc >= 0x200b && uc <= 0x200f /* ZW Space, ZWNJ, ZWJ, LRM and RLM */) \
+     || (uc >= 0x2028 && uc <= 0x202f /* LS, PS, LRE, RLE, PDF, LRO, RLO, NNBSP */) \
+     || (uc >= 0x206a && uc <= 0x206f /* ISS, ASS, IAFS, AFS, NADS, NODS */))
+
+HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item);
+
+#define HB_GetGlyphAdvances(shaper_item) \
+    shaper_item->font->klass->getGlyphAdvances(shaper_item->font, \
+                                               shaper_item->glyphs, shaper_item->num_glyphs, \
+                                               shaper_item->advances, \
+                                               shaper_item->face->current_flags);
+
+#define HB_DECLARE_STACKARRAY(Type, Name) \
+    Type stack##Name[512]; \
+    Type *Name = stack##Name;
+
+#define HB_INIT_STACKARRAY(Type, Name, Length) \
+    if ((Length) >= 512) \
+        Name = (Type *)malloc((Length) * sizeof(Type));
+
+#define HB_STACKARRAY(Type, Name, Length) \
+    HB_DECLARE_STACKARRAY(Type, Name) \
+    HB_INIT_STACKARRAY(Type, Name, Length)
+
+#define HB_FREE_STACKARRAY(Name) \
+    if (stack##Name != Name) \
+        free(Name);
+
+HB_END_HEADER
+
+#endif
diff --git a/third_party/harfbuzz/src/harfbuzz-shaper.cpp b/third_party/harfbuzz/src/harfbuzz-shaper.cpp
new file mode 100644
index 0000000..f1606e6
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-shaper.cpp
@@ -0,0 +1,1357 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-shaper-private.h"
+
+#include "harfbuzz-stream-private.h"
+#include <assert.h>
+#include <stdio.h>
+
+#define HB_MIN(a, b) ((a) < (b) ? (a) : (b))
+#define HB_MAX(a, b) ((a) > (b) ? (a) : (b))
+
+// -----------------------------------------------------------------------------------------------------
+//
+// The line break algorithm. See http://www.unicode.org/reports/tr14/tr14-13.html
+//
+// -----------------------------------------------------------------------------------------------------
+
+/* The Unicode algorithm does in our opinion allow line breaks at some
+   places they shouldn't be allowed. The following changes were thus
+   made in comparison to the Unicode reference:
+
+   EX->AL from DB to IB
+   SY->AL from DB to IB
+   SY->PO from DB to IB
+   SY->PR from DB to IB
+   SY->OP from DB to IB
+   AL->PR from DB to IB
+   AL->PO from DB to IB
+   PR->PR from DB to IB
+   PO->PO from DB to IB
+   PR->PO from DB to IB
+   PO->PR from DB to IB
+   HY->PO from DB to IB
+   HY->PR from DB to IB
+   HY->OP from DB to IB
+   NU->EX from PB to IB
+   EX->PO from DB to IB
+*/
+
+// The following line break classes are not treated by the table:
+//  AI, BK, CB, CR, LF, NL, SA, SG, SP, XX
+
+enum break_class {
+    // the first 4 values have to agree with the enum in QCharAttributes
+    ProhibitedBreak,            // PB in table
+    DirectBreak,                // DB in table
+    IndirectBreak,              // IB in table
+    CombiningIndirectBreak,     // CI in table
+    CombiningProhibitedBreak    // CP in table
+};
+#define DB DirectBreak
+#define IB IndirectBreak
+#define CI CombiningIndirectBreak
+#define CP CombiningProhibitedBreak
+#define PB ProhibitedBreak
+
+static const hb_uint8 breakTable[HB_LineBreak_JT+1][HB_LineBreak_JT+1] =
+{
+/*          OP  CL  QU  GL  NS  EX  SY  IS  PR  PO  NU  AL  ID  IN  HY  BA  BB  B2  ZW  CM  WJ  H2  H3  JL  JV  JT */
+/* OP */ { PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, CP, PB, PB, PB, PB, PB, PB },
+/* CL */ { DB, PB, IB, IB, PB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* QU */ { PB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
+/* GL */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
+/* NS */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* EX */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* SY */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* IS */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* PR */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, DB, IB, IB, DB, DB, PB, CI, PB, IB, IB, IB, IB, IB },
+/* PO */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* NU */ { IB, PB, IB, IB, IB, IB, PB, PB, IB, IB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* AL */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* ID */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* IN */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* HY */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* BA */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* BB */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
+/* B2 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, PB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* ZW */ { DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, PB, DB, DB, DB, DB, DB, DB, DB },
+/* CM */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
+/* WJ */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
+/* H2 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, IB, IB },
+/* H3 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, IB },
+/* JL */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, IB, IB, IB, IB, DB },
+/* JV */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, IB, IB },
+/* JT */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, IB }
+};
+#undef DB
+#undef IB
+#undef CI
+#undef CP
+#undef PB
+
+static const hb_uint8 graphemeTable[HB_Grapheme_LVT + 1][HB_Grapheme_LVT + 1] =
+{
+//      Other, CR,    LF,    Control,Extend,L,    V,     T,     LV,    LVT
+    { true , true , true , true , true , true , true , true , true , true  }, // Other, 
+    { true , true , true , true , true , true , true , true , true , true  }, // CR,
+    { true , false, true , true , true , true , true , true , true , true  }, // LF,
+    { true , true , true , true , true , true , true , true , true , true  }, // Control,
+    { false, true , true , true , false, false, false, false, false, false }, // Extend,
+    { true , true , true , true , true , false, true , true , true , true  }, // L, 
+    { true , true , true , true , true , false, false, true , false, true  }, // V, 
+    { true , true , true , true , true , true , false, false, false, false }, // T, 
+    { true , true , true , true , true , false, true , true , true , true  }, // LV, 
+    { true , true , true , true , true , false, true , true , true , true  }, // LVT
+};
+    
+static void calcLineBreaks(const HB_UChar16 *uc, hb_uint32 len, HB_CharAttributes *charAttributes)
+{
+    if (!len)
+        return;
+
+    // ##### can this fail if the first char is a surrogate?
+    HB_LineBreakClass cls;
+    HB_GraphemeClass grapheme;
+    HB_GetGraphemeAndLineBreakClass(*uc, &grapheme, &cls);
+    // handle case where input starts with an LF
+    if (cls == HB_LineBreak_LF)
+        cls = HB_LineBreak_BK;
+
+    charAttributes[0].whiteSpace = (cls == HB_LineBreak_SP || cls == HB_LineBreak_BK);
+    charAttributes[0].charStop = true;
+
+    int lcls = cls;
+    for (hb_uint32 i = 1; i < len; ++i) {
+        charAttributes[i].whiteSpace = false;
+        charAttributes[i].charStop = true;
+
+        HB_UChar32 code = uc[i];
+        HB_GraphemeClass ngrapheme;
+        HB_LineBreakClass ncls;
+        HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls);
+        charAttributes[i].charStop = graphemeTable[ngrapheme][grapheme];
+        // handle surrogates
+        if (ncls == HB_LineBreak_SG) {
+            if (HB_IsHighSurrogate(uc[i]) && i < len - 1 && HB_IsLowSurrogate(uc[i+1])) {
+                continue;
+            } else if (HB_IsLowSurrogate(uc[i]) && HB_IsHighSurrogate(uc[i-1])) {
+                code = HB_SurrogateToUcs4(uc[i-1], uc[i]);
+                HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls);
+                charAttributes[i].charStop = false;
+            } else {
+                ncls = HB_LineBreak_AL;
+            }
+        }
+
+        // set white space and char stop flag
+        if (ncls >= HB_LineBreak_SP)
+            charAttributes[i].whiteSpace = true;
+
+        HB_LineBreakType lineBreakType = HB_NoBreak;
+        if (cls >= HB_LineBreak_LF) {
+            lineBreakType = HB_ForcedBreak;
+        } else if(cls == HB_LineBreak_CR) {
+            lineBreakType = (ncls == HB_LineBreak_LF) ? HB_NoBreak : HB_ForcedBreak;
+        }
+
+        if (ncls == HB_LineBreak_SP)
+            goto next_no_cls_update;
+        if (ncls >= HB_LineBreak_CR)
+            goto next;
+
+        // two complex chars (thai or lao), thai_attributes might override, but here we do a best guess
+	if (cls == HB_LineBreak_SA && ncls == HB_LineBreak_SA) {
+            lineBreakType = HB_Break;
+            goto next;
+        }
+
+        {
+            int tcls = ncls;
+            if (tcls >= HB_LineBreak_SA)
+                tcls = HB_LineBreak_ID;
+            if (cls >= HB_LineBreak_SA)
+                cls = HB_LineBreak_ID;
+
+            int brk = breakTable[cls][tcls];
+            switch (brk) {
+            case DirectBreak:
+                lineBreakType = HB_Break;
+                if (uc[i-1] == 0xad) // soft hyphen
+                    lineBreakType = HB_SoftHyphen;
+                break;
+            case IndirectBreak:
+                lineBreakType = (lcls == HB_LineBreak_SP) ? HB_Break : HB_NoBreak;
+                break;
+            case CombiningIndirectBreak:
+                lineBreakType = HB_NoBreak;
+                if (lcls == HB_LineBreak_SP){
+                    if (i > 1)
+                        charAttributes[i-2].lineBreakType = HB_Break;
+                } else {
+                    goto next_no_cls_update;
+                }
+                break;
+            case CombiningProhibitedBreak:
+                lineBreakType = HB_NoBreak;
+                if (lcls != HB_LineBreak_SP)
+                    goto next_no_cls_update;
+            case ProhibitedBreak:
+            default:
+                break;
+            }
+        }
+    next:
+        cls = ncls;
+    next_no_cls_update:
+        lcls = ncls;
+        grapheme = ngrapheme;
+        charAttributes[i-1].lineBreakType = lineBreakType;
+    }
+    charAttributes[len-1].lineBreakType = HB_ForcedBreak;
+}
+
+// --------------------------------------------------------------------------------------------------------------------------------------------
+//
+// Basic processing
+//
+// --------------------------------------------------------------------------------------------------------------------------------------------
+
+static inline void positionCluster(HB_ShaperItem *item, int gfrom,  int glast)
+{
+    int nmarks = glast - gfrom;
+    assert(nmarks > 0);
+
+    HB_Glyph *glyphs = item->glyphs;
+    HB_GlyphAttributes *attributes = item->attributes;
+
+    HB_GlyphMetrics baseMetrics;
+    item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
+
+    if (item->item.script == HB_Script_Hebrew
+        && (-baseMetrics.y) > baseMetrics.height)
+        // we need to attach below the baseline, because of the hebrew iud.
+        baseMetrics.height = -baseMetrics.y;
+
+//     qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
+//     qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
+
+    HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10;
+    HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4;
+    if (size > HB_FIXED_CONSTANT(4))
+        offsetBase += HB_FIXED_CONSTANT(4);
+    else
+        offsetBase += size;
+    //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
+//     qDebug("offset = %f", offsetBase);
+
+    bool rightToLeft = item->item.bidiLevel % 2;
+
+    int i;
+    unsigned char lastCmb = 0;
+    HB_GlyphMetrics attachmentRect;
+    memset(&attachmentRect, 0, sizeof(attachmentRect));
+
+    for(i = 1; i <= nmarks; i++) {
+        HB_Glyph mark = glyphs[gfrom+i];
+        HB_GlyphMetrics markMetrics;
+        item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
+        HB_FixedPoint p;
+        p.x = p.y = 0;
+//          qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
+
+        HB_Fixed offset = offsetBase;
+        unsigned char cmb = attributes[gfrom+i].combiningClass;
+
+        // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
+        // bits  in the glyphAttributes structure.
+        if (cmb < 200) {
+            // fixed position classes. We approximate by mapping to one of the others.
+            // currently I added only the ones for arabic, hebrew, lao and thai.
+
+            // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
+
+            // add a bit more offset to arabic, a bit hacky
+            if (cmb >= 27 && cmb <= 36 && offset < 3)
+                offset +=1;
+            // below
+            if ((cmb >= 10 && cmb <= 18) ||
+                 cmb == 20 || cmb == 22 ||
+                 cmb == 29 || cmb == 32)
+                cmb = HB_Combining_Below;
+            // above
+            else if (cmb == 23 || cmb == 27 || cmb == 28 ||
+                      cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
+                cmb = HB_Combining_Above;
+            //below-right
+            else if (cmb == 9 || cmb == 103 || cmb == 118)
+                cmb = HB_Combining_BelowRight;
+            // above-right
+            else if (cmb == 24 || cmb == 107 || cmb == 122)
+                cmb = HB_Combining_AboveRight;
+            else if (cmb == 25)
+                cmb = HB_Combining_AboveLeft;
+            // fixed:
+            //  19 21
+
+        }
+
+        // combining marks of different class don't interact. Reset the rectangle.
+        if (cmb != lastCmb) {
+            //qDebug("resetting rect");
+            attachmentRect = baseMetrics;
+        }
+
+        switch(cmb) {
+        case HB_Combining_DoubleBelow:
+                // ### wrong in rtl context!
+        case HB_Combining_BelowLeft:
+            p.y += offset;
+        case HB_Combining_BelowLeftAttached:
+            p.x += attachmentRect.x - markMetrics.x;
+            p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
+            break;
+        case HB_Combining_Below:
+            p.y += offset;
+        case HB_Combining_BelowAttached:
+            p.x += attachmentRect.x - markMetrics.x;
+            p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
+
+            p.x += (attachmentRect.width - markMetrics.width) / 2;
+            break;
+        case HB_Combining_BelowRight:
+            p.y += offset;
+        case HB_Combining_BelowRightAttached:
+            p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
+            p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
+            break;
+        case HB_Combining_Left:
+            p.x -= offset;
+        case HB_Combining_LeftAttached:
+            break;
+        case HB_Combining_Right:
+            p.x += offset;
+        case HB_Combining_RightAttached:
+            break;
+        case HB_Combining_DoubleAbove:
+            // ### wrong in RTL context!
+        case HB_Combining_AboveLeft:
+            p.y -= offset;
+        case HB_Combining_AboveLeftAttached:
+            p.x += attachmentRect.x - markMetrics.x;
+            p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
+            break;
+        case HB_Combining_Above:
+            p.y -= offset;
+        case HB_Combining_AboveAttached:
+            p.x += attachmentRect.x - markMetrics.x;
+            p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
+
+            p.x += (attachmentRect.width - markMetrics.width) / 2;
+            break;
+        case HB_Combining_AboveRight:
+            p.y -= offset;
+        case HB_Combining_AboveRightAttached:
+            p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
+            p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
+            break;
+
+        case HB_Combining_IotaSubscript:
+            default:
+                break;
+        }
+//          qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
+        markMetrics.x += p.x;
+        markMetrics.y += p.y;
+
+        HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
+        unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
+        unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
+        unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
+        unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
+        attachmentRect = unitedAttachmentRect;
+
+        lastCmb = cmb;
+        if (rightToLeft) {
+            item->offsets[gfrom+i].x = p.x;
+            item->offsets[gfrom+i].y = p.y;
+        } else {
+            item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
+            item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
+        }
+        item->advances[gfrom+i] = 0;
+    }
+}
+
+void HB_HeuristicPosition(HB_ShaperItem *item)
+{
+    HB_GetGlyphAdvances(item);
+    HB_GlyphAttributes *attributes = item->attributes;
+
+    int cEnd = -1;
+    int i = item->num_glyphs;
+    while (i--) {
+        if (cEnd == -1 && attributes[i].mark) {
+            cEnd = i;
+        } else if (cEnd != -1 && !attributes[i].mark) {
+            positionCluster(item, i, cEnd);
+            cEnd = -1;
+        }
+    }
+}
+
+// set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
+// and no reordering.
+// also computes logClusters heuristically
+void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
+{
+    const HB_UChar16 *uc = item->string + item->item.pos;
+    hb_uint32 length = item->item.length;
+
+    // ### zeroWidth and justification are missing here!!!!!
+
+    assert(length <= item->num_glyphs);
+
+//     qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
+    HB_GlyphAttributes *attributes = item->attributes;
+    unsigned short *logClusters = item->log_clusters;
+
+    hb_uint32 glyph_pos = 0;
+    hb_uint32 i;
+    for (i = 0; i < length; i++) {
+        if (HB_IsHighSurrogate(uc[i]) && i < length - 1
+            && HB_IsLowSurrogate(uc[i + 1])) {
+            logClusters[i] = glyph_pos;
+            logClusters[++i] = glyph_pos;
+        } else {
+            logClusters[i] = glyph_pos;
+        }
+        ++glyph_pos;
+    }
+
+    // first char in a run is never (treated as) a mark
+    int cStart = 0;
+    const bool symbolFont = item->face->isSymbolFont;
+    attributes[0].mark = false;
+    attributes[0].clusterStart = true;
+    attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
+
+    int pos = 0;
+    HB_CharCategory lastCat;
+    int dummy;
+    HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
+    for (i = 1; i < length; ++i) {
+        if (logClusters[i] == pos)
+            // same glyph
+            continue;
+        ++pos;
+        while (pos < logClusters[i]) {
+            attributes[pos] = attributes[pos-1];
+            ++pos;
+        }
+        // hide soft-hyphens by default
+        if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
+            attributes[pos].dontPrint = true;
+        HB_CharCategory cat;
+        int cmb;
+        HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
+        if (cat != HB_Mark_NonSpacing) {
+            attributes[pos].mark = false;
+            attributes[pos].clusterStart = true;
+            attributes[pos].combiningClass = 0;
+            cStart = logClusters[i];
+        } else {
+            if (cmb == 0) {
+                // Fix 0 combining classes
+                if ((uc[pos] & 0xff00) == 0x0e00) {
+                    // thai or lao
+                    if (uc[pos] == 0xe31 ||
+                         uc[pos] == 0xe34 ||
+                         uc[pos] == 0xe35 ||
+                         uc[pos] == 0xe36 ||
+                         uc[pos] == 0xe37 ||
+                         uc[pos] == 0xe47 ||
+                         uc[pos] == 0xe4c ||
+                         uc[pos] == 0xe4d ||
+                         uc[pos] == 0xe4e) {
+                        cmb = HB_Combining_AboveRight;
+                    } else if (uc[pos] == 0xeb1 ||
+                                uc[pos] == 0xeb4 ||
+                                uc[pos] == 0xeb5 ||
+                                uc[pos] == 0xeb6 ||
+                                uc[pos] == 0xeb7 ||
+                                uc[pos] == 0xebb ||
+                                uc[pos] == 0xecc ||
+                                uc[pos] == 0xecd) {
+                        cmb = HB_Combining_Above;
+                    } else if (uc[pos] == 0xebc) {
+                        cmb = HB_Combining_Below;
+                    }
+                }
+            }
+
+            attributes[pos].mark = true;
+            attributes[pos].clusterStart = false;
+            attributes[pos].combiningClass = cmb;
+            logClusters[i] = cStart;
+        }
+        // one gets an inter character justification point if the current char is not a non spacing mark.
+        // as then the current char belongs to the last one and one gets a space justification point
+        // after the space char.
+        if (lastCat == HB_Separator_Space)
+            attributes[pos-1].justification = HB_Space;
+        else if (cat != HB_Mark_NonSpacing)
+            attributes[pos-1].justification = HB_Character;
+        else
+            attributes[pos-1].justification = HB_NoJustification;
+
+        lastCat = cat;
+    }
+    pos = logClusters[length-1];
+    if (lastCat == HB_Separator_Space)
+        attributes[pos].justification = HB_Space;
+    else
+        attributes[pos].justification = HB_Character;
+}
+
+#ifndef NO_OPENTYPE
+static const HB_OpenTypeFeature basic_features[] = {
+    { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
+    { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty },
+    { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty },
+    {0, 0}
+};
+#endif
+
+HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
+{
+    if (shaper_item->glyphIndicesPresent) {
+        shaper_item->num_glyphs = shaper_item->initialGlyphCount;
+        shaper_item->glyphIndicesPresent = false;
+        return true;
+    }
+    return shaper_item->font->klass
+           ->convertStringToGlyphIndices(shaper_item->font,
+                                         shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
+                                         shaper_item->glyphs, &shaper_item->num_glyphs,
+                                         shaper_item->item.bidiLevel % 2);
+}
+
+HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
+{
+#ifndef NO_OPENTYPE
+    const int availableGlyphs = shaper_item->num_glyphs;
+#endif
+
+    if (!HB_ConvertStringToGlyphIndices(shaper_item))
+        return false;
+
+    HB_HeuristicSetGlyphAttributes(shaper_item);
+
+#ifndef NO_OPENTYPE
+    if (HB_SelectScript(shaper_item, basic_features)) {
+        HB_OpenTypeShape(shaper_item, /*properties*/0);
+        return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
+    }
+#endif
+
+    HB_HeuristicPosition(shaper_item);
+    return true;
+}
+
+const HB_ScriptEngine HB_ScriptEngines[] = {
+    // Common
+    { HB_BasicShape, 0},
+    // Greek
+    { HB_BasicShape, 0},
+    // Cyrillic
+    { HB_BasicShape, 0},
+    // Armenian
+    { HB_BasicShape, 0},
+    // Hebrew
+    { HB_HebrewShape, 0 },
+    // Arabic
+    { HB_ArabicShape, 0},
+    // Syriac
+    { HB_ArabicShape, 0},
+    // Thaana
+    { HB_BasicShape, 0 },
+    // Devanagari
+    { HB_IndicShape, HB_IndicAttributes },
+    // Bengali
+    { HB_IndicShape, HB_IndicAttributes },
+    // Gurmukhi
+    { HB_IndicShape, HB_IndicAttributes },
+    // Gujarati
+    { HB_IndicShape, HB_IndicAttributes },
+    // Oriya
+    { HB_IndicShape, HB_IndicAttributes },
+    // Tamil
+    { HB_IndicShape, HB_IndicAttributes },
+    // Telugu
+    { HB_IndicShape, HB_IndicAttributes },
+    // Kannada
+    { HB_IndicShape, HB_IndicAttributes },
+    // Malayalam
+    { HB_IndicShape, HB_IndicAttributes },
+    // Sinhala
+    { HB_IndicShape, HB_IndicAttributes },
+    // Thai
+    { HB_BasicShape, HB_ThaiAttributes },
+    // Lao
+    { HB_BasicShape, 0 },
+    // Tibetan
+    { HB_TibetanShape, HB_TibetanAttributes },
+    // Myanmar
+    { HB_MyanmarShape, HB_MyanmarAttributes },
+    // Georgian
+    { HB_BasicShape, 0 },
+    // Hangul
+    { HB_HangulShape, 0 },
+    // Ogham
+    { HB_BasicShape, 0 },
+    // Runic
+    { HB_BasicShape, 0 },
+    // Khmer
+    { HB_KhmerShape, HB_KhmerAttributes },
+    // N'Ko
+    { HB_ArabicShape, 0}
+};
+
+void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
+                          const HB_ScriptItem *items, hb_uint32 numItems,
+                          HB_CharAttributes *attributes)
+{
+    calcLineBreaks(string, stringLength, attributes);
+
+    for (hb_uint32 i = 0; i < numItems; ++i) {
+        HB_Script script = items[i].script;
+        if (script == HB_Script_Inherited)
+            script = HB_Script_Common;
+        HB_AttributeFunction attributeFunction = HB_ScriptEngines[script].charAttributes;
+        if (!attributeFunction)
+            continue;
+        attributeFunction(script, string, items[i].pos, items[i].length, attributes);
+    }
+}
+
+
+enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 };
+
+static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = {
+//        Other    Format   Katakana ALetter  MidLetter MidNum  Numeric  ExtendNumLet
+    {   Break,   Break,   Break,   Break,   Break,   Break,   Break,   Break }, // Other
+    {   Break,   Break,   Break,   Break,   Break,   Break,   Break,   Break }, // Format 
+    {   Break,   Break, NoBreak,   Break,   Break,   Break,   Break, NoBreak }, // Katakana
+    {   Break,   Break,   Break, NoBreak,  Middle,   Break, NoBreak, NoBreak }, // ALetter
+    {   Break,   Break,   Break,   Break,   Break,   Break,   Break,   Break }, // MidLetter
+    {   Break,   Break,   Break,   Break,   Break,   Break,   Break,   Break }, // MidNum
+    {   Break,   Break,   Break, NoBreak,   Break,  Middle, NoBreak, NoBreak }, // Numeric
+    {   Break,   Break, NoBreak, NoBreak,   Break,   Break, NoBreak, NoBreak }, // ExtendNumLet
+};
+
+void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
+                          const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
+                          HB_CharAttributes *attributes)
+{
+    if (stringLength == 0)
+        return;
+    unsigned int brk = HB_GetWordClass(string[0]);
+    attributes[0].wordBoundary = true;
+    for (hb_uint32 i = 1; i < stringLength; ++i) {
+        if (!attributes[i].charStop) {
+            attributes[i].wordBoundary = false;
+            continue;
+        }
+        hb_uint32 nbrk = HB_GetWordClass(string[i]);
+        if (nbrk == HB_Word_Format) {
+            attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep);
+            continue;
+        }
+        BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk];
+        if (rule == Middle) {
+            rule = Break;
+            hb_uint32 lookahead = i + 1;
+            while (lookahead < stringLength) {
+                hb_uint32 testbrk = HB_GetWordClass(string[lookahead]);
+                if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) {
+                    ++lookahead;
+                    continue;
+                }
+                if (testbrk == brk) {
+                    rule = NoBreak;
+                    while (i < lookahead)
+                        attributes[i++].wordBoundary = false;
+                    nbrk = testbrk;
+                }
+                break;
+            }
+        }
+        attributes[i].wordBoundary = (rule == Break);
+        brk = nbrk;
+    }
+}
+
+
+enum SentenceBreakStates {
+    SB_Initial,
+    SB_Upper,
+    SB_UpATerm, 
+    SB_ATerm,
+    SB_ATermC, 
+    SB_ACS, 
+    SB_STerm, 
+    SB_STermC, 
+    SB_SCS,
+    SB_BAfter, 
+    SB_Break,
+    SB_Look
+};
+
+static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = {
+//        Other       Sep         Format      Sp          Lower       Upper       OLetter     Numeric     ATerm       STerm       Close
+      { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper  , SB_Initial, SB_Initial, SB_ATerm  , SB_STerm  , SB_Initial }, // SB_Initial,
+      { SB_Initial, SB_BAfter , SB_Upper  , SB_Initial, SB_Initial, SB_Upper  , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm  , SB_Initial }, // SB_Upper
+      
+      { SB_Look   , SB_BAfter , SB_UpATerm, SB_ACS    , SB_Initial, SB_Upper  , SB_Break  , SB_Initial, SB_ATerm  , SB_STerm  , SB_ATermC  }, // SB_UpATerm
+      { SB_Look   , SB_BAfter , SB_ATerm  , SB_ACS    , SB_Initial, SB_Break  , SB_Break  , SB_Initial, SB_ATerm  , SB_STerm  , SB_ATermC  }, // SB_ATerm
+      { SB_Look   , SB_BAfter , SB_ATermC , SB_ACS    , SB_Initial, SB_Break  , SB_Break  , SB_Look   , SB_ATerm  , SB_STerm  , SB_ATermC  }, // SB_ATermC,
+      { SB_Look   , SB_BAfter , SB_ACS    , SB_ACS    , SB_Initial, SB_Break  , SB_Break  , SB_Look   , SB_ATerm  , SB_STerm  , SB_Look    }, // SB_ACS,
+      
+      { SB_Break  , SB_BAfter , SB_STerm  , SB_SCS    , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_ATerm  , SB_STerm  , SB_STermC  }, // SB_STerm,
+      { SB_Break  , SB_BAfter , SB_STermC , SB_SCS    , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_ATerm  , SB_STerm  , SB_STermC  }, // SB_STermC,
+      { SB_Break  , SB_BAfter , SB_SCS    , SB_SCS    , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_ATerm  , SB_STerm  , SB_Break   }, // SB_SCS,
+      { SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break  , SB_Break   }, // SB_BAfter,
+};
+
+void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
+                              const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
+                              HB_CharAttributes *attributes)
+{
+    if (stringLength == 0)
+        return;
+    hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])];
+    attributes[0].sentenceBoundary = true;
+    for (hb_uint32 i = 1; i < stringLength; ++i) {
+        if (!attributes[i].charStop) {
+            attributes[i].sentenceBoundary = false;
+            continue;
+        }
+        brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])];
+        if (brk == SB_Look) {
+            brk = SB_Break;
+            hb_uint32 lookahead = i + 1;
+            while (lookahead < stringLength) {
+                hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]);
+                if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) {
+                    break;
+                } else if (sbrk == HB_Sentence_Lower) {
+                    brk = SB_Initial;
+                    break;
+                }
+                ++lookahead;
+            }
+            if (brk == SB_Initial) {
+                while (i < lookahead)
+                    attributes[i++].sentenceBoundary = false;
+            }
+        }
+        if (brk == SB_Break) {
+            attributes[i].sentenceBoundary = true;
+            brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])];
+        } else {
+            attributes[i].sentenceBoundary = false;
+        }
+    }
+}
+
+
+static inline char *tag_to_string(HB_UInt tag)
+{
+    static char string[5];
+    string[0] = (tag >> 24)&0xff;
+    string[1] = (tag >> 16)&0xff;
+    string[2] = (tag >> 8)&0xff;
+    string[3] = tag&0xff;
+    string[4] = 0;
+    return string;
+}
+
+#ifdef OT_DEBUG
+static void dump_string(HB_Buffer buffer)
+{
+    for (uint i = 0; i < buffer->in_length; ++i) {
+        qDebug("    %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
+    }
+}
+#define DEBUG printf
+#else
+#define DEBUG if (1) ; else printf
+#endif
+
+#define DefaultLangSys 0xffff
+#define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
+
+enum {
+    RequiresGsub = 1,
+    RequiresGpos = 2
+};
+
+struct OTScripts {
+    unsigned int tag;
+    int flags;
+};
+static const OTScripts ot_scripts [] = {
+    // Common
+    { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
+    // Greek
+    { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
+    // Cyrillic
+    { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
+    // Armenian
+    { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
+    // Hebrew
+    { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
+    // Arabic
+    { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
+    // Syriac
+    { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
+    // Thaana
+    { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
+    // Devanagari
+    { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
+    // Bengali
+    { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
+    // Gurmukhi
+    { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
+    // Gujarati
+    { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
+    // Oriya
+    { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
+    // Tamil
+    { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
+    // Telugu
+    { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
+    // Kannada
+    { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
+    // Malayalam
+    { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
+    // Sinhala
+    { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
+    // Thai
+    { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
+    // Lao
+    { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
+    // Tibetan
+    { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
+    // Myanmar
+    { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
+    // Georgian
+    { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
+    // Hangul
+    { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
+    // Ogham
+    { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
+    // Runic
+    { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
+    // Khmer
+    { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
+    // N'Ko
+    { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
+};
+enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
+
+static HB_Bool checkScript(HB_Face face, int script)
+{
+    assert(script < HB_ScriptCount);
+
+    if (!face->gsub && !face->gpos)
+        return false;
+
+    unsigned int tag = ot_scripts[script].tag;
+    int requirements = ot_scripts[script].flags;
+
+    if (requirements & RequiresGsub) {
+        if (!face->gsub)
+            return false;
+
+        HB_UShort script_index;
+        HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
+        if (error) {
+            DEBUG("could not select script %d in GSub table: %d", (int)script, error);
+            error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
+            if (error)
+                return false;
+        }
+    }
+
+    if (requirements & RequiresGpos) {
+        if (!face->gpos)
+            return false;
+
+        HB_UShort script_index;
+        HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
+        if (error) {
+            DEBUG("could not select script in gpos table: %d", error);
+            error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
+            if (error)
+                return false;
+        }
+
+    }
+    return true;
+}
+
+static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
+{
+    HB_Error error;
+    HB_UInt length = 0;
+    HB_Stream stream = 0;
+
+    if (!font)
+        return 0;
+
+    error = tableFunc(font, tag, 0, &length);
+    if (error)
+        return 0;
+    stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
+    if (!stream)
+        return 0;
+    stream->base = (HB_Byte*)malloc(length);
+    if (!stream->base) {
+        free(stream);
+        return 0;
+    }
+    error = tableFunc(font, tag, stream->base, &length);
+    if (error) {
+        _hb_close_stream(stream);
+        return 0;
+    }
+    stream->size = length;
+    stream->pos = 0;
+    stream->cursor = NULL;
+    return stream;
+}
+
+HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
+{
+    HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
+    if (!face)
+        return 0;
+
+    face->isSymbolFont = false;
+    face->gdef = 0;
+    face->gpos = 0;
+    face->gsub = 0;
+    face->current_script = HB_ScriptCount;
+    face->current_flags = HB_ShaperFlag_Default;
+    face->has_opentype_kerning = false;
+    face->tmpAttributes = 0;
+    face->tmpLogClusters = 0;
+    face->glyphs_substituted = false;
+    face->buffer = 0;
+
+    HB_Error error;
+    HB_Stream stream;
+    HB_Stream gdefStream;
+
+    gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
+    if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
+        //DEBUG("error loading gdef table: %d", error);
+        face->gdef = 0;
+    }
+
+    //DEBUG() << "trying to load gsub table";
+    stream = getTableStream(font, tableFunc, TTAG_GSUB);
+    if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
+        face->gsub = 0;
+        if (error != HB_Err_Not_Covered) {
+            //DEBUG("error loading gsub table: %d", error);
+        } else {
+            //DEBUG("face doesn't have a gsub table");
+        }
+    }
+    _hb_close_stream(stream);
+
+    stream = getTableStream(font, tableFunc, TTAG_GPOS);
+    if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
+        face->gpos = 0;
+        DEBUG("error loading gpos table: %d", error);
+    }
+    _hb_close_stream(stream);
+
+    _hb_close_stream(gdefStream);
+
+    for (unsigned int i = 0; i < HB_ScriptCount; ++i)
+        face->supported_scripts[i] = checkScript(face, i);
+
+    if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
+        HB_FreeFace(face);
+        return 0;
+    }
+
+    return face;
+}
+
+void HB_FreeFace(HB_Face face)
+{
+    if (!face)
+        return;
+    if (face->gpos)
+        HB_Done_GPOS_Table(face->gpos);
+    if (face->gsub)
+        HB_Done_GSUB_Table(face->gsub);
+    if (face->gdef)
+        HB_Done_GDEF_Table(face->gdef);
+    if (face->buffer)
+        hb_buffer_free(face->buffer);
+    if (face->tmpAttributes)
+        free(face->tmpAttributes);
+    if (face->tmpLogClusters)
+        free(face->tmpLogClusters);
+    free(face);
+}
+
+HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
+{
+    HB_Script script = shaper_item->item.script;
+
+    if (!shaper_item->face->supported_scripts[script])
+        return false;
+
+    HB_Face face = shaper_item->face;
+    if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
+        return true;
+
+    face->current_script = script;
+    face->current_flags = shaper_item->shaperFlags;
+
+    assert(script < HB_ScriptCount);
+    // find script in our list of supported scripts.
+    unsigned int tag = ot_scripts[script].tag;
+
+    if (face->gsub && features) {
+#ifdef OT_DEBUG
+        {
+            HB_FeatureList featurelist = face->gsub->FeatureList;
+            int numfeatures = featurelist.FeatureCount;
+            DEBUG("gsub table has %d features", numfeatures);
+            for (int i = 0; i < numfeatures; i++) {
+                HB_FeatureRecord *r = featurelist.FeatureRecord + i;
+                DEBUG("   feature '%s'", tag_to_string(r->FeatureTag));
+            }
+        }
+#endif
+        HB_GSUB_Clear_Features(face->gsub);
+        HB_UShort script_index;
+        HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
+        if (!error) {
+            DEBUG("script %s has script index %d", tag_to_string(script), script_index);
+            while (features->tag) {
+                HB_UShort feature_index;
+                error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
+                if (!error) {
+                    DEBUG("  adding feature %s", tag_to_string(features->tag));
+                    HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
+                }
+                ++features;
+            }
+        }
+    }
+
+    // reset
+    face->has_opentype_kerning = false;
+
+    if (face->gpos) {
+        HB_GPOS_Clear_Features(face->gpos);
+        HB_UShort script_index;
+        HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
+        if (!error) {
+#ifdef OT_DEBUG
+            {
+                HB_FeatureList featurelist = face->gpos->FeatureList;
+                int numfeatures = featurelist.FeatureCount;
+                DEBUG("gpos table has %d features", numfeatures);
+                for(int i = 0; i < numfeatures; i++) {
+                    HB_FeatureRecord *r = featurelist.FeatureRecord + i;
+                    HB_UShort feature_index;
+                    HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
+                    DEBUG("   feature '%s'", tag_to_string(r->FeatureTag));
+                }
+            }
+#endif
+            HB_UInt *feature_tag_list_buffer;
+            error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
+            if (!error) {
+                HB_UInt *feature_tag_list = feature_tag_list_buffer;
+                while (*feature_tag_list) {
+                    HB_UShort feature_index;
+                    if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
+                        if (face->current_flags & HB_ShaperFlag_NoKerning) {
+                            ++feature_tag_list;
+                            continue;
+                        }
+                        face->has_opentype_kerning = true;
+                    }
+                    error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
+                    if (!error)
+                        HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
+                    ++feature_tag_list;
+                }
+                FREE(feature_tag_list_buffer);
+            }
+        }
+    }
+
+    return true;
+}
+
+HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
+{
+    HB_GlyphAttributes *tmpAttributes;
+    unsigned int *tmpLogClusters;
+
+    HB_Face face = item->face;
+
+    face->length = item->num_glyphs;
+
+    hb_buffer_clear(face->buffer);
+
+    tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
+    if (!tmpAttributes)
+        return false;
+    face->tmpAttributes = tmpAttributes;
+
+    tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
+    if (!tmpLogClusters)
+        return false;
+    face->tmpLogClusters = tmpLogClusters;
+
+    for (int i = 0; i < face->length; ++i) {
+        hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
+        face->tmpAttributes[i] = item->attributes[i];
+        face->tmpLogClusters[i] = item->log_clusters[i];
+    }
+
+#ifdef OT_DEBUG
+    DEBUG("-----------------------------------------");
+//     DEBUG("log clusters before shaping:");
+//     for (int j = 0; j < length; j++)
+//         DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
+    DEBUG("original glyphs: %p", item->glyphs);
+    for (int i = 0; i < length; ++i)
+        DEBUG("   glyph=%4x", hb_buffer->in_string[i].gindex);
+//     dump_string(hb_buffer);
+#endif
+
+    face->glyphs_substituted = false;
+    if (face->gsub) {
+        unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
+        if (error && error != HB_Err_Not_Covered)
+            return false;
+        face->glyphs_substituted = (error != HB_Err_Not_Covered);
+    }
+
+#ifdef OT_DEBUG
+//     DEBUG("log clusters before shaping:");
+//     for (int j = 0; j < length; j++)
+//         DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
+    DEBUG("shaped glyphs:");
+    for (int i = 0; i < length; ++i)
+        DEBUG("   glyph=%4x", hb_buffer->in_string[i].gindex);
+    DEBUG("-----------------------------------------");
+//     dump_string(hb_buffer);
+#endif
+
+    return true;
+}
+
+/* See comments near the definition of HB_ShaperFlag_ForceMarksToZeroWidth for a description
+   of why this function exists. */
+void HB_FixupZeroWidth(HB_ShaperItem *item)
+{
+    HB_UShort property;
+
+    if (!item->face->gdef)
+        return;
+
+    for (unsigned int i = 0; i < item->num_glyphs; ++i) {
+        /* If the glyph is a mark, force its advance to zero. */
+        if (HB_GDEF_Get_Glyph_Property (item->face->gdef, item->glyphs[i], &property) == HB_Err_Ok &&
+            property == HB_GDEF_MARK) {
+            item->advances[i] = 0;
+        }
+    }
+}
+
+HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
+{
+    HB_Face face = item->face;
+
+    bool glyphs_positioned = false;
+    if (face->gpos) {
+        if (face->buffer->positions)
+            memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
+        // #### check that passing "false,false" is correct
+        glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
+    }
+
+    if (!face->glyphs_substituted && !glyphs_positioned) {
+        HB_GetGlyphAdvances(item);
+        if (item->face->current_flags & HB_ShaperFlag_ForceMarksToZeroWidth)
+            HB_FixupZeroWidth(item);
+        return true; // nothing to do for us
+    }
+
+    // make sure we have enough space to write everything back
+    if (availableGlyphs < (int)face->buffer->in_length) {
+        item->num_glyphs = face->buffer->in_length;
+        return false;
+    }
+
+    HB_Glyph *glyphs = item->glyphs;
+    HB_GlyphAttributes *attributes = item->attributes;
+
+    for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
+        glyphs[i] = face->buffer->in_string[i].gindex;
+        attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
+        if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
+            attributes[i].clusterStart = false;
+    }
+    item->num_glyphs = face->buffer->in_length;
+
+    if (doLogClusters && face->glyphs_substituted) {
+        // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
+        unsigned short *logClusters = item->log_clusters;
+        int clusterStart = 0;
+        int oldCi = 0;
+        // #### the reconstruction of the logclusters currently does not work if the original string
+        // contains surrogate pairs
+        for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
+            int ci = face->buffer->in_string[i].cluster;
+            //         DEBUG("   ci[%d] = %d mark=%d, cmb=%d, cs=%d",
+            //                i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
+            if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
+                for (int j = oldCi; j < ci; j++)
+                    logClusters[j] = clusterStart;
+                clusterStart = i;
+                oldCi = ci;
+            }
+        }
+        for (int j = oldCi; j < face->length; j++)
+            logClusters[j] = clusterStart;
+    }
+
+    // calulate the advances for the shaped glyphs
+//     DEBUG("unpositioned: ");
+
+    // positioning code:
+    if (glyphs_positioned) {
+        HB_GetGlyphAdvances(item);
+        HB_Position positions = face->buffer->positions;
+        HB_Fixed *advances = item->advances;
+
+//         DEBUG("positioned glyphs:");
+        for (unsigned int i = 0; i < face->buffer->in_length; i++) {
+//             DEBUG("    %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
+//                    glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
+//                    (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
+//                    (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
+//                    positions[i].back, positions[i].new_advance);
+
+            HB_Fixed adjustment = (item->item.bidiLevel % 2) ? -positions[i].x_advance : positions[i].x_advance;
+
+            if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
+                adjustment = HB_FIXED_ROUND(adjustment);
+
+            if (positions[i].new_advance) {
+                advances[i] = adjustment;
+            } else {
+                advances[i] += adjustment;
+            }
+
+            int back = 0;
+            HB_FixedPoint *offsets = item->offsets;
+            offsets[i].x = positions[i].x_pos;
+            offsets[i].y = positions[i].y_pos;
+            while (positions[i - back].back) {
+                back += positions[i - back].back;
+                offsets[i].x += positions[i - back].x_pos;
+                offsets[i].y += positions[i - back].y_pos;
+            }
+            offsets[i].y = -offsets[i].y;
+
+            if (item->item.bidiLevel % 2) {
+                // ### may need to go back multiple glyphs like in ltr
+                back = positions[i].back;
+                while (back--)
+                    offsets[i].x -= advances[i-back];
+            } else {
+                back = 0;
+                while (positions[i - back].back) {
+                    back += positions[i - back].back;
+                    offsets[i].x -= advances[i-back];
+                }
+            }
+//             DEBUG("   ->\tadv=%d\tpos=(%d/%d)",
+//                    glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
+        }
+        item->kerning_applied = face->has_opentype_kerning;
+    } else {
+        HB_HeuristicPosition(item);
+    }
+
+#ifdef OT_DEBUG
+    if (doLogClusters) {
+        DEBUG("log clusters after shaping:");
+        for (int j = 0; j < length; j++)
+            DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
+    }
+    DEBUG("final glyphs:");
+    for (int i = 0; i < (int)hb_buffer->in_length; ++i)
+        DEBUG("   glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
+               glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
+               glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
+               glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
+               glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
+    DEBUG("-----------------------------------------");
+#endif
+    return true;
+}
+
+HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
+{
+    HB_Bool result = false;
+    if (shaper_item->num_glyphs < shaper_item->item.length) {
+        shaper_item->num_glyphs = shaper_item->item.length;
+        return false;
+    }
+    assert(shaper_item->item.script < HB_ScriptCount);
+    result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
+    shaper_item->glyphIndicesPresent = false;
+    return result;
+}
+
diff --git a/third_party/harfbuzz/src/harfbuzz-shaper.h b/third_party/harfbuzz/src/harfbuzz-shaper.h
new file mode 100644
index 0000000..33fc85a
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-shaper.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_SHAPER_H
+#define HARFBUZZ_SHAPER_H
+
+#include "harfbuzz-global.h"
+#include "harfbuzz-gdef.h"
+#include "harfbuzz-gpos.h"
+#include "harfbuzz-gsub.h"
+#include "harfbuzz-external.h"
+#include "harfbuzz-stream-private.h"
+
+HB_BEGIN_HEADER
+
+typedef enum {
+        HB_Script_Common,
+        HB_Script_Greek,
+        HB_Script_Cyrillic,
+        HB_Script_Armenian,
+        HB_Script_Hebrew,
+        HB_Script_Arabic,
+        HB_Script_Syriac,
+        HB_Script_Thaana,
+        HB_Script_Devanagari,
+        HB_Script_Bengali,
+        HB_Script_Gurmukhi,
+        HB_Script_Gujarati,
+        HB_Script_Oriya,
+        HB_Script_Tamil,
+        HB_Script_Telugu,
+        HB_Script_Kannada,
+        HB_Script_Malayalam,
+        HB_Script_Sinhala,
+        HB_Script_Thai,
+        HB_Script_Lao,
+        HB_Script_Tibetan,
+        HB_Script_Myanmar,
+        HB_Script_Georgian,
+        HB_Script_Hangul,
+        HB_Script_Ogham,
+        HB_Script_Runic,
+        HB_Script_Khmer,
+        HB_Script_Nko,
+        HB_Script_Inherited,
+        HB_ScriptCount = HB_Script_Inherited
+        /*
+        HB_Script_Latin = Common,
+        HB_Script_Ethiopic = Common,
+        HB_Script_Cherokee = Common,
+        HB_Script_CanadianAboriginal = Common,
+        HB_Script_Mongolian = Common,
+        HB_Script_Hiragana = Common,
+        HB_Script_Katakana = Common,
+        HB_Script_Bopomofo = Common,
+        HB_Script_Han = Common,
+        HB_Script_Yi = Common,
+        HB_Script_OldItalic = Common,
+        HB_Script_Gothic = Common,
+        HB_Script_Deseret = Common,
+        HB_Script_Tagalog = Common,
+        HB_Script_Hanunoo = Common,
+        HB_Script_Buhid = Common,
+        HB_Script_Tagbanwa = Common,
+        HB_Script_Limbu = Common,
+        HB_Script_TaiLe = Common,
+        HB_Script_LinearB = Common,
+        HB_Script_Ugaritic = Common,
+        HB_Script_Shavian = Common,
+        HB_Script_Osmanya = Common,
+        HB_Script_Cypriot = Common,
+        HB_Script_Braille = Common,
+        HB_Script_Buginese = Common,
+        HB_Script_Coptic = Common,
+        HB_Script_NewTaiLue = Common,
+        HB_Script_Glagolitic = Common,
+        HB_Script_Tifinagh = Common,
+        HB_Script_SylotiNagri = Common,
+        HB_Script_OldPersian = Common,
+        HB_Script_Kharoshthi = Common,
+        HB_Script_Balinese = Common,
+        HB_Script_Cuneiform = Common,
+        HB_Script_Phoenician = Common,
+        HB_Script_PhagsPa = Common,
+        */
+} HB_Script;
+
+typedef struct
+{
+    hb_uint32 pos;
+    hb_uint32 length;
+    HB_Script script;
+    hb_uint8 bidiLevel;
+} HB_ScriptItem;
+
+typedef enum {
+    HB_NoBreak,
+    HB_SoftHyphen,
+    HB_Break,
+    HB_ForcedBreak
+} HB_LineBreakType;
+
+
+typedef struct {
+    /*HB_LineBreakType*/ unsigned lineBreakType  :2;
+    /*HB_Bool*/ unsigned whiteSpace              :1;     /* A unicode whitespace character, except NBSP, ZWNBSP */
+    /*HB_Bool*/ unsigned charStop                :1;     /* Valid cursor position (for left/right arrow) */
+    /*HB_Bool*/ unsigned wordBoundary            :1;
+    /*HB_Bool*/ unsigned sentenceBoundary        :1;
+    unsigned unused                  :2;
+} HB_CharAttributes;
+
+void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
+                          const HB_ScriptItem *items, hb_uint32 numItems,
+                          HB_CharAttributes *attributes);
+
+/* requires HB_GetCharAttributes to be called before */
+void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
+                          const HB_ScriptItem *items, hb_uint32 numItems,
+                          HB_CharAttributes *attributes);
+
+/* requires HB_GetCharAttributes to be called before */
+void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
+                              const HB_ScriptItem *items, hb_uint32 numItems,
+                              HB_CharAttributes *attributes);
+
+
+typedef enum {
+    HB_LeftToRight = 0,
+    HB_RightToLeft = 1
+} HB_StringToGlyphsFlags;
+
+typedef enum {
+    HB_ShaperFlag_Default = 0,
+    HB_ShaperFlag_NoKerning = 1,
+    HB_ShaperFlag_UseDesignMetrics = 1 << 1,
+    /* Arabic vowels in some fonts (Times New Roman, at least) have
+       non-zero advances, when they should be zero.  Setting this shaper
+       flag causes us to zero out the advances for mark glyphs. */
+    HB_ShaperFlag_ForceMarksToZeroWidth = 1 << 2
+} HB_ShaperFlag;
+
+/* 
+   highest value means highest priority for justification. Justification is done by first inserting kashidas
+   starting with the highest priority positions, then stretching spaces, afterwards extending inter char
+   spacing, and last spacing between arabic words.
+   NoJustification is for example set for arabic where no Kashida can be inserted or for diacritics.
+*/
+typedef enum {
+    HB_NoJustification= 0,   /* Justification can't be applied after this glyph */
+    HB_Arabic_Space   = 1,   /* This glyph represents a space inside arabic text */
+    HB_Character      = 2,   /* Inter-character justification point follows this glyph */
+    HB_Space          = 4,   /* This glyph represents a blank outside an Arabic run */
+    HB_Arabic_Normal  = 7,   /* Normal Middle-Of-Word glyph that connects to the right (begin) */
+    HB_Arabic_Waw     = 8,   /* Next character is final form of Waw/Ain/Qaf/Fa */
+    HB_Arabic_BaRa    = 9,   /* Next two chars are Ba + Ra/Ya/AlefMaksura */
+    HB_Arabic_Alef    = 10,  /* Next character is final form of Alef/Tah/Lam/Kaf/Gaf */
+    HB_Arabic_HaaDal  = 11,  /* Next character is final form of Haa/Dal/Taa Marbutah */
+    HB_Arabic_Seen    = 12,  /* Initial or Medial form Of Seen/Sad */
+    HB_Arabic_Kashida = 13   /* Kashida(U+640) in middle of word */
+} HB_JustificationClass;
+
+/* This structure is binary compatible with Uniscribe's SCRIPT_VISATTR. Would be nice to keep
+ * it like that. If this is a problem please tell Trolltech :)
+ */
+typedef struct {
+    unsigned justification   :4;  /* Justification class */
+    unsigned clusterStart    :1;  /* First glyph of representation of cluster */
+    unsigned mark            :1;  /* needs to be positioned around base char */
+    unsigned zeroWidth       :1;  /* ZWJ, ZWNJ etc, with no width */
+    unsigned dontPrint       :1;
+    unsigned combiningClass  :8;
+} HB_GlyphAttributes;
+
+typedef struct HB_FaceRec_ {
+    HB_Bool isSymbolFont;
+
+    HB_GDEF gdef;
+    HB_GSUB gsub;
+    HB_GPOS gpos;
+    HB_Bool supported_scripts[HB_ScriptCount];
+    HB_Buffer buffer;
+    HB_Script current_script;
+    int current_flags; /* HB_ShaperFlags */
+    HB_Bool has_opentype_kerning;
+    HB_Bool glyphs_substituted;
+    HB_GlyphAttributes *tmpAttributes;
+    unsigned int *tmpLogClusters;
+    int length;
+    int orig_nglyphs;
+} HB_FaceRec;
+
+typedef HB_Error (*HB_GetFontTableFunc)(void *font, HB_Tag tag, HB_Byte *buffer, HB_UInt *length);
+
+HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc);
+void HB_FreeFace(HB_Face face);
+
+typedef struct {
+    HB_Fixed x, y;
+    HB_Fixed width, height;
+    HB_Fixed xOffset, yOffset;
+} HB_GlyphMetrics;
+
+typedef enum {
+    HB_FontAscent
+} HB_FontMetric;
+
+typedef struct {
+    HB_Bool  (*convertStringToGlyphIndices)(HB_Font font, const HB_UChar16 *string, hb_uint32 length, HB_Glyph *glyphs, hb_uint32 *numGlyphs, HB_Bool rightToLeft);
+    void     (*getGlyphAdvances)(HB_Font font, const HB_Glyph *glyphs, hb_uint32 numGlyphs, HB_Fixed *advances, int flags /*HB_ShaperFlag*/);
+    HB_Bool  (*canRender)(HB_Font font, const HB_UChar16 *string, hb_uint32 length);
+    /* implementation needs to make sure to load a scaled glyph, so /no/ FT_LOAD_NO_SCALE */
+    HB_Error (*getPointInOutline)(HB_Font font, HB_Glyph glyph, int flags /*HB_ShaperFlag*/, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints);
+    void     (*getGlyphMetrics)(HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics);
+    HB_Fixed (*getFontMetric)(HB_Font font, HB_FontMetric metric);
+} HB_FontClass;
+
+typedef struct HB_Font_ {
+    const HB_FontClass *klass;
+
+    /* Metrics */
+    HB_UShort x_ppem, y_ppem;
+    HB_16Dot16 x_scale, y_scale;
+
+    void *userData;
+} HB_FontRec;
+
+typedef struct HB_ShaperItem_ HB_ShaperItem;
+
+struct HB_ShaperItem_ {
+    const HB_UChar16 *string;               /* input: the Unicode UTF16 text to be shaped */
+    hb_uint32 stringLength;                 /* input: the length of the input in 16-bit words */
+    HB_ScriptItem item;                     /* input: the current run to be shaped: a run of text all in the same script that is a substring of <string> */
+    HB_Font font;                           /* input: the font: scale, units and function pointers supplying glyph indices and metrics */
+    HB_Face face;                           /* input: the shaper state; current script, access to the OpenType tables , etc. */
+    int shaperFlags;                        /* input (unused) should be set to 0; intended to support flags defined in HB_ShaperFlag */
+    HB_Bool glyphIndicesPresent;            /* input: true if the <glyphs> array contains glyph indices ready to be shaped */
+    hb_uint32 initialGlyphCount;            /* input: if glyphIndicesPresent is true, the number of glyph indices in the <glyphs> array */
+
+    hb_uint32 num_glyphs;                   /* input: capacity of output arrays <glyphs>, <attributes>, <advances>, <offsets>, and <log_clusters>; */
+                                            /* output: required capacity (may be larger than actual capacity) */
+
+    HB_Glyph *glyphs;                       /* output: <num_glyphs> indices of shaped glyphs */
+    HB_GlyphAttributes *attributes;         /* output: <num_glyphs> glyph attributes */
+    HB_Fixed *advances;                     /* output: <num_glyphs> advances */
+    HB_FixedPoint *offsets;                 /* output: <num_glyphs> offsets */
+    unsigned short *log_clusters;           /* output: for each output glyph, the index in the input of the start of its logical cluster */
+
+    /* internal */
+    HB_Bool kerning_applied;                /* output: true if kerning was applied by the shaper */
+};
+
+HB_Bool HB_ShapeItem(HB_ShaperItem *item);
+
+HB_END_HEADER
+
+#endif
diff --git a/third_party/harfbuzz/src/harfbuzz-stream-private.h b/third_party/harfbuzz/src/harfbuzz-stream-private.h
new file mode 100644
index 0000000..fbd9f81
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-stream-private.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_STREAM_PRIVATE_H
+#define HARFBUZZ_STREAM_PRIVATE_H
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-stream.h"
+
+HB_BEGIN_HEADER
+
+HB_INTERNAL void
+_hb_close_stream( HB_Stream stream );
+
+HB_INTERNAL HB_Int
+_hb_stream_pos( HB_Stream stream );
+
+HB_INTERNAL HB_Error
+_hb_stream_seek( HB_Stream stream,
+                 HB_UInt   pos );
+
+HB_INTERNAL HB_Error
+_hb_stream_frame_enter( HB_Stream stream,
+                        HB_UInt   size );
+
+HB_INTERNAL void
+_hb_stream_frame_exit( HB_Stream stream );
+
+/* convenience macros */
+
+#define  SET_ERR(c)   ( (error = (c)) != 0 )
+
+#define  GOTO_Table(tag) (0)
+#define  FILE_Pos()      _hb_stream_pos( stream )
+#define  FILE_Seek(pos)  SET_ERR( _hb_stream_seek( stream, pos ) )
+#define  ACCESS_Frame(size)  SET_ERR( _hb_stream_frame_enter( stream, size ) )
+#define  FORGET_Frame()      _hb_stream_frame_exit( stream )
+
+#define  GET_Byte()      (*stream->cursor++)
+#define  GET_Short()     (stream->cursor += 2, (HB_Short)( \
+				(*(((HB_Byte*)stream->cursor)-2) << 8) | \
+				 *(((HB_Byte*)stream->cursor)-1) \
+			 ))
+#define  GET_Long()      (stream->cursor += 4, (HB_Int)( \
+				(*(((HB_Byte*)stream->cursor)-4) << 24) | \
+				(*(((HB_Byte*)stream->cursor)-3) << 16) | \
+				(*(((HB_Byte*)stream->cursor)-2) << 8) | \
+				 *(((HB_Byte*)stream->cursor)-1) \
+			 ))
+
+
+#define  GET_Char()      ((HB_Char)GET_Byte())
+#define  GET_UShort()    ((HB_UShort)GET_Short())
+#define  GET_ULong()     ((HB_UInt)GET_Long())
+#define  GET_Tag4()      GET_ULong()
+
+HB_END_HEADER
+
+#endif /* HARFBUZZ_STREAM_PRIVATE_H */
diff --git a/third_party/harfbuzz/src/harfbuzz-stream.c b/third_party/harfbuzz/src/harfbuzz-stream.c
new file mode 100644
index 0000000..2d9638f
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-stream.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2005  David Turner
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2007  Red Hat, Inc.
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "harfbuzz-impl.h"
+#include "harfbuzz-stream-private.h"
+#include <stdlib.h>
+
+#if 0
+#include <stdio.h>
+#define  LOG(x)  _hb_log x
+
+static void
+_hb_log( const char*   format, ... )
+{
+  va_list  ap;
+ 
+  va_start( ap, format );
+  vfprintf( stderr, format, ap );
+  va_end( ap );
+}
+
+#else
+#define  LOG(x)  do {} while (0)
+#endif
+
+HB_INTERNAL void
+_hb_close_stream( HB_Stream stream )
+{
+  if (!stream)
+      return;
+  free(stream->base);
+  free(stream);
+}
+
+
+HB_INTERNAL HB_Int
+_hb_stream_pos( HB_Stream stream )
+{
+  LOG(( "_hb_stream_pos() -> %ld\n", stream->pos ));
+  return stream->pos;
+}
+
+
+HB_INTERNAL HB_Error
+_hb_stream_seek( HB_Stream stream,
+		 HB_UInt pos )
+{
+  HB_Error  error = (HB_Error)0;
+
+  stream->pos = pos;
+  if (pos > stream->size)
+      error = ERR(HB_Err_Read_Error);
+
+  LOG(( "_hb_stream_seek(%ld) -> 0x%04X\n", pos, error ));
+  return error;
+}
+
+
+HB_INTERNAL HB_Error
+_hb_stream_frame_enter( HB_Stream stream,
+			HB_UInt count )
+{
+  HB_Error  error = HB_Err_Ok;
+
+  /* check new position, watch for overflow */
+  if (HB_UNLIKELY (stream->pos + count > stream->size ||
+		   stream->pos + count < stream->pos))
+  {
+    error = ERR(HB_Err_Read_Error);
+    goto Exit;
+  }
+
+  /* set cursor */
+  stream->cursor = stream->base + stream->pos;
+  stream->pos   += count;
+
+Exit:
+  LOG(( "_hb_stream_frame_enter(%ld) -> 0x%04X\n", count, error ));
+  return error;
+}
+
+
+HB_INTERNAL void
+_hb_stream_frame_exit( HB_Stream stream )
+{
+  stream->cursor = NULL;
+
+  LOG(( "_hb_stream_frame_exit()\n" ));
+}
diff --git a/third_party/harfbuzz/src/harfbuzz-stream.h b/third_party/harfbuzz/src/harfbuzz-stream.h
new file mode 100644
index 0000000..9991936
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-stream.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2005  David Turner
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_STREAM_H
+#define HARFBUZZ_STREAM_H
+
+#include "harfbuzz-global.h"
+
+HB_BEGIN_HEADER
+
+typedef struct HB_StreamRec_
+{
+    HB_Byte*       base;
+    HB_UInt        size;
+    HB_UInt        pos;
+    
+    HB_Byte*       cursor;
+} HB_StreamRec;
+
+
+HB_END_HEADER
+
+#endif
diff --git a/third_party/harfbuzz/src/harfbuzz-thai.c b/third_party/harfbuzz/src/harfbuzz-thai.c
new file mode 100644
index 0000000..1d1aa2f
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-thai.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-shaper-private.h"
+#include "harfbuzz-external.h"
+
+#include <assert.h>
+
+static void thaiWordBreaks(const HB_UChar16 *string, hb_uint32 len, HB_CharAttributes *attributes)
+{
+    typedef int (*th_brk_def)(const char*, int[], int);
+    static void *thaiCodec = 0;
+    static th_brk_def th_brk = 0;
+    char *cstr = 0;
+    int brp[128];
+    int *break_positions = brp;
+    hb_uint32 numbreaks;
+    hb_uint32 i;
+
+    if (!thaiCodec)
+        thaiCodec = HB_TextCodecForMib(2259);
+
+    /* load libthai dynamically */
+    if (!th_brk && thaiCodec) {
+        th_brk = (th_brk_def)HB_Library_Resolve("thai", "th_brk");
+        if (!th_brk)
+            thaiCodec = 0;
+    }
+
+    if (!th_brk)
+        return;
+
+    cstr = HB_TextCodec_ConvertFromUnicode(thaiCodec, string, len, 0);
+    if (!cstr)
+        return;
+
+    break_positions = brp;
+    numbreaks = th_brk(cstr, break_positions, 128);
+    if (numbreaks > 128) {
+        break_positions = (int *)malloc(numbreaks * sizeof(int));
+        numbreaks = th_brk(cstr, break_positions, numbreaks);
+    }
+
+    for (i = 0; i < len; ++i)
+        attributes[i].lineBreakType = HB_NoBreak;
+
+    for (i = 0; i < numbreaks; ++i) {
+        if (break_positions[i] > 0)
+            attributes[break_positions[i]-1].lineBreakType = HB_Break;
+    }
+
+    if (break_positions != brp)
+        free(break_positions);
+
+    HB_TextCodec_FreeResult(cstr);
+}
+
+
+void HB_ThaiAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes)
+{
+    assert(script == HB_Script_Thai);
+    attributes += from;
+    thaiWordBreaks(text + from, len, attributes);
+}
+
diff --git a/third_party/harfbuzz/src/harfbuzz-tibetan.c b/third_party/harfbuzz/src/harfbuzz-tibetan.c
new file mode 100644
index 0000000..bfa31b1
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz-tibetan.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include "harfbuzz-shaper.h"
+#include "harfbuzz-shaper-private.h"
+
+#include <assert.h>
+
+/*
+ tibetan syllables are of the form:
+    head position consonant
+    first sub-joined consonant
+    ....intermediate sub-joined consonants (if any)
+    last sub-joined consonant
+    sub-joined vowel (a-chung U+0F71)
+    standard or compound vowel sign (or 'virama' for devanagari transliteration)
+*/
+
+typedef enum {
+    TibetanOther,
+    TibetanHeadConsonant,
+    TibetanSubjoinedConsonant,
+    TibetanSubjoinedVowel,
+    TibetanVowel
+} TibetanForm;
+
+/* this table starts at U+0f40 */
+static const unsigned char tibetanForm[0x80] = {
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+    TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant, TibetanHeadConsonant,
+    TibetanOther, TibetanOther, TibetanOther, TibetanOther,
+
+    TibetanOther, TibetanVowel, TibetanVowel, TibetanVowel,
+    TibetanVowel, TibetanVowel, TibetanVowel, TibetanVowel,
+    TibetanVowel, TibetanVowel, TibetanVowel, TibetanVowel,
+    TibetanVowel, TibetanVowel, TibetanVowel, TibetanVowel,
+
+    TibetanVowel, TibetanVowel, TibetanVowel, TibetanVowel,
+    TibetanVowel, TibetanVowel, TibetanVowel, TibetanVowel,
+    TibetanOther, TibetanOther, TibetanOther, TibetanOther,
+    TibetanOther, TibetanOther, TibetanOther, TibetanOther,
+
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+    TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant, TibetanSubjoinedConsonant,
+    TibetanSubjoinedConsonant, TibetanOther, TibetanOther, TibetanOther
+};
+
+
+#define tibetan_form(c) \
+    (TibetanForm)tibetanForm[c - 0x0f40]
+
+static const HB_OpenTypeFeature tibetan_features[] = {
+    { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
+    { HB_MAKE_TAG('a', 'b', 'v', 's'), AboveSubstProperty },
+    { HB_MAKE_TAG('b', 'l', 'w', 's'), BelowSubstProperty },
+    { HB_MAKE_TAG('c', 'a', 'l', 't'), CaltProperty },
+    {0, 0}
+};
+
+static HB_Bool tibetan_shape_syllable(HB_Bool openType, HB_ShaperItem *item, HB_Bool invalid)
+{
+    hb_uint32 i;
+    const HB_UChar16 *str = item->string + item->item.pos;
+    int len = item->item.length;
+#ifndef NO_OPENTYPE
+    const int availableGlyphs = item->num_glyphs;
+#endif
+    HB_Bool haveGlyphs;
+    HB_STACKARRAY(HB_UChar16, reordered, len + 4);
+
+    if (item->num_glyphs < item->item.length + 4) {
+        item->num_glyphs = item->item.length + 4;
+        return FALSE;
+    }
+
+    if (invalid) {
+        *reordered = 0x25cc;
+        memcpy(reordered+1, str, len*sizeof(HB_UChar16));
+        len++;
+        str = reordered;
+    }
+
+    haveGlyphs = item->font->klass->convertStringToGlyphIndices(item->font,
+                                                                str, len,
+                                                                item->glyphs, &item->num_glyphs,
+                                                                item->item.bidiLevel % 2);
+
+    HB_FREE_STACKARRAY(reordered);
+
+    if (!haveGlyphs)
+        return FALSE;
+
+    for (i = 0; i < item->item.length; i++) {
+        item->attributes[i].mark = FALSE;
+        item->attributes[i].clusterStart = FALSE;
+        item->attributes[i].justification = 0;
+        item->attributes[i].zeroWidth = FALSE;
+/*        IDEBUG("    %d: %4x", i, str[i]); */
+    }
+
+    /* now we have the syllable in the right order, and can start running it through open type. */
+
+#ifndef NO_OPENTYPE
+    if (openType) {
+        HB_OpenTypeShape(item, /*properties*/0);
+        if (!HB_OpenTypePosition(item, availableGlyphs, /*doLogClusters*/FALSE))
+            return FALSE;
+    } else {
+        HB_HeuristicPosition(item);
+    }
+#endif
+
+    item->attributes[0].clusterStart = TRUE;
+    return TRUE;
+}
+
+
+static int tibetan_nextSyllableBoundary(const HB_UChar16 *s, int start, int end, HB_Bool *invalid)
+{
+    const HB_UChar16 *uc = s + start;
+
+    int pos = 0;
+    TibetanForm state = tibetan_form(*uc);
+
+/*     qDebug("state[%d]=%d (uc=%4x)", pos, state, uc[pos]);*/
+    pos++;
+
+    if (state != TibetanHeadConsonant) {
+        if (state != TibetanOther)
+            *invalid = TRUE;
+        goto finish;
+    }
+
+    while (pos < end - start) {
+        TibetanForm newState = tibetan_form(uc[pos]);
+        switch(newState) {
+        case TibetanSubjoinedConsonant:
+        case TibetanSubjoinedVowel:
+            if (state != TibetanHeadConsonant &&
+                 state != TibetanSubjoinedConsonant)
+                goto finish;
+            state = newState;
+            break;
+        case TibetanVowel:
+            if (state != TibetanHeadConsonant &&
+                 state != TibetanSubjoinedConsonant &&
+                 state != TibetanSubjoinedVowel)
+                goto finish;
+            break;
+        case TibetanOther:
+        case TibetanHeadConsonant:
+            goto finish;
+        }
+        pos++;
+    }
+
+finish:
+    *invalid = FALSE;
+    return start+pos;
+}
+
+HB_Bool HB_TibetanShape(HB_ShaperItem *item)
+{
+
+    HB_Bool openType = FALSE;
+    unsigned short *logClusters = item->log_clusters;
+
+    HB_ShaperItem syllable = *item;
+    int first_glyph = 0;
+
+    int sstart = item->item.pos;
+    int end = sstart + item->item.length;
+
+    assert(item->item.script == HB_Script_Tibetan);
+
+#ifndef QT_NO_OPENTYPE
+    openType = HB_SelectScript(item, tibetan_features);
+#endif
+
+    while (sstart < end) {
+        HB_Bool invalid;
+        int i;
+        int send = tibetan_nextSyllableBoundary(item->string, sstart, end, &invalid);
+/*        IDEBUG("syllable from %d, length %d, invalid=%s", sstart, send-sstart,
+                 invalid ? "TRUE" : "FALSE"); */
+        syllable.item.pos = sstart;
+        syllable.item.length = send-sstart;
+        syllable.glyphs = item->glyphs + first_glyph;
+        syllable.attributes = item->attributes + first_glyph;
+        syllable.offsets = item->offsets + first_glyph;
+        syllable.advances = item->advances + first_glyph;
+        syllable.num_glyphs = item->num_glyphs - first_glyph;
+        if (!tibetan_shape_syllable(openType, &syllable, invalid)) {
+            item->num_glyphs += syllable.num_glyphs;
+            return FALSE;
+        }
+        /* fix logcluster array */
+        for (i = sstart; i < send; ++i)
+            logClusters[i-item->item.pos] = first_glyph;
+        sstart = send;
+        first_glyph += syllable.num_glyphs;
+    }
+    item->num_glyphs = first_glyph;
+    return TRUE;
+}
+
+void HB_TibetanAttributes(HB_Script script, const HB_UChar16 *text, hb_uint32 from, hb_uint32 len, HB_CharAttributes *attributes)
+{
+    int end = from + len;
+    const HB_UChar16 *uc = text + from;
+    hb_uint32 i = 0;
+    HB_UNUSED(script);
+    attributes += from;
+    while (i < len) {
+        HB_Bool invalid;
+        hb_uint32 boundary = tibetan_nextSyllableBoundary(text, from+i, end, &invalid) - from;
+
+        attributes[i].charStop = TRUE;
+
+        if (boundary > len-1) boundary = len;
+        i++;
+        while (i < boundary) {
+            attributes[i].charStop = FALSE;
+            ++uc;
+            ++i;
+        }
+        assert(i == boundary);
+    }
+}
+
+
diff --git a/third_party/harfbuzz/src/harfbuzz.c b/third_party/harfbuzz/src/harfbuzz.c
new file mode 100644
index 0000000..3e4a30a
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#define HB_INTERNAL static
+#include "harfbuzz-buffer.c"
+#include "harfbuzz-gdef.c"
+#include "harfbuzz-gsub.c"
+#include "harfbuzz-gpos.c"
+#include "harfbuzz-impl.c"
+#include "harfbuzz-open.c"
+#include "harfbuzz-stream.c"
diff --git a/third_party/harfbuzz/src/harfbuzz.h b/third_party/harfbuzz/src/harfbuzz.h
new file mode 100644
index 0000000..e91a33e
--- /dev/null
+++ b/third_party/harfbuzz/src/harfbuzz.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ * Copyright (C) 2006  Behdad Esfahbod
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HARFBUZZ_H
+#define HARFBUZZ_H
+
+#include "harfbuzz-external.h"
+#include "harfbuzz-global.h"
+#include "harfbuzz-buffer.h"
+#include "harfbuzz-gdef.h"
+#include "harfbuzz-gsub.h"
+#include "harfbuzz-gpos.h"
+#include "harfbuzz-open.h"
+#include "harfbuzz-shaper.h"
+
+#endif /* HARFBUZZ_OPEN_H */
diff --git a/third_party/harfbuzz/tests/Makefile.am b/third_party/harfbuzz/tests/Makefile.am
new file mode 100644
index 0000000..febf890
--- /dev/null
+++ b/third_party/harfbuzz/tests/Makefile.am
@@ -0,0 +1,7 @@
+
+SUBDIRS =
+
+if QT
+SUBDIRS += linebreaking shaping
+endif
+
diff --git a/third_party/harfbuzz/tests/linebreaking/.gitignore b/third_party/harfbuzz/tests/linebreaking/.gitignore
new file mode 100644
index 0000000..81e019d
--- /dev/null
+++ b/third_party/harfbuzz/tests/linebreaking/.gitignore
@@ -0,0 +1,4 @@
+.deps
+linebreaking
+*.moc
+*.o
diff --git a/third_party/harfbuzz/tests/linebreaking/Makefile.am b/third_party/harfbuzz/tests/linebreaking/Makefile.am
new file mode 100644
index 0000000..b710896
--- /dev/null
+++ b/third_party/harfbuzz/tests/linebreaking/Makefile.am
@@ -0,0 +1,12 @@
+
+check_PROGRAMS = linebreaking
+
+linebreaking_SOURCES = main.cpp harfbuzz-qt.cpp
+linebreaking_LDADD = $(QT_GUI_LIBS) $(QT_QTEST_LIBS) ../../src/libharfbuzz-1.la
+
+main.o: main.moc
+
+main.moc: $(srcdir)/main.cpp
+	$(QT_MOC) -o main.moc $(srcdir)/main.cpp
+
+INCLUDES = -I$(top_srcdir)/src $(FREETYPE_CFLAGS) $(QT_GUI_CFLAGS) $(QT_QTEST_CFLAGS)
diff --git a/third_party/harfbuzz/tests/linebreaking/harfbuzz-qt.cpp b/third_party/harfbuzz/tests/linebreaking/harfbuzz-qt.cpp
new file mode 100644
index 0000000..ea03052
--- /dev/null
+++ b/third_party/harfbuzz/tests/linebreaking/harfbuzz-qt.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include <harfbuzz-external.h>
+#include <Qt/private/qunicodetables_p.h>
+#include <QLibrary>
+#include <QTextCodec>
+
+extern "C" {
+
+HB_LineBreakClass HB_GetLineBreakClass(HB_UChar32 ch)
+{
+#if QT_VERSION >= 0x040300
+    return (HB_LineBreakClass)QUnicodeTables::lineBreakClass(ch);
+#else
+#error "This test currently requires Qt >= 4.3"
+#endif
+}
+
+void HB_GetUnicodeCharProperties(HB_UChar32 ch, HB_CharCategory *category, int *combiningClass)
+{
+    *category = (HB_CharCategory)QChar::category(ch);
+    *combiningClass = QChar::combiningClass(ch);
+}
+
+HB_CharCategory HB_GetUnicodeCharCategory(HB_UChar32 ch)
+{
+    return (HB_CharCategory)QChar::category(ch);
+}
+
+int HB_GetUnicodeCharCombiningClass(HB_UChar32 ch)
+{
+    return QChar::combiningClass(ch);
+}
+
+HB_UChar16 HB_GetMirroredChar(HB_UChar16 ch)
+{
+    return QChar::mirroredChar(ch);
+}
+
+HB_WordClass HB_GetWordClass(HB_UChar32 ch)
+{
+    const QUnicodeTables::Properties *prop = QUnicodeTables::properties(ch);
+    return (HB_WordClass) prop->wordBreak;
+}
+
+
+HB_SentenceClass HB_GetSentenceClass(HB_UChar32 ch)
+{
+    const QUnicodeTables::Properties *prop = QUnicodeTables::properties(ch);
+    return (HB_SentenceClass) prop->sentenceBreak;
+}
+
+void HB_GetGraphemeAndLineBreakClass(HB_UChar32 ch, HB_GraphemeClass *grapheme, HB_LineBreakClass *lineBreak)
+{
+    const QUnicodeTables::Properties *prop = QUnicodeTables::properties(ch);
+    *grapheme = (HB_GraphemeClass) prop->graphemeBreak;
+    *lineBreak = (HB_LineBreakClass) prop->line_break_class;
+}
+
+void *HB_Library_Resolve(const char *library, const char *symbol)
+{
+    return QLibrary::resolve(library, symbol);
+}
+
+void *HB_TextCodecForMib(int mib)
+{
+    return QTextCodec::codecForMib(mib);
+}
+
+char *HB_TextCodec_ConvertFromUnicode(void *codec, const HB_UChar16 *unicode, hb_uint32 length, hb_uint32 *outputLength)
+{
+    QByteArray data = reinterpret_cast<QTextCodec *>(codec)->fromUnicode((const QChar *)unicode, length);
+    // ### suboptimal
+    char *output = (char *)malloc(data.length() + 1);
+    memcpy(output, data.constData(), data.length() + 1);
+    if (outputLength)
+        *outputLength = data.length();
+    return output;
+}
+
+void HB_TextCodec_FreeResult(char *string)
+{
+    free(string);
+}
+
+}
diff --git a/third_party/harfbuzz/tests/linebreaking/main.cpp b/third_party/harfbuzz/tests/linebreaking/main.cpp
new file mode 100644
index 0000000..3b2734a
--- /dev/null
+++ b/third_party/harfbuzz/tests/linebreaking/main.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+/*
+    !!!!!! Warning !!!!!
+    Please don't save this file in emacs. It contains utf8 text sequences emacs will
+    silently convert to a series of question marks.
+ */
+#include <QtTest/QtTest>
+#include <QtCore/qdebug.h>
+
+#include <harfbuzz-shaper.h>
+
+static QVector<HB_CharAttributes> getCharAttributes(const QString &str, HB_Script script = HB_Script_Common)
+{
+    QVector<HB_CharAttributes> attrs(str.length());
+    HB_ScriptItem item;
+    item.pos = 0;
+    item.length = str.length();
+    item.script = script;
+    HB_GetCharAttributes(str.utf16(), str.length(),
+                         &item, 1,
+                         attrs.data());
+    return attrs;
+}
+
+class tst_CharAttributes : public QObject
+{
+    Q_OBJECT
+
+public:
+    tst_CharAttributes();
+    virtual ~tst_CharAttributes();
+
+public slots:
+    void init();
+    void cleanup();
+private slots:
+    void lineBreaking();
+    void charWordStopOnLineSeparator();
+    void charStopForSurrogatePairs();
+    void thaiWordBreak();
+};
+
+
+tst_CharAttributes::tst_CharAttributes()
+{
+}
+
+tst_CharAttributes::~tst_CharAttributes()
+{
+}
+
+void tst_CharAttributes::init()
+{
+}
+
+void tst_CharAttributes::cleanup()
+{
+}
+
+
+void tst_CharAttributes::lineBreaking()
+{
+    struct Breaks {
+	const char *utf8;
+	uchar breaks[32];
+    };
+    Breaks brks[] = {
+	{ "11", { false, 0xff } },
+	{ "aa", { false, 0xff } },
+	{ "++", { false, 0xff } },
+	{ "--", { false, 0xff } },
+	{ "((", { false, 0xff } },
+	{ "))", { false, 0xff } },
+	{ "..", { false, 0xff } },
+	{ "\"\"", { false, 0xff } },
+	{ "$$", { false, 0xff } },
+	{ "!!", { false, 0xff } },
+	{ "??", { false, 0xff } },
+	{ ",,", { false, 0xff } },
+
+	{ ")()", { true, false, 0xff } },
+	{ "?!?", { false, false, 0xff } },
+	{ ".,.", { false, false, 0xff } },
+	{ "+-+", { false, false, 0xff } },
+	{ "+=+", { false, false, 0xff } },
+	{ "+(+", { false, false, 0xff } },
+	{ "+)+", { false, false, 0xff } },
+
+	{ "a b", { false, true, 0xff } },
+	{ "a(b", { false, false, 0xff } },
+	{ "a)b", { false, false, 0xff } },
+	{ "a-b", { false, true, 0xff } },
+	{ "a.b", { false, false, 0xff } },
+	{ "a+b", { false, false, 0xff } },
+	{ "a?b", { false, false, 0xff } },
+	{ "a!b", { false, false, 0xff } },
+	{ "a$b", { false, false, 0xff } },
+	{ "a,b", { false, false, 0xff } },
+	{ "a/b", { false, false, 0xff } },
+	{ "1/2", { false, false, 0xff } },
+	{ "./.", { false, false, 0xff } },
+	{ ",/,", { false, false, 0xff } },
+	{ "!/!", { false, false, 0xff } },
+	{ "\\/\\", { false, false, 0xff } },
+	{ "1 2", { false, true, 0xff } },
+	{ "1(2", { false, false, 0xff } },
+	{ "1)2", { false, false, 0xff } },
+	{ "1-2", { false, false, 0xff } },
+	{ "1.2", { false, false, 0xff } },
+	{ "1+2", { false, false, 0xff } },
+	{ "1?2", { false, true, 0xff } },
+	{ "1!2", { false, true, 0xff } },
+	{ "1$2", { false, false, 0xff } },
+	{ "1,2", { false, false, 0xff } },
+	{ "1/2", { false, false, 0xff } },
+	{ "\330\260\331\216\331\204\331\220\331\203\331\216", { false, false, false, false, false, 0xff } },
+	{ "\330\247\331\204\331\205 \330\247\331\204\331\205", { false, false, false, true, false, false, 0xff } },
+	{ "1#2", { false, false, 0xff } },
+	{ "!#!", { false, false, 0xff } },
+	{ 0, {} }
+    };
+    Breaks *b = brks;
+    while (b->utf8) {
+        QString str = QString::fromUtf8(b->utf8);
+
+        QVector<HB_CharAttributes> attrs = getCharAttributes(str);
+
+        int i;
+        for (i = 0; i < (int)str.length() - 1; ++i) {
+            QVERIFY(b->breaks[i] != 0xff);
+            if ( (attrs[i].lineBreakType != HB_NoBreak) != (bool)b->breaks[i] ) {
+                qDebug("test case \"%s\" failed at char %d; break type: %d", b->utf8, i, attrs[i].lineBreakType);
+                QCOMPARE( (attrs[i].lineBreakType != HB_NoBreak), (bool)b->breaks[i] );
+            }
+        }
+        QVERIFY(attrs[i].lineBreakType == HB_ForcedBreak);
+        QCOMPARE(b->breaks[i], (uchar)0xff);
+        ++b;
+    }
+}
+
+void tst_CharAttributes::charWordStopOnLineSeparator()
+{
+    const QChar lineSeparator(QChar::LineSeparator);
+    QString txt;
+    txt.append(lineSeparator);
+    txt.append(lineSeparator);
+    QVector<HB_CharAttributes> attrs = getCharAttributes(txt);
+    QVERIFY(attrs[1].charStop);
+}
+
+void tst_CharAttributes::charStopForSurrogatePairs()
+{
+    QString txt;
+    txt.append("a");
+    txt.append(0xd87e);
+    txt.append(0xdc25);
+    txt.append("b");
+    QVector<HB_CharAttributes> attrs = getCharAttributes(txt);
+    QVERIFY(attrs[0].charStop);
+    QVERIFY(attrs[1].charStop);
+    QVERIFY(!attrs[2].charStop);
+    QVERIFY(attrs[3].charStop);
+}
+
+void tst_CharAttributes::thaiWordBreak()
+{
+    // สวัสดีครับ นี่เป็นการงทดสอบตัวเอ
+    QTextCodec *codec = QTextCodec::codecForMib(2259);
+    QString txt = codec->toUnicode(QByteArray("\xca\xc7\xd1\xca\xb4\xd5\xa4\xc3\xd1\xba\x20\xb9\xd5\xe8\xe0\xbb\xe7\xb9\xa1\xd2\xc3\xb7\xb4\xca\xcd\xba\xb5\xd1\xc7\xe0\xcd\xa7"));
+
+
+    QCOMPARE(txt.length(), 32);
+    QVector<HB_CharAttributes> attrs = getCharAttributes(txt, HB_Script_Thai);
+    QVERIFY(attrs[0].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[1].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[2].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[3].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[4].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[5].lineBreakType == HB_Break);
+    QVERIFY(attrs[6].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[7].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[8].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[9].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[10].lineBreakType == HB_Break);
+    QVERIFY(attrs[11].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[12].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[13].lineBreakType == HB_Break);
+    QVERIFY(attrs[14].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[15].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[16].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[17].lineBreakType == HB_Break);
+    QVERIFY(attrs[18].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[19].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[20].lineBreakType == HB_Break);
+    QVERIFY(attrs[21].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[22].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[23].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[24].lineBreakType == HB_NoBreak);
+    QVERIFY(attrs[25].lineBreakType == HB_Break);
+    QVERIFY(attrs[26].lineBreakType == HB_NoBreak);
+    for (int i = 27; i < 32; ++i)
+        QVERIFY(attrs[i].lineBreakType == HB_NoBreak);
+}
+
+QTEST_MAIN(tst_CharAttributes)
+#include "main.moc"
diff --git a/third_party/harfbuzz/tests/shaping/.gitignore b/third_party/harfbuzz/tests/shaping/.gitignore
new file mode 100644
index 0000000..3f32cbe
--- /dev/null
+++ b/third_party/harfbuzz/tests/shaping/.gitignore
@@ -0,0 +1,2 @@
+harfbuzz-test-fonts-0.1.tar.bz2
+fonts
diff --git a/third_party/harfbuzz/tests/shaping/Makefile.am b/third_party/harfbuzz/tests/shaping/Makefile.am
new file mode 100644
index 0000000..31c6db7
--- /dev/null
+++ b/third_party/harfbuzz/tests/shaping/Makefile.am
@@ -0,0 +1,14 @@
+
+check_PROGRAMS = shaping
+
+shaping_SOURCES = main.cpp ../linebreaking/harfbuzz-qt.cpp
+shaping_LDADD = $(QT_GUI_LIBS) $(QT_QTEST_LIBS) ../../src/libharfbuzz-1.la
+
+main.o: main.moc
+
+main.moc: $(srcdir)/main.cpp
+	$(QT_MOC) -o main.moc $(srcdir)/main.cpp
+
+INCLUDES = -I$(top_srcdir)/src $(FREETYPE_CFLAGS) $(QT_GUI_CFLAGS) $(QT_QTEST_CFLAGS)
+AM_CPPFLAGS = -DQT_GUI_LIB -DSRCDIR=\"$(srcdir)\"
+
diff --git a/third_party/harfbuzz/tests/shaping/README b/third_party/harfbuzz/tests/shaping/README
new file mode 100644
index 0000000..1db1c5a
--- /dev/null
+++ b/third_party/harfbuzz/tests/shaping/README
@@ -0,0 +1,9 @@
+These shaper tests need some specific TrueType fonts. You can get a package of
+them from
+
+        http://people.freedesktop.org/~hausmann/harfbuzz-test-fonts-0.1.tar.bz2
+
+In addition you may need two fonts (Mangal and Tunga) from Microsoft Windows
+for some of the test cases. These fonts are not freely redistributable.
+
+The test program looks for them in a fonts/ subdirectory.
diff --git a/third_party/harfbuzz/tests/shaping/main.cpp b/third_party/harfbuzz/tests/shaping/main.cpp
new file mode 100644
index 0000000..1a3ef4f
--- /dev/null
+++ b/third_party/harfbuzz/tests/shaping/main.cpp
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ *
+ * This is part of HarfBuzz, an OpenType Layout engine library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#include <QtTest/QtTest>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TABLES_H
+
+#include <harfbuzz-shaper.h>
+#include <harfbuzz-global.h>
+#include <harfbuzz-gpos.h>
+
+static FT_Library freetype;
+
+static FT_Face loadFace(const char *name)
+{
+    FT_Face face;
+    char path[256];
+
+    strcpy(path, SRCDIR);
+    strcat(path, "/fonts/");
+    strcat(path, name);
+
+    if (FT_New_Face(freetype, path, /*index*/0, &face))
+        return 0;
+    return face;
+}
+
+static HB_UChar32 getChar(const HB_UChar16 *string, hb_uint32 length, hb_uint32 &i)
+{
+    HB_UChar32 ch;
+    if (HB_IsHighSurrogate(string[i])
+        && i < length - 1
+        && HB_IsLowSurrogate(string[i + 1])) {
+        ch = HB_SurrogateToUcs4(string[i], string[i + 1]);
+        ++i;
+    } else {
+        ch = string[i];
+    }
+    return ch;
+}
+
+static HB_Bool hb_stringToGlyphs(HB_Font font, const HB_UChar16 *string, hb_uint32 length, HB_Glyph *glyphs, hb_uint32 *numGlyphs, HB_Bool /*rightToLeft*/)
+{
+    FT_Face face = (FT_Face)font->userData;
+    if (length > *numGlyphs)
+        return false;
+
+    int glyph_pos = 0;
+    for (hb_uint32 i = 0; i < length; ++i) {
+        glyphs[glyph_pos] = FT_Get_Char_Index(face, getChar(string, length, i));
+        ++glyph_pos;
+    }
+
+    *numGlyphs = glyph_pos;
+
+    return true;
+}
+
+static void hb_getAdvances(HB_Font /*font*/, const HB_Glyph * /*glyphs*/, hb_uint32 numGlyphs, HB_Fixed *advances, int /*flags*/)
+{
+    for (hb_uint32 i = 0; i < numGlyphs; ++i)
+        advances[i] = 0; // ### not tested right now
+}
+
+static HB_Bool hb_canRender(HB_Font font, const HB_UChar16 *string, hb_uint32 length)
+{
+    FT_Face face = (FT_Face)font->userData;
+
+    for (hb_uint32 i = 0; i < length; ++i)
+        if (!FT_Get_Char_Index(face, getChar(string, length, i)))
+            return false;
+
+    return true;
+}
+
+static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length)
+{
+    FT_Face face = (FT_Face)font;
+    FT_ULong ftlen = *length;
+    FT_Error error = 0;
+
+    if (!FT_IS_SFNT(face))
+        return HB_Err_Invalid_Argument;
+
+    error = FT_Load_Sfnt_Table(face, tableTag, 0, buffer, &ftlen);
+    *length = ftlen;
+    return (HB_Error)error;
+}
+
+HB_Error hb_getPointInOutline(HB_Font font, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
+{
+    HB_Error error = HB_Err_Ok;
+    FT_Face face = (FT_Face)font->userData;
+
+    int load_flags = (flags & HB_ShaperFlag_UseDesignMetrics) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT;
+
+    if ((error = (HB_Error)FT_Load_Glyph(face, glyph, load_flags)))
+        return error;
+
+    if (face->glyph->format != ft_glyph_format_outline)
+        return (HB_Error)HB_Err_Invalid_SubTable;
+
+    *nPoints = face->glyph->outline.n_points;
+    if (!(*nPoints))
+        return HB_Err_Ok;
+
+    if (point > *nPoints)
+        return (HB_Error)HB_Err_Invalid_SubTable;
+
+    *xpos = face->glyph->outline.points[point].x;
+    *ypos = face->glyph->outline.points[point].y;
+
+    return HB_Err_Ok;
+}
+
+void hb_getGlyphMetrics(HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics)
+{
+    // ###
+    metrics->x = metrics->y = metrics->width = metrics->height = metrics->xOffset = metrics->yOffset = 0;
+}
+
+HB_Fixed hb_getFontMetric(HB_Font font, HB_FontMetric metric)
+{
+    return 0; // ####
+}
+
+const HB_FontClass hb_fontClass = {
+    hb_stringToGlyphs, hb_getAdvances, hb_canRender,
+    hb_getPointInOutline, hb_getGlyphMetrics, hb_getFontMetric
+};
+
+
+//TESTED_CLASS=
+//TESTED_FILES= gui/text/qscriptengine.cpp
+
+class tst_QScriptEngine : public QObject
+{
+Q_OBJECT
+
+public:
+    tst_QScriptEngine();
+    virtual ~tst_QScriptEngine();
+
+
+public slots:
+    void initTestCase();
+    void cleanupTestCase();
+private slots:
+    void devanagari();
+    void bengali();
+    void gurmukhi();
+    // gujarati missing
+    void oriya();
+    void tamil();
+    void telugu();
+    void kannada();
+    void malayalam();
+    // sinhala missing
+
+    void khmer();
+    void linearB();
+};
+
+tst_QScriptEngine::tst_QScriptEngine()
+{
+}
+
+tst_QScriptEngine::~tst_QScriptEngine()
+{
+}
+
+void tst_QScriptEngine::initTestCase()
+{
+    FT_Init_FreeType(&freetype);
+}
+
+void tst_QScriptEngine::cleanupTestCase()
+{
+    FT_Done_FreeType(freetype);
+}
+
+struct ShapeTable {
+    unsigned short unicode[16];
+    unsigned short glyphs[16];
+};
+
+static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script)
+{
+    QString str = QString::fromUtf16( s->unicode );
+
+    HB_Face hbFace = HB_NewFace(face, hb_getSFntTable);
+
+    HB_FontRec hbFont;
+    hbFont.klass = &hb_fontClass;
+    hbFont.userData = face;
+    hbFont.x_ppem  = face->size->metrics.x_ppem;
+    hbFont.y_ppem  = face->size->metrics.y_ppem;
+    hbFont.x_scale = face->size->metrics.x_scale;
+    hbFont.y_scale = face->size->metrics.y_scale;
+
+    HB_ShaperItem shaper_item;
+    shaper_item.kerning_applied = false;
+    shaper_item.string = reinterpret_cast<const HB_UChar16 *>(str.constData());
+    shaper_item.stringLength = str.length();
+    shaper_item.item.script = script;
+    shaper_item.item.pos = 0;
+    shaper_item.item.length = shaper_item.stringLength;
+    shaper_item.item.bidiLevel = 0; // ###
+    shaper_item.shaperFlags = 0;
+    shaper_item.font = &hbFont;
+    shaper_item.face = hbFace;
+    shaper_item.num_glyphs = shaper_item.item.length;
+    shaper_item.glyphIndicesPresent = false;
+    shaper_item.initialGlyphCount = 0;
+
+    QVarLengthArray<HB_Glyph> hb_glyphs(shaper_item.num_glyphs);
+    QVarLengthArray<HB_GlyphAttributes> hb_attributes(shaper_item.num_glyphs);
+    QVarLengthArray<HB_Fixed> hb_advances(shaper_item.num_glyphs);
+    QVarLengthArray<HB_FixedPoint> hb_offsets(shaper_item.num_glyphs);
+    QVarLengthArray<unsigned short> hb_logClusters(shaper_item.num_glyphs);
+
+    while (1) {
+        hb_glyphs.resize(shaper_item.num_glyphs);
+        hb_attributes.resize(shaper_item.num_glyphs);
+        hb_advances.resize(shaper_item.num_glyphs);
+        hb_offsets.resize(shaper_item.num_glyphs);
+        hb_logClusters.resize(shaper_item.num_glyphs);
+
+        memset(hb_glyphs.data(), 0, hb_glyphs.size() * sizeof(HB_Glyph));
+        memset(hb_attributes.data(), 0, hb_attributes.size() * sizeof(HB_GlyphAttributes));
+        memset(hb_advances.data(), 0, hb_advances.size() * sizeof(HB_Fixed));
+        memset(hb_offsets.data(), 0, hb_offsets.size() * sizeof(HB_FixedPoint));
+
+        shaper_item.glyphs = hb_glyphs.data();
+        shaper_item.attributes = hb_attributes.data();
+        shaper_item.advances = hb_advances.data();
+        shaper_item.offsets = hb_offsets.data();
+        shaper_item.log_clusters = hb_logClusters.data();
+
+        if (HB_ShapeItem(&shaper_item))
+            break;
+
+    }
+
+    HB_FreeFace(hbFace);
+
+    hb_uint32 nglyphs = 0;
+    const unsigned short *g = s->glyphs;
+    while ( *g ) {
+	nglyphs++;
+	g++;
+    }
+
+    if( nglyphs != shaper_item.num_glyphs )
+	goto error;
+
+    for (hb_uint32 i = 0; i < nglyphs; ++i) {
+	if ((shaper_item.glyphs[i]&0xffffff) != s->glyphs[i])
+	    goto error;
+    }
+    return true;
+ error:
+    str = "";
+    const unsigned short *uc = s->unicode;
+    while (*uc) {
+	str += QString("%1 ").arg(*uc, 4, 16);
+	++uc;
+    }
+    qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d",
+           face->family_name,
+           str.toLatin1().constData(),
+           shaper_item.num_glyphs, nglyphs);
+
+    str = "";
+    hb_uint32 i = 0;
+    while (i < shaper_item.num_glyphs) {
+	str += QString("%1 ").arg(shaper_item.glyphs[i], 4, 16);
+	++i;
+    }
+    qDebug("    glyph result = %s", str.toLatin1().constData());
+    return false;
+}
+
+void tst_QScriptEngine::devanagari()
+{
+    {
+        FT_Face face = loadFace("raghu.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		// Ka
+		{ { 0x0915, 0x0 },
+		  { 0x0080, 0x0 } },
+		// Ka Halant
+		{ { 0x0915, 0x094d, 0x0 },
+		  { 0x0080, 0x0051, 0x0 } },
+		// Ka Halant Ka
+		{ { 0x0915, 0x094d, 0x0915, 0x0 },
+		  { 0x00c8, 0x0080, 0x0 } },
+		// Ka MatraI
+		{ { 0x0915, 0x093f, 0x0 },
+		  { 0x01d1, 0x0080, 0x0 } },
+		// Ra Halant Ka
+		{ { 0x0930, 0x094d, 0x0915, 0x0 },
+		  { 0x0080, 0x005b, 0x0 } },
+		// Ra Halant Ka MatraI
+		{ { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
+		  { 0x01d1, 0x0080, 0x005b, 0x0 } },
+		// MatraI
+		{ { 0x093f, 0x0 },
+		  { 0x01d4, 0x029c, 0x0 } },
+		// Ka Nukta
+		{ { 0x0915, 0x093c, 0x0 },
+		  { 0x00a4, 0x0 } },
+		// Ka Halant Ra
+		{ { 0x0915, 0x094d, 0x0930, 0x0 },
+		  { 0x0110, 0x0 } },
+		// Ka Halant Ra Halant Ka
+		{ { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
+		  { 0x0158, 0x0080, 0x0 } },
+		{ { 0x0930, 0x094d, 0x200d, 0x0 },
+		  { 0x00e2, 0x0 } },
+		{ { 0x0915, 0x094d, 0x0930, 0x094d, 0x200d, 0x0 },
+		  { 0x0158, 0x0 } },
+
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Devanagari) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find raghu.ttf", SkipAll);
+	}
+    }
+
+    {
+        FT_Face face = loadFace("mangal.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		// Ka
+		{ { 0x0915, 0x0 },
+		  { 0x0080, 0x0 } },
+		// Ka Halant
+		{ { 0x0915, 0x094d, 0x0 },
+		  { 0x0080, 0x0051, 0x0 } },
+		// Ka Halant Ka
+		{ { 0x0915, 0x094d, 0x0915, 0x0 },
+		  { 0x00c8, 0x0080, 0x0 } },
+		// Ka MatraI
+		{ { 0x0915, 0x093f, 0x0 },
+		  { 0x01d1, 0x0080, 0x0 } },
+		// Ra Halant Ka
+		{ { 0x0930, 0x094d, 0x0915, 0x0 },
+		  { 0x0080, 0x005b, 0x0 } },
+		// Ra Halant Ka MatraI
+		{ { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
+		  { 0x01d1, 0x0080, 0x005b, 0x0 } },
+		// MatraI
+		{ { 0x093f, 0x0 },
+		  { 0x01d4, 0x029c, 0x0 } },
+		// Ka Nukta
+		{ { 0x0915, 0x093c, 0x0 },
+		  { 0x00a4, 0x0 } },
+		// Ka Halant Ra
+		{ { 0x0915, 0x094d, 0x0930, 0x0 },
+		  { 0x0110, 0x0 } },
+		// Ka Halant Ra Halant Ka
+		{ { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
+		  { 0x0158, 0x0080, 0x0 } },
+
+                { { 0x92b, 0x94d, 0x930, 0x0 },
+                  { 0x125, 0x0 } },
+                { { 0x92b, 0x93c, 0x94d, 0x930, 0x0 },
+                  { 0x149, 0x0 } }, 
+		{ {0}, {0} }
+	    };
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Devanagari) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couldn't find mangal.ttf", SkipAll);
+	}
+    }
+}
+
+void tst_QScriptEngine::bengali()
+{
+    {
+        FT_Face face = loadFace("AkaashNormal.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		// Ka
+		{ { 0x0995, 0x0 },
+		  { 0x0151, 0x0 } },
+		// Ka Halant
+		{ { 0x0995, 0x09cd, 0x0 },
+		  { 0x0151, 0x017d, 0x0 } },
+		// Ka Halant Ka
+		{ { 0x0995, 0x09cd, 0x0995, 0x0 },
+		  { 0x019b, 0x0 } },
+		// Ka MatraI
+		{ { 0x0995, 0x09bf, 0x0 },
+		  { 0x0173, 0x0151, 0x0 } },
+		// Ra Halant Ka
+		{ { 0x09b0, 0x09cd, 0x0995, 0x0 },
+		  { 0x0151, 0x0276, 0x0 } },
+		// Ra Halant Ka MatraI
+		{ { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
+		  { 0x0173, 0x0151, 0x0276, 0x0 } },
+		// Ka Nukta
+		{ { 0x0995, 0x09bc, 0x0 },
+		  { 0x0151, 0x0171, 0x0 } },
+		// Ka Halant Ra
+		{ { 0x0995, 0x09cd, 0x09b0, 0x0 },
+		  { 0x01f4, 0x0 } },
+		// Ka Halant Ra Halant Ka
+		{ { 0x0995, 0x09cd, 0x09b0, 0x09cd, 0x0995, 0x0 },
+		  { 0x025c, 0x0276, 0x0151, 0x0 } },
+		// Ya + Halant
+		{ { 0x09af, 0x09cd, 0x0 },
+		  { 0x016a, 0x017d, 0x0 } },
+		// Da Halant Ya -> Da Ya-Phala
+		{ { 0x09a6, 0x09cd, 0x09af, 0x0 },
+		  { 0x01e5, 0x0 } },
+		// A Halant Ya -> A Ya-phala
+		{ { 0x0985, 0x09cd, 0x09af, 0x0 },
+		  { 0x0145, 0x01cf, 0x0 } },
+		// Na Halant Ka
+		{ { 0x09a8, 0x09cd, 0x0995, 0x0 },
+		  { 0x026f, 0x0151, 0x0 } },
+		// Na Halant ZWNJ Ka
+		{ { 0x09a8, 0x09cd, 0x200c, 0x0995, 0x0 },
+		  { 0x0164, 0x017d, 0x0151, 0x0 } },
+		// Na Halant ZWJ Ka
+		{ { 0x09a8, 0x09cd, 0x200d, 0x0995, 0x0 },
+		  { 0x026f, 0x0151, 0x0 } },
+		// Ka Halant ZWNJ Ka
+		{ { 0x0995, 0x09cd, 0x200c, 0x0995, 0x0 },
+		  { 0x0151, 0x017d, 0x0151, 0x0 } },
+		// Ka Halant ZWJ Ka
+		{ { 0x0995, 0x09cd, 0x200d, 0x0995, 0x0 },
+		  { 0x025c, 0x0151, 0x0 } },
+		// Na Halant Ra
+		{ { 0x09a8, 0x09cd, 0x09b0, 0x0 },
+		  { 0x0207, 0x0 } },
+		// Na Halant ZWNJ Ra
+		{ { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
+		  { 0x0164, 0x017d, 0x016b, 0x0 } },
+		// Na Halant ZWJ Ra
+		{ { 0x09a8, 0x09cd, 0x200d, 0x09b0, 0x0 },
+		  { 0x026f, 0x016b, 0x0 } },
+		// Na Halant Ba
+		{ { 0x09a8, 0x09cd, 0x09ac, 0x0 },
+		  { 0x022f, 0x0 } },
+		// Na Halant ZWNJ Ba
+		{ { 0x09a8, 0x09cd, 0x200c, 0x09ac, 0x0 },
+		  { 0x0164, 0x017d, 0x0167, 0x0 } },
+		// Na Halant ZWJ Ba
+		{ { 0x09a8, 0x09cd, 0x200d, 0x09ac, 0x0 },
+		  { 0x026f, 0x0167, 0x0 } },
+		// Na Halant Dha
+		{ { 0x09a8, 0x09cd, 0x09a7, 0x0 },
+		  { 0x01d3, 0x0 } },
+		// Na Halant ZWNJ Dha
+		{ { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
+		  { 0x0164, 0x017d, 0x0163, 0x0 } },
+		// Na Halant ZWJ Dha
+		{ { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
+		  { 0x026f, 0x0163, 0x0 } },
+		// Ra Halant Ka MatraAU
+		{ { 0x09b0, 0x09cd, 0x0995, 0x09cc, 0x0 },
+		  { 0x0179, 0x0151, 0x0276, 0x017e, 0x0 } },
+		// Ra Halant Ba Halant Ba
+		{ { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
+		  { 0x0232, 0x0276, 0x0 } },
+                { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x982, 0x0 },
+                  { 0x151, 0x276, 0x172, 0x143, 0x0 } },
+                { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x983, 0x0 },
+                  { 0x151, 0x276, 0x172, 0x144, 0x0 } }, 
+
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Bengali) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find AkaashNormal.ttf", SkipAll);
+	}
+    }
+    {
+        FT_Face face = loadFace("MuktiNarrow.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		// Ka
+		{ { 0x0995, 0x0 },
+		  { 0x0073, 0x0 } },
+		// Ka Halant
+		{ { 0x0995, 0x09cd, 0x0 },
+		  { 0x00b9, 0x0 } },
+		// Ka Halant Ka
+		{ { 0x0995, 0x09cd, 0x0995, 0x0 },
+		  { 0x0109, 0x0 } },
+		// Ka MatraI
+		{ { 0x0995, 0x09bf, 0x0 },
+		  { 0x0095, 0x0073, 0x0 } },
+		// Ra Halant Ka
+		{ { 0x09b0, 0x09cd, 0x0995, 0x0 },
+		  { 0x0073, 0x00e1, 0x0 } },
+		// Ra Halant Ka MatraI
+		{ { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
+		  { 0x0095, 0x0073, 0x00e1, 0x0 } },
+		// MatraI
+ 		{ { 0x09bf, 0x0 },
+		  { 0x0095, 0x01c8, 0x0 } },
+		// Ka Nukta
+		{ { 0x0995, 0x09bc, 0x0 },
+		  { 0x0073, 0x0093, 0x0 } },
+		// Ka Halant Ra
+		{ { 0x0995, 0x09cd, 0x09b0, 0x0 },
+		  { 0x00e5, 0x0 } },
+		// Ka Halant Ra Halant Ka
+                { { 0x995, 0x9cd, 0x9b0, 0x9cd, 0x995, 0x0 },
+                  { 0x234, 0x24e, 0x73, 0x0 } }, 
+		// Ya + Halant
+		{ { 0x09af, 0x09cd, 0x0 },
+		  { 0x00d2, 0x0 } },
+		// Da Halant Ya -> Da Ya-Phala
+		{ { 0x09a6, 0x09cd, 0x09af, 0x0 },
+		  { 0x0084, 0x00e2, 0x0 } },
+		// A Halant Ya -> A Ya-phala
+		{ { 0x0985, 0x09cd, 0x09af, 0x0 },
+		  { 0x0067, 0x00e2, 0x0 } },
+		// Na Halant Ka
+		{ { 0x09a8, 0x09cd, 0x0995, 0x0 },
+		  { 0x0188, 0x0 } },
+		// Na Halant ZWNJ Ka
+                { { 0x9a8, 0x9cd, 0x200c, 0x995, 0x0 },
+                  { 0xcc, 0x73, 0x0 } }, 
+		// Na Halant ZWJ Ka
+                { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
+                  { 0x247, 0x73, 0x0 } }, 
+		// Ka Halant ZWNJ Ka
+                { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
+                  { 0x247, 0x73, 0x0 } }, 
+		// Ka Halant ZWJ Ka
+                { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
+                  { 0x247, 0x73, 0x0 } }, 
+		// Na Halant Ra
+		{ { 0x09a8, 0x09cd, 0x09b0, 0x0 },
+		  { 0x00f8, 0x0 } },
+		// Na Halant ZWNJ Ra
+		{ { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
+		  { 0xcc, 0x8d, 0x0 } },
+		// Na Halant ZWJ Ra
+                { { 0x9a8, 0x9cd, 0x200d, 0x9b0, 0x0 },
+                  { 0x247, 0x8d, 0x0 } }, 
+		// Na Halant Ba
+		{ { 0x09a8, 0x09cd, 0x09ac, 0x0 },
+		  { 0x0139, 0x0 } },
+		// Na Halant ZWNJ Ba
+                { { 0x9a8, 0x9cd, 0x200c, 0x9ac, 0x0 },
+                  { 0xcc, 0x89, 0x0 } }, 
+		// Na Halant ZWJ Ba
+                { { 0x9a8, 0x9cd, 0x200d, 0x9ac, 0x0 },
+                  { 0x247, 0x89, 0x0 } }, 
+		// Na Halant Dha
+		{ { 0x09a8, 0x09cd, 0x09a7, 0x0 },
+		  { 0x0145, 0x0 } },
+		// Na Halant ZWNJ Dha
+		{ { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
+		  { 0xcc, 0x85, 0x0 } },
+		// Na Halant ZWJ Dha
+		{ { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
+		  { 0x247, 0x85, 0x0 } },
+		// Ra Halant Ka MatraAU
+                { { 0x9b0, 0x9cd, 0x995, 0x9cc, 0x0 },
+                  { 0x232, 0x73, 0xe1, 0xa0, 0x0 } }, 
+		// Ra Halant Ba Halant Ba
+		{ { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
+		  { 0x013b, 0x00e1, 0x0 } },
+
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Bengali) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find MuktiNarrow.ttf", SkipAll);
+	}
+    }
+    {
+        FT_Face face = loadFace("LikhanNormal.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		{ { 0x09a8, 0x09cd, 0x09af, 0x0 },
+		  { 0x0192, 0x0 } },
+		{ { 0x09b8, 0x09cd, 0x09af, 0x0 },
+		  { 0x01d6, 0x0 } },
+		{ { 0x09b6, 0x09cd, 0x09af, 0x0 },
+		  { 0x01bc, 0x0 } },
+		{ { 0x09b7, 0x09cd, 0x09af, 0x0 },
+		  { 0x01c6, 0x0 } },
+		{ { 0x09b0, 0x09cd, 0x09a8, 0x09cd, 0x200d, 0x0 },
+		  { 0xd3, 0x12f, 0x0 } },
+
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Bengali) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find LikhanNormal.ttf", SkipAll);
+	}
+    }
+}
+
+void tst_QScriptEngine::gurmukhi()
+{
+    {
+        FT_Face face = loadFace("lohit.punjabi.1.1.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		{ { 0xA15, 0xA4D, 0xa39, 0x0 },
+		  { 0x3b, 0x8b, 0x0 } },
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Gurmukhi) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find lohit.punjabi.1.1.ttf", SkipAll);
+	}
+    }
+}
+
+void tst_QScriptEngine::oriya()
+{
+    {
+        FT_Face face = loadFace("utkalm.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+                { { 0xb15, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
+                  { 0x150, 0x125, 0x0 } }, 
+                { { 0xb24, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
+                  { 0x151, 0x120, 0x0 } }, 
+                { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
+                  { 0x152, 0x120, 0x0 } }, 
+                { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
+                  { 0x152, 0x120, 0x0 } }, 
+                { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
+                  { 0x176, 0x0 } }, 
+                { { 0xb38, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
+                  { 0x177, 0x0 } }, 
+                { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0xb4d, 0xb2f, 0x0 },
+                  { 0x176, 0x124, 0x0 } }, 
+                { {0}, {0} }
+
+            };
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Oriya) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find utkalm.ttf", SkipAll);
+	}
+    }
+}
+
+
+void tst_QScriptEngine::tamil()
+{
+    {
+        FT_Face face = loadFace("akruti1.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		{ { 0x0b95, 0x0bc2, 0x0 },
+		  { 0x004e, 0x0 } },
+		{ { 0x0bae, 0x0bc2, 0x0 },
+		  { 0x009e, 0x0 } },
+		{ { 0x0b9a, 0x0bc2, 0x0 },
+		  { 0x0058, 0x0 } },
+		{ { 0x0b99, 0x0bc2, 0x0 },
+		  { 0x0053, 0x0 } },
+		{ { 0x0bb0, 0x0bc2, 0x0 },
+		  { 0x00a8, 0x0 } },
+		{ { 0x0ba4, 0x0bc2, 0x0 },
+		  { 0x008e, 0x0 } },
+		{ { 0x0b9f, 0x0bc2, 0x0 },
+		  { 0x0062, 0x0 } },
+		{ { 0x0b95, 0x0bc6, 0x0 },
+		  { 0x000a, 0x0031, 0x0 } },
+		{ { 0x0b95, 0x0bca, 0x0 },
+		  { 0x000a, 0x0031, 0x0007, 0x0 } },
+		{ { 0x0b95, 0x0bc6, 0x0bbe, 0x0 },
+		  { 0x000a, 0x0031, 0x007, 0x0 } },
+		{ { 0x0b95, 0x0bcd, 0x0bb7, 0x0 },
+		  { 0x0049, 0x0 } },
+		{ { 0x0b95, 0x0bcd, 0x0bb7, 0x0bca, 0x0 },
+		  { 0x000a, 0x0049, 0x007, 0x0 } },
+		{ { 0x0b95, 0x0bcd, 0x0bb7, 0x0bc6, 0x0bbe, 0x0 },
+		  { 0x000a, 0x0049, 0x007, 0x0 } },
+		{ { 0x0b9f, 0x0bbf, 0x0 },
+		  { 0x005f, 0x0 } },
+		{ { 0x0b9f, 0x0bc0, 0x0 },
+		  { 0x0060, 0x0 } },
+		{ { 0x0bb2, 0x0bc0, 0x0 },
+		  { 0x00ab, 0x0 } },
+		{ { 0x0bb2, 0x0bbf, 0x0 },
+		  { 0x00aa, 0x0 } },
+		{ { 0x0bb0, 0x0bcd, 0x0 },
+		  { 0x00a4, 0x0 } },
+		{ { 0x0bb0, 0x0bbf, 0x0 },
+		  { 0x00a5, 0x0 } },
+		{ { 0x0bb0, 0x0bc0, 0x0 },
+		  { 0x00a6, 0x0 } },
+		{ { 0x0b83, 0x0 },
+		  { 0x0025, 0x0 } },
+		{ { 0x0b83, 0x0b95, 0x0 },
+		  { 0x0025, 0x0031, 0x0 } },
+
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Tamil) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find akruti1.ttf", SkipAll);
+	}
+    }
+}
+
+
+void tst_QScriptEngine::telugu()
+{
+    {
+        FT_Face face = loadFace("Pothana2000.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+                { { 0xc15, 0xc4d, 0x0 },
+                  { 0xbb, 0x0 } }, 
+                { { 0xc15, 0xc4d, 0xc37, 0x0 },
+                  { 0x4b, 0x0 } }, 
+                { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0x0 },
+                  { 0xe0, 0x0 } }, 
+                { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0xc23, 0x0 },
+                  { 0x4b, 0x91, 0x0 } }, 
+                { { 0xc15, 0xc4d, 0xc30, 0x0 },
+                  { 0x5a, 0xb2, 0x0 } }, 
+                { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0x0 },
+                  { 0xbb, 0xb2, 0x0 } }, 
+                { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0xc15, 0x0 },
+                  { 0x5a, 0xb2, 0x83, 0x0 } }, 
+                { { 0xc15, 0xc4d, 0xc30, 0xc3f, 0x0 },
+                  { 0xe2, 0xb2, 0x0 } }, 
+                { { 0xc15, 0xc4d, 0xc15, 0xc48, 0x0 },
+                  { 0xe6, 0xb3, 0x83, 0x0 } },
+                { { 0xc15, 0xc4d, 0xc30, 0xc48, 0x0 },
+                  { 0xe6, 0xb3, 0x9f, 0x0 } }, 
+		{ {0}, {0} }
+
+            };
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Telugu) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find Pothana2000.ttf", SkipAll);
+	}
+    }
+}
+
+
+void tst_QScriptEngine::kannada()
+{
+    {
+        FT_Face face = loadFace("Sampige.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		{ { 0x0ca8, 0x0ccd, 0x0ca8, 0x0 },
+		  { 0x0049, 0x00ba, 0x0 } },
+		{ { 0x0ca8, 0x0ccd, 0x0ca1, 0x0 },
+		  { 0x0049, 0x00b3, 0x0 } },
+		{ { 0x0caf, 0x0cc2, 0x0 },
+		  { 0x004f, 0x005d, 0x0 } },
+		{ { 0x0ce0, 0x0 },
+		  { 0x006a, 0x0 } },
+		{ { 0x0ce6, 0x0ce7, 0x0ce8, 0x0 },
+		  { 0x006b, 0x006c, 0x006d, 0x0 } },
+		{ { 0x0cb5, 0x0ccb, 0x0 },
+		  { 0x015f, 0x0067, 0x0 } },
+		{ { 0x0cb0, 0x0ccd, 0x0cae, 0x0 },
+		  { 0x004e, 0x0082, 0x0 } },
+		{ { 0x0cb0, 0x0ccd, 0x0c95, 0x0 },
+		  { 0x0036, 0x0082, 0x0 } },
+		{ { 0x0c95, 0x0ccd, 0x0cb0, 0x0 },
+		  { 0x0036, 0x00c1, 0x0 } },
+		{ { 0x0cb0, 0x0ccd, 0x200d, 0x0c95, 0x0 },
+		  { 0x0050, 0x00a7, 0x0 } },
+
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Kannada) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find Sampige.ttf", SkipAll);
+	}
+    }
+    {
+        FT_Face face = loadFace("tunga.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		{ { 0x0cb7, 0x0cc6, 0x0 },
+		  { 0x00b0, 0x006c, 0x0 } },
+		{ { 0x0cb7, 0x0ccd, 0x0 },
+		  { 0x0163, 0x0 } },
+
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Kannada) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find tunga.ttf", SkipAll);
+	}
+    }
+}
+
+
+
+void tst_QScriptEngine::malayalam()
+{
+    {
+        FT_Face face = loadFace("AkrutiMal2Normal.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		{ { 0x0d15, 0x0d46, 0x0 },
+		  { 0x005e, 0x0034, 0x0 } },
+		{ { 0x0d15, 0x0d47, 0x0 },
+		  { 0x005f, 0x0034, 0x0 } },
+		{ { 0x0d15, 0x0d4b, 0x0 },
+		  { 0x005f, 0x0034, 0x0058, 0x0 } },
+		{ { 0x0d15, 0x0d48, 0x0 },
+		  { 0x0060, 0x0034, 0x0 } },
+		{ { 0x0d15, 0x0d4a, 0x0 },
+		  { 0x005e, 0x0034, 0x0058, 0x0 } },
+		{ { 0x0d30, 0x0d4d, 0x0d15, 0x0 },
+		  { 0x009e, 0x0034, 0x0 } },
+		{ { 0x0d15, 0x0d4d, 0x0d35, 0x0 },
+		  { 0x0034, 0x007a, 0x0 } },
+		{ { 0x0d15, 0x0d4d, 0x0d2f, 0x0 },
+		  { 0x0034, 0x00a2, 0x0 } },
+		{ { 0x0d1f, 0x0d4d, 0x0d1f, 0x0 },
+		  { 0x0069, 0x0 } },
+		{ { 0x0d26, 0x0d4d, 0x0d26, 0x0 },
+		  { 0x0074, 0x0 } },
+		{ { 0x0d30, 0x0d4d, 0x0 },
+		  { 0x009e, 0x0 } },
+		{ { 0x0d30, 0x0d4d, 0x200c, 0x0 },
+		  { 0x009e, 0x0 } },
+		{ { 0x0d30, 0x0d4d, 0x200d, 0x0 },
+		  { 0x009e, 0x0 } },
+
+
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Malayalam) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find AkrutiMal2Normal.ttf", SkipAll);
+	}
+    }
+}
+
+
+
+void tst_QScriptEngine::khmer()
+{
+    {
+        FT_Face face = loadFace("KhmerOS.ttf");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		{ { 0x179a, 0x17cd, 0x0 },
+		  { 0x24c, 0x27f, 0x0 } },
+		{ { 0x179f, 0x17c5, 0x0 },
+		  { 0x273, 0x203, 0x0 } },
+		{ { 0x1790, 0x17d2, 0x1784, 0x17c3, 0x0 },
+		  { 0x275, 0x242, 0x182, 0x0 } },
+		{ { 0x179a, 0x0 },
+		  { 0x24c, 0x0 } },
+		{ { 0x1781, 0x17d2, 0x1798, 0x17c2, 0x0 },
+		  { 0x274, 0x233, 0x197, 0x0 } },
+		{ { 0x1798, 0x17b6, 0x0 },
+		  { 0x1cb, 0x0 } },
+		{ { 0x179a, 0x17b8, 0x0 },
+		  { 0x24c, 0x26a, 0x0 } },
+		{ { 0x1787, 0x17b6, 0x0 },
+		  { 0x1ba, 0x0 } },
+		{ { 0x1798, 0x17d2, 0x1796, 0x17bb, 0x0 },
+		  { 0x24a, 0x195, 0x26d, 0x0 } },
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Khmer) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find KhmerOS.ttf", SkipAll);
+	}
+    }
+}
+
+void tst_QScriptEngine::linearB()
+{
+    {
+        FT_Face face = loadFace("PENUTURE.TTF");
+        if (face) {
+	    const ShapeTable shape_table [] = {
+		{ { 0xd800, 0xdc01, 0xd800, 0xdc02, 0xd800, 0xdc03,  0 },
+                  { 0x5, 0x6, 0x7, 0 } },
+		{ {0}, {0} }
+	    };
+
+
+	    const ShapeTable *s = shape_table;
+	    while (s->unicode[0]) {
+		QVERIFY( shaping(face, s, HB_Script_Common) );
+		++s;
+	    }
+
+            FT_Done_Face(face);
+	} else {
+	    QSKIP("couln't find PENUTURE.TTF", SkipAll);
+	}
+    }
+}
+
+
+QTEST_MAIN(tst_QScriptEngine)
+#include "main.moc"
diff --git a/tools/PdfRenderer.cpp b/tools/PdfRenderer.cpp
new file mode 100644
index 0000000..8819266
--- /dev/null
+++ b/tools/PdfRenderer.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 "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;
+    }
+}
+
+bool PdfRenderer::write(const SkString& path) const {
+    SkPDFDocument doc;
+    doc.appendPage(fPDFDevice);
+    SkFILEWStream stream(path.c_str());
+    if (stream.isValid()) {
+        doc.emitPDF(&stream);
+        return true;
+    }
+    return false;
+}
+
+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..bce6197
--- /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)
+        {}
+
+    bool write(const SkString& path) 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..fbf1585
--- /dev/null
+++ b/tools/PictureBenchmark.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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)
+{}
+
+PictureBenchmark::~PictureBenchmark() {
+    SkSafeUnref(fRenderer);
+}
+
+BenchTimer* PictureBenchmark::setupTimer() {
+#if SK_SUPPORT_GPU
+    if (fRenderer != NULL && fRenderer->isUsingGpuDevice()) {
+        return SkNEW_ARGS(BenchTimer, (fRenderer->getGLContext()));
+    } else {
+        return SkNEW_ARGS(BenchTimer, (NULL));
+    }
+#else
+    return SkNEW_ARGS(BenchTimer, (NULL));
+#endif
+}
+
+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();
+
+    BenchTimer* timer = this->setupTimer();
+    bool usingGpu = false;
+#if SK_SUPPORT_GPU
+    usingGpu = fRenderer->isUsingGpuDevice();
+#endif
+
+    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();
+        timer->end();
+
+        timerData.appendTimes(timer, fRepeats - 1 == i);
+    }
+
+    const char* configName = usingGpu ? "gpu" : "raster";
+    SkString result = timerData.getResult(fLogPerIter, fPrintMin, fRepeats,
+                                          configName, fShowWallTime, fShowTruncatedWallTime,
+                                          fShowCpuTime, fShowTruncatedCpuTime,
+                                          usingGpu && fShowGpuTime);
+    result.append("\n");
+    this->logProgress(result.c_str());
+
+    fRenderer->end();
+    SkDELETE(timer);
+}
+
+}
diff --git a/tools/PictureBenchmark.h b/tools/PictureBenchmark.h
new file mode 100644
index 0000000..0ab5ff9
--- /dev/null
+++ b/tools/PictureBenchmark.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 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();
+
+    void run(SkPicture* pict);
+
+    void setRepeats(int repeats) {
+        fRepeats = repeats;
+    }
+
+    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;
+
+    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..ae2c94e
--- /dev/null
+++ b/tools/PictureRenderer.cpp
@@ -0,0 +1,525 @@
+/*
+ * Copyright 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 "SkMatrix.h"
+#include "SkPicture.h"
+#include "SkScalar.h"
+#include "SkString.h"
+#include "SkTemplates.h"
+#include "SkTDArray.h"
+#include "SkThreadUtils.h"
+#include "SkTypes.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;
+    fCanvas.reset(this->setupCanvas());
+}
+
+SkCanvas* PictureRenderer::setupCanvas() {
+    return this->setupCanvas(fPicture->width(), fPicture->height());
+}
+
+SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
+    switch(fDeviceType) {
+        case kBitmap_DeviceType: {
+            SkBitmap bitmap;
+            sk_tools::setup_bitmap(&bitmap, width, height);
+            return 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)));
+            return SkNEW_ARGS(SkCanvas, (device.get()));
+            break;
+        }
+#endif
+        default:
+            SkASSERT(0);
+    }
+
+    return NULL;
+}
+
+void PictureRenderer::end() {
+    this->resetState();
+    fPicture = NULL;
+    fCanvas.reset(NULL);
+}
+
+void PictureRenderer::resetState() {
+#if SK_SUPPORT_GPU
+    if (this->isUsingGpuDevice()) {
+        SkGLContext* glContext = fGrContextFactory.getGLContext(
+            GrContextFactory::kNative_GLContextType);
+
+        SkASSERT(glContext != NULL);
+        if (NULL == glContext) {
+            return;
+        }
+
+        fGrContext->flush();
+        SK_GL(*glContext, Finish());
+    }
+#endif
+}
+
+/**
+ * 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);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool RecordPictureRenderer::render(const SkString*) {
+    SkPicture replayer;
+    SkCanvas* recorder = replayer.beginRecording(fPicture->width(), fPicture->height());
+    fPicture->draw(recorder);
+    replayer.endRecording();
+    // Since this class does not actually render, return false.
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool PipePictureRenderer::render(const SkString* path) {
+    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();
+    return path != NULL && write(fCanvas, *path);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SimplePictureRenderer::render(const SkString* path) {
+    SkASSERT(fCanvas.get() != NULL);
+    SkASSERT(fPicture != NULL);
+    if (NULL == fCanvas.get() || NULL == fPicture) {
+        return false;
+    }
+
+    fCanvas->drawPicture(*fPicture);
+    fCanvas->flush();
+    return path != NULL && write(fCanvas, *path);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+TiledPictureRenderer::TiledPictureRenderer()
+    : fUsePipe(false)
+    , fTileWidth(kDefaultTileWidth)
+    , fTileHeight(kDefaultTileHeight)
+    , fTileWidthPercentage(0.0)
+    , fTileHeightPercentage(0.0)
+    , fTileMinPowerOf2Width(0)
+    , fTileCounter(0)
+    , fNumThreads(1)
+    , fPictureClones(NULL)
+    , fPipeController(NULL) { }
+
+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;
+
+    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();
+    }
+
+    if (this->multiThreaded()) {
+        for (int i = 0; i < fNumThreads; ++i) {
+            *fCanvasPool.append() = this->setupCanvas(fTileWidth, fTileHeight);
+        }
+        if (!fUsePipe) {
+            SkASSERT(NULL == fPictureClones);
+            // Only need to create fNumThreads - 1 clones, since one thread will use the base
+            // picture.
+            int numberOfClones = fNumThreads - 1;
+            // This will be deleted in end().
+            fPictureClones = SkNEW_ARRAY(SkPicture, numberOfClones);
+            fPicture->clone(fPictureClones, numberOfClones);
+        }
+    }
+}
+
+void TiledPictureRenderer::end() {
+    fTileRects.reset();
+    SkDELETE_ARRAY(fPictureClones);
+    fPictureClones = NULL;
+    fCanvasPool.unrefAll();
+    if (fPipeController != NULL) {
+        SkASSERT(fUsePipe);
+        SkDELETE(fPipeController);
+        fPipeController = NULL;
+    }
+    this->INHERITED::end();
+}
+
+TiledPictureRenderer::~TiledPictureRenderer() {
+    // end() must be called to delete fPictureClones and fPipeController
+    SkASSERT(NULL == fPictureClones);
+    SkASSERT(NULL == fPipeController);
+}
+
+void TiledPictureRenderer::setupTiles() {
+    for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
+        for (int tile_x_start = 0; tile_x_start < fPicture->width(); tile_x_start += fTileWidth) {
+            *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
+                                                    SkIntToScalar(tile_y_start),
+                                                    SkIntToScalar(fTileWidth),
+                                                    SkIntToScalar(fTileHeight));
+        }
+    }
+}
+
+// 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() {
+    int rounded_value = fPicture->width();
+    if (fPicture->width() % fTileMinPowerOf2Width != 0) {
+        rounded_value = fPicture->width() - (fPicture->width() % fTileMinPowerOf2Width)
+            + fTileMinPowerOf2Width;
+    }
+
+    int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(fPicture->width())));
+    int largest_possible_tile_size = 1 << num_bits;
+
+    // The tile height is constant for a particular picture.
+    for (int tile_y_start = 0; tile_y_start < fPicture->height(); tile_y_start += fTileHeight) {
+        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) {
+                *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
+    canvas->translate(-tileRect.fLeft, -tileRect.fTop);
+    playback->draw(canvas);
+    canvas->restoreToCount(saveCount);
+    canvas->flush();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Base class for data used both by pipe and clone picture multi threaded drawing.
+
+struct ThreadData {
+    ThreadData(SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
+               const SkString* path, bool* success)
+    : fCanvas(target)
+    , fPath(path)
+    , fSuccess(success)
+    , fTileCounter(tileCounter)
+    , fTileRects(tileRects) {
+        SkASSERT(target != NULL && tileCounter != NULL && tileRects != NULL);
+        // Success must start off true, and it will be set to false upon failure.
+        SkASSERT(success != NULL && *success);
+    }
+
+    int32_t nextTile(SkRect* rect) {
+        int32_t i = sk_atomic_inc(fTileCounter);
+        if (i < fTileRects->count()) {
+            SkASSERT(rect != NULL);
+            *rect = fTileRects->operator[](i);
+            return i;
+        }
+        return -1;
+    }
+
+    // All of these are pointers to objects owned elsewhere
+    SkCanvas*                fCanvas;
+    const SkString*          fPath;
+    bool*                    fSuccess;
+private:
+    // Shared by all threads, this states which is the next tile to be drawn.
+    int32_t*                 fTileCounter;
+    // Points to the array of rectangles. The array is already created before any threads are
+    // started and then it is unmodified, so there is no danger of race conditions.
+    const SkTDArray<SkRect>* fTileRects;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Draw using Pipe
+
+struct TileData : public ThreadData {
+    TileData(ThreadSafePipeController* controller, SkCanvas* canvas, int* tileCounter,
+             SkTDArray<SkRect>* tileRects, const SkString* path, bool* success)
+    : INHERITED(canvas, tileCounter, tileRects, path, success)
+    , fController(controller) {}
+
+    ThreadSafePipeController* fController;
+
+    typedef ThreadData INHERITED;
+};
+
+static void DrawTile(void* data) {
+    SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
+    TileData* tileData = static_cast<TileData*>(data);
+
+    SkRect tileRect;
+    int32_t i;
+    while ((i = tileData->nextTile(&tileRect)) != -1) {
+        DrawTileToCanvas(tileData->fCanvas, tileRect, tileData->fController);
+        if (!writeAppendNumber(tileData->fCanvas, tileData->fPath, i)) {
+            *tileData->fSuccess = false;
+            break;
+        }
+    }
+    SkDELETE(tileData);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Draw using Picture
+
+struct CloneData : public ThreadData {
+    CloneData(SkPicture* clone, SkCanvas* target, int* tileCounter, SkTDArray<SkRect>* tileRects,
+              const SkString* path, bool* success)
+    : INHERITED(target, tileCounter, tileRects, path, success)
+    , fClone(clone) {}
+
+    SkPicture* fClone;
+
+    typedef ThreadData INHERITED;
+};
+
+static void DrawClonedTiles(void* data) {
+    SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
+    CloneData* cloneData = static_cast<CloneData*>(data);
+
+    SkRect tileRect;
+    int32_t i;
+    while ((i = cloneData->nextTile(&tileRect)) != -1) {
+        DrawTileToCanvas(cloneData->fCanvas, tileRect, cloneData->fClone);
+        if (!writeAppendNumber(cloneData->fCanvas, cloneData->fPath, i)) {
+            *cloneData->fSuccess = false;
+            break;
+        }
+    }
+    SkDELETE(cloneData);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void TiledPictureRenderer::setup() {
+    if (this->multiThreaded()) {
+        // Reset to zero so we start with the first tile.
+        fTileCounter = 0;
+        if (fUsePipe) {
+            // Record the picture into the pipe controller. It is done here because unlike
+            // SkPicture, the pipe is modified (bitmaps can be removed) by drawing.
+            // fPipeController is deleted here after each call to render() except the last one and
+            // in end() for the last one.
+            if (fPipeController != NULL) {
+                SkDELETE(fPipeController);
+            }
+            fPipeController = SkNEW_ARGS(ThreadSafePipeController, (fTileRects.count()));
+            SkGPipeWriter writer;
+            SkCanvas* pipeCanvas = writer.startRecording(fPipeController,
+                                                         SkGPipeWriter::kSimultaneousReaders_Flag);
+            SkASSERT(fPicture != NULL);
+            fPicture->draw(pipeCanvas);
+            writer.endRecording();
+        }
+    }
+}
+
+bool TiledPictureRenderer::render(const SkString* path) {
+    SkASSERT(fPicture != NULL);
+    if (NULL == fPicture) {
+        return false;
+    }
+
+    if (this->multiThreaded()) {
+        SkASSERT(fCanvasPool.count() == fNumThreads);
+        SkTDArray<SkThread*> threads;
+        SkThread::entryPointProc proc = fUsePipe ? DrawTile : DrawClonedTiles;
+        bool success = true;
+        for (int i = 0; i < fNumThreads; ++i) {
+            // data will be deleted by the entryPointProc.
+            ThreadData* data;
+            if (fUsePipe) {
+                data = SkNEW_ARGS(TileData, (fPipeController, fCanvasPool[i], &fTileCounter,
+                                             &fTileRects, path, &success));
+            } else {
+                SkPicture* pic = (0 == i) ? fPicture : &fPictureClones[i-1];
+                data = SkNEW_ARGS(CloneData, (pic, fCanvasPool[i], &fTileCounter, &fTileRects, path,
+                                              &success));
+            }
+            SkThread* thread = SkNEW_ARGS(SkThread, (proc, data));
+            if (!thread->start()) {
+                SkDebugf("Could not start %s thread %i.\n", (fUsePipe ? "pipe" : "picture"), i);
+            }
+            *threads.append() = thread;
+        }
+        SkASSERT(threads.count() == fNumThreads);
+        for (int i = 0; i < fNumThreads; ++i) {
+            SkThread* thread = threads[i];
+            thread->join();
+            SkDELETE(thread);
+        }
+        threads.reset();
+        return success;
+    } else {
+        // For single thread, we really only need one canvas total.
+        SkCanvas* canvas = this->setupCanvas(fTileWidth, fTileHeight);
+        SkAutoUnref aur(canvas);
+
+        for (int i = 0; i < fTileRects.count(); ++i) {
+            DrawTileToCanvas(canvas, fTileRects[i], fPicture);
+            if (!writeAppendNumber(canvas, path, i)) {
+                return false;
+            }
+        }
+        return path != NULL;
+    }
+}
+
+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 in what the SkPicture says is the
+    // drawn-to area. 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.
+    SkRect clip = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
+                                 SkIntToScalar(fPicture->height()));
+    canvas->clipRect(clip);
+    return canvas;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void PlaybackCreationRenderer::setup() {
+    SkCanvas* recorder = fReplayer.beginRecording(fPicture->width(), fPicture->height());
+    fPicture->draw(recorder);
+}
+
+bool PlaybackCreationRenderer::render(const SkString*) {
+    fReplayer.endRecording();
+    // Since this class does not actually render, return false.
+    return false;
+}
+
+}
diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h
new file mode 100644
index 0000000..4829e1d
--- /dev/null
+++ b/tools/PictureRenderer.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright 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 "SkMath.h"
+#include "SkPicture.h"
+#include "SkTypes.h"
+#include "SkTDArray.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
+#include "SkString.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContextFactory.h"
+#include "GrContext.h"
+#endif
+
+class SkBitmap;
+class SkCanvas;
+class SkGLContext;
+class ThreadSafePipeController;
+
+namespace sk_tools {
+
+class PictureRenderer : public SkRefCnt {
+public:
+    enum SkDeviceTypes {
+        kBitmap_DeviceType,
+#if SK_SUPPORT_GPU
+        kGPU_DeviceType
+#endif
+    };
+
+    virtual void init(SkPicture* pict);
+
+    /**
+     * 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 path is non-null and the output is successfully written to a file.
+     */
+    virtual bool render(const SkString* path) = 0;
+
+    virtual void end();
+    void resetState();
+
+    void setDeviceType(SkDeviceTypes deviceType) {
+        fDeviceType = deviceType;
+    }
+
+    bool isUsingBitmapDevice() {
+        return kBitmap_DeviceType == fDeviceType;
+    }
+
+    virtual SkString getPerIterTimeFormat() { return SkString("%.2f"); }
+
+    virtual SkString getNormalTimeFormat() { return SkString("%6.2f"); }
+
+#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)
+#if SK_SUPPORT_GPU
+        , fGrContext(fGrContextFactory.get(GrContextFactory::kNative_GLContextType))
+#endif
+        {}
+
+protected:
+    SkCanvas* setupCanvas();
+    virtual SkCanvas* setupCanvas(int width, int height);
+
+    SkAutoTUnref<SkCanvas> fCanvas;
+    SkPicture* fPicture;
+    SkDeviceTypes fDeviceType;
+
+#if SK_SUPPORT_GPU
+    GrContextFactory fGrContextFactory;
+    GrContext* fGrContext;
+#endif
+
+private:
+    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*) SK_OVERRIDE;
+
+    virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
+
+    virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
+};
+
+class PipePictureRenderer : public PictureRenderer {
+public:
+    virtual bool render(const SkString*) SK_OVERRIDE;
+
+private:
+    typedef PictureRenderer INHERITED;
+};
+
+class SimplePictureRenderer : public PictureRenderer {
+public:
+    virtual bool render(const SkString*) SK_OVERRIDE;
+
+private:
+    typedef PictureRenderer INHERITED;
+};
+
+class TiledPictureRenderer : public PictureRenderer {
+public:
+    TiledPictureRenderer();
+
+    virtual void init(SkPicture* pict) SK_OVERRIDE;
+
+    virtual void setup() 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) 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;
+    }
+
+    /**
+     * Set the number of threads to use for drawing. Non-positive numbers will set it to 1.
+     */
+    void setNumberOfThreads(int num) {
+        fNumThreads = SkMax32(num, 1);
+    }
+
+    void setUsePipe(bool usePipe) {
+        fUsePipe = usePipe;
+    }
+
+    ~TiledPictureRenderer();
+
+private:
+    bool              fUsePipe;
+    int               fTileWidth;
+    int               fTileHeight;
+    double            fTileWidthPercentage;
+    double            fTileHeightPercentage;
+    int               fTileMinPowerOf2Width;
+    SkTDArray<SkRect> fTileRects;
+
+    // These are only used for multithreaded rendering
+    int32_t                   fTileCounter;
+    int                       fNumThreads;
+    SkTDArray<SkCanvas*>      fCanvasPool;
+    SkPicture*                fPictureClones;
+    ThreadSafePipeController* fPipeController;
+
+    void setupTiles();
+    void setupPowerOf2Tiles();
+    virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
+    bool multiThreaded() { return fNumThreads > 1; }
+
+    typedef PictureRenderer 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*) SK_OVERRIDE;
+
+    virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
+
+    virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
+
+private:
+    SkPicture fReplayer;
+    typedef PictureRenderer INHERITED;
+};
+
+}
+
+#endif  // PictureRenderer_DEFINED
diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp
new file mode 100644
index 0000000..e602bd9
--- /dev/null
+++ b/tools/bench_pictures_main.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright 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 "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 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] \n"
+"     [--mode pow2tile minWidth height[] | record | simple\n"
+"             | tile width[] height[] | playbackCreation]\n"
+"     [--pipe]\n"
+"     [--multi numThreads]\n"
+"     [--device bitmap"
+#if SK_SUPPORT_GPU
+" | gpu"
+#endif
+"]"
+, argv0);
+    SkDebugf("\n\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(
+"     --mode pow2tile minWidht 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");
+    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"
+"     --pipe: Benchmark SkGPipe rendering. Compatible with tiled, multithreaded rendering.\n");
+    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);
+}
+
+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;
+}
+
+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;
+
+    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;
+    bool isPowerOf2Mode = false;
+    const char* mode = NULL;
+    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");
+                    exit(-1);
+                }
+            } else {
+                gLogger.logError("Missing arg for --repeat\n");
+                usage(argv0);
+                exit(-1);
+            }
+        } 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");
+                usage(argv0);
+                exit(-1);
+            }
+        } else if (0 == strcmp(*argv, "--multi")) {
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing arg for --multi\n");
+                usage(argv0);
+                exit(-1);
+            }
+            numThreads = atoi(*argv);
+            if (numThreads < 2) {
+                gLogger.logError("Number of threads must be at least 2.\n");
+                usage(argv0);
+                exit(-1);
+            }
+        } else if (0 == strcmp(*argv, "--mode")) {
+
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing mode for --mode\n");
+                usage(argv0);
+                exit(-1);
+            }
+
+            if (0 == strcmp(*argv, "record")) {
+                renderer = SkNEW(sk_tools::RecordPictureRenderer);
+            } else if (0 == strcmp(*argv, "simple")) {
+                renderer = SkNEW(sk_tools::SimplePictureRenderer);
+            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) {
+                useTiles = true;
+                mode = *argv;
+
+                if (0 == strcmp(*argv, "pow2tile")) {
+                    isPowerOf2Mode = true;
+                }
+
+                ++argv;
+                if (argv >= stop) {
+                    SkString err;
+                    err.printf("Missing width for --mode %s\n", mode);
+                    gLogger.logError(err);
+                    usage(argv0);
+                    exit(-1);
+                }
+
+                widthString = *argv;
+                ++argv;
+                if (argv >= stop) {
+                    gLogger.logError("Missing height for --mode tile\n");
+                    usage(argv0);
+                    exit(-1);
+                }
+                heightString = *argv;
+            } else if (0 == strcmp(*argv, "playbackCreation")) {
+                renderer = SkNEW(sk_tools::PlaybackCreationRenderer);
+            } else {
+                SkString err;
+                err.printf("%s is not a valid mode for --mode\n", *argv);
+                gLogger.logError(err);
+                usage(argv0);
+                exit(-1);
+            }
+        }  else if (0 == strcmp(*argv, "--device")) {
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("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 {
+                SkString err;
+                err.printf("%s is not a valid mode for --device\n", *argv);
+                gLogger.logError(err);
+                usage(argv0);
+                exit(-1);
+            }
+        } 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");
+                usage(argv0);
+                exit(-1);
+            }
+        } 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");
+                usage(argv0);
+                exit(-1);
+            }
+        } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) {
+            usage(argv0);
+            exit(0);
+        } else {
+            inputs->push_back(SkString(*argv));
+        }
+    }
+
+    if (numThreads > 1 && !useTiles) {
+        gLogger.logError("Multithreaded drawing requires tiled rendering.\n");
+        usage(argv0);
+        exit(-1);
+    }
+
+    if (useTiles) {
+        SkASSERT(NULL == renderer);
+        sk_tools::TiledPictureRenderer* 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);
+                usage(argv0);
+                exit(-1);
+            }
+            tiledRenderer->setTileMinPowerOf2Width(minWidth);
+        } else if (sk_tools::is_percentage(widthString)) {
+            tiledRenderer->setTileWidthPercentage(atof(widthString));
+            if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
+                tiledRenderer->unref();
+                gLogger.logError("--mode tile must be given a width percentage > 0\n");
+                usage(argv0);
+                exit(-1);
+            }
+        } else {
+            tiledRenderer->setTileWidth(atoi(widthString));
+            if (!(tiledRenderer->getTileWidth() > 0)) {
+                tiledRenderer->unref();
+                gLogger.logError("--mode tile must be given a width > 0\n");
+                usage(argv0);
+                exit(-1);
+            }
+        }
+
+        if (sk_tools::is_percentage(heightString)) {
+            tiledRenderer->setTileHeightPercentage(atof(heightString));
+            if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
+                tiledRenderer->unref();
+                gLogger.logError("--mode tile must be given a height percentage > 0\n");
+                usage(argv0);
+                exit(-1);
+            }
+        } else {
+            tiledRenderer->setTileHeight(atoi(heightString));
+            if (!(tiledRenderer->getTileHeight() > 0)) {
+                tiledRenderer->unref();
+                gLogger.logError("--mode tile must be given a height > 0\n");
+                usage(argv0);
+                exit(-1);
+            }
+        }
+        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");
+                usage(argv0);
+                exit(-1);
+            }
+#endif
+            tiledRenderer->setNumberOfThreads(numThreads);
+        }
+        tiledRenderer->setUsePipe(usePipe);
+        renderer = tiledRenderer;
+    } else if (usePipe) {
+        renderer = SkNEW(sk_tools::PipePictureRenderer);
+    }
+    if (inputs->count() < 1) {
+        SkSafeUnref(renderer);
+        usage(argv0);
+        exit(-1);
+    }
+
+    if (NULL == renderer) {
+        renderer = SkNEW(sk_tools::SimplePictureRenderer);
+    }
+    benchmark->setRenderer(renderer)->unref();
+    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) {
+#ifdef 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/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..a804c4c
--- /dev/null
+++ b/tools/filtermain.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 "SkGraphics.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+
+static void usage() {
+    SkDebugf("Usage: filter -i inFile -o outFile [-h|--help]");
+    SkDebugf("\n\n");
+    SkDebugf("    -i inFile  : file to file.\n");
+    SkDebugf("    -o outFile : result of filtering.\n");
+    SkDebugf("    -h|--help  : Show this help message.\n");
+}
+
+// This function is not marked as 'static' so it can be referenced externally
+// in the iOS build.
+int tool_main(int argc, char** argv) {
+    SkGraphics::Init();
+
+    SkString inFile, outFile;
+
+    if (argc < 5) {
+        usage();
+        return -1;
+    }
+
+    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, "-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;
+        }
+    }
+
+    SkPicture* inPicture = NULL;
+
+    SkFILEStream inStream(inFile.c_str());
+    if (inStream.isValid()) {
+        inPicture = SkNEW_ARGS(SkPicture, (&inStream));
+    }
+
+    if (NULL == inPicture) {
+        SkDebugf("Could not read file %s\n", inFile.c_str());
+        return -1;
+    }
+
+    SkPicture outPicture;
+    inPicture->draw(outPicture.beginRecording(inPicture->width(), inPicture->height()));
+    outPicture.endRecording();
+
+    SkFILEWStream outStream(outFile.c_str());
+    outPicture.serialize(&outStream);
+
+    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/picture_utils.cpp b/tools/picture_utils.cpp
new file mode 100644
index 0000000..b9bedbb
--- /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(0);
+    }
+}
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..b82bd33
--- /dev/null
+++ b/tools/pinspect.cpp
@@ -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.
+ */
+
+
+#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) {
+    SkDebugfDumper dumper;
+    SkDumpCanvas canvas(&dumper);
+    canvas.drawPicture(*pic);
+}
+
+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/render_pictures_main.cpp b/tools/render_pictures_main.cpp
new file mode 100644
index 0000000..6d6e8db
--- /dev/null
+++ b/tools/render_pictures_main.cpp
@@ -0,0 +1,377 @@
+/*
+ * Copyright 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 "SkDevice.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.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>... <outputDir> \n"
+"     [--mode pow2tile minWidth height[%] | simple\n"
+"         | tile width[%] height[%]]\n"
+"     [--pipe]\n"
+"     [--multi count]\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[%] | simple\n"
+"          | tile width[%] height[%]: 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");
+    SkDebugf(
+"                     tile width[%] height[%], Do a simple render using tiles\n"
+"                                              with the given dimensions.\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"
+"     --pipe: Benchmark SkGPipe rendering. Compatible with tiled, multithreaded rendering.\n");
+    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) {
+    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(&inputStream, &success, &SkImageDecoder::DecodeStream);
+    if (!success) {
+        SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
+        return false;
+    }
+
+    SkDebugf("drawing... [%i %i] %s\n", picture.width(), picture.height(),
+             inputPath.c_str());
+
+    renderer.init(&picture);
+    renderer.setup();
+
+    SkString outputPath;
+    make_output_filepath(&outputPath, outputDir, inputFilename);
+
+    success = renderer.render(&outputPath);
+    if (!success) {
+        SkDebugf("Could not write to file %s\n", outputPath.c_str());
+    }
+
+    renderer.resetState();
+
+    renderer.end();
+    return success;
+}
+
+static int process_input(const SkString& input, const SkString& outputDir,
+                          sk_tools::PictureRenderer& renderer) {
+    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 (!render_picture(inputPath, outputDir, renderer)) {
+                ++failures;
+            }
+        } while(iter.next(&inputFilename));
+    } else if (SkStrEndsWith(input.c_str(), ".skp")) {
+        SkString inputPath(input);
+        if (!render_picture(inputPath, outputDir, renderer)) {
+            ++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){
+    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;
+    bool isPowerOf2Mode = false;
+    const char* mode = NULL;
+
+    for (++argv; argv < stop; ++argv) {
+        if (0 == strcmp(*argv, "--mode")) {
+            SkDELETE(renderer);
+
+            ++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"))) {
+                useTiles = true;
+                mode = *argv;
+
+                if (0 == strcmp(*argv, "pow2tile")) {
+                    isPowerOf2Mode = 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 tile\n");
+                    usage(argv0);
+                    exit(-1);
+                }
+                heightString = *argv;
+            } else {
+                SkDebugf("%s is not a valid mode for --mode\n", *argv);
+                usage(argv0);
+                exit(-1);
+            }
+        } else if (0 == strcmp(*argv, "--pipe")) {
+            usePipe = true;
+        } else if (0 == strcmp(*argv, "--multi")) {
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing arg for --multi\n");
+                usage(argv0);
+                exit(-1);
+            }
+            numThreads = atoi(*argv);
+            if (numThreads < 2) {
+                SkDebugf("Number of threads must be at least 2.\n");
+                usage(argv0);
+                exit(-1);
+            }
+        } else if (0 == strcmp(*argv, "--device")) {
+            ++argv;
+            if (argv >= stop) {
+                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 {
+                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"))) {
+            SkDELETE(renderer);
+            usage(argv0);
+            exit(-1);
+        } else {
+            inputs->push_back(SkString(*argv));
+        }
+    }
+
+    if (numThreads > 1 && !useTiles) {
+        SkDebugf("Multithreaded drawing requires tiled rendering.\n");
+        usage(argv0);
+        exit(-1);
+    }
+
+    if (useTiles) {
+        SkASSERT(NULL == renderer);
+        sk_tools::TiledPictureRenderer* 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)) {
+            tiledRenderer->setTileWidthPercentage(atof(widthString));
+            if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
+                tiledRenderer->unref();
+                SkDebugf("--mode tile must be given a width percentage > 0\n");
+                usage(argv0);
+                exit(-1);
+            }
+        } else {
+            tiledRenderer->setTileWidth(atoi(widthString));
+            if (!(tiledRenderer->getTileWidth() > 0)) {
+                tiledRenderer->unref();
+                SkDebugf("--mode tile must be given a width > 0\n");
+                usage(argv0);
+                exit(-1);
+            }
+        }
+
+        if (sk_tools::is_percentage(heightString)) {
+            tiledRenderer->setTileHeightPercentage(atof(heightString));
+            if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
+                tiledRenderer->unref();
+                SkDebugf("--mode tile must be given a height percentage > 0\n");
+                usage(argv0);
+                exit(-1);
+            }
+        } else {
+            tiledRenderer->setTileHeight(atoi(heightString));
+            if (!(tiledRenderer->getTileHeight() > 0)) {
+                tiledRenderer->unref();
+                SkDebugf("--mode tile must be given a height > 0\n");
+                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
+            tiledRenderer->setNumberOfThreads(numThreads);
+        }
+        tiledRenderer->setUsePipe(usePipe);
+        renderer = tiledRenderer;
+    } else if (usePipe) {
+        renderer = SkNEW(sk_tools::PipePictureRenderer);
+    }
+
+    if (inputs->count() < 2) {
+        SkDELETE(renderer);
+        usage(argv0);
+        exit(-1);
+    }
+
+    if (NULL == renderer) {
+        renderer = SkNEW(sk_tools::SimplePictureRenderer);
+    }
+
+    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;
+
+    parse_commandline(argc, argv, &inputs, renderer);
+    SkString outputDir = inputs[inputs.count() - 1];
+    SkASSERT(renderer);
+
+    int failures = 0;
+    for (int i = 0; i < inputs.count() - 1; i ++) {
+        failures += process_input(inputs[i], outputDir, *renderer);
+    }
+    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
+
+    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..d6e3ec6
--- /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: EOFNewlineAdder).
+    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 EOFNewlineAdder(file_content, file_path):
+  """Adds a LF at the end of the file if it does not have one."""
+  if file_content and file_content[-1] != '\n':
+    file_content += '\n'
+    print 'Added 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,
+          EOFNewlineAdder,
+          SvnEOLChecker,
+      ],
+      line_modifiers=[
+          CrlfReplacer,
+          TabReplacer,
+          TrailingWhitespaceRemover,
+      ],
+  ))
+
diff --git a/tools/skdiff_main.cpp b/tools/skdiff_main.cpp
new file mode 100644
index 0000000..46d4f00
--- /dev/null
+++ b/tools/skdiff_main.cpp
@@ -0,0 +1,1455 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkColorPriv.h"
+#include "SkData.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkOSFile.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "SkTemplates.h"
+#include "SkTime.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.
+ */
+
+#if SK_BUILD_FOR_WIN32
+    #define PATH_DIV_STR "\\"
+    #define PATH_DIV_CHAR '\\'
+#else
+    #define PATH_DIV_STR "/"
+    #define PATH_DIV_CHAR '/'
+#endif
+
+// Result of comparison for each pair of files.
+// Listed from "better" to "worse", for sorting of results.
+enum Result {
+    kEqualBits,
+    kEqualPixels,
+    kDifferentPixels,
+    kDifferentSizes,
+    kDifferentOther,
+    kComparisonMissing,
+    kBaseMissing,
+    kUnknown,
+    //
+    kNumResultTypes  // NOT A VALID VALUE--used to set up arrays. Must be last.
+};
+
+// Returns the Result with this name.
+// If there is no Result with this name, returns kNumResultTypes.
+// TODO: Is there a better return value for the fall-through case?
+static Result getResultByName(const char *name) {
+    if (0 == strcmp("EqualBits", name)) {
+        return kEqualBits;
+    }
+    if (0 == strcmp("EqualPixels", name)) {
+        return kEqualPixels;
+    }
+    if (0 == strcmp("DifferentPixels", name)) {
+        return kDifferentPixels;
+    }
+    if (0 == strcmp("DifferentSizes", name)) {
+        return kDifferentSizes;
+    }
+    if (0 == strcmp("DifferentOther", name)) {
+        return kDifferentOther;
+    }
+    if (0 == strcmp("ComparisonMissing", name)) {
+        return kComparisonMissing;
+    }
+    if (0 == strcmp("BaseMissing", name)) {
+        return kBaseMissing;
+    }
+    if (0 == strcmp("Unknown", name)) {
+        return kUnknown;
+    }
+    return kNumResultTypes;
+}
+
+// Returns a text description of the given Result type.
+static const char *getResultDescription(Result result) {
+    switch (result) {
+      case kEqualBits:
+        return "contain exactly the same bits";
+      case kEqualPixels:
+        return "contain the same pixel values, but not the same bits";
+      case kDifferentPixels:
+        return "have identical dimensions but some differing pixels";
+      case kDifferentSizes:
+        return "have differing dimensions";
+      case kDifferentOther:
+        return "contain different bits and are not parsable images";
+      case kBaseMissing:
+        return "missing from baseDir";
+      case kComparisonMissing:
+        return "missing from comparisonDir";
+      case kUnknown:
+        return "not compared yet";
+      default:
+        return NULL;
+    }
+}
+
+struct DiffRecord {
+    DiffRecord (const SkString filename,
+                const SkString basePath,
+                const SkString comparisonPath,
+                const Result result = kUnknown)
+        : fFilename (filename)
+        , fBasePath (basePath)
+        , fComparisonPath (comparisonPath)
+        , fBaseBitmap (new SkBitmap ())
+        , fComparisonBitmap (new SkBitmap ())
+        , fDifferenceBitmap (new SkBitmap ())
+        , fWhiteBitmap (new SkBitmap ())
+        , fBaseHeight (0)
+        , fBaseWidth (0)
+        , fFractionDifference (0)
+        , fWeightedFraction (0)
+        , fAverageMismatchR (0)
+        , fAverageMismatchG (0)
+        , fAverageMismatchB (0)
+        , fMaxMismatchR (0)
+        , fMaxMismatchG (0)
+        , fMaxMismatchB (0)
+        , fResult(result) {
+    };
+
+    SkString fFilename;
+    SkString fBasePath;
+    SkString fComparisonPath;
+
+    SkBitmap* fBaseBitmap;
+    SkBitmap* fComparisonBitmap;
+    SkBitmap* fDifferenceBitmap;
+    SkBitmap* fWhiteBitmap;
+
+    int fBaseHeight;
+    int fBaseWidth;
+
+    /// 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 fAverageMismatchR;
+    float fAverageMismatchG;
+    float fAverageMismatchB;
+
+    uint32_t fMaxMismatchR;
+    uint32_t fMaxMismatchG;
+    uint32_t fMaxMismatchB;
+
+    /// Which category of diff result.
+    Result fResult;
+};
+
+#define MAX2(a,b) (((b) < (a)) ? (a) : (b))
+#define MAX3(a,b,c) (((b) < (a)) ? MAX2((a), (c)) : MAX2((b), (c)))
+
+const SkPMColor PMCOLOR_WHITE = SkPreMultiplyColor(SK_ColorWHITE);
+const SkPMColor PMCOLOR_BLACK = SkPreMultiplyColor(SK_ColorBLACK);
+
+typedef SkTDArray<SkString*> StringArray;
+typedef StringArray FileArray;
+
+struct DiffSummary {
+    DiffSummary ()
+        : fNumMatches (0)
+        , fNumMismatches (0)
+        , fMaxMismatchV (0)
+        , fMaxMismatchPercent (0) { };
+
+    ~DiffSummary() {
+        for (int i = 0; i < kNumResultTypes; i++) {
+            fResultsOfType[i].deleteAll();
+        }
+    }
+
+    uint32_t fNumMatches;
+    uint32_t fNumMismatches;
+    uint32_t fMaxMismatchV;
+    float fMaxMismatchPercent;
+
+    FileArray fResultsOfType[kNumResultTypes];
+
+    // 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[kNumResultTypes]) {
+        printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
+        for (int resultInt = 0; resultInt < kNumResultTypes; resultInt++) {
+            Result result = static_cast<Result>(resultInt);
+            if (failOnResultType[result]) {
+                printf("[*] ");
+            } else {
+                printf("[_] ");
+            }
+            printContents(fResultsOfType[result], getResultDescription(result), listFilenames);
+        }
+        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;
+
+        fResultsOfType[drp->fResult].push(new SkString(drp->fFilename));
+        switch (drp->fResult) {
+          case kEqualBits:
+            fNumMatches++;
+            break;
+          case kEqualPixels:
+            fNumMatches++;
+            break;
+          case kDifferentSizes:
+            fNumMismatches++;
+            break;
+          case kDifferentPixels:
+            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 kDifferentOther:
+            fNumMismatches++;
+            break;
+          case kBaseMissing:
+            fNumMismatches++;
+            break;
+          case kComparisonMissing:
+            fNumMismatches++;
+            break;
+          case kUnknown:
+            SkDEBUGFAIL("adding uncategorized DiffRecord");
+            break;
+          default:
+            SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
+            break;
+        }
+    }
+};
+
+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->fFilename.c_str(), rhs->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);
+
+#if 0 // UNUSED
+static void expand_and_copy (int width, int height, SkBitmap** dest) {
+    SkBitmap* temp = new SkBitmap ();
+    temp->reset();
+    temp->setConfig((*dest)->config(), width, height);
+    temp->allocPixels();
+    (*dest)->copyPixelsTo(temp->getPixels(), temp->getSize(),
+                          temp->rowBytes());
+    *dest = temp;
+}
+#endif
+
+/// Returns true if the two buffers passed in are both non-NULL, and include
+/// exactly the same byte values (and identical lengths).
+static 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()));
+}
+
+/// Reads the file at the given path and returns its complete contents as an
+/// SkData object (or returns NULL on error).
+static 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);
+}
+
+/// Decodes binary contents of baseFile and comparisonFile into
+/// diffRecord->fBaseBitmap and diffRecord->fComparisonBitmap.
+/// Returns true if that succeeds.
+static bool get_bitmaps (SkData* baseFileContents,
+                         SkData* comparisonFileContents,
+                         DiffRecord* diffRecord) {
+    SkMemoryStream compareStream(comparisonFileContents->data(),
+                                 comparisonFileContents->size());
+    SkMemoryStream baseStream(baseFileContents->data(),
+                              baseFileContents->size());
+
+    SkImageDecoder* codec = SkImageDecoder::Factory(&baseStream);
+    if (NULL == codec) {
+        SkDebugf("ERROR: no codec found for basePath <%s>\n",
+                 diffRecord->fBasePath.c_str());
+        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);
+
+    baseStream.rewind();
+    if (!codec->decode(&baseStream, diffRecord->fBaseBitmap,
+                       SkBitmap::kARGB_8888_Config,
+                       SkImageDecoder::kDecodePixels_Mode)) {
+        SkDebugf("ERROR: codec failed for basePath <%s>\n",
+                 diffRecord->fBasePath.c_str());
+        return false;
+    }
+
+    diffRecord->fBaseWidth = diffRecord->fBaseBitmap->width();
+    diffRecord->fBaseHeight = diffRecord->fBaseBitmap->height();
+
+    if (!codec->decode(&compareStream, diffRecord->fComparisonBitmap,
+                       SkBitmap::kARGB_8888_Config,
+                       SkImageDecoder::kDecodePixels_Mode)) {
+        SkDebugf("ERROR: codec failed for comparisonPath <%s>\n",
+                 diffRecord->fComparisonPath.c_str());
+        return false;
+    }
+
+    return true;
+}
+
+static bool get_bitmap_height_width(const SkString& path,
+                                    int *height, int *width) {
+    SkFILEStream stream(path.c_str());
+    if (!stream.isValid()) {
+        SkDebugf("ERROR: couldn't open file <%s>\n",
+                 path.c_str());
+        return false;
+    }
+
+    SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
+    if (NULL == codec) {
+        SkDebugf("ERROR: no codec found for <%s>\n",
+                 path.c_str());
+        return false;
+    }
+
+    SkAutoTDelete<SkImageDecoder> ad(codec);
+    SkBitmap bm;
+
+    stream.rewind();
+    if (!codec->decode(&stream, &bm,
+                       SkBitmap::kARGB_8888_Config,
+                       SkImageDecoder::kDecodePixels_Mode)) {
+        SkDebugf("ERROR: codec failed for <%s>\n",
+                 path.c_str());
+        return false;
+    }
+
+    *height = bm.height();
+    *width = bm.width();
+
+    return true;
+}
+
+// from gm - 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);
+       }
+   }
+}
+
+// from gm
+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);
+}
+
+// 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));
+}
+
+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));
+}
+
+// based on gm
+// Postcondition: when we exit this method, dr->fResult should have some value
+// other than kUnknown.
+static void compute_diff(DiffRecord* dr,
+                         DiffMetricProc diffFunction,
+                         const int colorThreshold) {
+    SkAutoLockPixels alpDiff(*dr->fDifferenceBitmap);
+    SkAutoLockPixels alpWhite(*dr->fWhiteBitmap);
+
+    const int w = dr->fComparisonBitmap->width();
+    const int h = dr->fComparisonBitmap->height();
+    int mismatchedPixels = 0;
+    int totalMismatchR = 0;
+    int totalMismatchG = 0;
+    int totalMismatchB = 0;
+
+    if (w != dr->fBaseWidth || h != dr->fBaseHeight) {
+        dr->fResult = kDifferentSizes;
+        return;
+    }
+    // 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->fBaseBitmap->getAddr32(x, y);
+            SkPMColor c1 = *dr->fComparisonBitmap->getAddr32(x, y);
+            SkPMColor trueDifference = compute_diff_pmcolor(c0, c1);
+            SkPMColor outputDifference = diffFunction(c0, c1);
+            uint32_t thisR = SkGetPackedR32(trueDifference);
+            uint32_t thisG = SkGetPackedG32(trueDifference);
+            uint32_t thisB = SkGetPackedB32(trueDifference);
+            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 (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->fDifferenceBitmap->getAddr32(x, y) = outputDifference;
+                *dr->fWhiteBitmap->getAddr32(x, y) = PMCOLOR_WHITE;
+            } else {
+                *dr->fDifferenceBitmap->getAddr32(x, y) = 0;
+                *dr->fWhiteBitmap->getAddr32(x, y) = PMCOLOR_BLACK;
+            }
+        }
+    }
+    if (0 == mismatchedPixels) {
+        dr->fResult = kEqualPixels;
+        return;
+    }
+    dr->fResult = kDifferentPixels;
+    int pixelCount = w * h;
+    dr->fFractionDifference = ((float) mismatchedPixels) / pixelCount;
+    dr->fWeightedFraction /= pixelCount;
+    dr->fAverageMismatchR = ((float) totalMismatchR) / pixelCount;
+    dr->fAverageMismatchG = ((float) totalMismatchG) / pixelCount;
+    dr->fAverageMismatchB = ((float) totalMismatchB) / pixelCount;
+}
+
+/// 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;
+}
+
+/// Given a image filename, returns the name of the file containing the
+/// associated difference image.
+static SkString filename_to_diff_filename (const SkString& filename) {
+    return filename_to_derived_filename(filename, "-diff.png");
+}
+
+/// Given a image filename, returns the name of the file containing the
+/// "white" difference image.
+static SkString filename_to_white_filename (const SkString& filename) {
+    return filename_to_derived_filename(filename, "-white.png");
+}
+
+static void release_bitmaps(DiffRecord* drp) {
+    delete drp->fBaseBitmap;
+    drp->fBaseBitmap = NULL;
+    delete drp->fComparisonBitmap;
+    drp->fComparisonBitmap = NULL;
+    delete drp->fDifferenceBitmap;
+    drp->fDifferenceBitmap = NULL;
+    delete drp->fWhiteBitmap;
+    drp->fWhiteBitmap = NULL;
+}
+
+
+/// If outputDir.isEmpty(), don't write out diff files.
+static void create_and_write_diff_image(DiffRecord* drp,
+                                        DiffMetricProc dmp,
+                                        const int colorThreshold,
+                                        const SkString& outputDir,
+                                        const SkString& filename) {
+    const int w = drp->fBaseWidth;
+    const int h = drp->fBaseHeight;
+    drp->fDifferenceBitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    drp->fDifferenceBitmap->allocPixels();
+    drp->fWhiteBitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    drp->fWhiteBitmap->allocPixels();
+
+    SkASSERT(kUnknown == drp->fResult);
+    compute_diff(drp, dmp, colorThreshold);
+    SkASSERT(kUnknown != drp->fResult);
+
+    if ((kDifferentPixels == drp->fResult) && !outputDir.isEmpty()) {
+        SkString differencePath (outputDir);
+        differencePath.append(filename_to_diff_filename(filename));
+        write_bitmap(differencePath, drp->fDifferenceBitmap);
+        SkString whitePath (outputDir);
+        whitePath.append(filename_to_white_filename(filename));
+        write_bitmap(whitePath, drp->fWhiteBitmap);
+    }
+
+    release_bitmaps(drp);
+}
+
+/// 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());
+}
+
+/// 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,
+                                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);
+        basePath.append(*baseFiles[i]);
+        SkString comparisonPath (comparisonDir);
+        comparisonPath.append(*comparisonFiles[j]);
+
+        DiffRecord *drp = NULL;
+        int v = strcmp(baseFiles[i]->c_str(),
+                       comparisonFiles[j]->c_str());
+
+        if (v < 0) {
+            // in baseDir, but not in comparisonDir
+            drp = new DiffRecord(*baseFiles[i], basePath, comparisonPath,
+                                 kComparisonMissing);
+            ++i;
+        } else if (v > 0) {
+            // in comparisonDir, but not in baseDir
+            drp = new DiffRecord(*comparisonFiles[j], basePath, comparisonPath,
+                                 kBaseMissing);
+            ++j;
+        } else {
+            // Found the same filename in both baseDir and comparisonDir.
+            drp = new DiffRecord(*baseFiles[i], basePath, comparisonPath);
+            SkASSERT(kUnknown == drp->fResult);
+
+            SkData* baseFileBits = NULL;
+            SkData* comparisonFileBits = NULL;
+            if (NULL == (baseFileBits = read_file(basePath.c_str()))) {
+                SkDebugf("WARNING: couldn't read base file <%s>\n",
+                         basePath.c_str());
+                drp->fResult = kBaseMissing;
+            } else if (NULL == (comparisonFileBits = read_file(comparisonPath.c_str()))) {
+                SkDebugf("WARNING: couldn't read comparison file <%s>\n",
+                         comparisonPath.c_str());
+                drp->fResult = kComparisonMissing;
+            } else {
+                if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
+                    drp->fResult = kEqualBits;
+                } else if (get_bitmaps(baseFileBits, comparisonFileBits, drp)) {
+                    create_and_write_diff_image(drp, dmp, colorThreshold,
+                                                outputDir, *baseFiles[i]);
+                } else {
+                    drp->fResult = kDifferentOther;
+                }
+            }
+            if (baseFileBits) {
+                baseFileBits->unref();
+            }
+            if (comparisonFileBits) {
+                comparisonFileBits->unref();
+            }
+            ++i;
+            ++j;
+        }
+        SkASSERT(kUnknown != drp->fResult);
+        differences->push(drp);
+        summary->add(drp);
+    }
+
+    for (; i < baseFiles.count(); ++i) {
+        // files only in baseDir
+        SkString basePath (baseDir);
+        basePath.append(*baseFiles[i]);
+        SkString comparisonPath;
+        DiffRecord *drp = new DiffRecord(*baseFiles[i], basePath,
+                                         comparisonPath, kComparisonMissing);
+        differences->push(drp);
+        summary->add(drp);
+    }
+
+    for (; j < comparisonFiles.count(); ++j) {
+        // files only in comparisonDir
+        SkString basePath;
+        SkString comparisonPath(comparisonDir);
+        comparisonPath.append(*comparisonFiles[j]);
+        DiffRecord *drp = new DiffRecord(*comparisonFiles[j], basePath,
+                                         comparisonPath, kBaseMissing);
+        differences->push(drp);
+        summary->add(drp);
+    }
+
+    release_file_list(&baseFiles);
+    release_file_list(&comparisonFiles);
+}
+
+/// 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(" images 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.fBaseWidth *
+                                            diff.fBaseHeight));
+    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.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.fFilename.c_str());
+    stream->writeText("</b><br>");
+    switch (diff.fResult) {
+      case kEqualBits:
+        SkDEBUGFAIL("should not encounter DiffRecord with kEqualBits here");
+        return;
+      case kEqualPixels:
+        SkDEBUGFAIL("should not encounter DiffRecord with kEqualPixels here");
+        return;
+      case kDifferentSizes:
+        stream->writeText("Image sizes differ</td>");
+        return;
+      case kDifferentPixels:
+        sprintf(metricBuf, "%12.4f%%", 100 * diff.fFractionDifference);
+        stream->writeText(metricBuf);
+        stream->writeText(" of pixels differ");
+        stream->writeText("\n  (");
+        sprintf(metricBuf, "%12.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>Average color mismatch ");
+        stream->writeDecAsText(static_cast<int>(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 kDifferentOther:
+        stream->writeText("Files differ; unable to parse one or both files</td>");
+        return;
+      case kBaseMissing:
+        stream->writeText("Missing from baseDir</td>");
+        return;
+      case kComparisonMissing:
+        stream->writeText("Missing from comparisonDir</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>");
+}
+
+#if 0 // UNUSED
+static void print_text_cell (SkFILEWStream* stream, const char* text) {
+    stream->writeText("<td align=center>");
+    if (NULL != text) {
+        stream->writeText(text);
+    }
+    stream->writeText("</td>");
+}
+#endif
+
+static void print_diff_with_missing_file(SkFILEWStream* stream,
+                                         DiffRecord& diff,
+                                         const SkString& relativePath) {
+    stream->writeText("<tr>\n");
+    print_checkbox_cell(stream, diff);
+    print_label_cell(stream, diff);
+    stream->writeText("<td>N/A</td>");
+    stream->writeText("<td>N/A</td>");
+    if (kBaseMissing != diff.fResult) {
+        int h, w;
+        if (!get_bitmap_height_width(diff.fBasePath, &h, &w)) {
+            stream->writeText("<td>N/A</td>");
+        } else {
+            int height = compute_image_height(h, w);
+            if (!diff.fBasePath.startsWith(PATH_DIV_STR)) {
+                diff.fBasePath.prepend(relativePath);
+            }
+            print_image_cell(stream, diff.fBasePath, height);
+        }
+    } else {
+        stream->writeText("<td>N/A</td>");
+    }
+    if (kComparisonMissing != diff.fResult) {
+        int h, w;
+        if (!get_bitmap_height_width(diff.fComparisonPath, &h, &w)) {
+            stream->writeText("<td>N/A</td>");
+        } else {
+            int height = compute_image_height(h, w);
+            if (!diff.fComparisonPath.startsWith(PATH_DIV_STR)) {
+                diff.fComparisonPath.prepend(relativePath);
+            }
+            print_image_cell(stream, diff.fComparisonPath, height);
+        }
+    } else {
+        stream->writeText("<td>N/A</td>");
+    }
+    stream->writeText("</tr>\n");
+    stream->flush();
+}
+
+static 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 kEqualBits:
+          case kEqualPixels:
+            continue;
+          // Cases in which we want a detailed pixel diff.
+          case kDifferentPixels:
+            break;
+          // Cases in which the files differed, but we can't display the diff.
+          case kDifferentSizes:
+          case kDifferentOther:
+          case kBaseMissing:
+          case kComparisonMissing:
+            print_diff_with_missing_file(&outputStream, *diff, relativePath);
+            continue;
+          default:
+            SkDEBUGFAIL("encountered DiffRecord with unknown result type");
+            continue;
+        }
+
+        if (!diff->fBasePath.startsWith(PATH_DIV_STR)) {
+            diff->fBasePath.prepend(relativePath);
+        }
+        if (!diff->fComparisonPath.startsWith(PATH_DIV_STR)) {
+            diff->fComparisonPath.prepend(relativePath);
+        }
+
+        int height = compute_image_height(diff->fBaseHeight, diff->fBaseWidth);
+        outputStream.writeText("<tr>\n");
+        print_checkbox_cell(&outputStream, *diff);
+        print_label_cell(&outputStream, *diff);
+        print_image_cell(&outputStream,
+                         filename_to_white_filename(diff->fFilename), height);
+        print_image_cell(&outputStream,
+                         filename_to_diff_filename(diff->fFilename), height);
+        print_image_cell(&outputStream, diff->fBasePath, height);
+        print_image_cell(&outputStream, diff->fComparisonPath, height);
+        outputStream.writeText("</tr>\n");
+        outputStream.flush();
+    }
+    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();
+}
+
+static void usage (char * argv0) {
+    SkDebugf("Skia baseline image diff tool\n");
+    SkDebugf("\n"
+"Usage: \n"
+"    %s <baseDir> <comparisonDir> [outputDir] \n"
+, argv0, 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    --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[kNumResultTypes];
+    for (int i = 0; i < kNumResultTypes; i++) {
+        failOnResultType[i] = false;
+    }
+
+    int i;
+    int numUnflaggedArguments = 0;
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "--failonresult")) {
+            Result type = getResultByName(argv[++i]);
+            failOnResultType[type] = true;
+            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, &summary);
+    summary.print(listFilenames, failOnResultType);
+
+    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 < kNumResultTypes; i++) {
+        if (failOnResultType[i]) {
+            num_failing_results += summary.fResultsOfType[i].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/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..83f115c
--- /dev/null
+++ b/tools/skimage_main.cpp
@@ -0,0 +1,111 @@
+
+/*
+ * Copyright 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/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/run.sh b/tools/tests/run.sh
new file mode 100755
index 0000000..7f00ced
--- /dev/null
+++ b/tools/tests/run.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Tests for our tools.
+# TODO: for now, it only tests skdiff
+# TODO: for now, assumes that it is being run from .../trunk
+
+# 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 {
+  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 {
+  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 DifferentOther --failonresult Unknown --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..38575a5
--- /dev/null
+++ b/tools/tests/skdiff/identical-bits-or-pixels/output-expected/stdout
@@ -0,0 +1,16 @@
+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 contain different bits and are not parsable images
+[_] 0 file pairs missing from comparisonDir
+[_] 0 file pairs missing from baseDir
+[_] 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..8e27568
--- /dev/null
+++ b/tools/tests/skdiff/identical-bits/output-expected/stdout
@@ -0,0 +1,16 @@
+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 contain different bits and are not parsable images
+[_] 0 file pairs missing from comparisonDir
+[_] 0 file pairs missing from baseDir
+[_] 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..ed321e2
--- /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 images 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="missing-files/missing-from-baseDir.png" checked="yes"></td><td><b>missing-files/missing-from-baseDir.png</b><br>Missing from baseDir</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>Missing from baseDir</td><td>N/A</td><td>N/A</td><td>N/A</td><td>N/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>Missing from comparisonDir</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>Missing from comparisonDir</td><td>N/A</td><td>N/A</td><td>N/A</td><td>N/A</td></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>Files differ; unable to parse one or both files</td><td>N/A</td><td>N/A</td><td>N/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>Average color mismatch 89<br>Max color mismatch 239</td><td><a href="different-bits_very-different-pixels-same-size-white.png"><img src="different-bits_very-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_very-different-pixels-same-size-diff.png"><img src="different-bits_very-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td></tr>
+<tr>
+<td><input type="checkbox" name="different-bits/slightly-different-pixels-same-size.png" checked="yes"></td><td><b>different-bits/slightly-different-pixels-same-size.png</b><br>      0.6630% of pixels differ
+  (      0.1904% weighted)<br>(2164 pixels)<br>Average color mismatch 0<br>Max color mismatch 213</td><td><a href="different-bits_slightly-different-pixels-same-size-white.png"><img src="different-bits_slightly-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_slightly-different-pixels-same-size-diff.png"><img src="different-bits_slightly-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td></tr>
+</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..3208fc4
--- /dev/null
+++ b/tools/tests/skdiff/test1/output-expected/stdout
@@ -0,0 +1,23 @@
+ERROR: no codec found for basePath <tools/tests/skdiff/baseDir/different-bits/different-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>
+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/]
+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
+[_] 1 file pairs contain different bits and are not parsable images
+[_] 2 file pairs missing from comparisonDir
+[_] 2 file pairs missing from baseDir
+[_] 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..97724df
--- /dev/null
+++ b/tools/tests/skdiff/test2/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/skdiff --failonresult DifferentPixels --failonresult DifferentSizes --failonresult DifferentOther --failonresult Unknown --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..ef198e7
--- /dev/null
+++ b/tools/tests/skdiff/test2/output-expected/stdout
@@ -0,0 +1,19 @@
+ERROR: no codec found for basePath <tools/tests/skdiff/baseDir/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 
+[*] 1 file pairs contain different bits and are not parsable images: different-bits/different-bits-unknown-format.xyz 
+[_] 2 file pairs missing from comparisonDir: missing-files/missing-from-comparisonDir.png missing-files/missing-from-comparisonDir.xyz 
+[_] 2 file pairs missing from baseDir: 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
diff --git a/trybots_to_run/README.txt b/trybots_to_run/README.txt
new file mode 100644
index 0000000..b16d1b4
--- /dev/null
+++ b/trybots_to_run/README.txt
@@ -0,0 +1,8 @@
+This directory contains a few sets of trybots that are appropriate to run in various circumstances.
+See:
+ - https://sites.google.com/site/skiadocs/developer-documentation/managing-chrome-s-use-of-skia/how-to-run-skia-changes-through-chrome-trybots
+ - https://sites.google.com/site/skiadocs/developer-documentation/managing-chrome-s-use-of-skia/how-to-roll-skia-deps-within-the-chrome-repo-skia-gardening
+ - http://build.chromium.org/p/tryserver.chromium/waterfall
+
+depsroll: these are the trybots we should run before committing a Skia DEPS roll into Chrome
+layout: a smaller set to exercise most layout tests
diff --git a/trybots_to_run/depsroll b/trybots_to_run/depsroll
new file mode 100644
index 0000000..bd3757d
--- /dev/null
+++ b/trybots_to_run/depsroll
@@ -0,0 +1 @@
+android,cros_daisy,linux,linux_asan,linux_chromeos,linux_chromeos_asan,linux_gpu,linux_heapcheck,linux_layout,linux_layout_rel,mac,mac_asan,mac_gpu,mac_layout,mac_layout_rel,win,win_gpu,win_layout,win_layout_rel
\ No newline at end of file
diff --git a/trybots_to_run/layout b/trybots_to_run/layout
new file mode 100644
index 0000000..29afb80
--- /dev/null
+++ b/trybots_to_run/layout
@@ -0,0 +1 @@
+linux_gpu,linux_layout,linux_layout_rel,mac_gpu,mac_layout,mac_layout_rel,win_gpu,win_layout,win_layout_rel
\ No newline at end of file
diff --git a/whitespace.txt b/whitespace.txt
new file mode 100644
index 0000000..8ac0935
--- /dev/null
+++ b/whitespace.txt
@@ -0,0 +1,54 @@
+You can modify this file to create no-op changelists.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+